From f231edaa6d008508f53404da0f6158cdb5bb629f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 09:23:25 +0100 Subject: [PATCH 001/142] Update composer.json for #1045 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ed4265d187..4308b846be 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,7 @@ "laravel/framework": "5.5.*", "laravelcollective/html": "^5.4", "league/commonmark": "0.15.*", - "league/csv": "8.*", + "league/csv": "9.*", "pragmarx/google2fa": "1.*", "rcrowe/twigbridge": "0.9.*", "rmccue/requests": "1.*", From 0774258516e42533996c297cbb436fd89a70ce85 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 12:08:24 +0100 Subject: [PATCH 002/142] Exceptions when class does not exist. --- app/Http/Controllers/Import/BankController.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Import/BankController.php b/app/Http/Controllers/Import/BankController.php index 9358481970..fe4e6790ce 100644 --- a/app/Http/Controllers/Import/BankController.php +++ b/app/Http/Controllers/Import/BankController.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Import; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Support\Import\Information\InformationInterface; use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface; @@ -43,6 +44,9 @@ class BankController extends Controller public function form(string $bank) { $class = config(sprintf('firefly.import_pre.%s', $bank)); + if(!class_exists($class)) { + throw new FireflyException(sprintf('Cannot find class %s', $class)); + } /** @var PrerequisitesInterface $object */ $object = app($class); $object->setUser(auth()->user()); @@ -72,6 +76,9 @@ class BankController extends Controller public function postForm(Request $request, string $bank) { $class = config(sprintf('firefly.import_pre.%s', $bank)); + if(!class_exists($class)) { + throw new FireflyException(sprintf('Cannot find class %s', $class)); + } /** @var PrerequisitesInterface $object */ $object = app($class); $object->setUser(auth()->user()); @@ -110,6 +117,9 @@ class BankController extends Controller { Log::debug(sprintf('Now in postPrerequisites for %s', $bank)); $class = config(sprintf('firefly.import_pre.%s', $bank)); + if(!class_exists($class)) { + throw new FireflyException(sprintf('Cannot find class %s', $class)); + } /** @var PrerequisitesInterface $object */ $object = app($class); $object->setUser(auth()->user()); @@ -142,13 +152,17 @@ class BankController extends Controller public function prerequisites(string $bank) { $class = config(sprintf('firefly.import_pre.%s', $bank)); + if(!class_exists($class)) { + throw new FireflyException(sprintf('Cannot find class %s', $class)); + } /** @var PrerequisitesInterface $object */ $object = app($class); $object->setUser(auth()->user()); if ($object->hasPrerequisites()) { $view = $object->getView(); - $parameters = $object->getViewParameters(); + $parameters = ['title' => strval(trans('firefly.import_index_title')),'mainTitleIcon' => 'fa-archive']; + $parameters = $object->getViewParameters() + $parameters; return view($view, $parameters); } From aa9500f5ad5354bae5cb81d252b359965e363cf6 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 12:23:28 +0100 Subject: [PATCH 003/142] Initial code to get providers from Spectre. --- app/Jobs/GetSpectreProviders.php | 77 ++++ app/Models/SpectreProvider.php | 32 ++ app/Services/Bunq/Request/BunqRequest.php | 3 + .../Spectre/Request/ListProvidersRequest.php | 80 ++++ .../Spectre/Request/SpectreRequest.php | 378 ++++++++++++++++++ .../Prerequisites/SpectrePrerequisites.php | 229 +++++++++++ config/firefly.php | 44 +- .../2017_12_09_111046_changes_for_spectre.php | 47 +++ public/images/logos/bunq.png | Bin 1605 -> 3292 bytes public/images/logos/csv.png | Bin 0 -> 3515 bytes public/images/logos/plaid.png | Bin 0 -> 8326 bytes public/images/logos/spectre.png | Bin 0 -> 3446 bytes resources/lang/en_US/bank.php | 10 +- resources/lang/en_US/form.php | 4 + resources/views/import/index.twig | 29 +- .../views/import/spectre/prerequisites.twig | 58 +++ 16 files changed, 964 insertions(+), 27 deletions(-) create mode 100644 app/Jobs/GetSpectreProviders.php create mode 100644 app/Models/SpectreProvider.php create mode 100644 app/Services/Spectre/Request/ListProvidersRequest.php create mode 100644 app/Services/Spectre/Request/SpectreRequest.php create mode 100644 app/Support/Import/Prerequisites/SpectrePrerequisites.php create mode 100644 database/migrations/2017_12_09_111046_changes_for_spectre.php create mode 100644 public/images/logos/csv.png create mode 100644 public/images/logos/plaid.png create mode 100644 public/images/logos/spectre.png create mode 100644 resources/views/import/spectre/prerequisites.twig diff --git a/app/Jobs/GetSpectreProviders.php b/app/Jobs/GetSpectreProviders.php new file mode 100644 index 0000000000..0184d6ce4f --- /dev/null +++ b/app/Jobs/GetSpectreProviders.php @@ -0,0 +1,77 @@ +user = $user; + Log::debug('Constructed job GetSpectreProviders'); + } + + /** + * Execute the job. + */ + public function handle() + { + /** @var Configuration $configValue */ + $configValue = app('fireflyconfig')->get('spectre_provider_download', 0); + $now = time(); + if ($now - intval($configValue->data) < 86400) { + Log::debug(sprintf('Difference is %d, so will NOT execute job.', ($now - intval($configValue->data)))); + + return; + } + Log::debug(sprintf('Difference is %d, so will execute job.', ($now - intval($configValue->data)))); + + // get user + + // fire away! + $request = new ListProvidersRequest($this->user); + $request->call(); + + // store all providers: + $providers = $request->getProviders(); + foreach ($providers as $provider) { + // find provider? + $dbProvider = SpectreProvider::where('spectre_id', $provider['id'])->first(); + if (is_null($dbProvider)) { + $dbProvider = new SpectreProvider; + } + // update fields: + $dbProvider->spectre_id = $provider['id']; + $dbProvider->code = $provider['code']; + $dbProvider->mode = $provider['mode']; + $dbProvider->status = $provider['status']; + $dbProvider->interactive = $provider['interactive'] === 1; + $dbProvider->automatic_fetch = $provider['automatic_fetch'] === 1; + $dbProvider->country_code = $provider['country_code']; + $dbProvider->data = $provider; + $dbProvider->save(); + Log::debug(sprintf('Stored provider #%d under ID #%d', $provider['id'], $dbProvider->id)); + } + + app('fireflyconfig')->set('spectre_provider_download', time()); + + return; + } +} diff --git a/app/Models/SpectreProvider.php b/app/Models/SpectreProvider.php new file mode 100644 index 0000000000..4cdf9e9d55 --- /dev/null +++ b/app/Models/SpectreProvider.php @@ -0,0 +1,32 @@ + 'int', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'interactive' => 'boolean', + 'automatic_fetch' => 'boolean', + 'data' => 'array', + ]; + + protected $fillable = ['spectre_id', 'code', 'mode', 'name', 'status', 'interactive', 'automatic_fetch', 'country_code', 'data']; + +} \ No newline at end of file diff --git a/app/Services/Bunq/Request/BunqRequest.php b/app/Services/Bunq/Request/BunqRequest.php index dcc4a23729..ca73d0443c 100644 --- a/app/Services/Bunq/Request/BunqRequest.php +++ b/app/Services/Bunq/Request/BunqRequest.php @@ -160,6 +160,9 @@ abstract class BunqRequest return $result; } + /** + * @return array + */ protected function getDefaultHeaders(): array { $userAgent = sprintf('FireflyIII v%s', config('firefly.version')); diff --git a/app/Services/Spectre/Request/ListProvidersRequest.php b/app/Services/Spectre/Request/ListProvidersRequest.php new file mode 100644 index 0000000000..a4abd1ad2c --- /dev/null +++ b/app/Services/Spectre/Request/ListProvidersRequest.php @@ -0,0 +1,80 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Services\Spectre\Request; + +use Log; + +/** + * Class ListUserRequest. + */ +class ListProvidersRequest extends SpectreRequest +{ + protected $providers = []; + + /** + * + */ + public function call(): void + { + $hasNextPage = true; + $nextId = 0; + while ($hasNextPage) { + Log::debug(sprintf('Now calling for next_id %d', $nextId)); + $parameters = ['include_fake_providers' => 'true', 'include_provider_fields' => 'true', 'from_id' => $nextId]; + $uri = '/api/v3/providers?' . http_build_query($parameters); + $response = $this->sendSignedSpectreGet($uri, []); + + // count entries: + Log::debug(sprintf('Found %d entries in data-array', count($response['data']))); + + // extract next ID + $hasNextPage = false; + if (isset($response['meta']['next_id']) && intval($response['meta']['next_id']) > $nextId) { + $hasNextPage = true; + $nextId = $response['meta']['next_id']; + Log::debug(sprintf('Next ID is now %d.', $nextId)); + } else { + Log::debug('No next page.'); + } + + // store providers: + foreach ($response['data'] as $providerArray) { + $providerId = $providerArray['id']; + $this->providers[$providerId] = $providerArray; + Log::debug(sprintf('Stored provider #%d', $providerId)); + } + } + + return; + } + + /** + * @return array + */ + public function getProviders(): array + { + return $this->providers; + } + + +} diff --git a/app/Services/Spectre/Request/SpectreRequest.php b/app/Services/Spectre/Request/SpectreRequest.php new file mode 100644 index 0000000000..a7ce46f4d5 --- /dev/null +++ b/app/Services/Spectre/Request/SpectreRequest.php @@ -0,0 +1,378 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Services\Spectre\Request; + +use Exception; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\User; +use Log; +use Requests; +use Requests_Exception; + +//use FireflyIII\Services\Bunq\Object\ServerPublicKey; + +/** + * Class BunqRequest. + */ +abstract class SpectreRequest +{ + /** @var string */ + protected $clientId = ''; + protected $expiresAt = 0; + /** @var ServerPublicKey */ + protected $serverPublicKey; + /** @var string */ + protected $serviceSecret = ''; + /** @var string */ + private $privateKey = ''; + /** @var string */ + private $server = ''; + /** @var User */ + private $user; + + /** + * SpectreRequest constructor. + */ + public function __construct(User $user) + { + $this->user = $user; + $this->server = config('firefly.spectre.server'); + $this->expiresAt = time() + 180; + $privateKey = app('preferences')->get('spectre_private_key', null); + $this->privateKey = $privateKey->data; + + // set client ID + $clientId = app('preferences')->get('spectre_client_id', null); + $this->clientId = $clientId->data; + + // set service secret + $serviceSecret = app('preferences')->get('spectre_service_secret', null); + $this->serviceSecret = $serviceSecret->data; + } + + /** + * + */ + abstract public function call(): void; + + /** + * @return string + */ + public function getClientId(): string + { + return $this->clientId; + } + + /** + * @param string $clientId + */ + public function setClientId(string $clientId): void + { + $this->clientId = $clientId; + } + + /** + * @return string + */ + public function getServer(): string + { + return $this->server; + } + + /** + * @return ServerPublicKey + */ + public function getServerPublicKey(): ServerPublicKey + { + return $this->serverPublicKey; + } + + /** + * @param ServerPublicKey $serverPublicKey + */ + public function setServerPublicKey(ServerPublicKey $serverPublicKey) + { + $this->serverPublicKey = $serverPublicKey; + } + + /** + * @return string + */ + public function getServiceSecret(): string + { + return $this->serviceSecret; + } + + /** + * @param string $serviceSecret + */ + public function setServiceSecret(string $serviceSecret): void + { + $this->serviceSecret = $serviceSecret; + } + + /** + * @param string $privateKey + */ + public function setPrivateKey(string $privateKey) + { + $this->privateKey = $privateKey; + } + + /** + * @param string $secret + */ + public function setSecret(string $secret) + { + $this->secret = $secret; + } + + /** + * @param string $method + * @param string $uri + * @param string $data + * + * @return string + * + * @throws FireflyException + */ + protected function generateSignature(string $method, string $uri, string $data): string + { + if (0 === strlen($this->privateKey)) { + throw new FireflyException('No private key present.'); + } + if ('get' === strtolower($method) || 'delete' === strtolower($method)) { + $data = ''; + } + // base64(sha1_signature(private_key, "Expires-at|request_method|original_url|post_body|md5_of_uploaded_file|"))) + // Prepare the signature + $toSign = $this->expiresAt . '|' . strtoupper($method) . '|' . $uri . '|' . $data . ''; // no file so no content there. + Log::debug(sprintf('String to sign: %s', $toSign)); + $signature = ''; + + // Sign the data + openssl_sign($toSign, $signature, $this->privateKey, OPENSSL_ALGO_SHA1); + $signature = base64_encode($signature); + + return $signature; + } + + /** + * @return array + */ + protected function getDefaultHeaders(): array + { + $userAgent = sprintf('FireflyIII v%s', config('firefly.version')); + + return [ + 'Client-Id' => $this->getClientId(), + 'Service-Secret' => $this->getServiceSecret(), + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + 'Cache-Control' => 'no-cache', + 'User-Agent' => $userAgent, + 'Expires-at' => $this->expiresAt, + ]; + } + + /** + * @param string $uri + * @param array $headers + * + * @return array + * + * @throws Exception + */ + protected function sendSignedBunqDelete(string $uri, array $headers): array + { + if (0 === strlen($this->server)) { + throw new FireflyException('No bunq server defined'); + } + + $fullUri = $this->server . $uri; + $signature = $this->generateSignature('delete', $uri, $headers, ''); + $headers['X-Bunq-Client-Signature'] = $signature; + try { + $response = Requests::delete($fullUri, $headers); + } catch (Requests_Exception $e) { + return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]]; + } + + $body = $response->body; + $array = json_decode($body, true); + $responseHeaders = $response->headers->getAll(); + $statusCode = intval($response->status_code); + $array['ResponseHeaders'] = $responseHeaders; + $array['ResponseStatusCode'] = $statusCode; + + Log::debug(sprintf('Response to DELETE %s is %s', $fullUri, $body)); + if ($this->isErrorResponse($array)) { + $this->throwResponseError($array); + } + + if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) { + throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri)); + } + + return $array; + } + + /** + * @param string $uri + * @param array $data + * @param array $headers + * + * @return array + * + * @throws Exception + */ + protected function sendSignedBunqPost(string $uri, array $data, array $headers): array + { + $body = json_encode($data); + $fullUri = $this->server . $uri; + $signature = $this->generateSignature('post', $uri, $headers, $body); + $headers['X-Bunq-Client-Signature'] = $signature; + try { + $response = Requests::post($fullUri, $headers, $body); + } catch (Requests_Exception $e) { + return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]]; + } + + $body = $response->body; + $array = json_decode($body, true); + $responseHeaders = $response->headers->getAll(); + $statusCode = intval($response->status_code); + $array['ResponseHeaders'] = $responseHeaders; + $array['ResponseStatusCode'] = $statusCode; + + if ($this->isErrorResponse($array)) { + $this->throwResponseError($array); + } + + if (!$this->verifyServerSignature($body, $responseHeaders, $statusCode)) { + throw new FireflyException(sprintf('Could not verify signature for request to "%s"', $uri)); + } + + return $array; + } + + /** + * @param string $uri + * @param array $data + * @param array $headers + * + * @return array + * + * @throws Exception + */ + protected function sendSignedSpectreGet(string $uri, array $data): array + { + if (0 === strlen($this->server)) { + throw new FireflyException('No Spectre server defined'); + } + + $headers = $this->getDefaultHeaders(); + $body = json_encode($data); + $fullUri = $this->server . $uri; + $signature = $this->generateSignature('get', $fullUri, $body); + $headers['Signature'] = $signature; + + Log::debug('Final headers for spectre signed get request:', $headers); + try { + $response = Requests::get($fullUri, $headers); + } catch (Requests_Exception $e) { + throw new FireflyException(sprintf('Request Exception: %s', $e->getMessage())); + } + $statusCode = intval($response->status_code); + + if ($statusCode !== 200) { + throw new FireflyException(sprintf('Status code %d: %s', $statusCode, $response->body)); + } + + $body = $response->body; + $array = json_decode($body, true); + $responseHeaders = $response->headers->getAll(); + $array['ResponseHeaders'] = $responseHeaders; + $array['ResponseStatusCode'] = $statusCode; + + return $array; + } + + /** + * @param string $uri + * @param array $headers + * + * @return array + */ + protected function sendUnsignedBunqDelete(string $uri, array $headers): array + { + $fullUri = $this->server . $uri; + try { + $response = Requests::delete($fullUri, $headers); + } catch (Requests_Exception $e) { + return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]]; + } + $body = $response->body; + $array = json_decode($body, true); + $responseHeaders = $response->headers->getAll(); + $statusCode = $response->status_code; + $array['ResponseHeaders'] = $responseHeaders; + $array['ResponseStatusCode'] = $statusCode; + + if ($this->isErrorResponse($array)) { + $this->throwResponseError($array); + } + + return $array; + } + + /** + * @param string $uri + * @param array $data + * @param array $headers + * + * @return array + */ + protected function sendUnsignedBunqPost(string $uri, array $data, array $headers): array + { + $body = json_encode($data); + $fullUri = $this->server . $uri; + try { + $response = Requests::post($fullUri, $headers, $body); + } catch (Requests_Exception $e) { + return ['Error' => [0 => ['error_description' => $e->getMessage(), 'error_description_translated' => $e->getMessage()]]]; + } + $body = $response->body; + $array = json_decode($body, true); + $responseHeaders = $response->headers->getAll(); + $statusCode = $response->status_code; + $array['ResponseHeaders'] = $responseHeaders; + $array['ResponseStatusCode'] = $statusCode; + + if ($this->isErrorResponse($array)) { + $this->throwResponseError($array); + } + + return $array; + } +} diff --git a/app/Support/Import/Prerequisites/SpectrePrerequisites.php b/app/Support/Import/Prerequisites/SpectrePrerequisites.php new file mode 100644 index 0000000000..a55c0f87b6 --- /dev/null +++ b/app/Support/Import/Prerequisites/SpectrePrerequisites.php @@ -0,0 +1,229 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Support\Import\Prerequisites; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Jobs\GetSpectreProviders; +use FireflyIII\Models\Configuration; +use FireflyIII\Models\Preference; +use FireflyIII\User; +use Illuminate\Http\Request; +use Illuminate\Support\MessageBag; +use Log; +use Preferences; +use Requests; +use Requests_Exception; + +/** + * This class contains all the routines necessary to connect to Bunq. + */ +class SpectrePrerequisites implements PrerequisitesInterface +{ + /** @var User */ + private $user; + + /** + * Returns view name that allows user to fill in prerequisites. Currently asks for the API key. + * + * @return string + */ + public function getView(): string + { + return 'import.spectre.prerequisites'; + } + + /** + * Returns any values required for the prerequisites-view. + * + * @return array + */ + public function getViewParameters(): array + { + $publicKey = $this->getPublicKey(); + $subTitle = strval(trans('bank.spectre_title')); + $subTitleIcon = 'fa-archive'; + + return compact('publicKey', 'subTitle', 'subTitleIcon'); + } + + /** + * Returns if this import method has any special prerequisites such as config + * variables or other things. The only thing we verify is the presence of the API key. Everything else + * tumbles into place: no installation token? Will be requested. No device server? Will be created. Etc. + * + * @return bool + */ + public function hasPrerequisites(): bool + { + $values = [ + Preferences::getForUser($this->user, 'spectre_client_id', false), + Preferences::getForUser($this->user, 'spectre_app_secret', false), + Preferences::getForUser($this->user, 'spectre_service_secret', false), + ]; + /** @var Preference $value */ + foreach ($values as $value) { + if (false === $value->data || null === $value->data) { + Log::info(sprintf('Config var "%s" is missing.', $value->name)); + + return true; + } + } + Log::debug('All prerequisites are here!'); + + // at this point, check if all providers are present. Providers are shared amongst + // users in a multi-user environment. + GetSpectreProviders::dispatch($this->user); + + return false; + } + + /** + * Set the user for this Prerequisites-routine. Class is expected to implement and save this. + * + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + + return; + } + + /** + * This method responds to the user's submission of an API key. It tries to register this instance as a new Firefly III device. + * If this fails, the error is returned in a message bag and the user is notified (this is fairly friendly). + * + * @param Request $request + * + * @return MessageBag + */ + public function storePrerequisites(Request $request): MessageBag + { + Log::debug('Storing Spectre API keys..'); + Preferences::setForUser($this->user, 'spectre_client_id', $request->get('client_id')); + Preferences::setForUser($this->user, 'spectre_app_secret', $request->get('app_secret')); + Preferences::setForUser($this->user, 'spectre_service_secret', $request->get('service_secret')); + Log::debug('Done!'); + + return new MessageBag; + } + + /** + * 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 + * is stored encrypted in the database so it's something. + */ + private function createKeyPair(): void + { + Log::debug('Generate new Spectre key pair for user.'); + $keyConfig = [ + 'digest_alg' => 'sha512', + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ]; + // Create the private and public key + $res = openssl_pkey_new($keyConfig); + + // Extract the private key from $res to $privKey + $privKey = ''; + openssl_pkey_export($res, $privKey); + + // Extract the public key from $res to $pubKey + $pubKey = openssl_pkey_get_details($res); + + Preferences::setForUser($this->user, 'spectre_private_key', $privKey); + Preferences::setForUser($this->user, 'spectre_public_key', $pubKey['key']); + Log::debug('Created key pair'); + + return; + } + + /** + * Get the private key from the users preferences. + * + * @return string + */ + private function getPrivateKey(): string + { + Log::debug('get private key'); + $preference = Preferences::getForUser($this->user, 'spectre_private_key', null); + if (null === $preference) { + Log::debug('private key is null'); + // create key pair + $this->createKeyPair(); + } + $preference = Preferences::getForUser($this->user, 'spectre_private_key', null); + Log::debug('Return private key for user'); + + return $preference->data; + } + + /** + * Get a public key from the users preferences. + * + * @return string + */ + private function getPublicKey(): string + { + Log::debug('get public key'); + $preference = Preferences::getForUser($this->user, 'spectre_public_key', null); + if (null === $preference) { + Log::debug('public key is null'); + // create key pair + $this->createKeyPair(); + } + $preference = Preferences::getForUser($this->user, 'spectre_public_key', null); + Log::debug('Return public key for user'); + + return $preference->data; + } + + /** + * Request users server remote IP. Let's assume this value will not change any time soon. + * + * @return string + * + * @throws FireflyException + */ + private function getRemoteIp(): string + { + $preference = Preferences::getForUser($this->user, 'external_ip', null); + if (null === $preference) { + try { + $response = Requests::get('https://api.ipify.org'); + } catch (Requests_Exception $e) { + throw new FireflyException(sprintf('Could not retrieve external IP: %s', $e->getMessage())); + } + if (200 !== $response->status_code) { + throw new FireflyException(sprintf('Could not retrieve external IP: %d %s', $response->status_code, $response->body)); + } + $serverIp = $response->body; + Preferences::setForUser($this->user, 'external_ip', $serverIp); + + return $serverIp; + } + + return $preference->data; + } + +} diff --git a/config/firefly.php b/config/firefly.php index 744d82cfc3..b4219692d4 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -28,39 +28,49 @@ declare(strict_types=1); */ return [ - 'configuration' => [ + 'configuration' => [ 'single_user_mode' => true, 'is_demo_site' => false, ], - 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), - 'version' => '4.6.11.1', - 'maxUploadSize' => 15242880, - 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf','text/plain'], - 'list_length' => 10, - 'export_formats' => [ + 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), + 'version' => '4.6.11.1', + 'maxUploadSize' => 15242880, + 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf', 'text/plain'], + 'list_length' => 10, + 'export_formats' => [ 'csv' => 'FireflyIII\Export\Exporter\CsvExporter', ], - 'import_formats' => [ + 'import_formats' => [ 'csv' => 'FireflyIII\Import\Configurator\CsvConfigurator', ], - 'import_configurators' => [ + 'import_configurators' => [ 'csv' => 'FireflyIII\Import\Configurator\CsvConfigurator', ], - 'import_processors' => [ + 'import_processors' => [ 'csv' => 'FireflyIII\Import\FileProcessor\CsvProcessor', ], - 'import_pre' => [ - 'bunq' => 'FireflyIII\Support\Import\Prerequisites\BunqPrerequisites', + 'import_pre' => [ + 'bunq' => 'FireflyIII\Support\Import\Prerequisites\BunqPrerequisites', + 'spectre' => 'FireflyIII\Support\Import\Prerequisites\SpectrePrerequisites', + 'plaid' => 'FireflyIII\Support\Import\Prerequisites\PlairPrerequisites', ], - 'import_info' => [ - 'bunq' => 'FireflyIII\Support\Import\Information\BunqInformation', + 'import_info' => [ + 'bunq' => 'FireflyIII\Support\Import\Information\BunqInformation', + 'spectre' => 'FireflyIII\Support\Import\Information\SpectreInformation', + 'plaid' => 'FireflyIII\Support\Import\Information\PlaidInformation', ], - 'import_transactions' => [ - 'bunq' => 'FireflyIII\Support\Import\Transactions\BunqTransactions', + 'import_transactions' => [ + 'bunq' => 'FireflyIII\Support\Import\Transactions\BunqTransactions', + 'spectre' => 'FireflyIII\Support\Import\Transactions\SpectreTransactions', + 'plaid' => 'FireflyIII\Support\Import\Transactions\PlaidTransactions', ], - 'bunq' => [ + 'bunq' => [ 'server' => 'https://sandbox.public.api.bunq.com', ], + 'spectre' => [ + 'server' => 'https://www.saltedge.com', + ], + 'default_export_format' => 'csv', 'default_import_format' => 'csv', 'bill_periods' => ['weekly', 'monthly', 'quarterly', 'half-year', 'yearly'], diff --git a/database/migrations/2017_12_09_111046_changes_for_spectre.php b/database/migrations/2017_12_09_111046_changes_for_spectre.php new file mode 100644 index 0000000000..ff25ce1aef --- /dev/null +++ b/database/migrations/2017_12_09_111046_changes_for_spectre.php @@ -0,0 +1,47 @@ +increments('id'); + $table->timestamps(); + $table->softDeletes(); + //'spectre_id', 'code', 'mode', 'name', 'status', 'interactive', 'automatic_fetch', 'country_code', 'data' + $table->integer('spectre_id', false, true); + $table->string('code', 100); + $table->string('mode', 20); + $table->string('status', 20); + $table->boolean('interactive')->default(0); + $table->boolean('automatic_fetch')->default(0); + $table->string('country_code', 3); + $table->text('data'); + } + ); + } + } +} diff --git a/public/images/logos/bunq.png b/public/images/logos/bunq.png index c1223bc4fc2ad3cbc6fda0603369137f43bc60fd..fad92342b79678a336fa1396060bc0d3f44d4cad 100644 GIT binary patch delta 2453 zcmZ`*dpOgJ8#kBCq7-r$lKZhuF2g9AyWfb(k`80bT$mlJp>pUThy2`0 zC}B$dLavQMWNx|Jil6hxd7ksnc|XtlzMtpwd7t-r|9rbeCuLQBb3xmRLF6EOe0*Z| zb|}}wj{GaZM-Hp$k1CG?1G&tm}L+&FW#MBiK2N^NV z1+IG;!H`e_J>a^jO8}h^d<7M7xQH4MLb8A;{~buYpDFdGZy>>s_7C;YMN;tBP2C8g z(0|Ct!+ZXoW>$bsA2)^To5B#v`mn!<35+=kGC66Cgwk)2O)pXiI9dn=6A}{4Y!xs9 z(yqow@$rG(>`|5&qR{eH1{pIfdp?~vYO5q}#|M8~3GyVK$S<>#wMvLPCMs8IADpTE z<)P2BM?$BoI>xo#hAi{%qBwjI_a~3y<6X_%{FM>P1fSP-;A+8$1VMp5b-Z!w&dT%o z2QlFm%>>tkvop+uKdna1m9`&)$jUwBv%x%^bno45Z5z@g~yZ8j@#eR5@t?Ddne- zb#{+#Y4bW~xhR?4Q(F4HuG(Y|MZ$6-nA!;~*5g!1jbMupA@$Y9K|vd*yM`F)zDM$R zEIgYOmb#tetV0S}L5LW-yC$8W1k=wKQDEUebEUZ78i76b-SF5lQ0&pkD~A4Y8obIL zr-9UmEA)=*7xd&JQTVSTKEQLo$6rJShWLG3TNehyeZQ+!!^kfM-7F)?t1fe&T-_VOo)ev)B5gBQ4_s%G{o!e_Rh^zBN<+wCaF zPl?QgKJGVC(jp;7F3IC5=vT8TUAOX34rxU0CY$0XZzfo!2+;0EplgW17|-Io zX8Q*lQ0iQOrmxTM=Yn0eYbDc-pbCNc$$~;^M^1?jPt3@Wj~9s6SyD9Ze-i5$vy-Rs zw+oRU?(I>H-8hJO%@l_A`2)&Sf5*k*b~S{s&^i92oji|$6|nMR*HBf|N57f&Yu@;J zujNmm&abh*1qod#2RNxcUr(hspP}w+9{+j!Xk63Oi>F&`M(%4`hIrGB;fnJWokawh zyZ184itp!c3zH++sIp@P<=+;=Ad@~P8jzlWHeS-EeLAA?C{mhQ?OSDbtb)Y&tz7g;i0z!}{j)b@wyAjd$zb!m3= z&a{UA&gxue$2Cunoz<|A(VCfd|00pP%b44r_XnQH`1XP$ECw_SFUM)Lk29{V3Loq#pr|?Ybr4g7W zX-@A-b-{<+n)6@$o}o|rI8~`cbr=IunAX{bP1{P2kaQ0`1*fKd)^U<$cMX#QHxQJ# z)zsntX!_L-@y#ul?Efk##Od%C(DDqA*;|aIX5c7a%>c(!g{ghx#q>5Pn6WFKMFNpA zm)7V)_42h`Xd55e+I!fbS@vED**so=F9xQnzmy!8zhHJ(Ie{(-ll+9Q@nCj%s%th7 zmxGqL*X(@cJ|+1il&r7+5##iFKY@;Y@nrvgQ|D-gj|&~#KCu&e(=+(Z{+pJAIV;#^ zv<}7!AZFum55=cb#G7tI2J0%wRuUGdj>QE{q|*=Z+nN<>=(I@J!Msray2#P9_>s|% z!l#!UH{@w3w|dB2vy5zClp3hvqvedv_KN`p>@OKmTzkXi_cP0PI0^VjYr#%J!dyHYqtb3{^po4lmz?e{u=pe ziu+icmjwxFV(f%SjwuMw2W6G2;T*eILwf9uAMe;Lm?l;K|J=!?d!oY%6@Ru|ZjQua z3yKXZ1nM-JAvo2O@a8vM2ilD;uLGyI6GVO_w&d zd{=DYyGX&o_6XNDhR7PP4O4l$2rMoa4l6;DoMaiI?pFG~os$MFZILm*x*-Ks@Cbpb zf@4@TNpKssk`q@k)YU@u0O+cw`u{zuu|5X=`LECNPa1XzIohL4wKnJ;r;>C3h8LlwC$iS|daD8T@L5`5=O!&Y)yRy{xz}A<`ajO>ousAJdh%8!CqO4J!%*Mb z@O!Ck4;pdgZq`DQc{*oNybvyczF$2yqAc;#ODU~yrDrj%#Fp^3Lpm0g;)+2dY-dPkwDP(2fBUR~(f|Me delta 766 zcmVDb0b7^mGlQ#r5fBrxCb^rhZI7vi7R7efYl)Fz9Q51)FQHUlgXaSvpC^XVh zLQh2}-Bws&<)1-AYhh#Ptd-8nMiRm!p}|H7quFdC35npE_+GznGKXAl7GpWT{(6Jr?uf52H(R^<)KK{y55 zL*ef?6^HOG8nV*y#x+I(`Zrd&fF`5USI~wylLht@h*ciJeh051*BTWaFi2<_qk!<| z*oTlx-^M5)`swE>9Dw$DgB@PLA}ql+ShoV|4t$0^e=@D}Z{Mk^{&DrcL)n5yU_e)L z0YdtAoUx2)Y;&+Ke>gW*sZ8beY{5@>0B*wx>_V=WROs+NT!#bUIcF&H1HsA-u3b;7$IK> wCuTnS^T@n`+G?K6oIrVtYn!Esx!kqZUq|$!f?nC_Pyhe`07*qoM6N<$f;yU2RR910 diff --git a/public/images/logos/csv.png b/public/images/logos/csv.png new file mode 100644 index 0000000000000000000000000000000000000000..6b717761541d39bac7ecc7f280498896a2ddfa6b GIT binary patch literal 3515 zcmZ`+2{@E%8y;)2mR(KGm~3^J86(>?i0oT-BTkyeU>Ii13^S9ZBudnu7nl8mjC zB2f&|$x;-;WGQPZ3jd5c=lHuib6wwj?|VP@{XWnAyx;p>*B57RXD%YROAr76h*(;f zII{0Z&LzOl{zrU!Nn+o)$d2a5fQl~Q6nnCrXyHx<03y>k7uW8yB`5%ZR}troqM&ST z5Ew!r1no^YfrZcliEK7|B^rV~48&5W1=gEtun_Lu`h!JthFB|smHvb6^t zCXlcoEyw`~3~V3>0)dbuZy$uCiP;Z1d!`Teqfm$lD3nU2La3S$0?8Mup{J(@g~6e4 zxH_AmPNoM@&@}ZR^4{-@{Jo9|mW(0ch!h+l2*gok6uj@{dT zg#TA04Nd%>Y_o~qy&*Z3BOI}00zQP(0+%2h#Q=`nH1sq4Z%4kX4if^2BrKWCmKkXM zkZi?%=A-^iW1tJy{Gr&2{;W7c!m+KQIfWT${NQiJe&)OXGQ(DMi-Oa9gguUi#iLAc z?3Ql&(1szQzYP2=JWRk7NKQmF2Fs~zQ?dp6nZ23D{lC&|32YI1V-Vz!6aH8XWy^lC zcWv|Ocftlkx4=l~FWE@;vw*NAc;kHNCTI%Q01ng8L1@4cFkKKF#u@0a_w6UnmI^qL z!z3)4LLfO42zY}{7lE89NE4#-UAZ;>LyClQ&e4{0_5ILpqU=*^Ajnq!{RS`)yvyiV zWWPgTmL^8detf+i9(Yf4x%KV{D&yGTf_}ELh^0p+C^NxvM|M`NbBycs;V7N6tFA7_ z^8(#S1=`4Bvjo&4rCstWxW@`~<+`fvl>^ru-YN$H4~B++e&s&c5Ej}UGk1B6VWmCu zQlC+hgnWLI+Rgm7bZTj1LuZ3ou9cPn6YiXt$h|yRyH<4aJ_MH=nTd5D|N7>Ej!wUq zE}2EXfjeK&!F%&&XShms$Lv8=U!2MSERk$K4|Q_-P59|Y8%GsBVC?GWh~Xn(Xm4%B zOj@9amuU1zx7)2o!<<;+zT<@;8D8|RG1ZPz=j$24?{7`!eLA_TU=VQaYeFbBPqrH4 z;l(p)XlvJ~Q-BPc91tbyR5^WU5L(@Ct{`0dmzJP+oPmVd0^ngE>ZX&9*xXB({TRgqjPBCSEn(Pws@a_lSysCJF^U%$Rgp5LJ3L>t9?*a5 z!SLX|NgknkF2opeH*IvKtSlkM?$B?34@I6OEdf_rR}y3fqg}ZZffv&5A#zvl5Bizp zjhpTo7%aBYUg~U$ToU`B7C5W_?7r46{)+b*xR6LL#GyD*!6VFPJsRySu{g`=0_&o< zirp`q{EoB)haMO*{1{&BxURtatyDMsC>@?kAW?2|< zb(^mI>n_~`G|GYXSBnvW=-eSvG4{2}LdyNJKP>U1sDhx$jG3oxULK7zt$DPr*+=g3)1H3iF#_RAgW{R%qei5vT$7;! z-kjxm{bZ}hwsBSixs!J*@$|55x5Y#bj_H-zR7k$z4vu*gm!_uRwjI`x_F=$THidB1 z>ahsT`25M9B+RkLd>&VzH)YEB@9a0-4ioc!%bc1o{We~FuSQ)~f7q)aoB##fU@Yg4 zv=`r8<6~Z$8fBK%D;jz%`G<@~4(?XEuApbp(TuG1rsX<9*~XHXS3FrlO;-860F3IR1N(-l76?B+@5kd=9q)E>`}hO*bYzM)niq>754G*M z?e}r~wtkCQ@;k#z3WOVW5qqQ0jT-xFAPu$Djja%09T~Dj?dLrszVowIEM#s!Orv9SU!s`z8l5%5{_c$Ql;spFxE8$cjQm?3VhG$-z zZ}y3|X;QYynaqGvD(@T?94og+DTZZdO9mF0b za;J{FrQ(WPQK=T#;5dM$eQ0N@L|aCkZ=B6O95eBjxN;m|>B~Lq>8W=`nbp#AS=Sva znKkZ8M=foIb7D$`U--|@UMjP#f9_&Ujej%z#!+b7wVk_^?bfwwUJgUN;( zx3v`CMhWICohv@Av^IP5?X%G$nK#@dtC!Q>sWW3^a_BMw~2@eUx zH+!ZTTW%xn>CagE^U0SN0=N}nz5u1gTVtpamg$JQ1EBXeS;aA~ZxzA=XC?;6hg-0_ zmD_0obtyT34gS>@iROrTCXHrN{n^)E<=x)do?42StTfDU1Ee>+4#vgcufocA18gSh zHVTL(`z32Ghb6uz=qo$gx+2T_O2095R6U2m6`Jz9irS$YX6tR%mB{k#H)d3}sea}8 zmN&WzcZpo{c~PgXNROTcFnaBT9av#rF)npws|}~#MkFv9ZE+;+7^$I+FA8c0K@V5| z`h7gfOR>3gZ<9pxAU2pau{L)NfWIiOF!!o(-rp`LQL|@_@p)7&@|aM6ZG%&GN#Vim zl^JW}MJBr5)NU`(#`GEc>+83Y3voWy3fGo*mP>|PEq;|{rGr+Jb<@`B-AsbFKpni~%q%CCTU4jn)_80uR!3BC;$T{!oMZiEs(pe(lo zRp}`Hu(Rz*dZcXjq>V{K7?!_t0XVe3*@NG5rgJ;Bk9gzCW#E~i5C^D*y-_C%@&_Qc zR2aYrkfwsO1AI{O=eZOvz1CT`W|bs6&8k4tmOr{bACVF{!+nSD#trWZukYS83@fH)&9YuS{+vhB zusJdO?EX2cle5glUM@{kiZ}xXwFDcbM#Uz4bxLqv>GT+HT$h(CiG6x<8bAIfXU8dd zoy5n105hNr36<2UiW(|7j@w)F<%2k^DvxwNhrT?@?YZ*-|42x-gjiCKE_%daAf8!u z5nli+iy3r*w^rU_p;$$!Ro3$5c;M9DrJkazVp2~ooIco)l6%6T4)!~jX~6Mg{242= z+2H3~JQv>Cr7!aiT(}RIEff2cvJ&HTbvY^pE}eTh!d_8zT^RC$4zE9yMWr80$zNvr zwvaa#Juzv8X0JCs1i2gg%Yycmt}k%wLGz1-lD)3(2vAEGVN_6Yv1dB>JU0*9x3rkv zJZ)ofv}>n^$Eh&5AYW@!RSGWVAVluCZ)%*8+0pmFBQDg_F@^l}xnmM?;u4}IS)=yN#j>L#7C3ASmO@TvYp65RtmQb`o33vCTe7i~$2%XumIn3Wjt4Qcy ztZyu$-syTiB@|X(2Q1QL8q>>f;vJd>?AI%rvyAggeL=Rp=WBEBnO$go)MZdvQO+o@ zktFUbzwg+Edq8E18g-bho}EFN_hp7;i7cO*dHED96J4Uj`J%EkwKJ(OK7QtZL?HP} literal 0 HcmV?d00001 diff --git a/public/images/logos/plaid.png b/public/images/logos/plaid.png new file mode 100644 index 0000000000000000000000000000000000000000..23187ce8aa1f2923bd3517e1416ecfb6af29df96 GIT binary patch literal 8326 zcmZ{q1ymf{vW9VY0ttlRE`!_P9z3|a2ZjL#cXtRuLU4Bo?ykW#Sa1s#d~kcoIp@B6 z-g)=-S}nWk|7!25-Md$>ULEmHNd^;*7!3vn22=K}r0TD;;O`v;>DLkXu;}#b1Pf7> z5r_FQPO|^2f$H#92Lc0wj{kdyg-K0+1p@>+<;^(#MC zcM}Izb^sgee}bHCEdM>c|3m%u{8!Av-R8ewzde6qe-z`-F$(^wC{Ph>Ze!^oY2s`l z%)!Ra%fimV!p5i0!3JdK0CIB*vi?K!?+pK-C7diwoWV}&V6dI=I~#WkJJ9bYIVXVk zPx9~nzoEag04h7#So|vQ?;;Ab3$p${+duUZV33281qAY2&;6JF@3w#HRV^T3JD1<# zHSBGig*pC0{$c(nrSosUFz>&ZzZ?GvX#XeR@5a9Yzo!BCN1e1KZ9o?Gf8@f$Cdm39 z9sdL+z;<9KH3t(ji{JJ71NocjpXxul(fM!P{LS$fD9HNzPX61S{pYgz!}@F82&4U) z(SNN$VYCj;0}&V)5)WBPF?DOi9X&U1_5Q2QYx{{~%NhJ>6pD~wEHMnJ2v|uRF}9DC z2$!-+v~Tg*xKu<_rxAl6Br#|aaB*?q-_kjw;E+>hD*6sy%?JckE(e8v#9@q3o$N-3TH%CS24SbIK{Amq|0tRiasGt)``Dd&RN? zop~2-YtNuAW-0a&R}z=jbGAx7$BM#6U9&w9TS`kRb7EQwoD(6sRQ0Khn4+#I?3Tq5 z-13@GwXd$H{AjdJ1M83Bpogq)z#zJ@`j9y;iK%FRQ~%iT99wGBZi5^7Yjt3jUxC47 z#)ME<;?!AUGfgT>(ld*(x!2goi%8Dh6IaHHFCV_lSl{o({Gd-*+_VyNy@5&FM2DxWoxdrk*T6&1&flYQ2VniRz8)tb`gzMAd=@L;K!V?gBpP zUbVZNI5aXwWqPGkcxG2~ejhniN7MR^24QGWe955;*aGU^xfJsi5xvf{7F`Z5+1UD3 z=-ho1W03(FLrPv`?3#v}K?<#TF*1g2iIw&Dlmdnud?!3fSgOgs?=O7CpnQfbgqmF( z3iK?A1tLH{>LuOYrzP`HfzIch{AuD9*EzeI`SAiVn8pZ^HzB|HeHG*Ri-c zTcbv`j8~Go(Gamp+6ir>q`)io!vaq7YUrAn{KVixiQ-IrDAcZTQfePTb&t)FK8c)N zwN&fAE9ChF^A)#Yhtc^^V@WUR%S3M2s5KS3JObDv z$ZsoXJITN7^=*D|Fwn?AN{ptm_MWxA{MJ9Z?DBC-3(GbFiQ(CJ)qbb?KIOh%`aQMZ zP!7={9wE#XDM2cCz1;IjN=E zuKQ0d2OXpwFWics2FI1B)Akz~&?ou=>rby8gsYiNx~%D{tKui6!iuHye45EcjCzV} z;01R|_`9q*T(Zg zESpWY;Cc{n%Vz&f>>JN*PmBy{>1GWnWU|1^7~_U53AijTpWBNEMjJf>*pA52$`X9B zo0GIWzZn;Xs#ub0!}_~uJvG?d{VZP1kibHd3PhJAo?5oK99Z{gea6_iu!Q&K?aVJ4 z5*J7)2#yWbr@X^w3Zk!3rdsTmW2h}<-;=8cJS@!8g;`ZTHw#gaTu!Ba46$&zAGM6? z0kCt?5TbG)>3PxpL~Rz{-WkkBXjU)ZNJp|846r27c<(dqzo_@4)=`tp zt6a(Ic;uxST35rBgK&e-`6)X)yFuB<;4nEytXtZlK90?Yn&FP^H;`kdSZj@p2D0#W zmVk-(j%ve732;F6sd$2MfQDQ!lXYydPhr4Nl@TXt0Z5J?f5vZs_vEE!@pTGIIlOj+ zcJcAT0Mo`o3q#o64d(a!f-S7}X?A)siiF~$Y@K0qo+#4oqzYdlhmoR^{YK;?A;-(N zjs?JI>`zDnO9Of?&Le#$W_h{a=Ws}cgcA3~Q|lxA5s_33z^?`e`WF*%kZ)9dJH8on zU76*;(VB}go1?An&mNOfgz_V|Wbmsn5HN!=TBP&P7k!|^`#<9kge*Ckb|=aem`e}8 z&)Ggy*Q=?Td~P!7f4J}8QuT5}A+B_epTnJODpZulzii4jy(}eDdc#^WS0|Mrh8Ku= z<;Z`2IDB?;*1dVXc=FX!E_hV!Q!%af*Lq@uMo_Pn|F$kZvin%W*2ek_oC%Y$J~CeQ zUQgdBtV=%S5H2*&VT2<|A$<@zW~pg2QkS?Ma8`;FtUy_Mv@qc1{XJQ28b$jx3rxG; znOB(6(JQ`H9Y6(@EpfeD`<&=bk~-|6X0=3GjIo5b!OS@8YtHeNmG)1Q6dul^{^N_X zo{PgTzlAj*;UBu)b#EQ#V<9({>%K9w4A7Mjzo1A)dCtd=$dm8Th9EqWk2dva01zkZ zKsVhl8*Up13-uXGV$m+i{G7+~X^n4RxUmu^6V5QWbFt}Jh9Hl^Swm-K@lZn4CH-Y| z4|emu&SYjP(!w2X_yb3%>DE1KTGixP@4}VjRqVT{Ew5=(N$$a-jXh+g<0T5bFIGX1mL};ToJFUf%_~gOc}dQa{+3>tP95r8HK8g z{PcXvR7S?lz6>TiXi(nlV=p(v_TW-rlr}2SyP-?Gw|;=ey|`i?S>H=N@oui|NF`qh za+_c28x8c`aeQx6Putb;L@{nmB^>~^%jUcoF6ykQKP`J17#sV7UQ8Yi13)_&7zeT z@jk=WuL`o5e~XOS7Y$CV9dZo3I`KIhx^TKgOTZm7fY*NaQm#4wAg`NMF&f4B8II@h zw40ILS`~@}5w*1UIOs}QyP~dR^YgoX%G^EK&Y!cvgzXd8UYaWJO!Mv+cm#G*;7z*G zJCLzcNFh9yH@Qkf3j-?`@2^3dm_e*|o%hqBa`jJUX^r7A*({%@(wwB55RCZ0Xg;6H zoCYKii6qaxZ=Kd}LlY4T9Ol5Szbtd3wCcM&ebHau*$OPnHYH?)gCn%+lA@^CTca|E ze$qKB!r33epZAM5GHe|%<>{r@MMiwIs`@xV^|-PX3x2q9>N)peHEPOodt1SKch(&! zTipbV*j;(vsJ-kW5wMJJ5^jYjw+efI!@j!jIgoDMgwS9 zKCCuxi1TrKen^@>dzv?P<^|~R-h+U_6MrcKo0$0Binxzo#r*3MF?bgi$%f~2Zt%uv8s_^@sKBa%d5D8`#+8%uA}oPg#SKh-9>BfBo%PJKlWLzVfU zO36XDQOEWu#BcS+WV3@1z1{7L&a*6B=g!D6vY@mZ?gbAD#>@d!3W=uKZ*nwQ5VN%h za2mY)_vkx2<*uZ!w+zJXw;^rKjQR7S&`6SI-|wfiZQQ+ZQg4KOM!;LFyUMFxn#B9$ zw3D1IQLFZmfq;z)&l4lIRTC(R*#n8#hn#EHwmUYz0$nxi^`8cm+9~SdA-5jP!qvXR z2QLF?xQf6^XCi(A0_|08JbeI)xynJk@Xq@LH={!mPBU`_SexDn6CZ9x@Nf-dH?{g% z3ROKDU(i#d%;K^Z{}$deYNj6=l&UN4^_T!@SM z`r4;YI4>FwqwU~~Y`RfViy}ynV6MczDCPR-i9~0T;N$0Q&D2D2rS;0SRz7%GL5$ng zm9R~)Jp;Z{h7_V-m8z^gx^lX#V1L!Uh}eWc^A)QX`?*EVx=p=sOdy>Ako_>V=>i@X z^{cliEgqk=4t)L z&AdV@Kkg`nupE4SJIW8wU0S!T_`(BkxrlnTk2V zPinM#)NGW&1>W%aLYs8)U-oe<2dlUo<${%)FgV{IveP;#Qq4qzJ^(O2m zQ|TSnem3CrFBt%u+s#M)udlN@=I)e~hU`(!ntnaWra8J46mBolh`hv59QC@|YPVZa z=gA1{gN)NYTkDg+pa^ThCGh}?zH_*5KI>@(XS^@^AZ6*x4Ns+(apfz0;l0-J zkgMorg&Fr*jlB8zzOZZOU+>@G7Rb}K@iTVy-lB@o2Lxdw`-NzS?UQVJP0dKbZkTbL z@*#hkUOZwNt`di!iirs0@Muxs+reeUaczrp^i_6=^Pv`>yx11n@0(YDX)9pT67N?41qPfvuHv(#@Qobc$Uwb)m*xE^mo=0@7=-m3S_ z`pyVzL`DV99L~-o#NYwSPoXAlVtSz zkBN_w`sL%3ctZv_Ib34BNU%j@4JgCAAsUrj5)(Pa_o2D-=u8I>L)zyen;n;<6+ib% zjA%m6M)W)loA2EhDw8?$VsLUn>8INqt;MQ>%#Lf;JVH|)vBX-z*Gq0$-2vo>QqCyH z#m3?j31(`sT1}=7p*hWp02#RcR=El z6NU&ant5b{jYGw!f(A*u6^X*d&Ls;Hipl8$5B@*gD0>u zCPW-pQpH>peNtR}k~Sg26WYdHZB)7x|3lOaK{AiKEwT~49Mw*wsbYo7o0Ax=ob#r; zO89-sTla`^Gjnxt4MI_7`&5K_A4x{QUM_D@mqj80QUY%N02*eBQ+)Wr+UrRRm8Q;n z*RmdK;}<`<(|1CR#uyc%^qT16mBB!go;RIWY?%2B%l6XQZ!p@D|VL08xd2Y<6SZtEYW%kZ^X>fY0MPfHZy8+EOR3; zS=Y_$=Fu>3$%=hnit0G@=dvmnTG-g$`D!zJdB-NKEy(>27@=i|oi1S33*A5t^fp+9 zo7CxxstNj{J$+B7K}iPPvMQKbZwU};r2Pg3pe^!01q6a(Jl&hKEa+ujCc#q*PN?0C8Po@#l|Xn`ES{{0=pWq&UeDv{HS<*%uQy=l zgc<4!Enl`=Zr@I&q^W6Uv|U2!{V_opb2_y89|tP@!VtK8zs9NA>x}j6iSLvW z#}p#p9J4pH!!V=c_6k1E4+2uWbsLC~DvULUEZSV6yTP@GRsw$aTduBWMRTvzCDa_? z)_pZi)|9$W?9=Y;Yj>ql?jldBlVFcaC&cRB1?b`Tm124>R4*zq=Dh0gqBqZ4!)6D7 zF&c6RE|HgqW1ttr0K#ePn{3BISZJG#QBnHLUUVi81VO%6ma>^-zD(<4Ng|y>D?|-w zq_(Lm`Vd)s$ zG6TZBr{Bld4)Mx?lh$+6+7bwRgRkuy#Ddl?)P}>1mY+&`7ujVhQpnQxeGQfjVYje_ z1fZDZ{-L<&C8W5pT2ENa6pu@HA8K<0y+eBmw!GSQBIk)g-jvU9!>TX0x4e_;sQ9~k z+VdoEee*9ObYs{*wYt82FBU5|IzN(>3M}8fi2z?JZu)p`Y{l{GwXTh^$Ol~P&S9xU zglZPw1j7O6*OolClR%Kl&bU`6Tir86j<2yYl*fGy(S+N2+Z646cr!vc^%MGQ(iW$!HHUOa{Rv(oU1;36j zFKo%0j77bWzT82i?4RX@vF&P%5y?95RboQ|8Yq(FdAk2Lmn(eq0 z;>#;J=V-~;PuY#hcO#5F@5<^N1?_z7M-FnN>&5!q(Y%}2Q)-^Kh$os~V8T-Zpy*h< zc8UygL24C}*@d$vyzy#^>1c!eklMC#IR_tW*}ri1H8h;ptC+o>B8pjwww%K>GbJ2< z31d$@$GkxXLAtS>8MastVX0L*8#!x6E$(|GlW~>V;yXOZ(SC}7dg8M%7G?%P@Q;Oa z0~3XcQ|F`ii%(gIU`w6V<{t1>wKlvyULg2PFCX_|sRum@`G}}xV-&g3DN^fojC7iT zOajWT^6Um|Cc~}8tbG-RC$VBVD>9CO&vw6Kj~#}iTX~Mq>SlnzH=;^cw5Uglvv%5f z%2XI>G>P4xr^Ot5$kvCLmGg6lo(C8M#%*wYi-cTsn6&+Kdc8p|Frr5k8c-`x14_O) zeO29=s?S~W_0O0+_4WGvoNfWD)ggdx8Skm6nb~KM0v4}*Gu}P+RmdUS$y;=rS{3RS z!XZrfM1b*gPE%Y6bQ}2fEvC&Pv++4@a8|RSHoZaXk+ZeE&c1w7eT#6Fz<~`{?~(gE zf;rfAcq*17e!2k|o`ofg*yJ|1xywq=QuVqwp?Fn$OX~Blm7`iWJ5Q}K z3+L-R)npuG4Nf&-n@R^9{2KX-^b5f@zZPs%woJ%Lh9^zgx-H)wLIPTV2&@6-(TUs! z&&RZ1-xIZp=G?S0NHm{L;HZ7#9z@_2!sAFzd`H`VZltNH-#R%a{gC*4W(2T*HfM4^ z7iaIiLh*DsWKkPF!~l1z3>_PTCq~w;m{=dc50!WuCw?5yepr_kIHGaVF28>ckd;!B J{331~_&+?Cnk@hT literal 0 HcmV?d00001 diff --git a/public/images/logos/spectre.png b/public/images/logos/spectre.png new file mode 100644 index 0000000000000000000000000000000000000000..6c2189e96c34f3a4263953d0801a93fc4792eccf GIT binary patch literal 3446 zcmZ{nc|6qH8^=eCHG~MG8e65zW;ZmJvF`>MG?_6CP0V6uFv(=OQlca-8M0J#Q4&ga zBGNiinj%`2N|d;EzoG8$`uW}aeZ9{2e9!ZI-p@JDbI$qWo9y9cFD<}39UW{i zg5G?&iHi!}$oc0CK_|q-*jocC9;%H91`>1!KPCVmrLf$D0Ngxf06@5e?2Tn%U0skw z8U;$&PYWVJ*%Z1!Er`TM3O*?$76Htrgi)DD_HK<81yb<6Y=&uoS0t>^-5OX|4=|d> zAb|~`22i*LN)ikPn=|$YBQZ91zi`25x5fb$i;jfBqN1XpQTk9CBLs#pH8q97^ z5P<^1JVa#?*bpjH^JkF1;@FUwL8LrT128qC;F}!KCFq8+GO$wtd55f9SSX`CHY--ay+KI|cJ!}1h?3+EH^c~TG1<|^ z%KHHDHkX#F(52WK61+4W6Q$;zo=~Qy^I)bzWaAT~tscck9Ht+OdINU?{S&y;i+l+O zad}%O2a7!a^fC?WbxxTnoLEa6b>GsRnI&8OlYb}_%e$iDoEUDmJXN`Q(K;bRGgCt@ zLjnDtuX9k5&kj#H?Rxa}VPnovvoUS3(NNtL8B|*RlBY<)L`5_ZA_@}cIVTYH~_@vaF1y`FqZ6umRVcR~vCo zYmz_z#ysU96fRRP0=aj!Esu(1O~m7P>x;ZDzz*+m7gr1r{*tukNnpGtNHYF*#_>L( zO+}?zMP~hP$TjNT2O)Gh{|!OYq~voZaze4M%ubVcR|aDK*a`ymJ6zaoFKnRAIBj<& ztT)50OF083P90RwObDMgYb3MT{D9rDoVf~ILUGv(;gb{1KuckF)g$AJbH4U=U1@Ti zTjWbfXEabmc^6Ft#CrxGJT_Pd79M}mnN<88NN31Owf~tJRsZc`UBKKF$Mz4l!bc;A z(ViYV2&n02_U0zR3dQ+o_A&(+WF=%MVwr$bMw{|knl_t9Gt=RCgmL*BV?aa#gAZ8IqDgr=!$^M?T*QR{N zVnSK0-+QGt;uppTd{vYT1B{4gkv3f9dtN;B?SAn!N$FrCB_T=F-yo!}k4UjuL zZ?v9Qn#|;?8xT&FFtZSy6DbP1^5LI+Hua^Y)go>^yd_!LGg^3p3&d7U3@=W{mi5on zQN@+bs*0qg3T+`ZWmK3|Y`zPW_ zx7OT}x)bkXlTODEeDhvo%ii=+rJ^yEfnL?@K|L}n2XKXaDaXH@eCJSGv^`iGW1@z& ziyTNbE_r_DzDJy=vNh9r$jYQesIhw7%vq%1Xhd|DYkyciVQK-L>OJmq(;>isD0f}^p*BgWPcr9@ptuHK**Dd8z1G!tA ztz~AUvDCAP!IIuD>pvZX$m@{av^x3eX3;pDU1*GeyW_3u?kcl+V%#fosoeFgGO1NusKCY z6bZjJS9ewKtJjRiLqD-B6jD!vSa~h4@N0MXfy0rD5q&+^9=Yj?JkdS+?X6IHo_iSJz5vz}vozS2E7 zHHPalcx8)CfYBU9qZ`*ZKQGew?Nis_)f|^P(%_x-Q%m*x?3j+Q(WYZa!?uIb)pf>T zXwuD`KaW(!!iBd!+((q}ZybO6#j7@ut?toz&5hPfeTF;6aSZph|k9OwCM>szp@a?XQio#E{T&u_TuYftUG)*+^= zinCYY@K8E9`?HNX-S1-DcPgr~Oj>WK!15BWkC_Hklbp6`m3F%Gc_-=vZghxPw@;15 zW?~y=f)>v=9l7NcUk)6u-a(SJ6xA~L*cngZ4>)2i3Zg=@tk(guk`Lv4k|HfKB#H<+ zHATkfkWZ8IH={iqRQIyRG7xhFwDYI2b{>n^Ds$OiA|+Saim?~_?R`~>5zC;y-{U;& zb@>jPS%JAr82T$hTPx_d;LoiLqU^hdMfxLkzdX8K%f7M@ zwAW&9G;h4^bxfF+2@8xro}bzLHJOC3Gv;e($NXS@$FlFm_hs{HI_gX1)sP#;aelj9 z`ZeZu4(oCeK>ZtrpqAQT=bcw30^l^Bfu)!4!A)rQ@i%}6mc5$I56m{eZ@#o2K-4Z+ zNat18&(v(!uMqa*G>AY97Mfm|p5&g&JC=GUs;nOpwOL%b3gY&LpHytnQK55WA4zN-R3ZuwA==`d7r-@lNImII(ud!b zcNmQJ-i7TuT0AB*U;F+&tppS5JefTwt>&z$dp6<}=*8fDp2tV-l`X8{)_BvKwVUG9 zXKi^DS45AE;&Z)6a&qkc*gq!bU@9a-Ku%A^@#F7==*_C{6KuJ z=sKClppbW}VIu7EqFmASLfy2ZRJBB$x3gfBx(D&QvWY}&nAsDjk0tB+dFX}hVFx6d zDcA9C#+&)^eUs=7jpFN;EDjF$$m2ms1Y6CJ5}Nb$$Xox?Y@Qw-6b(PJS68=L^0}Nl o;k8p!V#&Ri<9z{~2cLc{C0#i;dq?|Q@$%1*qph1wg*86mKe6n '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_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.', + + // Spectre: + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 6d7a98c2fe..9e3af6052c 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -186,6 +186,10 @@ return [ 'csv_delimiter' => 'CSV field delimiter', 'csv_import_account' => 'Default import account', 'csv_config' => 'CSV import configuration', + 'client_id' => 'Client ID', + 'service_secret' => 'Service secret', + 'app_secret' => 'App secret', + 'public_key' => 'Public key', 'due_date' => 'Due date', diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index 8e2876dc52..bb941e7c1c 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -20,21 +20,34 @@
-
-

+

+
{# bunq import #} - {# - + bunq
Import from bunq
- #} -

+
+
+ {# import from Spectre #} + + Spectre
+ Import using Spectre +
+
+
+ {# import from Plaid #} + + Plaid
+ Import using Plaid +
+
diff --git a/resources/views/import/spectre/prerequisites.twig b/resources/views/import/spectre/prerequisites.twig new file mode 100644 index 0000000000..1b225e6a12 --- /dev/null +++ b/resources/views/import/spectre/prerequisites.twig @@ -0,0 +1,58 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} +
+
+ +
+
+
+

{{ trans('bank.spectre_prerequisites_title') }}

+
+
+
+
+

+ {{ trans('bank.spectre_prerequisites_text')|raw }} +

+
+
+ +
+
+ {{ ExpandedForm.text('client_id') }} + {{ ExpandedForm.text('service_secret') }} + {{ ExpandedForm.text('app_secret') }} +
+
+
+
+

{{ trans('bank.spectre_enter_pub_key')|raw }}

+
+ + +
+ +
+
+
+
+ +
+
+ +
+{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} From e488d7d84c1eeec153dff47aac09ad7dd9727e2d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 19:13:00 +0100 Subject: [PATCH 004/142] More code for Spectre import. --- .../Controllers/Import/BankController.php | 81 +---- .../Configurator/SpectreConfigurator.php | 178 ++++++++++ .../Configuration/Spectre/SelectBank.php | 76 ++++ .../Configuration/Spectre/SelectCountry.php | 329 ++++++++++++++++++ .../Import/Information/SpectreInformation.php | 207 +++++++++++ .../Prerequisites/SpectrePrerequisites.php | 57 +-- config/firefly.php | 4 +- resources/lang/en_US/bank.php | 14 +- resources/lang/en_US/form.php | 1 + .../views/import/spectre/select-bank.twig | 42 +++ .../views/import/spectre/select-country.twig | 42 +++ routes/web.php | 5 +- 12 files changed, 910 insertions(+), 126 deletions(-) create mode 100644 app/Import/Configurator/SpectreConfigurator.php create mode 100644 app/Support/Import/Configuration/Spectre/SelectBank.php create mode 100644 app/Support/Import/Configuration/Spectre/SelectCountry.php create mode 100644 app/Support/Import/Information/SpectreInformation.php create mode 100644 resources/views/import/spectre/select-bank.twig create mode 100644 resources/views/import/spectre/select-country.twig diff --git a/app/Http/Controllers/Import/BankController.php b/app/Http/Controllers/Import/BankController.php index fe4e6790ce..0bf671d4dc 100644 --- a/app/Http/Controllers/Import/BankController.php +++ b/app/Http/Controllers/Import/BankController.php @@ -24,7 +24,7 @@ namespace FireflyIII\Http\Controllers\Import; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; -use FireflyIII\Support\Import\Information\InformationInterface; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface; use Illuminate\Http\Request; use Log; @@ -32,72 +32,27 @@ use Session; class BankController extends Controller { - /** - * This method must ask the user all parameters necessary to start importing data. This may not be enough - * to finish the import itself (ie. mapping) but it should be enough to begin: accounts to import from, - * accounts to import into, data ranges, etc. - * - * @param string $bank - * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View - */ - public function form(string $bank) - { - $class = config(sprintf('firefly.import_pre.%s', $bank)); - if(!class_exists($class)) { - throw new FireflyException(sprintf('Cannot find class %s', $class)); - } - /** @var PrerequisitesInterface $object */ - $object = app($class); - $object->setUser(auth()->user()); - - if ($object->hasPrerequisites()) { - return redirect(route('import.bank.prerequisites', [$bank])); - } - $class = config(sprintf('firefly.import_info.%s', $bank)); - /** @var InformationInterface $object */ - $object = app($class); - $object->setUser(auth()->user()); - $remoteAccounts = $object->getAccounts(); - - return view('import.bank.form', compact('remoteAccounts', 'bank')); - } /** - * With the information given in the submitted form Firefly III will call upon the bank's classes to return transaction - * information as requested. The user will be able to map unknown data and continue. Or maybe, it's put into some kind of - * fake CSV file and forwarded to the import routine. + * Once there are no prerequisites, this method will create an importjob object and + * redirect the user to a view where this object can be used by a bank specific + * class to process. * - * @param Request $request - * @param string $bank + * @param ImportJobRepositoryInterface $repository + * @param string $bank * * @return \Illuminate\Http\RedirectResponse|null + * @throws FireflyException */ - public function postForm(Request $request, string $bank) + public function createJob(ImportJobRepositoryInterface $repository, string $bank) { $class = config(sprintf('firefly.import_pre.%s', $bank)); - if(!class_exists($class)) { + if (!class_exists($class)) { throw new FireflyException(sprintf('Cannot find class %s', $class)); } - /** @var PrerequisitesInterface $object */ - $object = app($class); - $object->setUser(auth()->user()); + $importJob = $repository->create($bank); - if ($object->hasPrerequisites()) { - return redirect(route('import.bank.prerequisites', [$bank])); - } - $remoteAccounts = $request->get('do_import'); - if (!is_array($remoteAccounts) || 0 === count($remoteAccounts)) { - Session::flash('error', 'Must select accounts'); - - return redirect(route('import.bank.form', [$bank])); - } - $remoteAccounts = array_keys($remoteAccounts); - $class = config(sprintf('firefly.import_pre.%s', $bank)); - // get import file - unset($remoteAccounts, $class); - - // get import config + return redirect(route('import.bank.configure', [$bank, $importJob->key])); } /** @@ -112,12 +67,13 @@ class BankController extends Controller * @param string $bank * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException */ public function postPrerequisites(Request $request, string $bank) { Log::debug(sprintf('Now in postPrerequisites for %s', $bank)); $class = config(sprintf('firefly.import_pre.%s', $bank)); - if(!class_exists($class)) { + if (!class_exists($class)) { throw new FireflyException(sprintf('Cannot find class %s', $class)); } /** @var PrerequisitesInterface $object */ @@ -126,7 +82,7 @@ class BankController extends Controller if (!$object->hasPrerequisites()) { Log::debug(sprintf('No more prerequisites for %s, move to form.', $bank)); - return redirect(route('import.bank.form', [$bank])); + return redirect(route('import.bank.create-job', [$bank])); } Log::debug('Going to store entered preprerequisites.'); // store post data @@ -138,7 +94,7 @@ class BankController extends Controller return redirect(route('import.bank.prerequisites', [$bank])); } - return redirect(route('import.bank.form', [$bank])); + return redirect(route('import.bank.create-job', [$bank])); } /** @@ -148,11 +104,12 @@ class BankController extends Controller * @param string $bank * * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + * @throws FireflyException */ public function prerequisites(string $bank) { $class = config(sprintf('firefly.import_pre.%s', $bank)); - if(!class_exists($class)) { + if (!class_exists($class)) { throw new FireflyException(sprintf('Cannot find class %s', $class)); } /** @var PrerequisitesInterface $object */ @@ -161,12 +118,12 @@ class BankController extends Controller if ($object->hasPrerequisites()) { $view = $object->getView(); - $parameters = ['title' => strval(trans('firefly.import_index_title')),'mainTitleIcon' => 'fa-archive']; + $parameters = ['title' => strval(trans('firefly.import_index_title')), 'mainTitleIcon' => 'fa-archive']; $parameters = $object->getViewParameters() + $parameters; return view($view, $parameters); } - return redirect(route('import.bank.form', [$bank])); + return redirect(route('import.bank.create-job', [$bank])); } } diff --git a/app/Import/Configurator/SpectreConfigurator.php b/app/Import/Configurator/SpectreConfigurator.php new file mode 100644 index 0000000000..9a8d295101 --- /dev/null +++ b/app/Import/Configurator/SpectreConfigurator.php @@ -0,0 +1,178 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Import\Configurator; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\ImportJob; +use FireflyIII\Support\Import\Configuration\ConfigurationInterface; +use FireflyIII\Support\Import\Configuration\Spectre\SelectBank; +use FireflyIII\Support\Import\Configuration\Spectre\SelectCountry; +use Log; + +/** + * Class SpectreConfigurator. + */ +class SpectreConfigurator implements ConfiguratorInterface +{ + /** @var ImportJob */ + private $job; + + /** @var string */ + private $warning = ''; + + /** + * ConfiguratorInterface constructor. + */ + public function __construct() + { + } + + /** + * Store any data from the $data array into the job. + * + * @param array $data + * + * @return bool + * + * @throws FireflyException + */ + public function configureJob(array $data): bool + { + $class = $this->getConfigurationClass(); + $job = $this->job; + /** @var ConfigurationInterface $object */ + $object = new $class($this->job); + $object->setJob($job); + $result = $object->storeConfiguration($data); + $this->warning = $object->getWarningMessage(); + + return $result; + } + + /** + * Return the data required for the next step in the job configuration. + * + * @return array + * + * @throws FireflyException + */ + public function getNextData(): array + { + $class = $this->getConfigurationClass(); + $job = $this->job; + /** @var ConfigurationInterface $object */ + $object = app($class); + $object->setJob($job); + + return $object->getData(); + } + + /** + * @return string + * + * @throws FireflyException + */ + public function getNextView(): string + { + if (!$this->job->configuration['selected-country']) { + return 'import.spectre.select-country'; + } + if (!$this->job->configuration['selected-bank']) { + return 'import.spectre.select-bank'; + } + + throw new FireflyException('No view for state'); + } + + /** + * Return possible warning to user. + * + * @return string + */ + public function getWarningMessage(): string + { + return $this->warning; + } + + /** + * @return bool + */ + public function isJobConfigured(): bool + { + $config = $this->job->configuration; + $config['selected-country'] = $config['selected-country'] ?? false; + $config['selected-bank'] = $config['selected-bank'] ?? false; + $this->job->configuration = $config; + $this->job->save(); + + if ($config['selected-country'] && $config['selected-bank'] && false) { + return true; + } + + return false; + } + + /** + * @param ImportJob $job + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + if (null === $this->job->configuration || 0 === count($this->job->configuration)) { + Log::debug(sprintf('Gave import job %s initial configuration.', $this->job->key)); + $this->job->configuration = [ + 'selected-country' => false, + ]; + $this->job->save(); + } + } + + /** + * @return string + * + * @throws FireflyException + */ + private function getConfigurationClass(): string + { + $class = false; + switch (true) { + case !$this->job->configuration['selected-country']: + $class = SelectCountry::class; + break; + case !$this->job->configuration['selected-bank']: + $class = SelectBank::class; + break; + default: + break; + } + + if (false === $class || 0 === strlen($class)) { + throw new FireflyException('Cannot handle current job state in getConfigurationClass().'); + } + if (!class_exists($class)) { + throw new FireflyException(sprintf('Class %s does not exist in getConfigurationClass().', $class)); + } + + return $class; + } +} diff --git a/app/Support/Import/Configuration/Spectre/SelectBank.php b/app/Support/Import/Configuration/Spectre/SelectBank.php new file mode 100644 index 0000000000..1a9622f710 --- /dev/null +++ b/app/Support/Import/Configuration/Spectre/SelectBank.php @@ -0,0 +1,76 @@ +job->configuration; + $selection = SpectreProvider::where('country_code', $config['country'])->where('status', 'active')->get(); + $providers = []; + /** @var SpectreProvider $provider */ + foreach ($selection as $provider) { + $providerId = $provider->spectre_id; + $name = $provider->data['name']; + $providers[$providerId] = $name; + } + + return compact('providers'); + } + + /** + * Return possible warning to user. + * + * @return string + */ + public function getWarningMessage(): string + { + return ''; + } + + /** + * @param ImportJob $job + * + * @return ConfigurationInterface + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } + + /** + * Store the result. + * + * @param array $data + * + * @return bool + */ + public function storeConfiguration(array $data): bool + { + $config = $this->job->configuration; + $config['bank'] = intval($data['bank_code']) ?? 0; // default to fake country. + $config['selected-bank'] = true; + $this->job->configuration = $config; + $this->job->save(); + + return true; + } +} \ No newline at end of file diff --git a/app/Support/Import/Configuration/Spectre/SelectCountry.php b/app/Support/Import/Configuration/Spectre/SelectCountry.php new file mode 100644 index 0000000000..de49d9545d --- /dev/null +++ b/app/Support/Import/Configuration/Spectre/SelectCountry.php @@ -0,0 +1,329 @@ + 'Afghanistan', + 'AX' => 'Aland Islands', + 'AL' => 'Albania', + 'DZ' => 'Algeria', + 'AS' => 'American Samoa', + 'AD' => 'Andorra', + 'AO' => 'Angola', + 'AI' => 'Anguilla', + 'AQ' => 'Antarctica', + 'AG' => 'Antigua and Barbuda', + 'AR' => 'Argentina', + 'AM' => 'Armenia', + 'AW' => 'Aruba', + 'AU' => 'Australia', + 'AT' => 'Austria', + 'AZ' => 'Azerbaijan', + 'BS' => 'Bahamas', + 'BH' => 'Bahrain', + 'BD' => 'Bangladesh', + 'BB' => 'Barbados', + 'BY' => 'Belarus', + 'BE' => 'Belgium', + 'BZ' => 'Belize', + 'BJ' => 'Benin', + 'BM' => 'Bermuda', + 'BT' => 'Bhutan', + 'BO' => 'Bolivia', + 'BQ' => 'Bonaire, Saint Eustatius and Saba', + 'BA' => 'Bosnia and Herzegovina', + 'BW' => 'Botswana', + 'BV' => 'Bouvet Island', + 'BR' => 'Brazil', + 'IO' => 'British Indian Ocean Territory', + 'VG' => 'British Virgin Islands', + 'BN' => 'Brunei', + 'BG' => 'Bulgaria', + 'BF' => 'Burkina Faso', + 'BI' => 'Burundi', + 'KH' => 'Cambodia', + 'CM' => 'Cameroon', + 'CA' => 'Canada', + 'CV' => 'Cape Verde', + 'KY' => 'Cayman Islands', + 'CF' => 'Central African Republic', + 'TD' => 'Chad', + 'CL' => 'Chile', + 'CN' => 'China', + 'CX' => 'Christmas Island', + 'CC' => 'Cocos Islands', + 'CO' => 'Colombia', + 'KM' => 'Comoros', + 'CK' => 'Cook Islands', + 'CR' => 'Costa Rica', + 'HR' => 'Croatia', + 'CU' => 'Cuba', + 'CW' => 'Curacao', + 'CY' => 'Cyprus', + 'CZ' => 'Czech Republic', + 'CD' => 'Democratic Republic of the Congo', + 'DK' => 'Denmark', + 'DJ' => 'Djibouti', + 'DM' => 'Dominica', + 'DO' => 'Dominican Republic', + 'TL' => 'East Timor', + 'EC' => 'Ecuador', + 'EG' => 'Egypt', + 'SV' => 'El Salvador', + 'GQ' => 'Equatorial Guinea', + 'ER' => 'Eritrea', + 'EE' => 'Estonia', + 'ET' => 'Ethiopia', + 'FK' => 'Falkland Islands', + 'FO' => 'Faroe Islands', + 'FJ' => 'Fiji', + 'FI' => 'Finland', + 'FR' => 'France', + 'GF' => 'French Guiana', + 'PF' => 'French Polynesia', + 'TF' => 'French Southern Territories', + 'GA' => 'Gabon', + 'GM' => 'Gambia', + 'GE' => 'Georgia', + 'DE' => 'Germany', + 'GH' => 'Ghana', + 'GI' => 'Gibraltar', + 'GR' => 'Greece', + 'GL' => 'Greenland', + 'GD' => 'Grenada', + 'GP' => 'Guadeloupe', + 'GU' => 'Guam', + 'GT' => 'Guatemala', + 'GG' => 'Guernsey', + 'GN' => 'Guinea', + 'GW' => 'Guinea-Bissau', + 'GY' => 'Guyana', + 'HT' => 'Haiti', + 'HM' => 'Heard Island and McDonald Islands', + 'HN' => 'Honduras', + 'HK' => 'Hong Kong', + 'HU' => 'Hungary', + 'IS' => 'Iceland', + 'IN' => 'India', + 'ID' => 'Indonesia', + 'IR' => 'Iran', + 'IQ' => 'Iraq', + 'IE' => 'Ireland', + 'IM' => 'Isle of Man', + 'IL' => 'Israel', + 'IT' => 'Italy', + 'CI' => 'Ivory Coast', + 'JM' => 'Jamaica', + 'JP' => 'Japan', + 'JE' => 'Jersey', + 'JO' => 'Jordan', + 'KZ' => 'Kazakhstan', + 'KE' => 'Kenya', + 'KI' => 'Kiribati', + 'XK' => 'Kosovo', + 'KW' => 'Kuwait', + 'KG' => 'Kyrgyzstan', + 'LA' => 'Laos', + 'LV' => 'Latvia', + 'LB' => 'Lebanon', + 'LS' => 'Lesotho', + 'LR' => 'Liberia', + 'LY' => 'Libya', + 'LI' => 'Liechtenstein', + 'LT' => 'Lithuania', + 'LU' => 'Luxembourg', + 'MO' => 'Macao', + 'MK' => 'Macedonia', + 'MG' => 'Madagascar', + 'MW' => 'Malawi', + 'MY' => 'Malaysia', + 'MV' => 'Maldives', + 'ML' => 'Mali', + 'MT' => 'Malta', + 'MH' => 'Marshall Islands', + 'MQ' => 'Martinique', + 'MR' => 'Mauritania', + 'MU' => 'Mauritius', + 'YT' => 'Mayotte', + 'MX' => 'Mexico', + 'FM' => 'Micronesia', + 'MD' => 'Moldova', + 'MC' => 'Monaco', + 'MN' => 'Mongolia', + 'ME' => 'Montenegro', + 'MS' => 'Montserrat', + 'MA' => 'Morocco', + 'MZ' => 'Mozambique', + 'MM' => 'Myanmar', + 'NA' => 'Namibia', + 'NR' => 'Nauru', + 'NP' => 'Nepal', + 'NL' => 'Netherlands', + 'NC' => 'New Caledonia', + 'NZ' => 'New Zealand', + 'NI' => 'Nicaragua', + 'NE' => 'Niger', + 'NG' => 'Nigeria', + 'NU' => 'Niue', + 'NF' => 'Norfolk Island', + 'KP' => 'North Korea', + 'MP' => 'Northern Mariana Islands', + 'NO' => 'Norway', + 'OM' => 'Oman', + 'PK' => 'Pakistan', + 'PW' => 'Palau', + 'PS' => 'Palestinian Territory', + 'PA' => 'Panama', + 'PG' => 'Papua New Guinea', + 'PY' => 'Paraguay', + 'PE' => 'Peru', + 'PH' => 'Philippines', + 'PN' => 'Pitcairn', + 'PL' => 'Poland', + 'PT' => 'Portugal', + 'PR' => 'Puerto Rico', + 'QA' => 'Qatar', + 'CG' => 'Republic of the Congo', + 'RE' => 'Reunion', + 'RO' => 'Romania', + 'RU' => 'Russia', + 'RW' => 'Rwanda', + 'BL' => 'Saint Barthelemy', + 'SH' => 'Saint Helena', + 'KN' => 'Saint Kitts and Nevis', + 'LC' => 'Saint Lucia', + 'MF' => 'Saint Martin', + 'PM' => 'Saint Pierre and Miquelon', + 'VC' => 'Saint Vincent and the Grenadines', + 'WS' => 'Samoa', + 'SM' => 'San Marino', + 'ST' => 'Sao Tome and Principe', + 'SA' => 'Saudi Arabia', + 'SN' => 'Senegal', + 'RS' => 'Serbia', + 'SC' => 'Seychelles', + 'SL' => 'Sierra Leone', + 'SG' => 'Singapore', + 'SX' => 'Sint Maarten', + 'SK' => 'Slovakia', + 'SI' => 'Slovenia', + 'SB' => 'Solomon Islands', + 'SO' => 'Somalia', + 'ZA' => 'South Africa', + 'GS' => 'South Georgia and the South Sandwich Islands', + 'KR' => 'South Korea', + 'SS' => 'South Sudan', + 'ES' => 'Spain', + 'LK' => 'Sri Lanka', + 'SD' => 'Sudan', + 'SR' => 'Suriname', + 'SJ' => 'Svalbard and Jan Mayen', + 'SZ' => 'Swaziland', + 'SE' => 'Sweden', + 'CH' => 'Switzerland', + 'SY' => 'Syria', + 'TW' => 'Taiwan', + 'TJ' => 'Tajikistan', + 'TZ' => 'Tanzania', + 'TH' => 'Thailand', + 'TG' => 'Togo', + 'TK' => 'Tokelau', + 'TO' => 'Tonga', + 'TT' => 'Trinidad and Tobago', + 'TN' => 'Tunisia', + 'TR' => 'Turkey', + 'TM' => 'Turkmenistan', + 'TC' => 'Turks and Caicos Islands', + 'TV' => 'Tuvalu', + 'VI' => 'U.S. Virgin Islands', + 'UG' => 'Uganda', + 'UA' => 'Ukraine', + 'AE' => 'United Arab Emirates', + 'GB' => 'United Kingdom', + 'US' => 'United States', + 'UM' => 'United States Minor Outlying Islands', + 'UY' => 'Uruguay', + 'UZ' => 'Uzbekistan', + 'VU' => 'Vanuatu', + 'VA' => 'Vatican', + 'VE' => 'Venezuela', + 'VN' => 'Vietnam', + 'WF' => 'Wallis and Futuna', + 'EH' => 'Western Sahara', + 'YE' => 'Yemen', + 'ZM' => 'Zambia', + 'ZW' => 'Zimbabwe', + 'XF' => 'Fake Country (for testing)', + 'XO' => 'Other financial applications', + ]; + /** @var ImportJob */ + private $job; + + /** + * Get the data necessary to show the configuration screen. + * + * @return array + */ + public function getData(): array + { + $providers = SpectreProvider::get(); + $countries = []; + /** @var SpectreProvider $provider */ + foreach ($providers as $provider) { + $countries[$provider->country_code] = $this->allCountries[$provider->country_code] ?? $provider->country_code; + } + asort($countries); + + return compact('countries'); + } + + /** + * Return possible warning to user. + * + * @return string + */ + public function getWarningMessage(): string + { + return ''; + } + + /** + * @param ImportJob $job + * + * @return ConfigurationInterface + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } + + /** + * Store the result. + * + * @param array $data + * + * @return bool + */ + public function storeConfiguration(array $data): bool + { + $config = $this->job->configuration; + $config['country'] = $data['country_code'] ?? 'XF'; // default to fake country. + $config['selected-country'] = true; + $this->job->configuration = $config; + $this->job->save(); + + return true; + } +} \ No newline at end of file diff --git a/app/Support/Import/Information/SpectreInformation.php b/app/Support/Import/Information/SpectreInformation.php new file mode 100644 index 0000000000..04a3037c3b --- /dev/null +++ b/app/Support/Import/Information/SpectreInformation.php @@ -0,0 +1,207 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Support\Import\Information; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Services\Bunq\Object\Alias; +use FireflyIII\Services\Bunq\Object\MonetaryAccountBank; +use FireflyIII\Services\Bunq\Request\DeleteDeviceSessionRequest; +use FireflyIII\Services\Bunq\Request\DeviceSessionRequest; +use FireflyIII\Services\Bunq\Request\ListMonetaryAccountRequest; +use FireflyIII\Services\Bunq\Request\ListUserRequest; +use FireflyIII\Services\Bunq\Token\SessionToken; +use FireflyIII\Support\CacheProperties; +use FireflyIII\User; +use Illuminate\Support\Collection; +use Log; +use Preferences; + +/** + * Class SpectreInformation + */ +class SpectreInformation implements InformationInterface +{ + /** @var User */ + private $user; + + /** + * Returns a collection of accounts. Preferrably, these follow a uniform Firefly III format so they can be managed over banks. + * + * The format for these bank accounts is basically this: + * + * id: bank specific id + * name: bank appointed name + * number: account number (usually IBAN) + * currency: ISO code of currency + * balance: current balance + * + * + * any other fields are optional but can be useful: + * image: logo or account specific thing + * color: any associated color. + * + * @return array + */ + public function getAccounts(): array + { + // cache for an hour: + $cache = new CacheProperties; + $cache->addProperty('bunq.get-accounts'); + $cache->addProperty(date('dmy h')); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + Log::debug('Now in getAccounts()'); + $sessionToken = $this->startSession(); + $userId = $this->getUserInformation($sessionToken); + // get list of Bunq accounts: + $accounts = $this->getMonetaryAccounts($sessionToken, $userId); + $return = []; + /** @var MonetaryAccountBank $account */ + foreach ($accounts as $account) { + $current = [ + 'id' => $account->getId(), + 'name' => $account->getDescription(), + 'currency' => $account->getCurrency(), + 'balance' => $account->getBalance()->getValue(), + 'color' => $account->getSetting()->getColor(), + ]; + /** @var Alias $alias */ + foreach ($account->getAliases() as $alias) { + if ('IBAN' === $alias->getType()) { + $current['number'] = $alias->getValue(); + } + } + $return[] = $current; + } + $cache->store($return); + + $this->closeSession($sessionToken); + + return $return; + } + + /** + * Set the user for this Prerequisites-routine. Class is expected to implement and save this. + * + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * @param SessionToken $sessionToken + */ + private function closeSession(SessionToken $sessionToken): void + { + Log::debug('Going to close session'); + $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; + $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; + $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; + $request = new DeleteDeviceSessionRequest(); + $request->setSecret($apiKey); + $request->setPrivateKey($privateKey); + $request->setServerPublicKey($serverPublicKey); + $request->setSessionToken($sessionToken); + $request->call(); + + return; + } + + /** + * @param SessionToken $sessionToken + * @param int $userId + * + * @return Collection + */ + private function getMonetaryAccounts(SessionToken $sessionToken, int $userId): Collection + { + $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; + $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; + $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; + $request = new ListMonetaryAccountRequest; + + $request->setSessionToken($sessionToken); + $request->setSecret($apiKey); + $request->setServerPublicKey($serverPublicKey); + $request->setPrivateKey($privateKey); + $request->setUserId($userId); + $request->call(); + + return $request->getMonetaryAccounts(); + } + + /** + * @param SessionToken $sessionToken + * + * @return int + * + * @throws FireflyException + */ + private function getUserInformation(SessionToken $sessionToken): int + { + $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; + $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; + $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; + $request = new ListUserRequest; + $request->setSessionToken($sessionToken); + $request->setSecret($apiKey); + $request->setServerPublicKey($serverPublicKey); + $request->setPrivateKey($privateKey); + $request->call(); + // return the first that isn't null? + $company = $request->getUserCompany(); + if ($company->getId() > 0) { + return $company->getId(); + } + $user = $request->getUserPerson(); + if ($user->getId() > 0) { + return $user->getId(); + } + throw new FireflyException('Expected user or company from Bunq, but got neither.'); + } + + /** + * @return SessionToken + */ + private function startSession(): SessionToken + { + Log::debug('Now in startSession.'); + $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; + $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; + $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; + $installationToken = Preferences::getForUser($this->user, 'bunq_installation_token')->data; + $request = new DeviceSessionRequest(); + $request->setSecret($apiKey); + $request->setServerPublicKey($serverPublicKey); + $request->setPrivateKey($privateKey); + $request->setInstallationToken($installationToken); + $request->call(); + $sessionToken = $request->getSessionToken(); + Log::debug(sprintf('Now have got session token: %s', serialize($sessionToken))); + + return $sessionToken; + } +} diff --git a/app/Support/Import/Prerequisites/SpectrePrerequisites.php b/app/Support/Import/Prerequisites/SpectrePrerequisites.php index a55c0f87b6..d67960b139 100644 --- a/app/Support/Import/Prerequisites/SpectrePrerequisites.php +++ b/app/Support/Import/Prerequisites/SpectrePrerequisites.php @@ -1,6 +1,6 @@ user, 'spectre_private_key', null); - if (null === $preference) { - Log::debug('private key is null'); - // create key pair - $this->createKeyPair(); - } - $preference = Preferences::getForUser($this->user, 'spectre_private_key', null); - Log::debug('Return private key for user'); - - return $preference->data; - } - /** * Get a public key from the users preferences. * @@ -197,33 +173,4 @@ class SpectrePrerequisites implements PrerequisitesInterface return $preference->data; } - - /** - * Request users server remote IP. Let's assume this value will not change any time soon. - * - * @return string - * - * @throws FireflyException - */ - private function getRemoteIp(): string - { - $preference = Preferences::getForUser($this->user, 'external_ip', null); - if (null === $preference) { - try { - $response = Requests::get('https://api.ipify.org'); - } catch (Requests_Exception $e) { - throw new FireflyException(sprintf('Could not retrieve external IP: %s', $e->getMessage())); - } - if (200 !== $response->status_code) { - throw new FireflyException(sprintf('Could not retrieve external IP: %d %s', $response->status_code, $response->body)); - } - $serverIp = $response->body; - Preferences::setForUser($this->user, 'external_ip', $serverIp); - - return $serverIp; - } - - return $preference->data; - } - } diff --git a/config/firefly.php b/config/firefly.php index b4219692d4..f0b20f60ff 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -41,10 +41,12 @@ return [ 'csv' => 'FireflyIII\Export\Exporter\CsvExporter', ], 'import_formats' => [ - 'csv' => 'FireflyIII\Import\Configurator\CsvConfigurator', + 'csv' => 'FireflyIII\Import\Configurator\CsvConfigurator', + 'spectre' => '', ], 'import_configurators' => [ 'csv' => 'FireflyIII\Import\Configurator\CsvConfigurator', + 'spectre' => 'FireflyIII\Import\Configurator\SpectreConfigurator', ], 'import_processors' => [ 'csv' => 'FireflyIII\Import\FileProcessor\CsvProcessor', diff --git a/resources/lang/en_US/bank.php b/resources/lang/en_US/bank.php index 369f72e145..1c970e1e7e 100644 --- a/resources/lang/en_US/bank.php +++ b/resources/lang/en_US/bank.php @@ -3,12 +3,14 @@ declare(strict_types=1); return [ - '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_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.', // Spectre: - 'spectre_title' => 'Import using Spectre', - 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', - 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', - 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', ]; diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 9e3af6052c..bb3f1fa635 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -190,6 +190,7 @@ return [ 'service_secret' => 'Service secret', 'app_secret' => 'App secret', 'public_key' => 'Public key', + 'country_code' => 'Country code', 'due_date' => 'Due date', diff --git a/resources/views/import/spectre/select-bank.twig b/resources/views/import/spectre/select-bank.twig new file mode 100644 index 0000000000..af0a8d56f4 --- /dev/null +++ b/resources/views/import/spectre/select-bank.twig @@ -0,0 +1,42 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} +
+
+ +
+
+
+

{{ trans('bank.spectre_select_bank_title') }}

+
+
+
+
+

+ {{ trans('bank.spectre_select_bank_title') }} +

+
+
+ +
+
+ {{ ExpandedForm.select('bank_code', data.providers, null)|raw }} +
+
+ +
+
+ +
+{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/resources/views/import/spectre/select-country.twig b/resources/views/import/spectre/select-country.twig new file mode 100644 index 0000000000..5c4f36a199 --- /dev/null +++ b/resources/views/import/spectre/select-country.twig @@ -0,0 +1,42 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} +
+
+ +
+
+
+

{{ trans('bank.spectre_select_country_title') }}

+
+
+
+
+

+ {{ trans('bank.spectre_select_country_text') }} +

+
+
+ +
+
+ {{ ExpandedForm.select('country_code', data.countries, null)|raw }} +
+
+ +
+
+ +
+{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/routes/web.php b/routes/web.php index 86b29cb942..ff61ef929a 100755 --- a/routes/web.php +++ b/routes/web.php @@ -420,8 +420,9 @@ Route::group( Route::get('bank/{bank}/prerequisites', ['uses' => 'Import\BankController@prerequisites', 'as' => 'bank.prerequisites']); Route::post('bank/{bank}/prerequisites', ['uses' => 'Import\BankController@postPrerequisites', 'as' => 'bank.prerequisites.post']); - Route::get('bank/{bank}/form', ['uses' => 'Import\BankController@form', 'as' => 'bank.form']); - Route::post('bank/{bank}/form', ['uses' => 'Import\BankController@postForm', 'as' => 'bank.form.post']); + Route::get('bank/{bank}/create', ['uses' => 'Import\BankController@createJob', 'as' => 'bank.create-job']); + Route:: get('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@configure', 'as' => 'bank.configure']); + Route::post('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@postConfigure', 'as' => 'bank.configure.post']); } ); From 17684a3ab39556a4b2b90b1fd3dbae8e87bfac62 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 19:14:31 +0100 Subject: [PATCH 005/142] Fix #1046 --- app/Http/Controllers/CategoryController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 1ea9509027..4c0a759a07 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -233,6 +233,7 @@ class CategoryController extends Controller $start = null; $end = null; $periods = new Collection; + $path = route('categories.show', [$category->id]); // prep for "all" view. if ('all' === $moment) { @@ -241,6 +242,7 @@ class CategoryController extends Controller /** @var Carbon $start */ $start = null === $first ? new Carbon : $first; $end = new Carbon; + $path = route('categories.show', [$category->id,'all']); } // prep for "specific date" view. @@ -253,6 +255,7 @@ class CategoryController extends Controller 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),] ); $periods = $this->getPeriodOverview($category); + $path = route('categories.show', [$category->id, $moment]); } // prep for current period @@ -275,7 +278,7 @@ class CategoryController extends Controller ->setCategory($category)->withBudgetInformation()->withCategoryInformation(); $collector->removeFilter(InternalTransferFilter::class); $transactions = $collector->getPaginatedJournals(); - $transactions->setPath(route('categories.show', [$category->id])); + $transactions->setPath($path); return view('categories.show', compact('category', 'moment', 'transactions', 'periods', 'subTitle', 'subTitleIcon', 'start', 'end')); } From 1fec7d6271e2cf0a3b35d6f6e24ac090c2b2cf41 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 19:19:15 +0100 Subject: [PATCH 006/142] Fix #1047 --- public/js/ff/accounts/reconcile.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/public/js/ff/accounts/reconcile.js b/public/js/ff/accounts/reconcile.js index 6609efd287..4d01a0046e 100644 --- a/public/js/ff/accounts/reconcile.js +++ b/public/js/ff/accounts/reconcile.js @@ -36,10 +36,12 @@ $(function () { Respond to changes in balance statements. */ $('input[type="number"]').on('change', function () { + console.log('On change number input.'); if (reconcileStarted) { calculateBalanceDifference(); difference = balanceDifference - selectedAmount; updateDifference(); + } changedBalances = true; }); @@ -48,6 +50,7 @@ $(function () { Respond to changes in the date range. */ $('input[type="date"]').on('change', function () { + console.log('On change date input.'); if (reconcileStarted) { // hide original instructions. $('.select_transactions_instruction').hide(); @@ -67,16 +70,19 @@ $(function () { }); function storeReconcile() { + console.log('In storeReconcile.'); // get modal HTML: var ids = []; $.each($('.reconcile_checkbox:checked'), function (i, v) { ids.push($(v).data('id')); }); + console.log('Ids is ' + ids); var cleared = []; $.each($('input[class="cleared"]'), function (i, v) { var obj = $(v); cleared.push(obj.data('id')); }); + console.log('Cleared is ' + ids); var variables = { startBalance: parseFloat($('input[name="start_balance"]').val()), @@ -86,13 +92,12 @@ function storeReconcile() { transactions: ids, cleared: cleared, }; + console.log var uri = overviewUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val()); $.getJSON(uri, variables).done(function (data) { - if (data.is_zero === false) { - $('#defaultModal').empty().html(data.html).modal('show'); - } + $('#defaultModal').empty().html(data.html).modal('show'); }); } @@ -101,6 +106,7 @@ function storeReconcile() { * @param e */ function checkReconciledBox(e) { + console.log('In checkReconciledBox.'); var el = $(e.target); var amount = parseFloat(el.val()); // if checked, add to selected amount @@ -120,6 +126,7 @@ function checkReconciledBox(e) { * and put it in balanceDifference. */ function calculateBalanceDifference() { + console.log('In calculateBalanceDifference.'); var startBalance = parseFloat($('input[name="start_balance"]').val()); var endBalance = parseFloat($('input[name="end_balance"]').val()); balanceDifference = startBalance - endBalance; @@ -133,6 +140,7 @@ function calculateBalanceDifference() { * This more or less resets the reconciliation. */ function getTransactionsForRange() { + console.log('In getTransactionsForRange.'); // clear out the box: $('#transactions_holder').empty().append($('

').addClass('text-center').html('')); var uri = transactionsUri.replace('%start%', $('input[name="start_date"]').val()).replace('%end%', $('input[name="end_date"]').val()); @@ -147,6 +155,7 @@ function getTransactionsForRange() { * */ function includeClearedTransactions() { + console.log('In includeClearedTransactions.'); $.each($('input[class="cleared"]'), function (i, v) { var obj = $(v); if (obj.data('younger') === false) { @@ -160,6 +169,7 @@ function includeClearedTransactions() { * @param data */ function placeTransactions(data) { + console.log('In placeTransactions.'); $('#transactions_holder').empty().html(data.html); selectedAmount = 0; // update start + end balance when user has not touched them. @@ -191,7 +201,7 @@ function placeTransactions(data) { * @returns {boolean} */ function startReconcile() { - + console.log('In startReconcile.'); reconcileStarted = true; // hide the start button. @@ -213,6 +223,7 @@ function startReconcile() { } function updateDifference() { + console.log('In updateDifference.'); var addClass = 'text-info'; if (difference > 0) { addClass = 'text-success'; From 2365fb69b4463ea066aff8868dbc7e29bb2669ba Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 20:02:26 +0100 Subject: [PATCH 007/142] Can handle some mandatory fields (not all). --- .../Controllers/Import/BankController.php | 2 +- .../Configurator/SpectreConfigurator.php | 27 +++-- .../Configuration/Spectre/InputMandatory.php | 100 ++++++++++++++++++ .../Configuration/Spectre/SelectCountry.php | 4 +- .../{SelectBank.php => SelectProvider.php} | 11 +- resources/lang/en_US/bank.php | 21 ++-- .../views/import/spectre/input-fields.twig | 65 ++++++++++++ .../views/import/spectre/select-country.twig | 2 +- ...{select-bank.twig => select-provider.twig} | 4 +- 9 files changed, 207 insertions(+), 29 deletions(-) create mode 100644 app/Support/Import/Configuration/Spectre/InputMandatory.php rename app/Support/Import/Configuration/Spectre/{SelectBank.php => SelectProvider.php} (81%) create mode 100644 resources/views/import/spectre/input-fields.twig rename resources/views/import/spectre/{select-bank.twig => select-provider.twig} (91%) diff --git a/app/Http/Controllers/Import/BankController.php b/app/Http/Controllers/Import/BankController.php index 0bf671d4dc..125f8968d3 100644 --- a/app/Http/Controllers/Import/BankController.php +++ b/app/Http/Controllers/Import/BankController.php @@ -52,7 +52,7 @@ class BankController extends Controller } $importJob = $repository->create($bank); - return redirect(route('import.bank.configure', [$bank, $importJob->key])); + return redirect(route('import.file.configure', [$importJob->key])); } /** diff --git a/app/Import/Configurator/SpectreConfigurator.php b/app/Import/Configurator/SpectreConfigurator.php index 9a8d295101..da118f8363 100644 --- a/app/Import/Configurator/SpectreConfigurator.php +++ b/app/Import/Configurator/SpectreConfigurator.php @@ -25,7 +25,8 @@ namespace FireflyIII\Import\Configurator; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; -use FireflyIII\Support\Import\Configuration\Spectre\SelectBank; +use FireflyIII\Support\Import\Configuration\Spectre\SelectProvider; +use FireflyIII\Support\Import\Configuration\Spectre\InputMandatory; use FireflyIII\Support\Import\Configuration\Spectre\SelectCountry; use Log; @@ -97,8 +98,11 @@ class SpectreConfigurator implements ConfiguratorInterface if (!$this->job->configuration['selected-country']) { return 'import.spectre.select-country'; } - if (!$this->job->configuration['selected-bank']) { - return 'import.spectre.select-bank'; + if (!$this->job->configuration['selected-provider']) { + return 'import.spectre.select-provider'; + } + if (!$this->job->configuration['has-input-mandatory']) { + return 'import.spectre.input-fields'; } throw new FireflyException('No view for state'); @@ -119,13 +123,14 @@ class SpectreConfigurator implements ConfiguratorInterface */ public function isJobConfigured(): bool { - $config = $this->job->configuration; - $config['selected-country'] = $config['selected-country'] ?? false; - $config['selected-bank'] = $config['selected-bank'] ?? false; - $this->job->configuration = $config; + $config = $this->job->configuration; + $config['selected-country'] = $config['selected-country'] ?? false; + $config['selected-provider'] = $config['selected-provider'] ?? false; + $config['has-input-mandatory'] = $config['has-input-mandatory'] ?? false; + $this->job->configuration = $config; $this->job->save(); - if ($config['selected-country'] && $config['selected-bank'] && false) { + if ($config['selected-country'] && $config['selected-provider'] && $config['has-input-mandatory'] && false) { return true; } @@ -159,9 +164,11 @@ class SpectreConfigurator implements ConfiguratorInterface case !$this->job->configuration['selected-country']: $class = SelectCountry::class; break; - case !$this->job->configuration['selected-bank']: - $class = SelectBank::class; + case !$this->job->configuration['selected-provider']: + $class = SelectProvider::class; break; + case !$this->job->configuration['has-input-mandatory']: + $class = InputMandatory::class; default: break; } diff --git a/app/Support/Import/Configuration/Spectre/InputMandatory.php b/app/Support/Import/Configuration/Spectre/InputMandatory.php new file mode 100644 index 0000000000..818f21878c --- /dev/null +++ b/app/Support/Import/Configuration/Spectre/InputMandatory.php @@ -0,0 +1,100 @@ +job->configuration; + $providerId = $config['provider']; + $provider = SpectreProvider::where('spectre_id', $providerId)->first(); + if (is_null($provider)) { + throw new FireflyException(sprintf('Cannot find Spectre provider with ID #%d', $providerId)); + } + $fields = $provider->data['required_fields'] ?? []; + $positions = []; + // Obtain a list of columns + foreach ($fields as $key => $row) { + $positions[$key] = $row['position']; + } + array_multisort($positions, SORT_ASC, $fields); + $country = SelectCountry::$allCountries[$config['country']] ?? $config['country']; + + return compact('provider', 'country', 'fields'); + } + + /** + * Return possible warning to user. + * + * @return string + */ + public function getWarningMessage(): string + { + return ''; + } + + /** + * @param ImportJob $job + * + * @return ConfigurationInterface + */ + public function setJob(ImportJob $job) + { + $this->job = $job; + } + + /** + * Store the result. + * + * @param array $data + * + * @return bool + */ + public function storeConfiguration(array $data): bool + { + $config = $this->job->configuration; + $providerId = $config['provider']; + $provider = SpectreProvider::where('spectre_id', $providerId)->first(); + if (is_null($provider)) { + throw new FireflyException(sprintf('Cannot find Spectre provider with ID #%d', $providerId)); + } + $mandatory = []; + $fields = $provider->data['required_fields'] ?? []; + foreach ($fields as $field) { + $name = $field['name']; + $mandatory[$name] = app('crypt')->encrypt($data[$name]) ?? null; + } + + // store in config of job: + $config['mandatory-fields'] = $mandatory; + $config['has-input-mandatory'] = true; + $this->job->configuration = $config; + $this->job->save(); + + // try to grab login for this job. See what happens? + // fire job that creates login object. user is redirected to "wait here" page (status page). Page should + // refresh and go back to interactive when user is supposed to enter SMS code or something. + // otherwise start downloading stuff + + return true; + } +} \ No newline at end of file diff --git a/app/Support/Import/Configuration/Spectre/SelectCountry.php b/app/Support/Import/Configuration/Spectre/SelectCountry.php index de49d9545d..e34c15b4e7 100644 --- a/app/Support/Import/Configuration/Spectre/SelectCountry.php +++ b/app/Support/Import/Configuration/Spectre/SelectCountry.php @@ -13,7 +13,7 @@ use FireflyIII\Support\Import\Configuration\ConfigurationInterface; */ class SelectCountry implements ConfigurationInterface { - private $allCountries + public static $allCountries = [ 'AF' => 'Afghanistan', 'AX' => 'Aland Islands', @@ -282,7 +282,7 @@ class SelectCountry implements ConfigurationInterface $countries = []; /** @var SpectreProvider $provider */ foreach ($providers as $provider) { - $countries[$provider->country_code] = $this->allCountries[$provider->country_code] ?? $provider->country_code; + $countries[$provider->country_code] = self::$allCountries[$provider->country_code] ?? $provider->country_code; } asort($countries); diff --git a/app/Support/Import/Configuration/Spectre/SelectBank.php b/app/Support/Import/Configuration/Spectre/SelectProvider.php similarity index 81% rename from app/Support/Import/Configuration/Spectre/SelectBank.php rename to app/Support/Import/Configuration/Spectre/SelectProvider.php index 1a9622f710..ec41ed78c5 100644 --- a/app/Support/Import/Configuration/Spectre/SelectBank.php +++ b/app/Support/Import/Configuration/Spectre/SelectProvider.php @@ -9,9 +9,9 @@ use FireflyIII\Models\SpectreProvider; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; /** - * Class SelectBank + * Class SelectProvider */ -class SelectBank implements ConfigurationInterface +class SelectProvider implements ConfigurationInterface { /** @var ImportJob */ private $job; @@ -32,8 +32,9 @@ class SelectBank implements ConfigurationInterface $name = $provider->data['name']; $providers[$providerId] = $name; } + $country = SelectCountry::$allCountries[$config['country']] ?? $config['country']; - return compact('providers'); + return compact('providers', 'country'); } /** @@ -66,8 +67,8 @@ class SelectBank implements ConfigurationInterface public function storeConfiguration(array $data): bool { $config = $this->job->configuration; - $config['bank'] = intval($data['bank_code']) ?? 0; // default to fake country. - $config['selected-bank'] = true; + $config['provider'] = intval($data['provider_code']) ?? 0; // default to fake country. + $config['selected-provider'] = true; $this->job->configuration = $config; $this->job->save(); diff --git a/resources/lang/en_US/bank.php b/resources/lang/en_US/bank.php index 1c970e1e7e..34e8812920 100644 --- a/resources/lang/en_US/bank.php +++ b/resources/lang/en_US/bank.php @@ -3,14 +3,19 @@ declare(strict_types=1); return [ - '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_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.', // Spectre: - 'spectre_title' => 'Import using Spectre', - 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', - 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', - 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', - 'spectre_select_country_title' => 'Select a country', - 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_select_provider_title' => 'Select a bank', + 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', + 'spectre_input_fields_title' => 'Input mandatory fields', + 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', + 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', ]; diff --git a/resources/views/import/spectre/input-fields.twig b/resources/views/import/spectre/input-fields.twig new file mode 100644 index 0000000000..de77ba5b3f --- /dev/null +++ b/resources/views/import/spectre/input-fields.twig @@ -0,0 +1,65 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} +

+
+ +
+
+
+

{{ trans('bank.spectre_input_fields_title') }}

+
+
+
+
+

+ {{ trans('bank.spectre_input_fields_text',{provider: data.provider.data.name, country: data.country})|raw }} +

+

+ {{ trans('bank.spectre_instructions_english') }} +

+

+ {{ data.provider.data.instruction|nl2br }} +

+
+
+ +
+
+ {% for field in data.fields %} + {# text, password, select, file #} + {% if field.nature == 'text' %} + {{ ExpandedForm.text(field.name,null, {label: field.english_name ~ ' ('~field.localized_name~')'}) }} + {% endif %} + {% if field.nature == 'password' %} + {{ ExpandedForm.password(field.name, {label: field.english_name ~ ' ('~field.localized_name~')'}) }} + {% endif %} + {% if field.nature == 'select' %} + DO NOT SUPPORT + {{ dump(field) }} + {% endif %} + {% if field.narture == 'file' %} + DO NOT SUPPORT + {{ dump(field) }} + {% endif %} + + {% endfor %} +
+
+ +
+
+ +
+{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/resources/views/import/spectre/select-country.twig b/resources/views/import/spectre/select-country.twig index 5c4f36a199..ba4f4a0c59 100644 --- a/resources/views/import/spectre/select-country.twig +++ b/resources/views/import/spectre/select-country.twig @@ -16,7 +16,7 @@

- {{ trans('bank.spectre_select_country_text') }} + {{ trans('bank.spectre_select_country_text')|raw }}

diff --git a/resources/views/import/spectre/select-bank.twig b/resources/views/import/spectre/select-provider.twig similarity index 91% rename from resources/views/import/spectre/select-bank.twig rename to resources/views/import/spectre/select-provider.twig index af0a8d56f4..5be257d082 100644 --- a/resources/views/import/spectre/select-bank.twig +++ b/resources/views/import/spectre/select-provider.twig @@ -16,14 +16,14 @@

- {{ trans('bank.spectre_select_bank_title') }} + {{ trans('bank.spectre_select_provider_text',{country: data.country})|raw }}

- {{ ExpandedForm.select('bank_code', data.providers, null)|raw }} + {{ ExpandedForm.select('provider_code', data.providers, null)|raw }}
diff --git a/resources/views/reports/options/account.twig b/resources/views/reports/options/account.twig new file mode 100644 index 0000000000..34792dc3cf --- /dev/null +++ b/resources/views/reports/options/account.twig @@ -0,0 +1,10 @@ +
+ +
+ +
+
diff --git a/routes/web.php b/routes/web.php index 86b29cb942..099baa9b65 100755 --- a/routes/web.php +++ b/routes/web.php @@ -567,6 +567,7 @@ Route::group( Route::get('category/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'ReportController@categoryReport', 'as' => 'report.category']); Route::get('budget/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'ReportController@budgetReport', 'as' => 'report.budget']); Route::get('tag/{accountList}/{tagList}/{start_date}/{end_date}', ['uses' => 'ReportController@tagReport', 'as' => 'report.tag']); + Route::get('account/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ReportController@accountReport', 'as' => 'report.account']); Route::post('', ['uses' => 'ReportController@postIndex', 'as' => 'index.post']); } From 583f376f1120875948e6a62fda799663c8049140 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 21:49:33 +0100 Subject: [PATCH 010/142] Updated composer file. --- composer.lock | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/composer.lock b/composer.lock index 60d58eb54b..7d197e7c1a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "a68c1a305e8e57a21936600540421f11", + "content-hash": "43695e0e3a5d775ae8fbd13e70bf95b7", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1058,36 +1058,43 @@ }, { "name": "league/csv", - "version": "8.2.2", + "version": "9.1.1", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "fa8bc05f64eb6c66b96edfaf60648f022ecb5f55" + "reference": "66118f5c2a7e4da77e743e69f74773c63b73e8f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/fa8bc05f64eb6c66b96edfaf60648f022ecb5f55", - "reference": "fa8bc05f64eb6c66b96edfaf60648f022ecb5f55", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/66118f5c2a7e4da77e743e69f74773c63b73e8f9", + "reference": "66118f5c2a7e4da77e743e69f74773c63b73e8f9", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=5.5.0" + "php": ">=7.0.10" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^1.9", - "phpunit/phpunit": "^4.0" + "ext-curl": "*", + "friendsofphp/php-cs-fixer": "^2.0", + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "8.2-dev" + "dev-master": "9.x-dev" } }, "autoload": { "psr-4": { "League\\Csv\\": "src" - } + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1111,7 +1118,7 @@ "read", "write" ], - "time": "2017-07-12T07:18:20+00:00" + "time": "2017-11-28T08:29:49+00:00" }, { "name": "league/flysystem", From 9bf891f3fb060faf3313eb8f05c539ad4d36871c Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 9 Dec 2017 21:50:23 +0100 Subject: [PATCH 011/142] Add newline to file. [skip ci] --- resources/views/accounts/reconcile/transactions.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/accounts/reconcile/transactions.twig b/resources/views/accounts/reconcile/transactions.twig index fb1ddbf4eb..446c0b436f 100644 --- a/resources/views/accounts/reconcile/transactions.twig +++ b/resources/views/accounts/reconcile/transactions.twig @@ -130,4 +130,4 @@ {% set startSet = true %} {% endif %} - \ No newline at end of file + From 089214709fe77be33c05b4d599e7936927e98016 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 08:56:11 +0100 Subject: [PATCH 012/142] Remove wrong copyright blocks [skip ci] --- app/Console/Commands/UseEncryption.php | 8 -- app/Console/Kernel.php | 9 -- app/Exceptions/Handler.php | 9 -- .../Auth/ForgotPasswordController.php | 9 -- app/Http/Controllers/Auth/LoginController.php | 9 -- .../Controllers/Auth/RegisterController.php | 9 -- .../Auth/ResetPasswordController.php | 9 -- app/Http/Kernel.php | 9 -- app/Http/Middleware/EncryptCookies.php | 9 -- .../Middleware/RedirectIfAuthenticated.php | 9 -- app/Http/Middleware/TrimStrings.php | 9 -- app/Http/Middleware/TrustProxies.php | 9 -- app/Http/Middleware/VerifyCsrfToken.php | 9 -- app/Import/Specifics/SnsDescription.php | 11 +-- app/Mail/RegisteredUser.php | 9 -- app/Mail/RequestedNewPassword.php | 9 -- app/Providers/AuthServiceProvider.php | 9 -- app/Providers/BroadcastServiceProvider.php | 9 -- app/Providers/RouteServiceProvider.php | 9 -- composer.json | 2 +- database/factories/ModelFactory.php | 9 -- ...016_06_16_000000_create_support_tables.php | 9 -- .../2016_06_16_000001_create_users_table.php | 9 -- .../2016_06_16_000002_create_main_tables.php | 9 -- .../2016_08_25_091522_changes_for_3101.php | 9 -- .../2016_09_12_121359_fix_nullables.php | 9 -- ...10_09_150037_expand_transactions_table.php | 9 -- .../2016_10_22_075804_changes_for_v410.php | 9 -- .../2016_11_24_210552_changes_for_v420.php | 9 -- .../2016_12_22_150431_changes_for_v430.php | 9 -- .../2016_12_28_203205_changes_for_v431.php | 9 -- .../2017_04_13_163623_changes_for_v440.php | 9 -- .../2017_06_02_105232_changes_for_v450.php | 9 -- database/seeds/AccountTypeSeeder.php | 9 -- database/seeds/DatabaseSeeder.php | 9 -- database/seeds/LinkTypeSeeder.php | 8 -- database/seeds/PermissionSeeder.php | 9 -- database/seeds/TransactionCurrencySeeder.php | 9 -- database/seeds/TransactionTypeSeeder.php | 9 -- phpunit.coverage.xml | 9 -- resources/lang/en_US/auth.php | 11 --- resources/lang/en_US/breadcrumbs.php | 12 --- resources/lang/en_US/config.php | 11 --- resources/lang/en_US/csv.php | 10 -- resources/lang/en_US/demo.php | 9 -- resources/lang/en_US/firefly.php | 99 +++++++++---------- resources/lang/en_US/form.php | 11 --- resources/lang/en_US/intro.php | 9 -- resources/lang/en_US/list.php | 11 --- resources/lang/en_US/pagination.php | 11 --- resources/lang/en_US/passwords.php | 11 --- resources/lang/en_US/validation.php | 11 --- 52 files changed, 47 insertions(+), 522 deletions(-) diff --git a/app/Console/Commands/UseEncryption.php b/app/Console/Commands/UseEncryption.php index 648533fa20..fcb5d2babe 100644 --- a/app/Console/Commands/UseEncryption.php +++ b/app/Console/Commands/UseEncryption.php @@ -21,14 +21,6 @@ */ declare(strict_types=1); -/** - * UseEncryption.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Console\Commands; use Illuminate\Console\Command; diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index b444bbef42..7ef7eade7c 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * Kernel.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Console; use Illuminate\Console\Scheduling\Schedule; diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 02e1670b03..788b5f8731 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * Handler.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Exceptions; use ErrorException; diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php index e969737770..9a475eefd3 100644 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * ForgotPasswordController.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Controllers\Auth; use FireflyIII\Http\Controllers\Controller; diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 829915e5ef..57f16dfef1 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * LoginController.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Controllers\Auth; use FireflyConfig; diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 7125a0de68..d6f961f685 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * RegisterController.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Controllers\Auth; use FireflyConfig; diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php index 9009af2485..db288d8d3f 100644 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * ResetPasswordController.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Controllers\Auth; use FireflyIII\Http\Controllers\Controller; diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index d1e66021d3..dc9ae702cc 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * Kernel.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http; use FireflyIII\Http\Middleware\Authenticate; diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index ef7bcceca5..c54f9920c4 100644 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * EncryptCookies.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Middleware; use Illuminate\Cookie\Middleware\EncryptCookies as Middleware; diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 8b6e5232fb..174fe612cb 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * RedirectIfAuthenticated.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Middleware; use Closure; diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index 21d5495130..5cf7ccd723 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * TrimStrings.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Middleware; use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware; diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 62ef14a3f3..b66bca8c09 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * TrustProxies.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Middleware; use Fideloper\Proxy\TrustProxies as Middleware; diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 838f340dcc..82e05ffddd 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * VerifyCsrfToken.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; diff --git a/app/Import/Specifics/SnsDescription.php b/app/Import/Specifics/SnsDescription.php index 3f52171846..c4fa347421 100644 --- a/app/Import/Specifics/SnsDescription.php +++ b/app/Import/Specifics/SnsDescription.php @@ -1,7 +1,7 @@ . */ -/** - * snsDescription.php - * Author 2017 hugovanduijn@gmail.com. - * - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ declare(strict_types=1); namespace FireflyIII\Import\Specifics; diff --git a/app/Mail/RegisteredUser.php b/app/Mail/RegisteredUser.php index 1e01647930..c8a6be8920 100644 --- a/app/Mail/RegisteredUser.php +++ b/app/Mail/RegisteredUser.php @@ -21,15 +21,6 @@ */ declare(strict_types=1); -/** - * RegisteredUser.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Mail; use Illuminate\Bus\Queueable; diff --git a/app/Mail/RequestedNewPassword.php b/app/Mail/RequestedNewPassword.php index 6041424d62..4545095120 100644 --- a/app/Mail/RequestedNewPassword.php +++ b/app/Mail/RequestedNewPassword.php @@ -21,15 +21,6 @@ */ declare(strict_types=1); -/** - * RequestedNewPassword.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Mail; use Illuminate\Bus\Queueable; diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index b70d194202..bee943223f 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * AuthServiceProvider.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php index 0d6648fe02..674ed8dbda 100644 --- a/app/Providers/BroadcastServiceProvider.php +++ b/app/Providers/BroadcastServiceProvider.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * BroadcastServiceProvider.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Providers; use Illuminate\Support\Facades\Broadcast; diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 1be7d7c064..5637e53681 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -20,15 +20,6 @@ */ declare(strict_types=1); -/** - * RouteServiceProvider.php - * Copyright (c) 2017 thegrumpydictator@gmail.com - * This software may be modified and distributed under the terms of the - * Creative Commons Attribution-ShareAlike 4.0 International License. - * - * See the LICENSE file for details. - */ - namespace FireflyIII\Providers; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; diff --git a/composer.json b/composer.json index 4308b846be..36f451efe8 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "transfers", "management" ], - "license": "Creative Commons Attribution-ShareAlike 4.0 International License", + "license": "GPLv3", "homepage": "https://github.com/firefly-iii/firefly-iii", "type": "project", "authors": [ diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index 85bedc97cf..cd2bbb02a3 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -1,13 +1,4 @@ - - 'Home', 'edit_currency' => 'Edit currency ":name"', diff --git a/resources/lang/en_US/config.php b/resources/lang/en_US/config.php index 9fe187d98c..0579902b74 100644 --- a/resources/lang/en_US/config.php +++ b/resources/lang/en_US/config.php @@ -1,16 +1,5 @@ 'en, English, en_US, en_US.utf8, en_US.UTF-8', 'month' => '%B %Y', diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 4586dd6ac5..6043bf1e0c 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -1,14 +1,4 @@ 'Sorry, there is no extra demo-explanation text for this page.', 'see_help_icon' => 'However, the -icon in the top right corner may tell you more.', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 78058c3f35..06497d765f 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1,16 +1,5 @@ 'Close', @@ -874,53 +863,55 @@ return [ 'account_role_ccAsset' => 'Credit card', 'budget_chart_click' => 'Please click on a budget name in the table above to see a chart.', 'category_chart_click' => 'Please click on a category name in the table above to see a chart.', - - 'in_out_accounts' => 'Earned and spent per combined account', - 'in_out_accounts_period' => 'Earned and spent, grouped by :period', - 'in_out_per_category' => 'Earned and spent per category', - 'out_per_budget' => 'Spent per budget', + 'in_out_accounts' => 'Earned and spent per combined account', + 'in_out_accounts_period_day' => 'Earned and spent, grouped by day', + 'in_out_accounts_period_month' => 'Earned and spent, grouped by month', + 'in_out_accounts_period_year' => 'Earned and spent, grouped by year', + 'in_out_per_category' => 'Earned and spent per category', + 'out_per_budget' => 'Spent per budget', + 'select_expense_revenue' => 'Select expense/revenue account', // charts: - 'chart' => 'Chart', - 'month' => 'Month', - 'budget' => 'Budget', - 'spent' => 'Spent', - 'spent_in_budget' => 'Spent in budget', - 'left_to_spend' => 'Left to spend', - 'earned' => 'Earned', - 'overspent' => 'Overspent', - 'left' => 'Left', - 'max-amount' => 'Maximum amount', - 'min-amount' => 'Minumum amount', - 'journal-amount' => 'Current bill entry', - 'name' => 'Name', - 'date' => 'Date', - 'paid' => 'Paid', - 'unpaid' => 'Unpaid', - 'day' => 'Day', - 'budgeted' => 'Budgeted', - 'period' => 'Period', - 'balance' => 'Balance', - 'sum' => 'Sum', - 'average' => 'Average', - 'balanceFor' => 'Balance for :name', + 'chart' => 'Chart', + 'month' => 'Month', + 'budget' => 'Budget', + 'spent' => 'Spent', + 'spent_in_budget' => 'Spent in budget', + 'left_to_spend' => 'Left to spend', + 'earned' => 'Earned', + 'overspent' => 'Overspent', + 'left' => 'Left', + 'max-amount' => 'Maximum amount', + 'min-amount' => 'Minumum amount', + 'journal-amount' => 'Current bill entry', + 'name' => 'Name', + 'date' => 'Date', + 'paid' => 'Paid', + 'unpaid' => 'Unpaid', + 'day' => 'Day', + 'budgeted' => 'Budgeted', + 'period' => 'Period', + 'balance' => 'Balance', + 'sum' => 'Sum', + 'average' => 'Average', + 'balanceFor' => 'Balance for :name', // piggy banks: - 'add_money_to_piggy' => 'Add money to piggy bank ":name"', - 'piggy_bank' => 'Piggy bank', - 'new_piggy_bank' => 'New piggy bank', - 'store_piggy_bank' => 'Store new piggy bank', - 'stored_piggy_bank' => 'Store new piggy bank ":name"', - 'account_status' => 'Account status', - 'left_for_piggy_banks' => 'Left for piggy banks', - 'sum_of_piggy_banks' => 'Sum of piggy banks', - 'saved_so_far' => 'Saved so far', - 'left_to_save' => 'Left to save', - 'suggested_amount' => 'Suggested monthly amount to save', - 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', - 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', - 'add' => 'Add', - 'no_money_for_piggy' => 'You have no money to put in this piggy bank.', + 'add_money_to_piggy' => 'Add money to piggy bank ":name"', + 'piggy_bank' => 'Piggy bank', + 'new_piggy_bank' => 'New piggy bank', + 'store_piggy_bank' => 'Store new piggy bank', + 'stored_piggy_bank' => 'Store new piggy bank ":name"', + 'account_status' => 'Account status', + 'left_for_piggy_banks' => 'Left for piggy banks', + 'sum_of_piggy_banks' => 'Sum of piggy banks', + 'saved_so_far' => 'Saved so far', + 'left_to_save' => 'Left to save', + 'suggested_amount' => 'Suggested monthly amount to save', + 'add_money_to_piggy_title' => 'Add money to piggy bank ":name"', + 'remove_money_from_piggy_title' => 'Remove money from piggy bank ":name"', + 'add' => 'Add', + 'no_money_for_piggy' => 'You have no money to put in this piggy bank.', 'remove' => 'Remove', 'max_amount_add' => 'The maximum amount you can add is', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 6d7a98c2fe..20c76d5daa 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -1,16 +1,5 @@ 'Welcome to the index page of Firefly III. Please take the time to walk through this intro to get a feeling of how Firefly III works.', diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index 8129b082fc..55e73e3e43 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -1,16 +1,5 @@ 'Buttons', 'icon' => 'Icon', diff --git a/resources/lang/en_US/pagination.php b/resources/lang/en_US/pagination.php index 6e1c86af46..d3016a093a 100644 --- a/resources/lang/en_US/pagination.php +++ b/resources/lang/en_US/pagination.php @@ -1,16 +1,5 @@ '« Previous', diff --git a/resources/lang/en_US/passwords.php b/resources/lang/en_US/passwords.php index aae1d74962..515a252b46 100644 --- a/resources/lang/en_US/passwords.php +++ b/resources/lang/en_US/passwords.php @@ -1,16 +1,5 @@ 'Passwords must be at least six characters and match the confirmation.', 'user' => 'We can\'t find a user with that e-mail address.', diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 9ea55191b5..569840b794 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -1,16 +1,5 @@ 'This is not a valid IBAN.', 'unique_account_number_for_user' => 'It looks like this account number is already in use.', From 2a87add74552fce9b97ddb6acc7cc30561aab542 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 08:56:20 +0100 Subject: [PATCH 013/142] New stuff for report. --- .../Report/Account/MonthReportGenerator.php | 17 +- .../Account/MultiYearReportGenerator.php | 7 + .../Report/Account/YearReportGenerator.php | 7 + .../Controllers/Report/ExpenseController.php | 15 + public/js/ff/reports/account/all.js | 8 + public/js/ff/reports/account/month.js | 21 ++ resources/views/reports/account/report.twig | 320 +----------------- routes/web.php | 51 ++- 8 files changed, 127 insertions(+), 319 deletions(-) create mode 100644 app/Http/Controllers/Report/ExpenseController.php create mode 100644 public/js/ff/reports/account/all.js create mode 100644 public/js/ff/reports/account/month.js diff --git a/app/Generator/Report/Account/MonthReportGenerator.php b/app/Generator/Report/Account/MonthReportGenerator.php index 9e0e54a504..12d5e8dc15 100644 --- a/app/Generator/Report/Account/MonthReportGenerator.php +++ b/app/Generator/Report/Account/MonthReportGenerator.php @@ -47,13 +47,14 @@ class MonthReportGenerator implements ReportGeneratorInterface */ public function generate(): string { - $accountIds = join(',', $this->accounts->pluck('id')->toArray()); - $expenseIds = join(',', $this->expense->pluck('id')->toArray()); - $reportType = 'account'; + $accountIds = join(',', $this->accounts->pluck('id')->toArray()); + $expenseIds = join(',', $this->expense->pluck('id')->toArray()); + $reportType = 'account'; + $preferredPeriod = $this->preferredPeriod(); return view( 'reports.account.report', - compact('accountIds', 'reportType','expenseIds') + compact('accountIds', 'reportType', 'expenseIds', 'preferredPeriod') )->with('start', $this->start)->with('end', $this->end)->render(); } @@ -134,4 +135,12 @@ class MonthReportGenerator implements ReportGeneratorInterface { return $this; } + + /** + * @return string + */ + protected function preferredPeriod(): string + { + return 'day'; + } } diff --git a/app/Generator/Report/Account/MultiYearReportGenerator.php b/app/Generator/Report/Account/MultiYearReportGenerator.php index cd88b4ea75..16fd8e2767 100644 --- a/app/Generator/Report/Account/MultiYearReportGenerator.php +++ b/app/Generator/Report/Account/MultiYearReportGenerator.php @@ -28,4 +28,11 @@ namespace FireflyIII\Generator\Report\Account; class MultiYearReportGenerator extends MonthReportGenerator { // Doesn't do anything different. + + /** + * @return string + */ + protected function preferredPeriod(): string { + return 'year'; + } } diff --git a/app/Generator/Report/Account/YearReportGenerator.php b/app/Generator/Report/Account/YearReportGenerator.php index 3debf38d0c..cd4854710b 100644 --- a/app/Generator/Report/Account/YearReportGenerator.php +++ b/app/Generator/Report/Account/YearReportGenerator.php @@ -28,4 +28,11 @@ namespace FireflyIII\Generator\Report\Account; class YearReportGenerator extends MonthReportGenerator { // Doesn't do anything different. + + /** + * @return string + */ + protected function preferredPeriod(): string { + return 'month'; + } } diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php new file mode 100644 index 0000000000..1c4d2372fb --- /dev/null +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -0,0 +1,15 @@ +
-
+
-

{{ 'in_out_accounts_period'|_ }}

+

{{ ('in_out_accounts_period_'~preferredPeriod)|_ }}

-
+
{# loading indicator #}
@@ -89,7 +89,7 @@

{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})

-
+
{# loading indicator #}
@@ -112,297 +112,6 @@
- {# -
- {% if categories.count > 1 %} -
-
-
-

{{ 'income_per_category'|_ }}

-
-
- - -
-
-
-
-
-
-

{{ 'expense_per_category'|_ }}

-
-
- - -
-
-
- {% endif %} -
-
-
-

{{ 'income_per_account'|_ }}

-
-
- - -
-
-
-
-
-
-

{{ 'expense_per_account'|_ }}

-
-
- - -
-
-
-
- -
-
-
-
-

{{ 'income_and_expenses'|_ }}

-
-
- -
-
-
-
-
- {% if averageExpenses|length > 0 %} -
-
-
-

{{ 'average_spending_per_account'|_ }}

-
-
- - - - - - - - - - - {% for row in averageExpenses %} - {% if loop.index > listLength %} - - {% else %} - - {% endif %} - - - - - - {% endfor %} - - - {% if averageExpenses|length > listLength %} - - - - {% endif %} - -
{{ 'account'|_ }}{{ 'spent_average'|_ }}{{ 'total'|_ }}{{ 'transaction_count'|_ }}
- {{ row.name }} - - {{ row.average|formatAmount }} - - {{ row.sum|formatAmount }} - - {{ row.count }} -
- {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} -
-
-
-
- {% endif %} - {% if topExpenses.count > 0 %} -
- -
-
-

{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})

-
-
- - - - - - - - - - - {% for row in topExpenses %} - {% if loop.index > listLength %} - - {% else %} - - {% endif %} - - - - - - {% endfor %} - - - {% if topExpenses|length > listLength %} - - - - {% endif %} - -
{{ 'description'|_ }}{{ 'date'|_ }}{{ 'account'|_ }}{{ 'amount'|_ }}
- - {% if row.transaction_description|length > 0 %} - {{ row.transaction_description }} ({{ row.description }}) - {% else %} - {{ row.description }} - {% endif %} - - - {{ row.date.formatLocalized(monthAndDayFormat) }} - - - {{ row.opposing_account_name }} - - - {{ row.transaction_amount|formatAmount }} -
- {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} -
-
-
-
- {% endif %} -
-
- {% if averageIncome|length > 0 %} -
-
-
-

{{ 'average_income_per_account'|_ }}

-
-
- - - - - - - - - - - {% for row in averageIncome %} - - - - - - - {% endfor %} - -
{{ 'account'|_ }}{{ 'income_average'|_ }}{{ 'total'|_ }}{{ 'transaction_count'|_ }}
- {{ row.name }} - - {{ row.average|formatAmount }} - - {{ row.sum|formatAmount }} - - {{ row.count }} -
-
-
-
- {% endif %} -
- {% if topIncome.count > 0 %} -
-
-

{{ 'income'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})

-
-
- - - - - - - - - - - {% for row in topIncome %} - {% if loop.index > listLength %} - - {% else %} - - {% endif %} - - - - - - {% endfor %} - - - {% if topIncome.count > listLength %} - - - - {% endif %} - -
{{ 'description'|_ }}{{ 'date'|_ }}{{ 'account'|_ }}{{ 'amount'|_ }}
- - {% if row.transaction_description|length > 0 %} - {{ row.transaction_description }} ({{ row.description }}) - {% else %} - {{ row.description }} - {% endif %} - - - {{ row.date.formatLocalized(monthAndDayFormat) }} - - - {{ row.opposing_account_name }} - - - {{ row.transaction_amount|formatAmount }} -
- {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} -
-
-
- {% endif %} -
-
- #} {% endblock %} @@ -417,19 +126,24 @@ var startDate = '{{ start.format('Ymd') }}'; var endDate = '{{ end.format('Ymd') }}'; var accountIds = '{{ accountIds }}'; - var categoryIds = '{{ categoryIds }}'; + var expenseIds = '{{ expenseIds }}'; // chart uri's - var categoryIncomeUri = '{{ route('chart.category.category-income', [accountIds, categoryIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; - var categoryExpenseUri = '{{ route('chart.category.category-expense', [accountIds, categoryIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; - var accountIncomeUri = '{{ route('chart.category.account-income', [accountIds, categoryIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; - var accountExpenseUri = '{{ route('chart.category.account-expense', [accountIds, categoryIds, start.format('Ymd'), end.format('Ymd'),'OTHERS']) }}'; - var mainUri = '{{ route('chart.category.main', [accountIds, categoryIds, start.format('Ymd'), end.format('Ymd')]) }}'; + var mainUri = '{{ route('chart.expense.main', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + + // boxes with stuff: + var spentUri = '{{ route('report-data.expense.spent', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + var groupedUri = '{{ route('report-data.expense.spent-grouped', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + var categoryUri = '{{ route('report-data.expense.category', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + var budgetUri = '{{ route('report-data.expense.budget', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + var expenseUri = '{{ route('report-data.expense.expenses', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + var incomeUri = '{{ route('report-data.expense.income', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; + - - + + {% endblock %} diff --git a/routes/web.php b/routes/web.php index 099baa9b65..59ca359b92 100755 --- a/routes/web.php +++ b/routes/web.php @@ -1,14 +1,4 @@ 'Account\ReconcileController@reconcile', 'as' => 'reconcile']); - Route::get('reconcile/{account}/transactions/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@transactions', 'as' => 'reconcile.transactions']); + Route::get( + 'reconcile/{account}/transactions/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@transactions', 'as' => 'reconcile.transactions'] + ); Route::get('reconcile/{account}/overview/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@overview', 'as' => 'reconcile.overview']); Route::post('reconcile/{account}/submit/{start_date?}/{end_date?}', ['uses' => 'Account\ReconcileController@submit', 'as' => 'reconcile.submit']); @@ -224,7 +216,7 @@ Route::group( ); /** - * Chart\Account Controller + * Chart\Account Controller (default report) */ Route::group( ['middleware' => 'user-full-auth', 'namespace' => 'Chart', 'prefix' => 'chart/account', 'as' => 'chart.account.'], function () { @@ -246,6 +238,7 @@ Route::group( } ); + /** * Chart\Bill Controller */ @@ -374,6 +367,19 @@ Route::group( } ); +/** + * Chart\Expense Controller (for expense/revenue report). + */ +Route::group( + ['middleware' => 'user-full-auth', 'namespace' => 'Chart', 'prefix' => 'chart/expense', 'as' => 'chart.expense.'], function () { + Route::get( + 'operations/{accountList}/{expenseList}/{start_date}/{end_date}', + ['uses' => 'ExpenseReportController@mainChart', 'as' => 'main'] + ); +} +); + + /** * Chart\PiggyBank Controller */ @@ -582,6 +588,27 @@ Route::group( } ); +/** + * Report Data Expense / Revenue Account Controller + */ +Route::group( + ['middleware' => 'user-full-auth', 'namespace' => 'Report', 'prefix' => 'report-data/expense', 'as' => 'report-data.expense.'], function () { + + // spent per period / spent grouped + Route::get('spent/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@spent', 'as' => 'spent']); + Route::get('spent-grouped/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@spentGrouped', 'as' => 'spent-grouped']); + + // per category && per budget + Route::get('category/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@category', 'as' => 'category']); + Route::get('budget/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@budget', 'as' => 'budget']); + + //expense earned top X + Route::get('expenses/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topX', 'as' => 'expenses']); + Route::get('income/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topXPeriod', 'as' => 'income']); + +} +); + /** * Report Data Income/Expenses Controller (called financial operations) */ From 6ad90c7a87db51e055c9cb8d0dd644a652b93d59 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 09:02:26 +0100 Subject: [PATCH 014/142] Update copyright stuff [skip ci] --- .../Controllers/Report/ExpenseController.php | 20 +++++++++++++++++++ bootstrap/app.php | 20 +++++++++++++++++++ database/factories/ModelFactory.php | 20 +++++++++++++++++++ ...016_06_16_000000_create_support_tables.php | 20 +++++++++++++++++++ .../2016_06_16_000001_create_users_table.php | 20 +++++++++++++++++++ .../2016_06_16_000002_create_main_tables.php | 20 +++++++++++++++++++ .../2016_08_25_091522_changes_for_3101.php | 20 +++++++++++++++++++ .../2016_09_12_121359_fix_nullables.php | 20 +++++++++++++++++++ ...10_09_150037_expand_transactions_table.php | 20 +++++++++++++++++++ .../2016_10_22_075804_changes_for_v410.php | 20 +++++++++++++++++++ .../2016_11_24_210552_changes_for_v420.php | 20 +++++++++++++++++++ .../2016_12_22_150431_changes_for_v430.php | 20 +++++++++++++++++++ .../2016_12_28_203205_changes_for_v431.php | 20 +++++++++++++++++++ .../2017_04_13_163623_changes_for_v440.php | 20 +++++++++++++++++++ .../2017_06_02_105232_changes_for_v450.php | 20 +++++++++++++++++++ .../2017_08_20_062014_changes_for_v470.php | 19 ++++++++++++++++++ .../2017_11_04_170844_changes_for_v470a.php | 19 ++++++++++++++++++ database/seeds/AccountTypeSeeder.php | 20 +++++++++++++++++++ database/seeds/DatabaseSeeder.php | 20 +++++++++++++++++++ database/seeds/LinkTypeSeeder.php | 20 +++++++++++++++++++ database/seeds/PermissionSeeder.php | 20 +++++++++++++++++++ database/seeds/TransactionCurrencySeeder.php | 20 +++++++++++++++++++ database/seeds/TransactionTypeSeeder.php | 20 +++++++++++++++++++ public/js/ff/reports/account/all.js | 20 +++++++++++++++++++ public/js/ff/reports/account/month.js | 20 +++++++++++++++++++ resources/lang/en_US/auth.php | 20 +++++++++++++++++++ resources/lang/en_US/bank.php | 20 +++++++++++++++++++ resources/lang/en_US/breadcrumbs.php | 20 +++++++++++++++++++ resources/lang/en_US/config.php | 20 +++++++++++++++++++ resources/lang/en_US/csv.php | 20 +++++++++++++++++++ resources/lang/en_US/demo.php | 20 +++++++++++++++++++ resources/lang/en_US/firefly.php | 20 +++++++++++++++++++ resources/lang/en_US/form.php | 20 +++++++++++++++++++ resources/lang/en_US/intro.php | 20 +++++++++++++++++++ resources/lang/en_US/list.php | 20 +++++++++++++++++++ resources/lang/en_US/pagination.php | 20 +++++++++++++++++++ resources/lang/en_US/passwords.php | 20 +++++++++++++++++++ resources/lang/en_US/validation.php | 20 +++++++++++++++++++ routes/api.php | 19 ++++++++++++++++++ routes/channels.php | 19 ++++++++++++++++++ routes/console.php | 19 ++++++++++++++++++ routes/web.php | 20 +++++++++++++++++++ 42 files changed, 835 insertions(+) diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index 1c4d2372fb..a738e8f529 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; diff --git a/bootstrap/app.php b/bootstrap/app.php index 1206138d26..1b508e3539 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php index cd2bbb02a3..f062df2915 100644 --- a/database/factories/ModelFactory.php +++ b/database/factories/ModelFactory.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Carbon\Carbon; diff --git a/database/migrations/2016_06_16_000000_create_support_tables.php b/database/migrations/2016_06_16_000000_create_support_tables.php index 5d78e87275..ca71717423 100644 --- a/database/migrations/2016_06_16_000000_create_support_tables.php +++ b/database/migrations/2016_06_16_000000_create_support_tables.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_06_16_000001_create_users_table.php b/database/migrations/2016_06_16_000001_create_users_table.php index deb00bb108..bbe1be7c80 100644 --- a/database/migrations/2016_06_16_000001_create_users_table.php +++ b/database/migrations/2016_06_16_000001_create_users_table.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index 31434a31d2..e25fa7b0e5 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_08_25_091522_changes_for_3101.php b/database/migrations/2016_08_25_091522_changes_for_3101.php index 2a7260d695..923122df1b 100644 --- a/database/migrations/2016_08_25_091522_changes_for_3101.php +++ b/database/migrations/2016_08_25_091522_changes_for_3101.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_09_12_121359_fix_nullables.php b/database/migrations/2016_09_12_121359_fix_nullables.php index 0ba1d0ec73..6ae393bb5c 100644 --- a/database/migrations/2016_09_12_121359_fix_nullables.php +++ b/database/migrations/2016_09_12_121359_fix_nullables.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_10_09_150037_expand_transactions_table.php b/database/migrations/2016_10_09_150037_expand_transactions_table.php index 511c65c758..9c7956f061 100644 --- a/database/migrations/2016_10_09_150037_expand_transactions_table.php +++ b/database/migrations/2016_10_09_150037_expand_transactions_table.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_10_22_075804_changes_for_v410.php b/database/migrations/2016_10_22_075804_changes_for_v410.php index ea2450f4f3..f43e497d2a 100644 --- a/database/migrations/2016_10_22_075804_changes_for_v410.php +++ b/database/migrations/2016_10_22_075804_changes_for_v410.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_11_24_210552_changes_for_v420.php b/database/migrations/2016_11_24_210552_changes_for_v420.php index 01e1594b51..39a37e5fa0 100644 --- a/database/migrations/2016_11_24_210552_changes_for_v420.php +++ b/database/migrations/2016_11_24_210552_changes_for_v420.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_12_22_150431_changes_for_v430.php b/database/migrations/2016_12_22_150431_changes_for_v430.php index 0d8530053c..dc23bc143d 100644 --- a/database/migrations/2016_12_22_150431_changes_for_v430.php +++ b/database/migrations/2016_12_22_150431_changes_for_v430.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2016_12_28_203205_changes_for_v431.php b/database/migrations/2016_12_28_203205_changes_for_v431.php index 1ab70b5082..3c27a4856a 100644 --- a/database/migrations/2016_12_28_203205_changes_for_v431.php +++ b/database/migrations/2016_12_28_203205_changes_for_v431.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2017_04_13_163623_changes_for_v440.php b/database/migrations/2017_04_13_163623_changes_for_v440.php index 6d1741e72c..e18524e7e8 100644 --- a/database/migrations/2017_04_13_163623_changes_for_v440.php +++ b/database/migrations/2017_04_13_163623_changes_for_v440.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2017_06_02_105232_changes_for_v450.php b/database/migrations/2017_06_02_105232_changes_for_v450.php index 8c3ed17d95..811ba8e1de 100644 --- a/database/migrations/2017_06_02_105232_changes_for_v450.php +++ b/database/migrations/2017_06_02_105232_changes_for_v450.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Migrations\Migration; diff --git a/database/migrations/2017_08_20_062014_changes_for_v470.php b/database/migrations/2017_08_20_062014_changes_for_v470.php index 0f1173a17f..9c6fa7ec73 100644 --- a/database/migrations/2017_08_20_062014_changes_for_v470.php +++ b/database/migrations/2017_08_20_062014_changes_for_v470.php @@ -1,4 +1,23 @@ . + */ declare(strict_types=1); diff --git a/database/migrations/2017_11_04_170844_changes_for_v470a.php b/database/migrations/2017_11_04_170844_changes_for_v470a.php index 6ebbdb0a23..4de603be49 100644 --- a/database/migrations/2017_11_04_170844_changes_for_v470a.php +++ b/database/migrations/2017_11_04_170844_changes_for_v470a.php @@ -1,4 +1,23 @@ . + */ declare(strict_types=1); diff --git a/database/seeds/AccountTypeSeeder.php b/database/seeds/AccountTypeSeeder.php index da197ec149..85bc3b7d8e 100644 --- a/database/seeds/AccountTypeSeeder.php +++ b/database/seeds/AccountTypeSeeder.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use FireflyIII\Models\AccountType; diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 3dea62c233..4326840fa2 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use Illuminate\Database\Seeder; diff --git a/database/seeds/LinkTypeSeeder.php b/database/seeds/LinkTypeSeeder.php index 96bee08d2f..64393aa296 100644 --- a/database/seeds/LinkTypeSeeder.php +++ b/database/seeds/LinkTypeSeeder.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use FireflyIII\Models\LinkType; diff --git a/database/seeds/PermissionSeeder.php b/database/seeds/PermissionSeeder.php index 2dcc304457..4c3cc140b9 100644 --- a/database/seeds/PermissionSeeder.php +++ b/database/seeds/PermissionSeeder.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use FireflyIII\Models\Role; diff --git a/database/seeds/TransactionCurrencySeeder.php b/database/seeds/TransactionCurrencySeeder.php index ba34918ac2..5c26248ab0 100644 --- a/database/seeds/TransactionCurrencySeeder.php +++ b/database/seeds/TransactionCurrencySeeder.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use FireflyIII\Models\TransactionCurrency; diff --git a/database/seeds/TransactionTypeSeeder.php b/database/seeds/TransactionTypeSeeder.php index d29d4e3dd5..823b5c4f02 100644 --- a/database/seeds/TransactionTypeSeeder.php +++ b/database/seeds/TransactionTypeSeeder.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); use FireflyIII\Models\TransactionType; diff --git a/public/js/ff/reports/account/all.js b/public/js/ff/reports/account/all.js index 8dab6cfa85..81a80886b1 100644 --- a/public/js/ff/reports/account/all.js +++ b/public/js/ff/reports/account/all.js @@ -1,3 +1,23 @@ +/* + * all.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * + * This file is part of Firefly III. + * + * Firefly III is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Firefly III is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Firefly III. If not, see . + */ + function loadAjaxPartial(holder, uri) { "use strict"; $.get(uri).done(function (data) { diff --git a/public/js/ff/reports/account/month.js b/public/js/ff/reports/account/month.js index b437a57e35..914908f4a1 100644 --- a/public/js/ff/reports/account/month.js +++ b/public/js/ff/reports/account/month.js @@ -1,4 +1,24 @@ +/* + * month.js + * Copyright (c) 2017 thegrumpydictator@gmail.com + * + * This file is part of Firefly III. + * + * Firefly III is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Firefly III is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Firefly III. If not, see . + */ + $(function () { "use strict"; drawChart(); diff --git a/resources/lang/en_US/auth.php b/resources/lang/en_US/auth.php index b7caa5b426..9b87133b5b 100644 --- a/resources/lang/en_US/auth.php +++ b/resources/lang/en_US/auth.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ diff --git a/resources/lang/en_US/bank.php b/resources/lang/en_US/bank.php index 4becc5984c..d7bbee6d98 100644 --- a/resources/lang/en_US/bank.php +++ b/resources/lang/en_US/bank.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); diff --git a/resources/lang/en_US/breadcrumbs.php b/resources/lang/en_US/breadcrumbs.php index cf706f0fe5..6c834d0d02 100644 --- a/resources/lang/en_US/breadcrumbs.php +++ b/resources/lang/en_US/breadcrumbs.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ 'home' => 'Home', diff --git a/resources/lang/en_US/config.php b/resources/lang/en_US/config.php index 0579902b74..807af7d3d1 100644 --- a/resources/lang/en_US/config.php +++ b/resources/lang/en_US/config.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ 'locale' => 'en, English, en_US, en_US.utf8, en_US.UTF-8', diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 6043bf1e0c..8889ca3aab 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ diff --git a/resources/lang/en_US/demo.php b/resources/lang/en_US/demo.php index 757f0bdb97..b1796eb4fd 100644 --- a/resources/lang/en_US/demo.php +++ b/resources/lang/en_US/demo.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ 'no_demo_text' => 'Sorry, there is no extra demo-explanation text for this page.', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 06497d765f..7f3180faf9 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ // general stuff: diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 20c76d5daa..035d405f7d 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ diff --git a/resources/lang/en_US/intro.php b/resources/lang/en_US/intro.php index ce2faaf9b9..d0277f2704 100644 --- a/resources/lang/en_US/intro.php +++ b/resources/lang/en_US/intro.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ // index diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index 55e73e3e43..4e65fd049c 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ 'buttons' => 'Buttons', diff --git a/resources/lang/en_US/pagination.php b/resources/lang/en_US/pagination.php index d3016a093a..dd10f0b21e 100644 --- a/resources/lang/en_US/pagination.php +++ b/resources/lang/en_US/pagination.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ diff --git a/resources/lang/en_US/passwords.php b/resources/lang/en_US/passwords.php index 515a252b46..a6a8f034d5 100644 --- a/resources/lang/en_US/passwords.php +++ b/resources/lang/en_US/passwords.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ 'password' => 'Passwords must be at least six characters and match the confirmation.', diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index 569840b794..e239c56077 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); return [ 'iban' => 'This is not a valid IBAN.', diff --git a/routes/api.php b/routes/api.php index c641ca5e5b..f2da7c5098 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,4 +1,23 @@ . + */ use Illuminate\Http\Request; diff --git a/routes/channels.php b/routes/channels.php index f16a20b9bf..5bdaf5a618 100644 --- a/routes/channels.php +++ b/routes/channels.php @@ -1,4 +1,23 @@ . + */ /* |-------------------------------------------------------------------------- diff --git a/routes/console.php b/routes/console.php index 75dd0cdedb..a48dab62ae 100644 --- a/routes/console.php +++ b/routes/console.php @@ -1,4 +1,23 @@ . + */ use Illuminate\Foundation\Inspiring; diff --git a/routes/web.php b/routes/web.php index 59ca359b92..9dabbc018e 100755 --- a/routes/web.php +++ b/routes/web.php @@ -1,4 +1,24 @@ . + */ + declare(strict_types=1); From 06683c57dd30c95933384e7770b90fd1bf3d583d Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 11:52:14 +0100 Subject: [PATCH 015/142] Update verify routine for issue #1048 --- .env.example | 2 +- app/Console/Commands/VerifyDatabase.php | 75 +++++++++++++++++++++- app/Http/Controllers/NewUserController.php | 4 +- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 0a7bd1b7ff..159b802948 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,7 @@ APP_DEBUG=false APP_NAME=FireflyIII APP_KEY=SomeRandomStringOf32CharsExactly APP_LOG=daily -APP_LOG_LEVEL=warning +APP_LOG_LEVEL=notice APP_URL=http://localhost TRUSTED_PROXIES= diff --git a/app/Console/Commands/VerifyDatabase.php b/app/Console/Commands/VerifyDatabase.php index 79e07b02ea..570d323af8 100644 --- a/app/Console/Commands/VerifyDatabase.php +++ b/app/Console/Commands/VerifyDatabase.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands; use Crypt; +use DB; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\Budget; @@ -93,6 +94,7 @@ class VerifyDatabase extends Command $this->repairPiggyBanks(); $this->createLinkTypes(); $this->createAccessTokens(); + $this->fixDoubleAmounts(); } /** @@ -100,6 +102,7 @@ class VerifyDatabase extends Command */ private function createAccessTokens() { + $count = 0; $users = User::get(); /** @var User $user */ foreach ($users as $user) { @@ -108,8 +111,12 @@ class VerifyDatabase extends Command $token = $user->generateAccessToken(); Preferences::setForUser($user, 'access_token', $token); $this->line(sprintf('Generated access token for user %s', $user->email)); + $count++; } } + if ($count === 0) { + $this->info('All access tokens OK!'); + } } /** @@ -117,7 +124,8 @@ class VerifyDatabase extends Command */ private function createLinkTypes() { - $set = [ + $count = 0; + $set = [ 'Related' => ['relates to', 'relates to'], 'Refund' => ['(partially) refunds', 'is (partially) refunded by'], 'Paid' => ['(partially) pays for', 'is (partially) paid for by'], @@ -130,10 +138,61 @@ class VerifyDatabase extends Command $link->name = $name; $link->outward = $values[0]; $link->inward = $values[1]; + $count++; } $link->editable = false; $link->save(); } + if ($count === 0) { + $this->info('All link types OK!'); + } + } + + private function fixDoubleAmounts() + { + $count = 0; + // get invalid journals + $errored = []; + $journals = DB::table('transactions') + ->groupBy('transaction_journal_id') + ->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]); + /** @var stdClass $entry */ + foreach ($journals as $entry) { + if (bccomp(strval($entry->the_sum), '0') !== 0) { + $errored[] = $entry->transaction_journal_id; + } + } + foreach ($errored as $journalId) { + // select and update: + $res = Transaction::where('transaction_journal_id', $journalId)->groupBy('amount')->get([DB::raw('MIN(id) as first_id')]); + $ids = $res->pluck('first_id')->toArray(); + DB::table('transactions')->whereIn('id', $ids)->update(['amount' => DB::raw('amount * -1')]); + $count++; + // report about it + /** @var TransactionJournal $journal */ + $journal = TransactionJournal::find($journalId); + if ($journal->transactionType->type === TransactionType::OPENING_BALANCE) { + $this->error( + sprintf( + 'Transaction #%d was stored incorrectly. One of your asset accounts may show the wrong balance. Please visit /transactions/show/%d to verify the opening balance.', + $journalId, $journalId + ) + ); + } + if ($journal->transactionType->type !== TransactionType::OPENING_BALANCE) { + $this->error( + sprintf( + 'Transaction #%d was stored incorrectly. Could be that the transaction shows the wrong amount. Please visit /transactions/show/%d to verify the opening balance.', + $journalId, $journalId + ) + ); + } + } + if ($count === 0) { + $this->info('Amount integrity OK!'); + } + + return; } /** @@ -141,7 +200,7 @@ class VerifyDatabase extends Command */ private function repairPiggyBanks(): void { - $set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get(); + $set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get(); $set->each( function (PiggyBankEvent $event) { if (null === $event->transaction_journal_id) { @@ -290,6 +349,7 @@ class VerifyDatabase extends Command */ private function reportJournals() { + $count = 0; $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->whereNotNull('transaction_journals.deleted_at')// USE THIS ->whereNull('transactions.deleted_at') @@ -308,6 +368,10 @@ class VerifyDatabase extends Command 'Error: Transaction #' . $entry->transaction_id . ' should have been deleted, but has not.' . ' Find it in the table called "transactions" and change the "deleted_at" field to: "' . $entry->journal_deleted . '"' ); + $count++; + } + if($count === 0) { + $this->info('No orphaned transactions!'); } } @@ -316,6 +380,7 @@ class VerifyDatabase extends Command */ private function reportNoTransactions() { + $count = 0; $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->groupBy('transaction_journals.id') ->whereNull('transactions.transaction_journal_id') @@ -325,6 +390,10 @@ class VerifyDatabase extends Command $this->error( 'Error: Journal #' . $entry->id . ' has zero transactions. Open table "transaction_journals" and delete the entry with id #' . $entry->id ); + $count++; + } + if($count === 0) { + $this->info('No orphaned journals!'); } } @@ -379,6 +448,8 @@ class VerifyDatabase extends Command $sum = strval($user->transactions()->sum('amount')); if (0 !== bccomp($sum, '0')) { $this->error('Error: Transactions for user #' . $user->id . ' (' . $user->email . ') are off by ' . $sum . '!'); + } else { + $this->info(sprintf('Amount integrity OK for user #%d', $user->id)); } } } diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index efa5ae5ab7..b8a88a0289 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -114,7 +114,7 @@ class NewUserController extends Controller 'virtualBalance' => 0, 'active' => true, 'accountRole' => 'defaultAsset', - 'openingBalance' => round($request->input('bank_balance'), 12), + 'openingBalance' => $request->input('bank_balance'), 'openingBalanceDate' => new Carbon, 'currency_id' => intval($request->input('amount_currency_id_bank_balance')), ]; @@ -139,7 +139,7 @@ class NewUserController extends Controller 'virtualBalance' => 0, 'active' => true, 'accountRole' => 'savingAsset', - 'openingBalance' => round($request->input('savings_balance'), 12), + 'openingBalance' => $request->input('savings_balance'), 'openingBalanceDate' => new Carbon, 'currency_id' => intval($request->input('amount_currency_id_bank_balance')), ]; From b93a96db23b2b51b88b0369d863586a8b64c9910 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 12:00:08 +0100 Subject: [PATCH 016/142] Expand code for #384 --- .../Controllers/Report/ExpenseController.php | 58 +++++++++++++++++++ .../Account/AccountRepository.php | 9 +-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index a738e8f529..fdf896db97 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -23,7 +23,12 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; +use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use Illuminate\Support\Collection; /** @@ -31,5 +36,58 @@ use FireflyIII\Http\Controllers\Controller; */ class ExpenseController extends Controller { + /** @var AccountRepositoryInterface */ + protected $accountRepository; + + /** + * + */ + public function __construct() + { + parent::__construct(); + + // translations: + $this->middleware( + function ($request, $next) { + $this->accountRepository = app(AccountRepositoryInterface::class); + + return $next($request); + } + ); + } + + /** + * @param Collection $accounts + * @param Collection $expense + * @param Carbon $start + * @param Carbon $end + */ + public function spentGrouped(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + $combined = $this->combineAccounts($expense); + // for period, get spent and earned for each account (by name) + /** + * @var string $name + * @var Collection $combi + */ + foreach($combined as $name => $combi) { + + } + } + + protected function combineAccounts(Collection $accounts): array + { + $combined = []; + /** @var Account $expenseAccount */ + foreach ($accounts as $expenseAccount) { + $combined[$expenseAccount->name] = [$expenseAccount]; + $revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]); + if (!is_null($revenue->id)) { + $combined[$expenseAccount->name][] = $revenue; + } + } + + return $combined; + } } \ No newline at end of file diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index cb4b8f87fc..83f97f97aa 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -160,6 +160,7 @@ class AccountRepository implements AccountRepositoryInterface * @param array $data * * @return Account + * @throws FireflyException */ public function store(array $data): Account { @@ -364,13 +365,13 @@ class AccountRepository implements AccountRepositoryInterface 'date' => $data['openingBalanceDate'], ] ); - Log::debug(sprintf('Created new opening balance journal: #%d', $journal->id)); + Log::notice(sprintf('Created new opening balance journal: #%d', $journal->id)); $firstAccount = $account; $secondAccount = $opposing; $firstAmount = $amount; $secondAmount = bcmul($amount, '-1'); - Log::debug(sprintf('First amount is %s, second amount is %s', $firstAmount, $secondAmount)); + Log::notice(sprintf('First amount is %s, second amount is %s', $firstAmount, $secondAmount)); if (bccomp($amount, '0') === -1) { Log::debug(sprintf('%s is a negative number.', $amount)); @@ -378,7 +379,7 @@ class AccountRepository implements AccountRepositoryInterface $secondAccount = $account; $firstAmount = bcmul($amount, '-1'); $secondAmount = $amount; - Log::debug(sprintf('First amount is %s, second amount is %s', $firstAmount, $secondAmount)); + Log::notice(sprintf('First amount is %s, second amount is %s', $firstAmount, $secondAmount)); } $one = new Transaction( @@ -400,7 +401,7 @@ class AccountRepository implements AccountRepositoryInterface ); $two->save(); // second transaction: to - Log::debug(sprintf('Stored two transactions, #%d and #%d', $one->id, $two->id)); + Log::notice(sprintf('Stored two transactions for new account, #%d and #%d', $one->id, $two->id)); return $journal; } From 54c449f11bc2d15ae5df7c1acd1e90e00b0b651e Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 17:55:06 +0100 Subject: [PATCH 017/142] Some code to get #384 working. --- app/Helpers/Collector/JournalCollector.php | 14 + .../Collector/JournalCollectorInterface.php | 7 + .../Controllers/Report/ExpenseController.php | 323 +++++++++++++++++- public/js/ff/reports/account/all.js | 29 ++ .../reports/partials/exp-categories.twig | 36 ++ .../views/reports/partials/exp-grouped.twig | 37 ++ .../reports/partials/exp-not-grouped.twig | 33 ++ 7 files changed, 469 insertions(+), 10 deletions(-) create mode 100644 resources/views/reports/partials/exp-categories.twig create mode 100644 resources/views/reports/partials/exp-grouped.twig create mode 100644 resources/views/reports/partials/exp-not-grouped.twig diff --git a/app/Helpers/Collector/JournalCollector.php b/app/Helpers/Collector/JournalCollector.php index 33621ad862..d719a74ee2 100644 --- a/app/Helpers/Collector/JournalCollector.php +++ b/app/Helpers/Collector/JournalCollector.php @@ -497,6 +497,20 @@ class JournalCollector implements JournalCollectorInterface return $this; } + /** + * @param Collection $accounts + * + * @return JournalCollectorInterface + */ + public function setOpposingAccounts(Collection $accounts): JournalCollectorInterface + { + $this->withOpposingAccount(); + + $this->query->whereIn('opposing.account_id', $accounts->pluck('id')->toArray()); + + return $this; + } + /** * @param int $page * diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index 5ac5e90bed..e23991e18c 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -85,6 +85,13 @@ interface JournalCollectorInterface */ public function removeFilter(string $filter): JournalCollectorInterface; + /** + * @param Collection $accounts + * + * @return JournalCollectorInterface + */ + public function setOpposingAccounts(Collection $accounts): JournalCollectorInterface; + /** * @param Collection $accounts * diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index fdf896db97..3b2e2002a0 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -24,10 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Report; use Carbon\Carbon; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; @@ -56,23 +59,150 @@ class ExpenseController extends Controller ); } + public function category(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-category'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + $combined = $this->combineAccounts($expense); + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + // now find spent / earned: + $spent = $this->spentByCategory($accounts, $all, $start, $end); + $earned = $this->earnedByCategory($accounts, $combi, $start, $end); + + // join arrays somehow: + $together = []; + foreach($spent as $categoryId => $spentInfo) { + if(!isset($together[$categoryId])) { + $together[$categoryId]['spent'] = $spentInfo; + // get category info: + $first = reset($spentInfo); + $together[$categoryId]['category'] = $first['category']; + } + } + + foreach($earned as $categoryId => $earnedInfo) { + if(!isset($together[$categoryId])) { + $together[$categoryId]['earned'] = $earnedInfo; + // get category info: + $first = reset($earnedInfo); + $together[$categoryId]['category'] = $first['category']; + } + } + + $result = view('reports.partials.exp-categories', compact('together'))->render(); + $cache->store($result); + + return $result; + } + /** * @param Collection $accounts * @param Collection $expense * @param Carbon $start * @param Carbon $end + * + * @return array|mixed|string + * @throws \Throwable + */ + public function spent(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // chart properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-spent'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + + $combined = $this->combineAccounts($expense); + $result = []; + + foreach ($combined as $name => $combi) { + /** + * @var string $name + * @var Collection $combi + */ + $spent = $this->spentInPeriod($accounts, $combi, $start, $end); + $earned = $this->earnedInPeriod($accounts, $combi, $start, $end); + $result[$name] = [ + 'spent' => $spent, + 'earned' => $earned, + ]; + } + $result = view('reports.partials.exp-not-grouped', compact('result'))->render(); + $cache->store($result); + + return $result; + // for period, get spent and earned for each account (by name) + + } + + /** + * @param Collection $accounts + * @param Collection $expense + * @param Carbon $start + * @param Carbon $end + * + * @return array|mixed|string + * @throws \Throwable */ public function spentGrouped(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) { - $combined = $this->combineAccounts($expense); - // for period, get spent and earned for each account (by name) - /** - * @var string $name - * @var Collection $combi - */ - foreach($combined as $name => $combi) { - + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-spent-grouped'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore } + + $combined = $this->combineAccounts($expense); + $format = app('navigation')->preferredRangeFormat($start, $end); + $result = []; + + foreach ($combined as $name => $combi) { + $current = clone $start; + $combiSet = []; + while ($current <= $end) { + $period = $current->format('Ymd'); + $periodName = app('navigation')->periodShow($current, $format); + $currentEnd = app('navigation')->endOfPeriod($current, $format); + /** + * @var string $name + * @var Collection $combi + */ + $spent = $this->spentInPeriod($accounts, $combi, $current, $currentEnd); + $earned = $this->earnedInPeriod($accounts, $combi, $current, $currentEnd); + $current = app('navigation')->addPeriod($current, $format, 0); + $combiSet[$period] = [ + 'period' => $periodName, + 'spent' => $spent, + 'earned' => $earned, + ]; + } + $result[$name] = $combiSet; + } + $result = view('reports.partials.exp-grouped', compact('result'))->render(); + $cache->store($result); + + return $result; } protected function combineAccounts(Collection $accounts): array @@ -80,14 +210,187 @@ class ExpenseController extends Controller $combined = []; /** @var Account $expenseAccount */ foreach ($accounts as $expenseAccount) { - $combined[$expenseAccount->name] = [$expenseAccount]; + $collection = new Collection; + $collection->push($expenseAccount); + $revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]); if (!is_null($revenue->id)) { - $combined[$expenseAccount->name][] = $revenue; + $collection->push($revenue); } + $combined[$expenseAccount->name] = $collection; } return $combined; } + protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets); + $collector->setOpposingAccounts($opposing); + $set = $collector->getJournals(); + $sum = []; + // loop to support multi currency + foreach ($set as $transaction) { + $currencyId = $transaction->transaction_currency_id; + + // if not set, set to zero: + if (!isset($sum[$currencyId])) { + $sum[$currencyId] = [ + 'sum' => '0', + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ]; + } + + // add amount + $sum[$currencyId]['sum'] = bcadd($sum[$currencyId]['sum'], $transaction->transaction_amount); + } + + return $sum; + } + + /** + * @param Collection $assets + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets); + $collector->setOpposingAccounts($opposing)->withCategoryInformation(); + $set = $collector->getJournals(); + $sum = []; + // loop to support multi currency + foreach ($set as $transaction) { + $currencyId = $transaction->transaction_currency_id; + $categoryName = $transaction->transaction_category_name; + $categoryId = intval($transaction->transaction_category_id); + // if null, grab from journal: + if ($categoryId === 0) { + $categoryName = $transaction->transaction_journal_category_name; + $categoryId = intval($transaction->transaction_journal_category_id); + } + if($categoryId !== 0) { + $categoryName = app('steam')->tryDecrypt($categoryName); + } + + // if not set, set to zero: + if (!isset($sum[$categoryId][$currencyId])) { + $sum[$categoryId][$currencyId] = [ + 'sum' => '0', + 'category' => [ + 'id' => $categoryId, + 'name' => $categoryName, + ], + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ]; + } + + // add amount + $sum[$categoryId][$currencyId]['sum'] = bcadd($sum[$categoryId][$currencyId]['sum'], $transaction->transaction_amount); + } + + return $sum; + } + + /** + * @param Collection $assets + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets); + $collector->setOpposingAccounts($opposing)->withCategoryInformation(); + $set = $collector->getJournals(); + $sum = []; + // loop to support multi currency + foreach ($set as $transaction) { + $currencyId = $transaction->transaction_currency_id; + $categoryName = $transaction->transaction_category_name; + $categoryId = intval($transaction->transaction_category_id); + // if null, grab from journal: + if ($categoryId === 0) { + $categoryName = $transaction->transaction_journal_category_name; + $categoryId = intval($transaction->transaction_journal_category_id); + } + + // if not set, set to zero: + if (!isset($sum[$categoryId][$currencyId])) { + $sum[$categoryId][$currencyId] = [ + 'sum' => '0', + 'category' => [ + 'id' => $categoryId, + 'name' => $categoryName, + ], + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ]; + } + + // add amount + $sum[$categoryId][$currencyId]['sum'] = bcadd($sum[$categoryId][$currencyId]['sum'], $transaction->transaction_amount); + } + + return $sum; + } + + + /** + * @param Collection $assets + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets); + $collector->setOpposingAccounts($opposing); + $set = $collector->getJournals(); + $sum = []; + // loop to support multi currency + foreach ($set as $transaction) { + $currencyId = $transaction->transaction_currency_id; + + // if not set, set to zero: + if (!isset($sum[$currencyId])) { + $sum[$currencyId] = [ + 'sum' => '0', + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ]; + } + + // add amount + $sum[$currencyId]['sum'] = bcadd($sum[$currencyId]['sum'], $transaction->transaction_amount); + } + + return $sum; + } + } \ No newline at end of file diff --git a/public/js/ff/reports/account/all.js b/public/js/ff/reports/account/all.js index 81a80886b1..539b599256 100644 --- a/public/js/ff/reports/account/all.js +++ b/public/js/ff/reports/account/all.js @@ -25,4 +25,33 @@ function loadAjaxPartial(holder, uri) { }).fail(function () { failAjaxPartial(uri, holder); }); +} + +function failAjaxPartial(uri, holder) { + "use strict"; + var holderObject = $('#' + holder); + holderObject.parent().find('.overlay').remove(); + holderObject.addClass('general-chart-error'); + +} + +function displayAjaxPartial(data, holder) { + "use strict"; + var obj = $('#' + holder); + obj.html(data); + obj.parent().find('.overlay').remove(); + + // call some often needed recalculations and what-not: + + // find a sortable table and make it sortable: + if (typeof $.bootstrapSortable === "function") { + $.bootstrapSortable(true); + } + + // find the info click things and respond to them: + triggerInfoClick(); + + // trigger list thing + listLengthInitial(); + } \ No newline at end of file diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig new file mode 100644 index 0000000000..8934902b14 --- /dev/null +++ b/resources/views/reports/partials/exp-categories.twig @@ -0,0 +1,36 @@ + + + + + + + + + + {% for categoryId, entry in together %} + + + + + + {% endfor %} + +
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}
+ {% if entry.category.name|length ==0 %}{{ 'noCategory'|_ }}{% else %}{{ entry.category.name }}{% endif %} + + {% if entry.spent|length ==0 %} + {{ '0'|formatAmount }} + {% else %} + {% for expense in entry.spent %} + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
+ {% endfor %} + {% endif %} +
+ {% if entry.earned|length ==0 %} + {{ '0'|formatAmount }} + {% else %} + {% for income in entry.earned %} + {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
+ {% endfor %} + {% endif %} +
diff --git a/resources/views/reports/partials/exp-grouped.twig b/resources/views/reports/partials/exp-grouped.twig new file mode 100644 index 0000000000..b2bb66271f --- /dev/null +++ b/resources/views/reports/partials/exp-grouped.twig @@ -0,0 +1,37 @@ + + + + + + + + + + + {% for name, periods in result %} + {% for periodIndex, period in periods %} + + + + + + + {% endfor %} + {% endfor %} + + +
{{ 'name'|_ }}{{ 'period'|_ }}
{{ name }}{{ period.period }} + {% if period.spent|length == 0%} + {{ '0'|formatAmount }} + {% endif %} + {% for expense in period.spent %} + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
+ {% endfor %} +
+ {% if period.earned|length == 0 %} + {{ '0'|formatAmount }} + {% endif %} + {% for income in period.earned %} + {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
+ {% endfor %} +
diff --git a/resources/views/reports/partials/exp-not-grouped.twig b/resources/views/reports/partials/exp-not-grouped.twig new file mode 100644 index 0000000000..7a231112f3 --- /dev/null +++ b/resources/views/reports/partials/exp-not-grouped.twig @@ -0,0 +1,33 @@ + + + + + + + + + + {% for name, amounts in result %} + + + + + + {% endfor %} + + +
{{ 'name'|_ }}
{{ name }} + {% if amounts.spent|length == 0%} + {{ '0'|formatAmount }} + {% endif %} + {% for expense in amounts.spent %} + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
+ {% endfor %} +
+ {% if amounts.earned|length == 0 %} + {{ '0'|formatAmount }} + {% endif %} + {% for income in amounts.earned %} + {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
+ {% endfor %} +
From b1b6fe553a0b7ee981d4bc1b4aa41013cdafc1b2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 18:09:36 +0100 Subject: [PATCH 018/142] Fix tests, expand flush routine. --- .../Report/Standard/MultiYearReportGenerator.php | 10 ++++++++++ app/Generator/Report/Standard/YearReportGenerator.php | 10 ++++++++++ app/Http/Controllers/HomeController.php | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/app/Generator/Report/Standard/MultiYearReportGenerator.php b/app/Generator/Report/Standard/MultiYearReportGenerator.php index 7bc83c6dc7..8910fe2fe0 100644 --- a/app/Generator/Report/Standard/MultiYearReportGenerator.php +++ b/app/Generator/Report/Standard/MultiYearReportGenerator.php @@ -98,6 +98,16 @@ class MultiYearReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $expense + * + * @return ReportGeneratorInterface + */ + public function setExpense(Collection $expense): ReportGeneratorInterface + { + return $this; + } + /** * @param Carbon $date * diff --git a/app/Generator/Report/Standard/YearReportGenerator.php b/app/Generator/Report/Standard/YearReportGenerator.php index fc591c0e97..06bced562e 100644 --- a/app/Generator/Report/Standard/YearReportGenerator.php +++ b/app/Generator/Report/Standard/YearReportGenerator.php @@ -98,6 +98,16 @@ class YearReportGenerator implements ReportGeneratorInterface return $this; } + /** + * @param Collection $expense + * + * @return ReportGeneratorInterface + */ + public function setExpense(Collection $expense): ReportGeneratorInterface + { + return $this; + } + /** * @param Carbon $date * diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 0632f35c37..8d432147a1 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -164,7 +164,17 @@ class HomeController extends Controller { Preferences::mark(); $request->session()->forget(['start', 'end', '_previous', 'viewRange', 'range', 'is_custom_range']); + Log::debug('Call cache:clear...'); Artisan::call('cache:clear'); + Log::debug('Call config:clear...'); + Artisan::call('config:clear'); + Log::debug('Call route:clear...'); + Artisan::call('route:clear'); + Log::debug('Call twig:clean...'); + Artisan::call('twig:clean'); + Log::debug('Call view:clear...'); + Artisan::call('view:clear'); + Log::debug('Done! Redirecting...'); return redirect(route('index')); } From 0c529cb7e769e7bcb1aa84d2063dc39838cd434b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 20:03:10 +0100 Subject: [PATCH 019/142] Updated code for #384 --- .../Controllers/Report/ExpenseController.php | 154 +++++++++++++++--- resources/lang/en_US/firefly.php | 2 + .../views/reports/partials/exp-budgets.twig | 31 ++++ .../reports/partials/exp-categories.twig | 14 +- .../views/reports/partials/exp-grouped.twig | 8 +- .../reports/partials/exp-not-grouped.twig | 6 +- 6 files changed, 177 insertions(+), 38 deletions(-) create mode 100644 resources/views/reports/partials/exp-budgets.twig diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index 3b2e2002a0..1aa3354107 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -59,6 +59,60 @@ class ExpenseController extends Controller ); } + /** + * @param Collection $accounts + * @param Collection $expense + * @param Carbon $start + * @param Carbon $end + * + * @return string + * @throws \Throwable + */ + public function budget(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-budget'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + $combined = $this->combineAccounts($expense); + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + // now find spent / earned: + $spent = $this->spentByBudget($accounts, $all, $start, $end); + // do some merging to get the budget info ready. + $together = []; + foreach ($spent as $budgetId => $spentInfo) { + if (!isset($together[$budgetId])) { + $together[$budgetId]['spent'] = $spentInfo; + // get category info: + $first = reset($spentInfo); + $together[$budgetId]['budget'] = $first['budget']; + } + } + + $result = view('reports.partials.exp-budgets', compact('together'))->render(); + $cache->store($result); + + return $result; + } + + /** + * @param Collection $accounts + * @param Collection $expense + * @param Carbon $start + * @param Carbon $end + * + * @return string + * @throws \Throwable + */ public function category(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) { // Properties for cache: @@ -82,20 +136,20 @@ class ExpenseController extends Controller // join arrays somehow: $together = []; - foreach($spent as $categoryId => $spentInfo) { - if(!isset($together[$categoryId])) { + foreach ($spent as $categoryId => $spentInfo) { + if (!isset($together[$categoryId])) { $together[$categoryId]['spent'] = $spentInfo; // get category info: - $first = reset($spentInfo); + $first = reset($spentInfo); $together[$categoryId]['category'] = $first['category']; } } - foreach($earned as $categoryId => $earnedInfo) { - if(!isset($together[$categoryId])) { + foreach ($earned as $categoryId => $earnedInfo) { + if (!isset($together[$categoryId])) { $together[$categoryId]['earned'] = $earnedInfo; // get category info: - $first = reset($earnedInfo); + $first = reset($earnedInfo); $together[$categoryId]['category'] = $first['category']; } } @@ -223,6 +277,58 @@ class ExpenseController extends Controller return $combined; } + /** + * @param Collection $assets + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets); + $collector->setOpposingAccounts($opposing)->withCategoryInformation(); + $set = $collector->getJournals(); + $sum = []; + // loop to support multi currency + foreach ($set as $transaction) { + $currencyId = $transaction->transaction_currency_id; + $categoryName = $transaction->transaction_category_name; + $categoryId = intval($transaction->transaction_category_id); + // if null, grab from journal: + if ($categoryId === 0) { + $categoryName = $transaction->transaction_journal_category_name; + $categoryId = intval($transaction->transaction_journal_category_id); + } + if ($categoryId !== 0) { + $categoryName = app('steam')->tryDecrypt($categoryName); + } + + // if not set, set to zero: + if (!isset($sum[$categoryId][$currencyId])) { + $sum[$categoryId][$currencyId] = [ + 'sum' => '0', + 'category' => [ + 'id' => $categoryId, + 'name' => $categoryName, + ], + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ]; + } + + // add amount + $sum[$categoryId][$currencyId]['sum'] = bcadd($sum[$categoryId][$currencyId]['sum'], $transaction->transaction_amount); + } + + return $sum; + } + protected function earnedInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array { /** @var JournalCollectorInterface $collector */ @@ -259,37 +365,34 @@ class ExpenseController extends Controller * @param Carbon $start * @param Carbon $end * - * @return string + * @return array */ - protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array + protected function spentByBudget(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array { /** @var JournalCollectorInterface $collector */ $collector = app(JournalCollectorInterface::class); - $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets); - $collector->setOpposingAccounts($opposing)->withCategoryInformation(); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets); + $collector->setOpposingAccounts($opposing)->withBudgetInformation(); $set = $collector->getJournals(); $sum = []; // loop to support multi currency foreach ($set as $transaction) { - $currencyId = $transaction->transaction_currency_id; - $categoryName = $transaction->transaction_category_name; - $categoryId = intval($transaction->transaction_category_id); + $currencyId = $transaction->transaction_currency_id; + $budgetName = $transaction->transaction_budget_name; + $budgetId = intval($transaction->transaction_budget_id); // if null, grab from journal: - if ($categoryId === 0) { - $categoryName = $transaction->transaction_journal_category_name; - $categoryId = intval($transaction->transaction_journal_category_id); - } - if($categoryId !== 0) { - $categoryName = app('steam')->tryDecrypt($categoryName); + if ($budgetId === 0) { + $budgetName = $transaction->transaction_journal_budget_name; + $budgetId = intval($transaction->transaction_journal_budget_id); } // if not set, set to zero: - if (!isset($sum[$categoryId][$currencyId])) { - $sum[$categoryId][$currencyId] = [ + if (!isset($sum[$budgetId][$currencyId])) { + $sum[$budgetId][$currencyId] = [ 'sum' => '0', - 'category' => [ - 'id' => $categoryId, - 'name' => $categoryName, + 'budget' => [ + 'id' => $budgetId, + 'name' => $budgetName, ], 'currency' => [ 'symbol' => $transaction->transaction_currency_symbol, @@ -299,7 +402,7 @@ class ExpenseController extends Controller } // add amount - $sum[$categoryId][$currencyId]['sum'] = bcadd($sum[$categoryId][$currencyId]['sum'], $transaction->transaction_amount); + $sum[$budgetId][$currencyId]['sum'] = bcadd($sum[$budgetId][$currencyId]['sum'], $transaction->transaction_amount); } return $sum; @@ -354,7 +457,6 @@ class ExpenseController extends Controller return $sum; } - /** * @param Collection $assets * @param Collection $opposing diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 7f3180faf9..9afe0998a8 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -20,6 +20,7 @@ */ declare(strict_types=1); + return [ // general stuff: 'close' => 'Close', @@ -716,6 +717,7 @@ return [ 'mass_edit_journals' => 'Edit a number of transactions', 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', 'no_budget' => 'none', + 'no_budget_squared' => '(no budget)', 'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious.', 'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).', 'mass_edited_transactions_success' => 'Updated :amount transaction(s)', diff --git a/resources/views/reports/partials/exp-budgets.twig b/resources/views/reports/partials/exp-budgets.twig new file mode 100644 index 0000000000..1d5e382ce9 --- /dev/null +++ b/resources/views/reports/partials/exp-budgets.twig @@ -0,0 +1,31 @@ + + + + + + + + + {% for budgetId, entry in together %} + + + + + {% endfor %} + +
{{ 'budget'|_ }}{{ 'spent'|_ }}
+ + {% if entry.budget.name|length ==0 %} + {{ 'no_budget_squared'|_ }} + {% else %} + {{ entry.budget.name }} + {% endif %} + + {% if entry.spent|length ==0 %} + {{ '0'|formatAmount }} + {% else %} + {% for expense in entry.spent %} + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
+ {% endfor %} + {% endif %} +
diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig index 8934902b14..1e62c3055a 100644 --- a/resources/views/reports/partials/exp-categories.twig +++ b/resources/views/reports/partials/exp-categories.twig @@ -1,16 +1,20 @@ - - - + + + {% for categoryId, entry in together %} -
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}
- {% if entry.category.name|length ==0 %}{{ 'noCategory'|_ }}{% else %}{{ entry.category.name }}{% endif %} + + {% if entry.category.name|length ==0 %} + {{ 'noCategory'|_ }} + {% else %} + {{ entry.category.name }} + {% endif %} {% if entry.spent|length ==0 %} diff --git a/resources/views/reports/partials/exp-grouped.twig b/resources/views/reports/partials/exp-grouped.twig index b2bb66271f..a5b4ea4936 100644 --- a/resources/views/reports/partials/exp-grouped.twig +++ b/resources/views/reports/partials/exp-grouped.twig @@ -1,10 +1,10 @@ - - - - + + + + diff --git a/resources/views/reports/partials/exp-not-grouped.twig b/resources/views/reports/partials/exp-not-grouped.twig index 7a231112f3..c946e8e307 100644 --- a/resources/views/reports/partials/exp-not-grouped.twig +++ b/resources/views/reports/partials/exp-not-grouped.twig @@ -1,9 +1,9 @@
{{ 'name'|_ }}{{ 'period'|_ }}{{ 'name'|_ }}{{ 'period'|_ }}
- - - + + + From 4a1e4bb8214b28b4919d1f327abe379f02865939 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 20:05:56 +0100 Subject: [PATCH 020/142] Decrypt object names. [skip ci] --- app/Http/Controllers/Report/ExpenseController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index 1aa3354107..6633d20425 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -385,6 +385,9 @@ class ExpenseController extends Controller $budgetName = $transaction->transaction_journal_budget_name; $budgetId = intval($transaction->transaction_journal_budget_id); } + if ($budgetId !== 0) { + $budgetName = app('steam')->tryDecrypt($budgetName); + } // if not set, set to zero: if (!isset($sum[$budgetId][$currencyId])) { @@ -434,6 +437,9 @@ class ExpenseController extends Controller $categoryName = $transaction->transaction_journal_category_name; $categoryId = intval($transaction->transaction_journal_category_id); } + if ($categoryId !== 0) { + $categoryName = app('steam')->tryDecrypt($categoryName); + } // if not set, set to zero: if (!isset($sum[$categoryId][$currencyId])) { From 6fed773cb8945f094b9f85f1950d75035c67dfd9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 20:07:23 +0100 Subject: [PATCH 021/142] Fix view [skip ci] --- resources/views/reports/partials/exp-categories.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig index 1e62c3055a..6483faf128 100644 --- a/resources/views/reports/partials/exp-categories.twig +++ b/resources/views/reports/partials/exp-categories.twig @@ -25,7 +25,7 @@ {% endfor %} {% endif %} -
{{ 'name'|_ }}{{ 'name'|_ }}
+ {% if entry.earned|length ==0 %} {{ '0'|formatAmount }} {% else %} From d3a6ccacf05a39f557e7ef69806a1e82696079e2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 20:10:04 +0100 Subject: [PATCH 022/142] Fix earned by category view [skip ci] --- app/Http/Controllers/Report/ExpenseController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index 6633d20425..5d40104f28 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -132,7 +132,7 @@ class ExpenseController extends Controller } // now find spent / earned: $spent = $this->spentByCategory($accounts, $all, $start, $end); - $earned = $this->earnedByCategory($accounts, $combi, $start, $end); + $earned = $this->earnedByCategory($accounts, $all, $start, $end); // join arrays somehow: $together = []; From 12c15ea590c6645d049b4037287dd89e8923be1b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 10 Dec 2017 21:12:04 +0100 Subject: [PATCH 023/142] Fix #1049 --- app/Support/Twig/Extension/Transaction.php | 2 +- resources/lang/en_US/firefly.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Support/Twig/Extension/Transaction.php b/app/Support/Twig/Extension/Transaction.php index ef8e443d78..33b4eb4aec 100644 --- a/app/Support/Twig/Extension/Transaction.php +++ b/app/Support/Twig/Extension/Transaction.php @@ -394,7 +394,7 @@ class Transaction extends Twig_Extension $txt = sprintf('', trans('firefly.transfer')); break; case TransactionType::OPENING_BALANCE: - $txt = sprintf('', trans('firefly.openingBalance')); + $txt = sprintf('', trans('firefly.opening_balance')); break; case TransactionType::RECONCILIATION: $txt = sprintf('', trans('firefly.reconciliation_transaction')); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 9afe0998a8..43bf852d52 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -775,6 +775,7 @@ return [ 'piggyBanks' => 'Piggy banks', 'bills' => 'Bills', 'withdrawal' => 'Withdrawal', + 'opening_balance' => 'Opening balance', 'deposit' => 'Deposit', 'account' => 'Account', 'transfer' => 'Transfer', From d7f2b7cd630d8a366484cb30aa2d44d0346f530a Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 05:57:07 +0100 Subject: [PATCH 024/142] Remove unused overview #384 --- .../Controllers/Report/ExpenseController.php | 51 +------------------ public/js/ff/reports/account/month.js | 1 - resources/views/reports/account/report.twig | 15 ------ .../views/reports/partials/exp-grouped.twig | 37 -------------- routes/web.php | 3 +- 5 files changed, 2 insertions(+), 105 deletions(-) delete mode 100644 resources/views/reports/partials/exp-grouped.twig diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index 5d40104f28..ccaad3cd78 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -207,58 +207,9 @@ class ExpenseController extends Controller /** * @param Collection $accounts - * @param Collection $expense - * @param Carbon $start - * @param Carbon $end * - * @return array|mixed|string - * @throws \Throwable + * @return array */ - public function spentGrouped(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) - { - // Properties for cache: - $cache = new CacheProperties; - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty('expense-spent-grouped'); - $cache->addProperty($accounts->pluck('id')->toArray()); - $cache->addProperty($expense->pluck('id')->toArray()); - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } - - $combined = $this->combineAccounts($expense); - $format = app('navigation')->preferredRangeFormat($start, $end); - $result = []; - - foreach ($combined as $name => $combi) { - $current = clone $start; - $combiSet = []; - while ($current <= $end) { - $period = $current->format('Ymd'); - $periodName = app('navigation')->periodShow($current, $format); - $currentEnd = app('navigation')->endOfPeriod($current, $format); - /** - * @var string $name - * @var Collection $combi - */ - $spent = $this->spentInPeriod($accounts, $combi, $current, $currentEnd); - $earned = $this->earnedInPeriod($accounts, $combi, $current, $currentEnd); - $current = app('navigation')->addPeriod($current, $format, 0); - $combiSet[$period] = [ - 'period' => $periodName, - 'spent' => $spent, - 'earned' => $earned, - ]; - } - $result[$name] = $combiSet; - } - $result = view('reports.partials.exp-grouped', compact('result'))->render(); - $cache->store($result); - - return $result; - } - protected function combineAccounts(Collection $accounts): array { $combined = []; diff --git a/public/js/ff/reports/account/month.js b/public/js/ff/reports/account/month.js index 914908f4a1..099cf9a906 100644 --- a/public/js/ff/reports/account/month.js +++ b/public/js/ff/reports/account/month.js @@ -24,7 +24,6 @@ $(function () { drawChart(); loadAjaxPartial('inOutAccounts', spentUri); - loadAjaxPartial('inOutPeriod', groupedUri); loadAjaxPartial('inOutCategory', categoryUri); loadAjaxPartial('inOutBudget', budgetUri); loadAjaxPartial('topXexpense', expenseUri); diff --git a/resources/views/reports/account/report.twig b/resources/views/reports/account/report.twig index 9f84f39170..4dbaf2b5f1 100644 --- a/resources/views/reports/account/report.twig +++ b/resources/views/reports/account/report.twig @@ -21,20 +21,6 @@ - -
-
-
-

{{ ('in_out_accounts_period_'~preferredPeriod)|_ }}

-
-
- {# loading indicator #} -
- -
-
-
-
{# chart #} @@ -133,7 +119,6 @@ // boxes with stuff: var spentUri = '{{ route('report-data.expense.spent', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; - var groupedUri = '{{ route('report-data.expense.spent-grouped', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; var categoryUri = '{{ route('report-data.expense.category', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; var budgetUri = '{{ route('report-data.expense.budget', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; var expenseUri = '{{ route('report-data.expense.expenses', [accountIds, expenseIds, start.format('Ymd'), end.format('Ymd')]) }}'; diff --git a/resources/views/reports/partials/exp-grouped.twig b/resources/views/reports/partials/exp-grouped.twig deleted file mode 100644 index a5b4ea4936..0000000000 --- a/resources/views/reports/partials/exp-grouped.twig +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - {% for name, periods in result %} - {% for periodIndex, period in periods %} - - - - - - - {% endfor %} - {% endfor %} - - -
{{ 'name'|_ }}{{ 'period'|_ }}
{{ name }}{{ period.period }} - {% if period.spent|length == 0%} - {{ '0'|formatAmount }} - {% endif %} - {% for expense in period.spent %} - {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
- {% endfor %} -
- {% if period.earned|length == 0 %} - {{ '0'|formatAmount }} - {% endif %} - {% for income in period.earned %} - {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
- {% endfor %} -
diff --git a/routes/web.php b/routes/web.php index 9dabbc018e..39083bbe46 100755 --- a/routes/web.php +++ b/routes/web.php @@ -614,9 +614,8 @@ Route::group( Route::group( ['middleware' => 'user-full-auth', 'namespace' => 'Report', 'prefix' => 'report-data/expense', 'as' => 'report-data.expense.'], function () { - // spent per period / spent grouped + // spent per period Route::get('spent/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@spent', 'as' => 'spent']); - Route::get('spent-grouped/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@spentGrouped', 'as' => 'spent-grouped']); // per category && per budget Route::get('category/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@category', 'as' => 'category']); From 1b5b79897a22b35a6ea2e455e51acf1b4c4fab36 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 10:23:29 +0100 Subject: [PATCH 025/142] Fix demo user rights in admin. --- app/Http/Controllers/Admin/ConfigurationController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index fa4b3ab1d5..73ff3efc11 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsLimitedUser; use FireflyIII\Http\Requests\ConfigurationRequest; use FireflyIII\Support\Facades\FireflyConfig; use Preferences; @@ -50,6 +51,7 @@ class ConfigurationController extends Controller return $next($request); } ); + $this->middleware(IsLimitedUser::class)->except(['postIndex']); } /** From c91bb5a56559fcd21908d58f04fdce7e5053155a Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 10:26:44 +0100 Subject: [PATCH 026/142] Update admin rights for demo user. --- .../Controllers/Admin/ConfigurationController.php | 2 +- app/Http/Controllers/Admin/HomeController.php | 11 +++++++++++ app/Http/Controllers/Admin/LinkController.php | 2 ++ app/Http/Controllers/Admin/UserController.php | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index 73ff3efc11..3dba4e77b4 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -51,7 +51,7 @@ class ConfigurationController extends Controller return $next($request); } ); - $this->middleware(IsLimitedUser::class)->except(['postIndex']); + $this->middleware(IsLimitedUser::class)->except(['index']); } /** diff --git a/app/Http/Controllers/Admin/HomeController.php b/app/Http/Controllers/Admin/HomeController.php index 4fa7948909..8e3c57ad0b 100644 --- a/app/Http/Controllers/Admin/HomeController.php +++ b/app/Http/Controllers/Admin/HomeController.php @@ -24,6 +24,7 @@ namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Events\AdminRequestedTestMessage; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsLimitedUser; use Illuminate\Http\Request; use Log; use Session; @@ -33,6 +34,16 @@ use Session; */ class HomeController extends Controller { + /** + * ConfigurationController constructor. + */ + public function __construct() + { + parent::__construct(); + $this->middleware(IsLimitedUser::class)->except(['index']); + } + + /** * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index 38bf575b7d..ec66da1a36 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsLimitedUser; use FireflyIII\Http\Requests\LinkTypeFormRequest; use FireflyIII\Models\LinkType; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; @@ -50,6 +51,7 @@ class LinkController extends Controller return $next($request); } ); + $this->middleware(IsLimitedUser::class)->except(['index','show']); } /** diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 8670e95e90..e23f91e012 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Admin; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Middleware\IsLimitedUser; use FireflyIII\Http\Requests\UserFormRequest; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\User; @@ -51,6 +52,7 @@ class UserController extends Controller return $next($request); } ); + $this->middleware(IsLimitedUser::class)->except(['index','show']); } /** From 5d9e547d05b0858cbc5ffa38d429d5bc2ce316c5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 10:28:10 +0100 Subject: [PATCH 027/142] Allow profile index [skip ci] --- app/Http/Controllers/ProfileController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 959b46b342..3235961b5e 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -62,7 +62,7 @@ class ProfileController extends Controller return $next($request); } ); - $this->middleware(IsLimitedUser::class)->except(['confirmEmailChange', 'undoEmailChange']); + $this->middleware(IsLimitedUser::class)->except(['confirmEmailChange','index', 'undoEmailChange']); } /** From d1c8e54798a96da88884255d32e470a2756c3bda Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 10:59:28 +0100 Subject: [PATCH 028/142] Null pointer in verify routine. --- app/Console/Commands/VerifyDatabase.php | 43 +++++++++++++------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/app/Console/Commands/VerifyDatabase.php b/app/Console/Commands/VerifyDatabase.php index 570d323af8..b07b113ad3 100644 --- a/app/Console/Commands/VerifyDatabase.php +++ b/app/Console/Commands/VerifyDatabase.php @@ -164,13 +164,16 @@ class VerifyDatabase extends Command } foreach ($errored as $journalId) { // select and update: - $res = Transaction::where('transaction_journal_id', $journalId)->groupBy('amount')->get([DB::raw('MIN(id) as first_id')]); + $res = Transaction::whereNull('deleted_at')->where('transaction_journal_id', $journalId)->groupBy('amount')->get([DB::raw('MIN(id) as first_id')]); $ids = $res->pluck('first_id')->toArray(); DB::table('transactions')->whereIn('id', $ids)->update(['amount' => DB::raw('amount * -1')]); $count++; // report about it /** @var TransactionJournal $journal */ $journal = TransactionJournal::find($journalId); + if (is_null($journal)) { + continue; + } if ($journal->transactionType->type === TransactionType::OPENING_BALANCE) { $this->error( sprintf( @@ -200,7 +203,7 @@ class VerifyDatabase extends Command */ private function repairPiggyBanks(): void { - $set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get(); + $set = PiggyBankEvent::with(['PiggyBank', 'TransactionJournal', 'TransactionJournal.TransactionType'])->get(); $set->each( function (PiggyBankEvent $event) { if (null === $event->transaction_journal_id) { @@ -350,18 +353,18 @@ class VerifyDatabase extends Command private function reportJournals() { $count = 0; - $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->whereNotNull('transaction_journals.deleted_at')// USE THIS - ->whereNull('transactions.deleted_at') - ->whereNotNull('transactions.id') - ->get( - [ - 'transaction_journals.id as journal_id', - 'transaction_journals.description', - 'transaction_journals.deleted_at as journal_deleted', - 'transactions.id as transaction_id', - 'transactions.deleted_at as transaction_deleted_at',] - ); + $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->whereNotNull('transaction_journals.deleted_at')// USE THIS + ->whereNull('transactions.deleted_at') + ->whereNotNull('transactions.id') + ->get( + [ + 'transaction_journals.id as journal_id', + 'transaction_journals.description', + 'transaction_journals.deleted_at as journal_deleted', + 'transactions.id as transaction_id', + 'transactions.deleted_at as transaction_deleted_at',] + ); /** @var stdClass $entry */ foreach ($set as $entry) { $this->error( @@ -370,7 +373,7 @@ class VerifyDatabase extends Command ); $count++; } - if($count === 0) { + if ($count === 0) { $this->info('No orphaned transactions!'); } } @@ -381,10 +384,10 @@ class VerifyDatabase extends Command private function reportNoTransactions() { $count = 0; - $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') - ->groupBy('transaction_journals.id') - ->whereNull('transactions.transaction_journal_id') - ->get(['transaction_journals.id']); + $set = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') + ->groupBy('transaction_journals.id') + ->whereNull('transactions.transaction_journal_id') + ->get(['transaction_journals.id']); foreach ($set as $entry) { $this->error( @@ -392,7 +395,7 @@ class VerifyDatabase extends Command ); $count++; } - if($count === 0) { + if ($count === 0) { $this->info('No orphaned journals!'); } } From f35aa6a035c5d46e850ea38115bb0338bdc79c75 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 14:52:30 +0100 Subject: [PATCH 029/142] Remove GA event codes. --- .../Account/ReconcileController.php | 2 - app/Http/Controllers/AccountController.php | 6 - app/Http/Controllers/AttachmentController.php | 2 - app/Http/Controllers/BillController.php | 6 - app/Http/Controllers/BudgetController.php | 6 - app/Http/Controllers/CategoryController.php | 6 - app/Http/Controllers/CurrencyController.php | 6 - app/Http/Controllers/HomeController.php | 7 +- app/Http/Controllers/PiggyBankController.php | 6 - app/Http/Controllers/ProfileController.php | 3 - .../Controllers/Report/ExpenseController.php | 168 +++++++++++------- app/Http/Controllers/RuleController.php | 6 - app/Http/Controllers/RuleGroupController.php | 6 - app/Http/Controllers/TagController.php | 6 - .../Transaction/MassController.php | 4 - .../Transaction/SingleController.php | 6 - .../Transaction/SplitController.php | 2 - resources/views/layout/default.twig | 10 -- resources/views/layout/empty.twig | 10 -- resources/views/layout/guest.twig | 10 -- .../views/reports/partials/exp-budgets.twig | 17 +- .../reports/partials/exp-categories.twig | 23 +-- .../reports/partials/exp-not-grouped.twig | 44 ++--- 23 files changed, 154 insertions(+), 208 deletions(-) diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index b5573c83a4..06fc158fcc 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -90,8 +90,6 @@ class ReconcileController extends Controller ]; Session::flash('preFilled', $preFilled); - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'edit-reconciliation'); // put previous url in session if not redirect from store (not "return_to_edit"). if (true !== session('reconcile.edit.fromUpdate')) { diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index e8dd4be714..aa85f922c2 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -96,8 +96,6 @@ class AccountController extends Controller $this->rememberPreviousUri('accounts.create.uri'); } $request->session()->forget('accounts.create.fromStore'); - $request->session()->flash('gaEventCategory', 'accounts'); - $request->session()->flash('gaEventAction', 'create-' . $what); return view('accounts.create', compact('subTitleIcon', 'what', 'subTitle', 'currencySelectList', 'allCurrencies', 'roles')); } @@ -118,8 +116,6 @@ class AccountController extends Controller // put previous url in session $this->rememberPreviousUri('accounts.delete.uri'); - $request->session()->flash('gaEventCategory', 'accounts'); - $request->session()->flash('gaEventAction', 'delete-' . $typeName); return view('accounts.delete', compact('account', 'subTitle', 'accountList')); } @@ -198,8 +194,6 @@ class AccountController extends Controller 'currency_id' => $currency->id, ]; $request->session()->flash('preFilled', $preFilled); - $request->session()->flash('gaEventCategory', 'accounts'); - $request->session()->flash('gaEventAction', 'edit-' . $what); return view( 'accounts.edit', diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index b2ef0b7875..dc156b49e4 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -70,8 +70,6 @@ class AttachmentController extends Controller // put previous url in session $this->rememberPreviousUri('attachments.delete.uri'); - $request->session()->flash('gaEventCategory', 'attachments'); - $request->session()->flash('gaEventAction', 'delete-attachment'); return view('attachments.delete', compact('attachment', 'subTitle')); } diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 7aa321d9a8..a433b292ac 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -85,8 +85,6 @@ class BillController extends Controller $this->rememberPreviousUri('bills.create.uri'); } $request->session()->forget('bills.create.fromStore'); - $request->session()->flash('gaEventCategory', 'bills'); - $request->session()->flash('gaEventAction', 'create'); return view('bills.create', compact('periods', 'subTitle')); } @@ -101,8 +99,6 @@ class BillController extends Controller { // put previous url in session $this->rememberPreviousUri('bills.delete.uri'); - $request->session()->flash('gaEventCategory', 'bills'); - $request->session()->flash('gaEventAction', 'delete'); $subTitle = trans('firefly.delete_bill', ['name' => $bill->name]); return view('bills.delete', compact('bill', 'subTitle')); @@ -162,8 +158,6 @@ class BillController extends Controller $request->session()->flash('preFilled', $preFilled); $request->session()->forget('bills.edit.fromUpdate'); - $request->session()->flash('gaEventCategory', 'bills'); - $request->session()->flash('gaEventAction', 'edit'); return view('bills.edit', compact('subTitle', 'periods', 'bill')); } diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 0350973a73..f7bce98240 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -111,8 +111,6 @@ class BudgetController extends Controller $this->rememberPreviousUri('budgets.create.uri'); } $request->session()->forget('budgets.create.fromStore'); - $request->session()->flash('gaEventCategory', 'budgets'); - $request->session()->flash('gaEventAction', 'create'); $subTitle = (string)trans('firefly.create_new_budget'); return view('budgets.create', compact('subTitle')); @@ -130,8 +128,6 @@ class BudgetController extends Controller // put previous url in session $this->rememberPreviousUri('budgets.delete.uri'); - $request->session()->flash('gaEventCategory', 'budgets'); - $request->session()->flash('gaEventAction', 'delete'); return view('budgets.delete', compact('budget', 'subTitle')); } @@ -167,8 +163,6 @@ class BudgetController extends Controller $this->rememberPreviousUri('budgets.edit.uri'); } $request->session()->forget('budgets.edit.fromUpdate'); - $request->session()->flash('gaEventCategory', 'budgets'); - $request->session()->flash('gaEventAction', 'edit'); return view('budgets.edit', compact('budget', 'subTitle')); } diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 4c0a759a07..1773902ee8 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -74,8 +74,6 @@ class CategoryController extends Controller $this->rememberPreviousUri('categories.create.uri'); } $request->session()->forget('categories.create.fromStore'); - $request->session()->flash('gaEventCategory', 'categories'); - $request->session()->flash('gaEventAction', 'create'); $subTitle = trans('firefly.create_new_category'); return view('categories.create', compact('subTitle')); @@ -93,8 +91,6 @@ class CategoryController extends Controller // put previous url in session $this->rememberPreviousUri('categories.delete.uri'); - $request->session()->flash('gaEventCategory', 'categories'); - $request->session()->flash('gaEventAction', 'delete'); return view('categories.delete', compact('category', 'subTitle')); } @@ -132,8 +128,6 @@ class CategoryController extends Controller $this->rememberPreviousUri('categories.edit.uri'); } $request->session()->forget('categories.edit.fromUpdate'); - $request->session()->flash('gaEventCategory', 'categories'); - $request->session()->flash('gaEventAction', 'edit'); return view('categories.edit', compact('category', 'subTitle')); } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index 911aa55673..ca7cf7b9f3 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -83,8 +83,6 @@ class CurrencyController extends Controller $this->rememberPreviousUri('currencies.create.uri'); } $request->session()->forget('currencies.create.fromStore'); - $request->session()->flash('gaEventCategory', 'currency'); - $request->session()->flash('gaEventAction', 'create'); return view('currencies.create', compact('subTitleIcon', 'subTitle')); } @@ -131,8 +129,6 @@ class CurrencyController extends Controller // put previous url in session $this->rememberPreviousUri('currencies.delete.uri'); - $request->session()->flash('gaEventCategory', 'currency'); - $request->session()->flash('gaEventAction', 'delete'); $subTitle = trans('form.delete_currency', ['name' => $currency->name]); return view('currencies.delete', compact('currency', 'subTitle')); @@ -191,8 +187,6 @@ class CurrencyController extends Controller $this->rememberPreviousUri('currencies.edit.uri'); } $request->session()->forget('currencies.edit.fromUpdate'); - $request->session()->flash('gaEventCategory', 'currency'); - $request->session()->flash('gaEventAction', 'edit'); return view('currencies.edit', compact('currency', 'subTitle', 'subTitleIcon')); } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 8d432147a1..1dd55b1cc9 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -36,6 +36,7 @@ use Illuminate\Support\Collection; use Log; use Monolog\Handler\RotatingFileHandler; use Preferences; +use ReflectionException; use Route as RouteFacade; use Session; use View; @@ -171,7 +172,11 @@ class HomeController extends Controller Log::debug('Call route:clear...'); Artisan::call('route:clear'); Log::debug('Call twig:clean...'); - Artisan::call('twig:clean'); + try { + Artisan::call('twig:clean'); + } catch(ReflectionException $e) { + // dont care + } Log::debug('Call view:clear...'); Artisan::call('view:clear'); Log::debug('Done! Redirecting...'); diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 875adce839..5d150b6193 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -120,8 +120,6 @@ class PiggyBankController extends Controller $this->rememberPreviousUri('piggy-banks.create.uri'); } Session::forget('piggy-banks.create.fromStore'); - Session::flash('gaEventCategory', 'piggy-banks'); - Session::flash('gaEventAction', 'create'); return view('piggy-banks.create', compact('accounts', 'subTitle', 'subTitleIcon')); } @@ -137,8 +135,6 @@ class PiggyBankController extends Controller // put previous url in session $this->rememberPreviousUri('piggy-banks.delete.uri'); - Session::flash('gaEventCategory', 'piggy-banks'); - Session::flash('gaEventAction', 'delete'); return view('piggy-banks.delete', compact('piggyBank', 'subTitle')); } @@ -188,8 +184,6 @@ class PiggyBankController extends Controller 'note' => null === $note ? '' : $note->text, ]; Session::flash('preFilled', $preFilled); - Session::flash('gaEventCategory', 'piggy-banks'); - Session::flash('gaEventAction', 'edit'); // put previous url in session if not redirect from store (not "return_to_edit"). if (true !== session('piggy-banks.edit.fromUpdate')) { diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 3235961b5e..88532a8d74 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -241,9 +241,6 @@ class ProfileController extends Controller Session::flush(); $repository->destroy($user); - Session::flash('gaEventCategory', 'user'); - Session::flash('gaEventAction', 'delete-account'); - return redirect(route('index')); } diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index ccaad3cd78..b23fbf8cf8 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -43,7 +43,7 @@ class ExpenseController extends Controller protected $accountRepository; /** - * + * Constructor for ExpenseController */ public function __construct() { @@ -60,6 +60,8 @@ class ExpenseController extends Controller } /** + * Generates the overview per budget. + * * @param Collection $accounts * @param Collection $expense * @param Carbon $start @@ -86,18 +88,18 @@ class ExpenseController extends Controller $all = $all->merge($combi); } // now find spent / earned: - $spent = $this->spentByBudget($accounts, $all, $start, $end); - // do some merging to get the budget info ready. + $spent = $this->spentByBudget($accounts, $all, $start, $end); + // join arrays somehow: $together = []; - foreach ($spent as $budgetId => $spentInfo) { - if (!isset($together[$budgetId])) { - $together[$budgetId]['spent'] = $spentInfo; - // get category info: - $first = reset($spentInfo); - $together[$budgetId]['budget'] = $first['budget']; + foreach ($spent as $categoryId => $spentInfo) { + if (!isset($together[$categoryId])) { + $together[$categoryId]['spent'] = $spentInfo; + $together[$categoryId]['budget'] = $spentInfo['name']; + $together[$categoryId]['grand_total'] = '0'; } + $together[$categoryId]['grand_total'] = bcadd($spentInfo['grand_total'], $together[$categoryId]['grand_total']); } - + unset($spentInfo); $result = view('reports.partials.exp-budgets', compact('together'))->render(); $cache->store($result); @@ -105,6 +107,8 @@ class ExpenseController extends Controller } /** + * Generates the overview per category (spent and earned). + * * @param Collection $accounts * @param Collection $expense * @param Carbon $start @@ -133,25 +137,24 @@ class ExpenseController extends Controller // now find spent / earned: $spent = $this->spentByCategory($accounts, $all, $start, $end); $earned = $this->earnedByCategory($accounts, $all, $start, $end); - // join arrays somehow: $together = []; foreach ($spent as $categoryId => $spentInfo) { if (!isset($together[$categoryId])) { - $together[$categoryId]['spent'] = $spentInfo; - // get category info: - $first = reset($spentInfo); - $together[$categoryId]['category'] = $first['category']; + $together[$categoryId]['spent'] = $spentInfo; + $together[$categoryId]['category'] = $spentInfo['name']; + $together[$categoryId]['grand_total'] = '0'; } + $together[$categoryId]['grand_total'] = bcadd($spentInfo['grand_total'], $together[$categoryId]['grand_total']); } - + unset($spentInfo); foreach ($earned as $categoryId => $earnedInfo) { if (!isset($together[$categoryId])) { - $together[$categoryId]['earned'] = $earnedInfo; - // get category info: - $first = reset($earnedInfo); - $together[$categoryId]['category'] = $first['category']; + $together[$categoryId]['earned'] = $earnedInfo; + $together[$categoryId]['category'] = $earnedInfo['name']; + $together[$categoryId]['grand_total'] = '0'; } + $together[$categoryId]['grand_total'] = bcadd($earnedInfo['grand_total'], $together[$categoryId]['grand_total']); } $result = view('reports.partials.exp-categories', compact('together'))->render(); @@ -161,6 +164,8 @@ class ExpenseController extends Controller } /** + * Overview of spending + * * @param Collection $accounts * @param Collection $expense * @param Carbon $start @@ -234,7 +239,7 @@ class ExpenseController extends Controller * @param Carbon $start * @param Carbon $end * - * @return string + * @return array */ protected function earnedByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array { @@ -260,21 +265,32 @@ class ExpenseController extends Controller // if not set, set to zero: if (!isset($sum[$categoryId][$currencyId])) { - $sum[$categoryId][$currencyId] = [ - 'sum' => '0', - 'category' => [ - 'id' => $categoryId, - 'name' => $categoryName, - ], - 'currency' => [ - 'symbol' => $transaction->transaction_currency_symbol, - 'dp' => $transaction->transaction_currency_dp, + + $sum[$categoryId] = [ + 'grand_total' => '0', + 'name' => $categoryName, + 'per_currency' => [ + $currencyId => [ + 'sum' => '0', + 'category' => [ + 'id' => $categoryId, + 'name' => $categoryName, + ], + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ], ], ]; + } // add amount - $sum[$categoryId][$currencyId]['sum'] = bcadd($sum[$categoryId][$currencyId]['sum'], $transaction->transaction_amount); + $sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd( + $sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount + ); + $sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount); } return $sum; @@ -287,14 +303,17 @@ class ExpenseController extends Controller $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($assets); $collector->setOpposingAccounts($opposing); $set = $collector->getJournals(); - $sum = []; + $sum = [ + 'grand_sum' => '0', + 'per_currency' => [], + ]; // loop to support multi currency foreach ($set as $transaction) { $currencyId = $transaction->transaction_currency_id; // if not set, set to zero: - if (!isset($sum[$currencyId])) { - $sum[$currencyId] = [ + if (!isset($sum['per_currency'][$currencyId])) { + $sum['per_currency'][$currencyId] = [ 'sum' => '0', 'currency' => [ 'symbol' => $transaction->transaction_currency_symbol, @@ -304,7 +323,8 @@ class ExpenseController extends Controller } // add amount - $sum[$currencyId]['sum'] = bcadd($sum[$currencyId]['sum'], $transaction->transaction_amount); + $sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount); + $sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount); } return $sum; @@ -328,7 +348,7 @@ class ExpenseController extends Controller $sum = []; // loop to support multi currency foreach ($set as $transaction) { - $currencyId = $transaction->transaction_currency_id; + $currencyId = $transaction->transaction_currency_id; $budgetName = $transaction->transaction_budget_name; $budgetId = intval($transaction->transaction_budget_id); // if null, grab from journal: @@ -342,21 +362,31 @@ class ExpenseController extends Controller // if not set, set to zero: if (!isset($sum[$budgetId][$currencyId])) { - $sum[$budgetId][$currencyId] = [ - 'sum' => '0', - 'budget' => [ - 'id' => $budgetId, - 'name' => $budgetName, - ], - 'currency' => [ - 'symbol' => $transaction->transaction_currency_symbol, - 'dp' => $transaction->transaction_currency_dp, + + $sum[$budgetId] = [ + 'grand_total' => '0', + 'name' => $budgetName, + 'per_currency' => [ + $currencyId => [ + 'sum' => '0', + 'budget' => [ + 'id' => $budgetId, + 'name' => $budgetName, + ], + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ], ], ]; } // add amount - $sum[$budgetId][$currencyId]['sum'] = bcadd($sum[$budgetId][$currencyId]['sum'], $transaction->transaction_amount); + $sum[$budgetId]['per_currency'][$currencyId]['sum'] = bcadd( + $sum[$budgetId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount + ); + $sum[$budgetId]['grand_total'] = bcadd($sum[$budgetId]['grand_total'], $transaction->transaction_amount); } return $sum; @@ -368,7 +398,7 @@ class ExpenseController extends Controller * @param Carbon $start * @param Carbon $end * - * @return string + * @return array */ protected function spentByCategory(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array { @@ -394,21 +424,31 @@ class ExpenseController extends Controller // if not set, set to zero: if (!isset($sum[$categoryId][$currencyId])) { - $sum[$categoryId][$currencyId] = [ - 'sum' => '0', - 'category' => [ - 'id' => $categoryId, - 'name' => $categoryName, - ], - 'currency' => [ - 'symbol' => $transaction->transaction_currency_symbol, - 'dp' => $transaction->transaction_currency_dp, + + $sum[$categoryId] = [ + 'grand_total' => '0', + 'name' => $categoryName, + 'per_currency' => [ + $currencyId => [ + 'sum' => '0', + 'category' => [ + 'id' => $categoryId, + 'name' => $categoryName, + ], + 'currency' => [ + 'symbol' => $transaction->transaction_currency_symbol, + 'dp' => $transaction->transaction_currency_dp, + ], + ], ], ]; } // add amount - $sum[$categoryId][$currencyId]['sum'] = bcadd($sum[$categoryId][$currencyId]['sum'], $transaction->transaction_amount); + $sum[$categoryId]['per_currency'][$currencyId]['sum'] = bcadd( + $sum[$categoryId]['per_currency'][$currencyId]['sum'], $transaction->transaction_amount + ); + $sum[$categoryId]['grand_total'] = bcadd($sum[$categoryId]['grand_total'], $transaction->transaction_amount); } return $sum; @@ -420,7 +460,7 @@ class ExpenseController extends Controller * @param Carbon $start * @param Carbon $end * - * @return string + * @return array */ protected function spentInPeriod(Collection $assets, Collection $opposing, Carbon $start, Carbon $end): array { @@ -429,14 +469,17 @@ class ExpenseController extends Controller $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($assets); $collector->setOpposingAccounts($opposing); $set = $collector->getJournals(); - $sum = []; + $sum = [ + 'grand_sum' => '0', + 'per_currency' => [], + ]; // loop to support multi currency foreach ($set as $transaction) { $currencyId = $transaction->transaction_currency_id; // if not set, set to zero: - if (!isset($sum[$currencyId])) { - $sum[$currencyId] = [ + if (!isset($sum['per_currency'][$currencyId])) { + $sum['per_currency'][$currencyId] = [ 'sum' => '0', 'currency' => [ 'symbol' => $transaction->transaction_currency_symbol, @@ -446,7 +489,8 @@ class ExpenseController extends Controller } // add amount - $sum[$currencyId]['sum'] = bcadd($sum[$currencyId]['sum'], $transaction->transaction_amount); + $sum['per_currency'][$currencyId]['sum'] = bcadd($sum['per_currency'][$currencyId]['sum'], $transaction->transaction_amount); + $sum['grand_sum'] = bcadd($sum['grand_sum'], $transaction->transaction_amount); } return $sum; diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index aeec282409..82f184600b 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -102,8 +102,6 @@ class RuleController extends Controller $this->rememberPreviousUri('rules.create.uri'); } Session::forget('rules.create.fromStore'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'create-rule'); return view( 'rules.rule.create', @@ -124,8 +122,6 @@ class RuleController extends Controller // put previous url in session $this->rememberPreviousUri('rules.delete.uri'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'delete-rule'); return view('rules.rule.delete', compact('rule', 'subTitle')); } @@ -203,8 +199,6 @@ class RuleController extends Controller $this->rememberPreviousUri('rules.edit.uri'); } Session::forget('rules.edit.fromUpdate'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'edit-rule'); return view( 'rules.rule.edit', diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 0ba9141041..9528ee4178 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -71,8 +71,6 @@ class RuleGroupController extends Controller $this->rememberPreviousUri('rule-groups.create.uri'); } Session::forget('rule-groups.create.fromStore'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'create-rule-group'); return view('rules.rule-group.create', compact('subTitleIcon', 'subTitle')); } @@ -92,8 +90,6 @@ class RuleGroupController extends Controller // put previous url in session $this->rememberPreviousUri('rule-groups.delete.uri'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'delete-rule-group'); return view('rules.rule-group.delete', compact('ruleGroup', 'subTitle', 'ruleGroupList')); } @@ -145,8 +141,6 @@ class RuleGroupController extends Controller $this->rememberPreviousUri('rule-groups.edit.uri'); } Session::forget('rule-groups.edit.fromUpdate'); - Session::flash('gaEventCategory', 'rules'); - Session::flash('gaEventAction', 'edit-rule-group'); return view('rules.rule-group.edit', compact('ruleGroup', 'subTitle')); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 550b67574d..3d1bb319d6 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -88,8 +88,6 @@ class TagController extends Controller $this->rememberPreviousUri('tags.create.uri'); } Session::forget('tags.create.fromStore'); - Session::flash('gaEventCategory', 'tags'); - Session::flash('gaEventAction', 'create'); return view('tags.create', compact('subTitle', 'subTitleIcon', 'apiKey')); } @@ -107,8 +105,6 @@ class TagController extends Controller // put previous url in session $this->rememberPreviousUri('tags.delete.uri'); - Session::flash('gaEventCategory', 'tags'); - Session::flash('gaEventAction', 'delete'); return view('tags.delete', compact('tag', 'subTitle')); } @@ -147,8 +143,6 @@ class TagController extends Controller $this->rememberPreviousUri('tags.edit.uri'); } Session::forget('tags.edit.fromUpdate'); - Session::flash('gaEventCategory', 'tags'); - Session::flash('gaEventAction', 'edit'); return view('tags.edit', compact('tag', 'subTitle', 'subTitleIcon', 'apiKey')); } diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index ed25b29bf6..ae7a3dabcc 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -70,8 +70,6 @@ class MassController extends Controller // put previous url in session $this->rememberPreviousUri('transactions.mass-delete.uri'); - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'mass-delete'); return view('transactions.mass-delete', compact('journals', 'subTitle')); } @@ -166,8 +164,6 @@ class MassController extends Controller // put previous url in session $this->rememberPreviousUri('transactions.mass-edit.uri'); - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'mass-edit'); // collect some useful meta data for the mass edit: $filtered->each( diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 8e746e85e3..13a0578616 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -173,8 +173,6 @@ class SingleController extends Controller $this->rememberPreviousUri('transactions.create.uri'); } Session::forget('transactions.create.fromStore'); - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'create-' . $what); asort($piggies); @@ -205,8 +203,6 @@ class SingleController extends Controller // put previous url in session $this->rememberPreviousUri('transactions.delete.uri'); - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'delete-' . $what); return view('transactions.single.delete', compact('journal', 'subTitle', 'what')); } @@ -313,8 +309,6 @@ class SingleController extends Controller } Session::flash('preFilled', $preFilled); - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'edit-' . $what); // put previous url in session if not redirect from store (not "return_to_edit"). if (true !== session('transactions.edit.fromUpdate')) { diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index a5fe0a63ef..9b141fe5e8 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -116,8 +116,6 @@ class SplitController extends Controller $accountArray[$account->id]['currency_id'] = intval($account->getMeta('currency_id')); } - Session::flash('gaEventCategory', 'transactions'); - Session::flash('gaEventAction', 'edit-split-' . $preFilled['what']); // put previous url in session if not redirect from store (not "return_to_edit"). if (true !== session('transactions.edit-split.fromUpdate')) { diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index 7c52a8acff..d01ab308c0 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -215,16 +215,6 @@ })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); ga('create', '{{ env('ANALYTICS_ID', 'XXX-XX-X') }}', {'siteSpeedSampleRate': 100}); ga('send', 'pageview'); - - // send an event if relevant: - {% if Session.has('gaEventCategory') and Session.has('gaEventAction') and not Session.has('gaEventLabel') %} - ga('send', 'event', '{{ Session.get('gaEventCategory') }}', '{{ Session.get('gaEventAction') }}'); - {% endif %} - - // send event if relevant: - {% if Session.has('gaEventCategory') and Session.has('gaEventAction') and Session.has('gaEventLabel') %} - ga('send', 'event', '{{ Session.get('gaEventCategory') }}', '{{ Session.get('gaEventAction') }}', '{{ Session.get('gaEventLabel') }}'); - {% endif %} {% endif %} diff --git a/resources/views/layout/empty.twig b/resources/views/layout/empty.twig index 02fc0c111e..f96c8615d1 100644 --- a/resources/views/layout/empty.twig +++ b/resources/views/layout/empty.twig @@ -47,16 +47,6 @@ })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); ga('create', '{{ env('ANALYTICS_ID', 'XXX-XX-X') }}', {'siteSpeedSampleRate': 100}); ga('send', 'pageview'); - - // send an event if relevant: - {% if Session.has('gaEventCategory') and Session.has('gaEventAction') and not Session.has('gaEventLabel') %} - ga('send', 'event', '{{ Session.get('gaEventCategory') }}', '{{ Session.get('gaEventAction') }}'); - {% endif %} - - // send event if relevant: - {% if Session.has('gaEventCategory') and Session.has('gaEventAction') and Session.has('gaEventLabel') %} - ga('send', 'event', '{{ Session.get('gaEventCategory') }}', '{{ Session.get('gaEventAction') }}', '{{ Session.get('gaEventLabel') }}'); - {% endif %} {% endif %} diff --git a/resources/views/layout/guest.twig b/resources/views/layout/guest.twig index 2b2b10e8fd..3742c6d910 100644 --- a/resources/views/layout/guest.twig +++ b/resources/views/layout/guest.twig @@ -58,16 +58,6 @@ })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga'); ga('create', '{{ env('ANALYTICS_ID', 'XXX-XX-X') }}', {'siteSpeedSampleRate': 100}); ga('send', 'pageview'); - - // send an event if relevant: - {% if Session.has('gaEventCategory') and Session.has('gaEventAction') and not Session.has('gaEventLabel') %} - ga('send', 'event', '{{ Session.get('gaEventCategory') }}', '{{ Session.get('gaEventAction') }}'); - {% endif %} - - // send event if relevant: - {% if Session.has('gaEventCategory') and Session.has('gaEventAction') and Session.has('gaEventLabel') %} - ga('send', 'event', '{{ Session.get('gaEventCategory') }}', '{{ Session.get('gaEventAction') }}', '{{ Session.get('gaEventLabel') }}'); - {% endif %} {% endif %} diff --git a/resources/views/reports/partials/exp-budgets.twig b/resources/views/reports/partials/exp-budgets.twig index 1d5e382ce9..de59cc258e 100644 --- a/resources/views/reports/partials/exp-budgets.twig +++ b/resources/views/reports/partials/exp-budgets.twig @@ -1,26 +1,25 @@ - - + + {% for budgetId, entry in together %} - - - - + + {% for categoryId, entry in together %} - - - {% endfor %} diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index 650c224fac..ac042e5dd6 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -63,22 +63,22 @@

{% if job.configuration['has-config-file'] != false %} - + {% endif %}
{% if job.configuration['has-config-file'] != false %} -

-   -

-

- {{ trans('import.status_ready_share')|raw }} -

+

+   +

+

+ {{ trans('import.status_ready_share')|raw }} +

{% endif %} diff --git a/resources/views/index.twig b/resources/views/index.twig index bab812d8b9..c763197739 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -112,7 +112,8 @@ {# EXPENSE ACCOUNTS #}
@@ -123,7 +124,8 @@ {% if showDeps %}
@@ -146,9 +148,9 @@ {% if start.lte(today) and end.gte(today) %} - var today = {{ today.diffInDays(start) + 1 }}; + var today = {{ today.diffInDays(start) + 1 }}; {% else %} - var today = -1; + var today = -1; {% endif %} diff --git a/resources/views/javascript/variables.twig b/resources/views/javascript/variables.twig index 0da9d3eadf..1bd44157cc 100644 --- a/resources/views/javascript/variables.twig +++ b/resources/views/javascript/variables.twig @@ -1,4 +1,3 @@ - // date ranges var ranges = {} {% for title, range in dateRangeConfig.ranges %} @@ -7,22 +6,22 @@ var ranges = {} // date range meta configuration var dateRangeMeta = { - title: "{{ dateRangeTitle }}", - uri: "{{ route('daterange') }}", - labels: { - apply: "{{ 'apply'|_ }}", - cancel: "{{ 'cancel'|_ }}", - from: "{{ 'from'|_ }}", - to: "{{ 'to'|_ }}", - customRange: "{{ 'customRange'|_ }}" - } +title: "{{ dateRangeTitle }}", +uri: "{{ route('daterange') }}", +labels: { +apply: "{{ 'apply'|_ }}", +cancel: "{{ 'cancel'|_ }}", +from: "{{ 'from'|_ }}", +to: "{{ 'to'|_ }}", +customRange: "{{ 'customRange'|_ }}" +} }; // date range actual configuration: var dateRangeConfig = { - startDate: moment("{{ dateRangeConfig.start }}"), - endDate: moment("{{ dateRangeConfig.end }}"), - ranges: ranges +startDate: moment("{{ dateRangeConfig.start }}"), +endDate: moment("{{ dateRangeConfig.end }}"), +ranges: ranges }; diff --git a/resources/views/json/piggy-banks.twig b/resources/views/json/piggy-banks.twig index d8cc226c5e..478ba1d95b 100644 --- a/resources/views/json/piggy-banks.twig +++ b/resources/views/json/piggy-banks.twig @@ -5,9 +5,10 @@
{% for entry in info %} - {{ entry.name }}
+ {{ entry.name }}
-
+
{% if entry.percentage >=20 %}{{ entry.amount|formatAmountPlain }}{% endif %}
{% if entry.percentage < 20 %} {{ entry.amount|formatAmountPlain }}{% endif %} diff --git a/resources/views/layout/default.twig b/resources/views/layout/default.twig index d01ab308c0..f39e95761a 100644 --- a/resources/views/layout/default.twig +++ b/resources/views/layout/default.twig @@ -185,7 +185,8 @@ - + {% if not shownDemo %} diff --git a/resources/views/list/accounts.twig b/resources/views/list/accounts.twig index 90a031efbb..90c69be280 100644 --- a/resources/views/list/accounts.twig +++ b/resources/views/list/accounts.twig @@ -21,7 +21,14 @@ {% for account in accounts %}
{% if what == "asset" %} diff --git a/resources/views/list/bills.twig b/resources/views/list/bills.twig index e7bb11c980..e4e8f6afc8 100644 --- a/resources/views/list/bills.twig +++ b/resources/views/list/bills.twig @@ -19,7 +19,9 @@ {% for entry in bills %}
{{ 'budget'|_ }}{{ 'spent'|_ }}{{ 'category'|_ }}{{ 'spent'|_ }}
- - {% if entry.budget.name|length ==0 %} + + {% if entry.budget == null %} {{ 'no_budget_squared'|_ }} {% else %} - {{ entry.budget.name }} + {{ entry.budget }} {% endif %} - {% if entry.spent|length ==0 %} + + {% if entry.spent.per_currency|length ==0 %} {{ '0'|formatAmount }} {% else %} - {% for expense in entry.spent %} + {% for expense in entry.spent.per_currency %} {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
{% endfor %} {% endif %} diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig index 6483faf128..aff8d62089 100644 --- a/resources/views/reports/partials/exp-categories.twig +++ b/resources/views/reports/partials/exp-categories.twig @@ -2,34 +2,35 @@
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}
- {% if entry.category.name|length ==0 %} + + {% if entry.category == null %} {{ 'noCategory'|_ }} {% else %} - {{ entry.category.name }} + {{ entry.category }} + {% endif %} - {% if entry.spent|length ==0 %} + + {% if entry.spent.per_currency|length ==0 %} {{ '0'|formatAmount }} {% else %} - {% for expense in entry.spent %} + {% for expense in entry.spent.per_currency %} {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
{% endfor %} {% endif %}
- {% if entry.earned|length ==0 %} + + {% if entry.earned.per_currency|length ==0 %} {{ '0'|formatAmount }} {% else %} - {% for income in entry.earned %} + {% for income in entry.earned.per_currency %} {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
{% endfor %} {% endif %} diff --git a/resources/views/reports/partials/exp-not-grouped.twig b/resources/views/reports/partials/exp-not-grouped.twig index c946e8e307..bc454b7d44 100644 --- a/resources/views/reports/partials/exp-not-grouped.twig +++ b/resources/views/reports/partials/exp-not-grouped.twig @@ -1,33 +1,33 @@ + - - + + {% for name, amounts in result %} - - - - - + + + + + {% endfor %} -
{{ 'name'|_ }}
{{ name }} - {% if amounts.spent|length == 0%} - {{ '0'|formatAmount }} - {% endif %} - {% for expense in amounts.spent %} - {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
- {% endfor %} -
- {% if amounts.earned|length == 0 %} - {{ '0'|formatAmount }} - {% endif %} - {% for income in amounts.earned %} - {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
- {% endfor %} -
{{ name }} + {% if amounts.spent.per_currency|length == 0%} + {{ '0'|formatAmount }} + {% endif %} + {% for expense in amounts.spent.per_currency %} + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
+ {% endfor %} +
+ {% if amounts.earned.per_currency|length == 0%} + {{ '0'|formatAmount }} + {% endif %} + {% for income in amounts.earned.per_currency %} + {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
+ {% endfor %} +
From 5a7cf04a7ce1e22ee79a71f00607d906a238e076 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 15:17:02 +0100 Subject: [PATCH 030/142] Update import index [skip ci] --- resources/lang/en_US/firefly.php | 4 ++++ resources/views/import/index.twig | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 43bf852d52..6e1b252c24 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1073,6 +1073,10 @@ return [ // import bread crumbs and titles: 'import' => 'Import', 'import_data' => 'Import data', + 'import_from_bunq' => 'Import from bunq', + 'import_using_spectre' => 'Import using Spectre', + 'import_using_plaid' => 'Import using Plaid', + // import index page: 'import_index_title' => 'Import data into Firefly III', diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index bb941e7c1c..e83cef961b 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -20,32 +20,32 @@
+ {# file import #} + {# bunq import #} + {# import from Spectre #}
From cacd889193da7c80dc47884e76e223466c06c9ac Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 11 Dec 2017 19:42:57 +0100 Subject: [PATCH 031/142] New translations [skip ci] --- resources/lang/de_DE/auth.php | 21 ++++++++++----- resources/lang/de_DE/bank.php | 37 +++++++++++++++++++++++-- resources/lang/de_DE/breadcrumbs.php | 22 ++++++++++----- resources/lang/de_DE/config.php | 21 ++++++++++----- resources/lang/de_DE/csv.php | 18 ++++++++++--- resources/lang/de_DE/demo.php | 21 +++++++++++---- resources/lang/de_DE/firefly.php | 38 +++++++++++++++++++------- resources/lang/de_DE/form.php | 26 +++++++++++++----- resources/lang/de_DE/intro.php | 19 ++++++++++--- resources/lang/de_DE/list.php | 21 ++++++++++----- resources/lang/de_DE/pagination.php | 21 ++++++++++----- resources/lang/de_DE/passwords.php | 21 ++++++++++----- resources/lang/de_DE/validation.php | 21 ++++++++++----- resources/lang/en_US/firefly.php | 7 ++--- resources/lang/fr_FR/auth.php | 21 ++++++++++----- resources/lang/fr_FR/bank.php | 37 +++++++++++++++++++++++-- resources/lang/fr_FR/breadcrumbs.php | 22 ++++++++++----- resources/lang/fr_FR/config.php | 21 ++++++++++----- resources/lang/fr_FR/csv.php | 18 ++++++++++--- resources/lang/fr_FR/demo.php | 21 +++++++++++---- resources/lang/fr_FR/firefly.php | 36 +++++++++++++++++++------ resources/lang/fr_FR/form.php | 26 +++++++++++++----- resources/lang/fr_FR/intro.php | 21 +++++++++++---- resources/lang/fr_FR/list.php | 21 ++++++++++----- resources/lang/fr_FR/pagination.php | 21 ++++++++++----- resources/lang/fr_FR/passwords.php | 21 ++++++++++----- resources/lang/fr_FR/validation.php | 21 ++++++++++----- resources/lang/nl_NL/auth.php | 21 ++++++++++----- resources/lang/nl_NL/bank.php | 37 +++++++++++++++++++++++-- resources/lang/nl_NL/breadcrumbs.php | 22 ++++++++++----- resources/lang/nl_NL/config.php | 21 ++++++++++----- resources/lang/nl_NL/csv.php | 18 ++++++++++--- resources/lang/nl_NL/demo.php | 21 +++++++++++---- resources/lang/nl_NL/firefly.php | 36 +++++++++++++++++++------ resources/lang/nl_NL/form.php | 26 +++++++++++++----- resources/lang/nl_NL/intro.php | 19 ++++++++++--- resources/lang/nl_NL/list.php | 21 ++++++++++----- resources/lang/nl_NL/pagination.php | 21 ++++++++++----- resources/lang/nl_NL/passwords.php | 21 ++++++++++----- resources/lang/nl_NL/validation.php | 21 ++++++++++----- resources/lang/pl_PL/auth.php | 21 ++++++++++----- resources/lang/pl_PL/bank.php | 37 +++++++++++++++++++++++-- resources/lang/pl_PL/breadcrumbs.php | 22 ++++++++++----- resources/lang/pl_PL/config.php | 21 ++++++++++----- resources/lang/pl_PL/csv.php | 18 ++++++++++--- resources/lang/pl_PL/demo.php | 21 +++++++++++---- resources/lang/pl_PL/firefly.php | 40 +++++++++++++++++++++------- resources/lang/pl_PL/form.php | 26 +++++++++++++----- resources/lang/pl_PL/intro.php | 19 ++++++++++--- resources/lang/pl_PL/list.php | 21 ++++++++++----- resources/lang/pl_PL/pagination.php | 21 ++++++++++----- resources/lang/pl_PL/passwords.php | 21 ++++++++++----- resources/lang/pl_PL/validation.php | 21 ++++++++++----- 53 files changed, 938 insertions(+), 297 deletions(-) diff --git a/resources/lang/de_DE/auth.php b/resources/lang/de_DE/auth.php index 2ed9237587..c7a6e201d4 100644 --- a/resources/lang/de_DE/auth.php +++ b/resources/lang/de_DE/auth.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ /* diff --git a/resources/lang/de_DE/bank.php b/resources/lang/de_DE/bank.php index 86a0c3062b..ada6c1c3db 100644 --- a/resources/lang/de_DE/bank.php +++ b/resources/lang/de_DE/bank.php @@ -1,8 +1,41 @@ . + */ + declare(strict_types=1); return [ - 'bunq_prerequisites_title' => 'Voraussetzungen für einen Import von bunq', - 'bunq_prerequisites_text' => 'Um aus bunq importieren zu können, benötigen Sie einen API-Schlüssel. Sie können dies über die App tun.', + 'bunq_prerequisites_title' => 'Voraussetzungen für einen Import von bunq', + 'bunq_prerequisites_text' => 'Um aus bunq importieren zu können, benötigen Sie einen API-Schlüssel. Sie können dies über die App tun.', + + // Spectre: + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_select_provider_title' => 'Select a bank', + 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', + 'spectre_input_fields_title' => 'Input mandatory fields', + 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', + 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', ]; diff --git a/resources/lang/de_DE/breadcrumbs.php b/resources/lang/de_DE/breadcrumbs.php index 6070b07090..59deb8579b 100644 --- a/resources/lang/de_DE/breadcrumbs.php +++ b/resources/lang/de_DE/breadcrumbs.php @@ -1,17 +1,25 @@ . */ +declare(strict_types=1); return [ 'home' => 'Home', 'edit_currency' => 'Bearbeite die Währung ":name"', diff --git a/resources/lang/de_DE/config.php b/resources/lang/de_DE/config.php index 0556b0f612..ddcbc4d0fe 100644 --- a/resources/lang/de_DE/config.php +++ b/resources/lang/de_DE/config.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'locale' => 'de, Deutsch, de_DE, de_DE.utf8, de_DE.UTF-8', 'month' => '%B %Y', diff --git a/resources/lang/de_DE/csv.php b/resources/lang/de_DE/csv.php index 5533ad319a..5f5b4fcab1 100644 --- a/resources/lang/de_DE/csv.php +++ b/resources/lang/de_DE/csv.php @@ -1,12 +1,22 @@ . */ declare(strict_types=1); diff --git a/resources/lang/de_DE/demo.php b/resources/lang/de_DE/demo.php index eec5193c40..596bd1e06a 100644 --- a/resources/lang/de_DE/demo.php +++ b/resources/lang/de_DE/demo.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ 'no_demo_text' => 'Leider gibt es keine zusätzlichen Demoerklärungen für diese Seite.', 'see_help_icon' => 'Trotzdem kann der -Icon in der oberen rechten Ecke mehr erzählen.', diff --git a/resources/lang/de_DE/firefly.php b/resources/lang/de_DE/firefly.php index 0ded8338a7..cc8f76c703 100644 --- a/resources/lang/de_DE/firefly.php +++ b/resources/lang/de_DE/firefly.php @@ -1,16 +1,26 @@ . */ +declare(strict_types=1); + return [ // general stuff: 'close' => 'Schließen', @@ -71,7 +81,7 @@ return [ 'source_accounts' => 'Herkunftskonto', 'destination_accounts' => 'Zielkonto', 'user_id_is' => 'Ihre Benutzerkennung ist :user', - 'field_supports_markdown' => 'Diese Feld unterstützt Abschlag .', + 'field_supports_markdown' => 'Dieses Feld unterstützt Markdown.', 'need_more_help' => 'Wenn Sie Hilfe beim Bedienen von Firefly III brauchen, erstellen Sie ein Ticket auf Github.', 'reenable_intro_text' => 'Sie können auch die Einführung wieder aktivieren.', 'intro_boxes_after_refresh' => 'Die Einführungsfelder werden wieder angezeigt, wenn Sie die Seite aktualisieren.', @@ -708,6 +718,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'mass_edit_journals' => 'Bearbeiten Sie eine Reihe von Überweisungen', 'cannot_edit_other_fields' => 'You cannot mass-edit other fields than the ones here, because there is no room to show them. Please follow the link and edit them by one-by-one, if you need to edit these fields.', 'no_budget' => '(kein Budget)', + 'no_budget_squared' => '(no budget)', 'perm-delete-many' => 'Das Löschen von mehreren Elementen auf einmal kann sich störend auswirken. Bitte seien Sie vorsichtig.', 'mass_deleted_transactions_success' => ':amount Überweisung(en) gelöscht.', 'mass_edited_transactions_success' => ':amount Überweisung(en) aktualisiert', @@ -765,6 +776,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'piggyBanks' => 'Sparschweine', 'bills' => 'Rechnungen', 'withdrawal' => 'Ausgabe', + 'opening_balance' => 'Opening balance', 'deposit' => 'Einnahme', 'account' => 'Konto', 'transfer' => 'Überweisung', @@ -787,6 +799,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'report_default' => 'Default financial report between :start and :end', 'report_audit' => 'Transaction history overview between :start and :end', 'report_category' => 'Category report between :start and :end', + 'report_account' => 'Expense/revenue account report between :start and :end', 'report_budget' => 'Budget report between :start and :end', 'report_tag' => 'Tag report between :start and :end', 'quick_link_reports' => 'Schnellzugriff', @@ -822,6 +835,7 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'report_type_category' => 'Kategorie-Bericht', 'report_type_budget' => 'Budgetbericht', 'report_type_tag' => 'Tag report', + 'report_type_account' => 'Expense/revenue account report', 'more_info_help' => 'Weitere Informationen über diese Art von Berichten finden Sie in der Hilfe. Drücken Sie hierfür das (?)-Symbol in der oberen rechten Ecke.', 'report_included_accounts' => 'Eingezogene Konten', 'report_date_range' => 'Zeitraum', @@ -873,7 +887,10 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'account_role_ccAsset' => 'Kreditkarte', 'budget_chart_click' => 'Klicken Sie auf einen Budgetnamen in der obigen Tabelle, um ein Diagramm anzuzeigen.', 'category_chart_click' => 'Klicken Sie auf einen Kategorienamen in der obigen Tabelle, um ein Diagramm zu sehen.', - + 'in_out_accounts' => 'Earned and spent per combination', + 'in_out_per_category' => 'Earned and spent per category', + 'out_per_budget' => 'Spent per budget', + 'select_expense_revenue' => 'Select expense/revenue account', // charts: 'chart' => 'Diagram', @@ -885,7 +902,6 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', 'earned' => 'Verdient', 'overspent' => 'Zuviel ausgegeben', 'left' => 'Übrig', - 'no_budget' => '(no budget)', 'max-amount' => 'Höchstbetrag', 'min-amount' => 'Mindestbetrag', 'journal-amount' => 'Aktueller Rechnungseintrag', @@ -1055,6 +1071,10 @@ Sollen zusätzlich Ihre Girokonten angezeigt werden?', // import bread crumbs and titles: 'import' => 'Import', 'import_data' => 'Daten importieren', + 'import_from_bunq' => 'Import from bunq', + 'import_using_spectre' => 'Import using Spectre', + 'import_using_plaid' => 'Import using Plaid', + // import index page: 'import_index_title' => 'Daten in Firefly III importieren', diff --git a/resources/lang/de_DE/form.php b/resources/lang/de_DE/form.php index 2714fae3e4..84fcba927b 100644 --- a/resources/lang/de_DE/form.php +++ b/resources/lang/de_DE/form.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ // new user: @@ -186,6 +195,11 @@ return [ 'csv_delimiter' => 'CSV-Trennzeichen', 'csv_import_account' => 'Standard Import-Konto', 'csv_config' => 'CSV-Import Einstellungen', + 'client_id' => 'Client ID', + 'service_secret' => 'Service secret', + 'app_secret' => 'App secret', + 'public_key' => 'Public key', + 'country_code' => 'Country code', 'due_date' => 'Fälligkeitstermin', diff --git a/resources/lang/de_DE/intro.php b/resources/lang/de_DE/intro.php index 7f181bb9f6..75a7ab10a9 100644 --- a/resources/lang/de_DE/intro.php +++ b/resources/lang/de_DE/intro.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ // index 'index_intro' => 'Wilkommen auf der Startseite von Firefly III. Bitte nehmen Sie sich die Zeit, um ein Gefühl dafür zu bekommen, wie Firefly III funktioniert.', diff --git a/resources/lang/de_DE/list.php b/resources/lang/de_DE/list.php index e021c7d5e9..e4c1cf8677 100644 --- a/resources/lang/de_DE/list.php +++ b/resources/lang/de_DE/list.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'buttons' => 'Schaltfläche', 'icon' => 'Icon', diff --git a/resources/lang/de_DE/pagination.php b/resources/lang/de_DE/pagination.php index 93f901bbf6..c272e75227 100644 --- a/resources/lang/de_DE/pagination.php +++ b/resources/lang/de_DE/pagination.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'previous' => '« Vorherige', diff --git a/resources/lang/de_DE/passwords.php b/resources/lang/de_DE/passwords.php index 272678f176..9d64fef788 100644 --- a/resources/lang/de_DE/passwords.php +++ b/resources/lang/de_DE/passwords.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'password' => 'Passwörter müssen mindestens 6 Zeichen lang sein und übereinstimmen.', 'user' => 'Wir können keinen Benutzer mit dieser E-Mail Adresse finden.', diff --git a/resources/lang/de_DE/validation.php b/resources/lang/de_DE/validation.php index 2633ffb165..166db4156e 100644 --- a/resources/lang/de_DE/validation.php +++ b/resources/lang/de_DE/validation.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'iban' => 'Dies ist keine gültige IBAN.', 'unique_account_number_for_user' => 'Es sieht so aus, als ob diese Kontonummer bereits verwendet würde.', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6e1b252c24..bf9fb278a4 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -834,7 +834,7 @@ return [ 'report_type_category' => 'Category report', 'report_type_budget' => 'Budget report', 'report_type_tag' => 'Tag report', - 'report_type_account' => 'Combined account report', + 'report_type_account' => 'Expense/revenue account report', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'Included accounts', 'report_date_range' => 'Date range', @@ -886,10 +886,7 @@ return [ 'account_role_ccAsset' => 'Credit card', 'budget_chart_click' => 'Please click on a budget name in the table above to see a chart.', 'category_chart_click' => 'Please click on a category name in the table above to see a chart.', - 'in_out_accounts' => 'Earned and spent per combined account', - 'in_out_accounts_period_day' => 'Earned and spent, grouped by day', - 'in_out_accounts_period_month' => 'Earned and spent, grouped by month', - 'in_out_accounts_period_year' => 'Earned and spent, grouped by year', + 'in_out_accounts' => 'Earned and spent per combination', 'in_out_per_category' => 'Earned and spent per category', 'out_per_budget' => 'Spent per budget', 'select_expense_revenue' => 'Select expense/revenue account', diff --git a/resources/lang/fr_FR/auth.php b/resources/lang/fr_FR/auth.php index 67482eb063..2d9f9fe8a3 100644 --- a/resources/lang/fr_FR/auth.php +++ b/resources/lang/fr_FR/auth.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ /* diff --git a/resources/lang/fr_FR/bank.php b/resources/lang/fr_FR/bank.php index dc45c22851..d82a6b9891 100644 --- a/resources/lang/fr_FR/bank.php +++ b/resources/lang/fr_FR/bank.php @@ -1,8 +1,41 @@ . + */ + declare(strict_types=1); return [ - 'bunq_prerequisites_title' => 'Pré-requis pour un import depuis Bunq', - 'bunq_prerequisites_text' => 'Pour pouvoir importer depuis Bunq, vous avez besoin d\'obtenir une clé API. Vous pouvez faire celà depuis l\'appli.', + 'bunq_prerequisites_title' => 'Pré-requis pour un import depuis Bunq', + 'bunq_prerequisites_text' => 'Pour pouvoir importer depuis Bunq, vous avez besoin d\'obtenir une clé API. Vous pouvez faire celà depuis l\'appli.', + + // Spectre: + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_select_provider_title' => 'Select a bank', + 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', + 'spectre_input_fields_title' => 'Input mandatory fields', + 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', + 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', ]; diff --git a/resources/lang/fr_FR/breadcrumbs.php b/resources/lang/fr_FR/breadcrumbs.php index 20ac87c186..1e719eca2e 100644 --- a/resources/lang/fr_FR/breadcrumbs.php +++ b/resources/lang/fr_FR/breadcrumbs.php @@ -1,17 +1,25 @@ . */ +declare(strict_types=1); return [ 'home' => 'Accueil', 'edit_currency' => 'Modifier la devise "%name"', diff --git a/resources/lang/fr_FR/config.php b/resources/lang/fr_FR/config.php index 846407e08a..186c364d33 100644 --- a/resources/lang/fr_FR/config.php +++ b/resources/lang/fr_FR/config.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'locale' => 'fr, French, fr_FR, fr_FR.utf8, fr_FR.UTF-8', 'month' => '%B %Y', diff --git a/resources/lang/fr_FR/csv.php b/resources/lang/fr_FR/csv.php index 9d173e73e4..232a452d09 100644 --- a/resources/lang/fr_FR/csv.php +++ b/resources/lang/fr_FR/csv.php @@ -1,12 +1,22 @@ . */ declare(strict_types=1); diff --git a/resources/lang/fr_FR/demo.php b/resources/lang/fr_FR/demo.php index 6466b291ac..498e4dc479 100644 --- a/resources/lang/fr_FR/demo.php +++ b/resources/lang/fr_FR/demo.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ 'no_demo_text' => 'Désolé, il n’y a aucun texte supplémentaire de démonstration ou d\'explication pour cette page.', 'see_help_icon' => 'Cependant, l\'icône située dans le coin supérieur droit peut vous en dire plus.', diff --git a/resources/lang/fr_FR/firefly.php b/resources/lang/fr_FR/firefly.php index 34a67766e1..a99a90a0db 100644 --- a/resources/lang/fr_FR/firefly.php +++ b/resources/lang/fr_FR/firefly.php @@ -1,16 +1,26 @@ . */ +declare(strict_types=1); + return [ // general stuff: 'close' => 'Fermer', @@ -708,6 +718,7 @@ return [ 'mass_edit_journals' => 'Modifier un certain nombre d’opérations', 'cannot_edit_other_fields' => 'Vous ne peut pas modifier en masse d\'autres champs que ceux ici, car il n’y a pas de place pour tous les montrer. S’il vous plaît suivez le lien et modifiez les par un par un, si vous devez modifier ces champs.', 'no_budget' => '(pas de budget)', + 'no_budget_squared' => '(no budget)', 'perm-delete-many' => 'Supprimer de nombreux éléments en une seule fois peut être très problématique. Soyez prudent.', 'mass_deleted_transactions_success' => 'Montant des opérations supprimées : :amount.', 'mass_edited_transactions_success' => 'Montant des opérations mises à jour : :amount', @@ -765,6 +776,7 @@ return [ 'piggyBanks' => 'Tirelires', 'bills' => 'Factures', 'withdrawal' => 'Retrait', + 'opening_balance' => 'Opening balance', 'deposit' => 'Dépôt', 'account' => 'Compte', 'transfer' => 'Transfert', @@ -787,6 +799,7 @@ return [ 'report_default' => 'Rapport financier par défaut entre :start et :end', 'report_audit' => 'Aperçu de l\'historique des transactions entre :start et :end', 'report_category' => 'Rapport de catégorie entre :start et :end', + 'report_account' => 'Expense/revenue account report between :start and :end', 'report_budget' => 'Rapport du budget entre :start et :end', 'report_tag' => 'Marquer le rapport entre :start and :end', 'quick_link_reports' => 'Liens rapides', @@ -822,6 +835,7 @@ return [ 'report_type_category' => 'Rapport par catégories', 'report_type_budget' => 'Rapport du budget', 'report_type_tag' => 'Rapport de tag', + 'report_type_account' => 'Expense/revenue account report', 'more_info_help' => 'Plus d’informations sur ces types de rapports se trouvent dans les pages d’aide. Appuyez sur l’icône ( ?) dans le coin supérieur droit.', 'report_included_accounts' => 'Comptes inclus', 'report_date_range' => 'Intervalle de dates', @@ -873,7 +887,10 @@ return [ 'account_role_ccAsset' => 'Carte de crédit', 'budget_chart_click' => 'Cliquez sur le nom du budget dans le tableau ci-dessus pour voir un graphique.', 'category_chart_click' => 'Cliquez sur un nom de catégorie dans le tableau ci-dessus pour voir un graphique.', - + 'in_out_accounts' => 'Earned and spent per combination', + 'in_out_per_category' => 'Earned and spent per category', + 'out_per_budget' => 'Spent per budget', + 'select_expense_revenue' => 'Select expense/revenue account', // charts: 'chart' => 'Diagramme', @@ -885,7 +902,6 @@ return [ 'earned' => 'Gagné', 'overspent' => 'Trop dépensé', 'left' => 'Reste', - 'no_budget' => '(pas de budget)', 'max-amount' => 'Montant maximum', 'min-amount' => 'Montant minimum', 'journal-amount' => 'Entrée de facture courante', @@ -1055,6 +1071,10 @@ return [ // import bread crumbs and titles: 'import' => 'Import', 'import_data' => 'Importer des données', + 'import_from_bunq' => 'Import from bunq', + 'import_using_spectre' => 'Import using Spectre', + 'import_using_plaid' => 'Import using Plaid', + // import index page: 'import_index_title' => 'Importer des données dans Firefly III', diff --git a/resources/lang/fr_FR/form.php b/resources/lang/fr_FR/form.php index 7c8f86d20e..bfde978d26 100644 --- a/resources/lang/fr_FR/form.php +++ b/resources/lang/fr_FR/form.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ // new user: @@ -186,6 +195,11 @@ return [ 'csv_delimiter' => 'Délimiteur de champ CSV', 'csv_import_account' => 'Compte d’importation par défaut', 'csv_config' => 'Configuration d\'importation CSV', + 'client_id' => 'Client ID', + 'service_secret' => 'Service secret', + 'app_secret' => 'App secret', + 'public_key' => 'Public key', + 'country_code' => 'Country code', 'due_date' => 'Échéance', diff --git a/resources/lang/fr_FR/intro.php b/resources/lang/fr_FR/intro.php index be721ae9cc..b339a3ab5d 100644 --- a/resources/lang/fr_FR/intro.php +++ b/resources/lang/fr_FR/intro.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ // index 'index_intro' => 'Bienvenue sur la page index de Firefly III. Veuillez prendre le temps de parcourir l\'introduction pour comprendre comment Firefly III fonctionne.', @@ -81,7 +92,7 @@ return [ // bill index 'bills_index_paid_in_period' => 'Ce champ indique quand la facture a été payée pour la dernière fois.', - 'bills_index_expected_in_period' => 'Ce champ indique pour chaque facture si et quand la prochaine facture devrait frapper.', + 'bills_index_expected_in_period' => 'Ce champ indique pour chaque facture si et quand la facture suivante est attendue.', // show bill 'bills_show_billInfo' => 'Ce tableau présente des informations générales sur cette facture.', diff --git a/resources/lang/fr_FR/list.php b/resources/lang/fr_FR/list.php index d857ad4361..01918ce364 100644 --- a/resources/lang/fr_FR/list.php +++ b/resources/lang/fr_FR/list.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'buttons' => 'Boutons', 'icon' => 'Icône', diff --git a/resources/lang/fr_FR/pagination.php b/resources/lang/fr_FR/pagination.php index 7d1b7f8bf6..bf80314e60 100644 --- a/resources/lang/fr_FR/pagination.php +++ b/resources/lang/fr_FR/pagination.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'previous' => '« Précédent', diff --git a/resources/lang/fr_FR/passwords.php b/resources/lang/fr_FR/passwords.php index e14a1018c8..04e96c174b 100644 --- a/resources/lang/fr_FR/passwords.php +++ b/resources/lang/fr_FR/passwords.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'password' => 'Les mots de passe doivent contenir au moins six caractères et correspondre à la confirmation.', 'user' => 'Nous ne pouvons pas trouver d\'utilisateur avec cette adresse e-mail.', diff --git a/resources/lang/fr_FR/validation.php b/resources/lang/fr_FR/validation.php index 10dcb26b8a..290011e214 100644 --- a/resources/lang/fr_FR/validation.php +++ b/resources/lang/fr_FR/validation.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'iban' => 'Il ne s\'agit pas d\'un IBAN valide.', 'unique_account_number_for_user' => 'Il semble que ce numéro de compte est déjà utilisé.', diff --git a/resources/lang/nl_NL/auth.php b/resources/lang/nl_NL/auth.php index b89dee9b5b..49fb52d3dc 100644 --- a/resources/lang/nl_NL/auth.php +++ b/resources/lang/nl_NL/auth.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ /* diff --git a/resources/lang/nl_NL/bank.php b/resources/lang/nl_NL/bank.php index 9afeaf242d..becb9c3ffe 100644 --- a/resources/lang/nl_NL/bank.php +++ b/resources/lang/nl_NL/bank.php @@ -1,8 +1,41 @@ . + */ + declare(strict_types=1); return [ - 'bunq_prerequisites_title' => 'Voorwaarden voor een import van bunq', - 'bunq_prerequisites_text' => 'Om transacties bij bunq te importeren heb je een API sleutel nodig. Dit kan via de app.', + 'bunq_prerequisites_title' => 'Voorwaarden voor een import van bunq', + 'bunq_prerequisites_text' => 'Om transacties bij bunq te importeren heb je een API sleutel nodig. Dit kan via de app.', + + // Spectre: + 'spectre_title' => 'Importeer via Spectre', + 'spectre_prerequisites_title' => 'Voorwaarden voor een import via Spectre', + 'spectre_prerequisites_text' => 'Als je gegevens wilt importeren via de Spectre API, moet je een aantal geheime codes bezitten. Ze zijn te vinden op de secrets pagina.', + 'spectre_enter_pub_key' => 'Het importeren werkt alleen als je deze publieke sleutel op uw security pagina invoert.', + 'spectre_select_country_title' => 'Selecteer een land', + 'spectre_select_country_text' => 'Firefly III bevat een groot aantal banken en sites waaruit Spectre transactiegegevens voor je kan downloaden. Deze banken zijn gesorteerd per land. Let op: er is een "Fake Country" voor wanneer je dingen wilt testen. Als je uit andere financiële apps wilt importeren, gebruik dan het denkbeeldige land "Andere financiële applicaties". In Spectre kun je standaard alleen gegevens van nep-banken downloaden. Zorg ervoor dat je status "Live" is op je Dashboard als je wilt downloaden van echte banken.', + 'spectre_select_provider_title' => 'Selecteer een bank', + 'spectre_select_provider_text' => 'Spectre ondersteunt de volgende banken of financiële apps onder :country. Kies degene waaruit je wilt importeren.', + 'spectre_input_fields_title' => 'Verplichte velden', + 'spectre_input_fields_text' => 'De volgende velden zijn verplicht voor ":provider" (uit :country).', + 'spectre_instructions_english' => 'Deze instructies worden door Spectre verstrekt. Ze zijn in het Engels:', ]; diff --git a/resources/lang/nl_NL/breadcrumbs.php b/resources/lang/nl_NL/breadcrumbs.php index 5e56c5e727..9e6bbdc473 100644 --- a/resources/lang/nl_NL/breadcrumbs.php +++ b/resources/lang/nl_NL/breadcrumbs.php @@ -1,17 +1,25 @@ . */ +declare(strict_types=1); return [ 'home' => 'Home', 'edit_currency' => 'Wijzig valuta ":name"', diff --git a/resources/lang/nl_NL/config.php b/resources/lang/nl_NL/config.php index c69f3c6fb3..d982510cea 100644 --- a/resources/lang/nl_NL/config.php +++ b/resources/lang/nl_NL/config.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'locale' => 'nl, Dutch, nl_NL, nl_NL.utf8, nl_NL.UTF-8', 'month' => '%B %Y', diff --git a/resources/lang/nl_NL/csv.php b/resources/lang/nl_NL/csv.php index 52fc84ce26..1151554caf 100644 --- a/resources/lang/nl_NL/csv.php +++ b/resources/lang/nl_NL/csv.php @@ -1,12 +1,22 @@ . */ declare(strict_types=1); diff --git a/resources/lang/nl_NL/demo.php b/resources/lang/nl_NL/demo.php index 631ff2236e..ad13199c2a 100644 --- a/resources/lang/nl_NL/demo.php +++ b/resources/lang/nl_NL/demo.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ 'no_demo_text' => 'Sorry, er is geen extra uitleg voor deze pagina.', 'see_help_icon' => 'Maar het -icoontje kan je wellicht meer vertellen.', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 77a7f0b8b4..5b12a2a69b 100644 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -1,16 +1,26 @@ . */ +declare(strict_types=1); + return [ // general stuff: 'close' => 'Sluiten', @@ -707,6 +717,7 @@ return [ 'mass_edit_journals' => 'Wijzig een aantal transacties', 'cannot_edit_other_fields' => 'Je kan andere velden dan de velden die je hier ziet niet groepsgewijs wijzigen. Er is geen ruimte om ze te laten zien. Als je deze velden toch wilt wijzigen, volg dan de link naast de transactie en wijzig ze stuk voor stuk.', 'no_budget' => '(geen budget)', + 'no_budget_squared' => '(geen budget)', 'perm-delete-many' => 'Veel items in één keer verwijderen kan zeer storend zijn. Wees voorzichtig.', 'mass_deleted_transactions_success' => 'Verwijder :amount transactie(s).', 'mass_edited_transactions_success' => 'Wijzig :amount transactie(s)', @@ -764,6 +775,7 @@ return [ 'piggyBanks' => 'Spaarpotjes', 'bills' => 'Contracten', 'withdrawal' => 'Uitgave', + 'opening_balance' => 'Startsaldo', 'deposit' => 'Inkomsten', 'account' => 'Rekening', 'transfer' => 'Overschrijving', @@ -786,6 +798,7 @@ return [ 'report_default' => 'Standaard financieel rapport (:start tot :end)', 'report_audit' => 'Transactiehistorie-overzicht van :start tot :end', 'report_category' => 'Categorierapport van :start tot :end', + 'report_account' => 'Debiteuren/crediteurenrapport tussen :start en :end', 'report_budget' => 'Budgetrapport van :start tot :end', 'report_tag' => 'Tagrapport van :start tot :end', 'quick_link_reports' => 'Snelle links', @@ -821,6 +834,7 @@ return [ 'report_type_category' => 'Categorierapport', 'report_type_budget' => 'Budgetrapport', 'report_type_tag' => 'Tagrapport', + 'report_type_account' => 'Debiteuren/crediteurenrapport', 'more_info_help' => 'Meer informatie over deze rapporten vind je in de hulppagina\'s. Klik daarvoor op het (?) icoontje rechtsboven.', 'report_included_accounts' => 'Accounts in rapport', 'report_date_range' => 'Datumbereik', @@ -872,7 +886,10 @@ return [ 'account_role_ccAsset' => 'Credit card', 'budget_chart_click' => 'Klik op een budgetnaam in de tabel hierboven om een ​​grafiek te zien.', 'category_chart_click' => 'Klik op een categorienaam in de tabel hierboven om een ​​grafiek te zien.', - + 'in_out_accounts' => 'Inkomsten en uitgaven per combinatie', + 'in_out_per_category' => 'Verdiend en uitgegeven per categorie', + 'out_per_budget' => 'Uitgaven per budget', + 'select_expense_revenue' => 'Selecteer debiteur+crediteur', // charts: 'chart' => 'Diagram', @@ -884,7 +901,6 @@ return [ 'earned' => 'Verdiend', 'overspent' => 'Teveel uitgegeven', 'left' => 'Over', - 'no_budget' => '(geen budget)', 'max-amount' => 'Maximumbedrag', 'min-amount' => 'Minimumbedrag', 'journal-amount' => 'Bedrag voor dit contract', @@ -1054,6 +1070,10 @@ return [ // import bread crumbs and titles: 'import' => 'Import', 'import_data' => 'Importeer data', + 'import_from_bunq' => 'Importeer uit bunq', + 'import_using_spectre' => 'Importeer via Spectre', + 'import_using_plaid' => 'Importeer via Plaid', + // import index page: 'import_index_title' => 'Gegevens importeren in Firefly III', diff --git a/resources/lang/nl_NL/form.php b/resources/lang/nl_NL/form.php index 6e395b39b1..a39170b71c 100644 --- a/resources/lang/nl_NL/form.php +++ b/resources/lang/nl_NL/form.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ // new user: @@ -186,6 +195,11 @@ return [ 'csv_delimiter' => 'CSV scheidingsteken', 'csv_import_account' => 'Standaard rekening voor importeren', 'csv_config' => 'Configuratiebestand', + 'client_id' => 'Client ID', + 'service_secret' => 'Service secret', + 'app_secret' => 'App secret', + 'public_key' => 'Publieke sleutel', + 'country_code' => 'Landcode', 'due_date' => 'Vervaldatum', diff --git a/resources/lang/nl_NL/intro.php b/resources/lang/nl_NL/intro.php index b175eac5be..60d8778acb 100644 --- a/resources/lang/nl_NL/intro.php +++ b/resources/lang/nl_NL/intro.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ // index 'index_intro' => 'Welkom op de homepage van Firefly III. Neem even de tijd voor deze introductie zodat je Firefly III leert kennen.', diff --git a/resources/lang/nl_NL/list.php b/resources/lang/nl_NL/list.php index 2678a80cd3..302f31f784 100644 --- a/resources/lang/nl_NL/list.php +++ b/resources/lang/nl_NL/list.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'buttons' => 'Knoppen', 'icon' => 'Icoon', diff --git a/resources/lang/nl_NL/pagination.php b/resources/lang/nl_NL/pagination.php index 82de8727a0..fed6200128 100644 --- a/resources/lang/nl_NL/pagination.php +++ b/resources/lang/nl_NL/pagination.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'previous' => '« Vorige', diff --git a/resources/lang/nl_NL/passwords.php b/resources/lang/nl_NL/passwords.php index 83ed725b7d..cab41c9cf7 100644 --- a/resources/lang/nl_NL/passwords.php +++ b/resources/lang/nl_NL/passwords.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'password' => 'Wachtwoorden moeten zes karakters lang zijn, en natuurlijk 2x hetzelfde invoeren.', 'user' => 'Geen gebruiker met dat e-mailadres.', diff --git a/resources/lang/nl_NL/validation.php b/resources/lang/nl_NL/validation.php index de20c75a0c..68195f1a08 100644 --- a/resources/lang/nl_NL/validation.php +++ b/resources/lang/nl_NL/validation.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'iban' => 'Dit is niet een geldige IBAN.', 'unique_account_number_for_user' => 'Het lijkt erop dat dit rekeningnummer al in gebruik is.', diff --git a/resources/lang/pl_PL/auth.php b/resources/lang/pl_PL/auth.php index a743bd1901..25966cc415 100644 --- a/resources/lang/pl_PL/auth.php +++ b/resources/lang/pl_PL/auth.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ /* diff --git a/resources/lang/pl_PL/bank.php b/resources/lang/pl_PL/bank.php index 7f2f4c5db0..e8ae734a63 100644 --- a/resources/lang/pl_PL/bank.php +++ b/resources/lang/pl_PL/bank.php @@ -1,8 +1,41 @@ . + */ + declare(strict_types=1); return [ - 'bunq_prerequisites_title' => 'Wymagania wstÄ™pne dotyczÄ…ce importowania z bunq', - 'bunq_prerequisites_text' => 'Aby zaimportować dane z bunq, musisz uzyskać klucz API. Możesz to zrobić za poÅ›rednictwem aplikacji.', + 'bunq_prerequisites_title' => 'Wymagania wstÄ™pne dotyczÄ…ce importowania z bunq', + 'bunq_prerequisites_text' => 'Aby zaimportować dane z bunq, musisz uzyskać klucz API. Możesz to zrobić za poÅ›rednictwem aplikacji.', + + // Spectre: + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_select_provider_title' => 'Select a bank', + 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', + 'spectre_input_fields_title' => 'Input mandatory fields', + 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', + 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', ]; diff --git a/resources/lang/pl_PL/breadcrumbs.php b/resources/lang/pl_PL/breadcrumbs.php index 8f05e92cee..4be868a18a 100644 --- a/resources/lang/pl_PL/breadcrumbs.php +++ b/resources/lang/pl_PL/breadcrumbs.php @@ -1,17 +1,25 @@ . */ +declare(strict_types=1); return [ 'home' => 'Strona główna', 'edit_currency' => 'Modyfikuj walutÄ™ ":name"', diff --git a/resources/lang/pl_PL/config.php b/resources/lang/pl_PL/config.php index 526668d33e..7a9e5d98dc 100644 --- a/resources/lang/pl_PL/config.php +++ b/resources/lang/pl_PL/config.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'locale' => 'pl, Polish, polski, pl_PL, pl_PL.utf8, pl_PL.UTF-8', 'month' => '%B %Y', diff --git a/resources/lang/pl_PL/csv.php b/resources/lang/pl_PL/csv.php index c78ddf0f9e..dde5a38272 100644 --- a/resources/lang/pl_PL/csv.php +++ b/resources/lang/pl_PL/csv.php @@ -1,12 +1,22 @@ . */ declare(strict_types=1); diff --git a/resources/lang/pl_PL/demo.php b/resources/lang/pl_PL/demo.php index 094bff01a7..f07383e7d4 100644 --- a/resources/lang/pl_PL/demo.php +++ b/resources/lang/pl_PL/demo.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ 'no_demo_text' => 'Sorry, there is no extra demo-explanation text for this page.', 'see_help_icon' => 'Jednakże ikona w prawym górnym rogu może powiedzieć Ci wiÄ™cej.', diff --git a/resources/lang/pl_PL/firefly.php b/resources/lang/pl_PL/firefly.php index 6709e20631..b00b3a4f85 100644 --- a/resources/lang/pl_PL/firefly.php +++ b/resources/lang/pl_PL/firefly.php @@ -1,16 +1,26 @@ . */ +declare(strict_types=1); + return [ // general stuff: 'close' => 'Zamknij', @@ -159,11 +169,11 @@ return [ 'import_and_export' => 'Import / eksport', 'export_data' => 'Eksportuj dane', 'export_and_backup_data' => 'Eksport i kopia zapasowa', - 'export_data_intro' => 'For backup purposes, when migrating to another system or when migrating to another Firefly III installation.', + 'export_data_intro' => 'Do celów tworzenia kopii zapasowych, migracji do innego systemu lub migracji do innej instalacji Firefly III.', 'export_format' => 'Format eksportu', 'export_format_csv' => 'WartoÅ›ci oddzielone przecinkami (plik CSV)', 'export_format_mt940' => 'Format kompatybilny z MT940', - 'include_old_uploads_help' => 'Firefly III does not throw away the original CSV files you have imported in the past. You can include them in your export.', + 'include_old_uploads_help' => 'Firefly III nie wyrzuca oryginalnych plików CSV zaimportowanych w przeszÅ‚oÅ›ci. Możesz uwzglÄ™dnić je w eksporcie.', 'do_export' => 'Eksportuj', 'export_status_never_started' => 'Eksport nie zostaÅ‚ jeszcze rozpoczÄ™ty', 'export_status_make_exporter' => 'Creating exporter thing...', @@ -707,6 +717,7 @@ return [ 'mass_edit_journals' => 'Modyfikuj wiele transakcji', 'cannot_edit_other_fields' => 'Nie możesz masowo modyfikować innych pól niż te tutaj, ponieważ nie ma miejsca, aby je pokazać. ProszÄ™ użyć ikony edycji i edytować je jedno po drugim, jeÅ›li chcesz edytować te pola.', 'no_budget' => '(bez budżetu)', + 'no_budget_squared' => '(no budget)', 'perm-delete-many' => 'Usuwanie wielu elementów jednoczeÅ›nie może być bardzo destrukcyjne. ProszÄ™ zachować ostrożność.', 'mass_deleted_transactions_success' => 'UsuniÄ™to :amount transakcjÄ™(i).', 'mass_edited_transactions_success' => 'Zaktualizowano :amount transakcjÄ™(i)', @@ -764,6 +775,7 @@ return [ 'piggyBanks' => 'Skarbonki', 'bills' => 'Rachunki', 'withdrawal' => 'WypÅ‚ata', + 'opening_balance' => 'Opening balance', 'deposit' => 'WpÅ‚ata', 'account' => 'Konto', 'transfer' => 'Transfer', @@ -786,6 +798,7 @@ return [ 'report_default' => 'DomyÅ›lny raport finansowy miÄ™dzy :start i :end', 'report_audit' => 'PrzeglÄ…d historii transakcji miÄ™dzy :start i :end', 'report_category' => 'Raport kategorii miÄ™dzy :start a :end', + 'report_account' => 'Expense/revenue account report between :start and :end', 'report_budget' => 'Raport budżetów miÄ™dzy :start a :end', 'report_tag' => 'Raport tagów miÄ™dzy :start a :end', 'quick_link_reports' => 'Szybkie linki', @@ -821,6 +834,7 @@ return [ 'report_type_category' => 'Raport kategorii', 'report_type_budget' => 'Raport budżetów', 'report_type_tag' => 'Raport tagów', + 'report_type_account' => 'Expense/revenue account report', 'more_info_help' => 'More information about these types of reports can be found in the help pages. Press the (?) icon in the top right corner.', 'report_included_accounts' => 'UwzglÄ™dnione konta', 'report_date_range' => 'Zakres dat', @@ -872,7 +886,10 @@ return [ 'account_role_ccAsset' => 'Karta kredytowa', 'budget_chart_click' => 'Kliknij na nazwÄ™ budżetu w tabeli powyżej, aby zobaczyć wykres.', 'category_chart_click' => 'Kliknij na nazwÄ™ kategorii w tabeli powyżej, aby zobaczyć wykres.', - + 'in_out_accounts' => 'Earned and spent per combination', + 'in_out_per_category' => 'Earned and spent per category', + 'out_per_budget' => 'Spent per budget', + 'select_expense_revenue' => 'Select expense/revenue account', // charts: 'chart' => 'Wykres', @@ -884,7 +901,6 @@ return [ 'earned' => 'Zarobiono', 'overspent' => 'PrzepÅ‚acono', 'left' => 'PozostaÅ‚o', - 'no_budget' => '(no budget)', 'max-amount' => 'Maksymalna kwota', 'min-amount' => 'Minimalna kwota', 'journal-amount' => 'Faktyczna kwota', @@ -1054,6 +1070,10 @@ return [ // import bread crumbs and titles: 'import' => 'Importuj', 'import_data' => 'Importuj dane', + 'import_from_bunq' => 'Import from bunq', + 'import_using_spectre' => 'Import using Spectre', + 'import_using_plaid' => 'Import using Plaid', + // import index page: 'import_index_title' => 'Importuj dane do Firefly III', diff --git a/resources/lang/pl_PL/form.php b/resources/lang/pl_PL/form.php index 396568faf3..8edf715eaf 100644 --- a/resources/lang/pl_PL/form.php +++ b/resources/lang/pl_PL/form.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ // new user: @@ -186,6 +195,11 @@ return [ 'csv_delimiter' => 'CSV field delimiter', 'csv_import_account' => 'DomyÅ›lne konto importu', 'csv_config' => 'Konfiguracja importu CSV', + 'client_id' => 'Client ID', + 'service_secret' => 'Service secret', + 'app_secret' => 'App secret', + 'public_key' => 'Public key', + 'country_code' => 'Country code', 'due_date' => 'Termin realizacji', diff --git a/resources/lang/pl_PL/intro.php b/resources/lang/pl_PL/intro.php index 056a2ae65d..1687aef144 100644 --- a/resources/lang/pl_PL/intro.php +++ b/resources/lang/pl_PL/intro.php @@ -1,14 +1,25 @@ . */ +declare(strict_types=1); return [ // index 'index_intro' => 'Witamy na stronie domowej Firefly III. ProszÄ™ poÅ›więć trochÄ™ czasu, aby przejść przez to wprowadzenie, aby poznać sposób dziaÅ‚ania Firefly III.', diff --git a/resources/lang/pl_PL/list.php b/resources/lang/pl_PL/list.php index b32a13c8ba..3ac5b8d583 100644 --- a/resources/lang/pl_PL/list.php +++ b/resources/lang/pl_PL/list.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'buttons' => 'Przyciski', 'icon' => 'Ikona', diff --git a/resources/lang/pl_PL/pagination.php b/resources/lang/pl_PL/pagination.php index 478654dd97..c2b81f1257 100644 --- a/resources/lang/pl_PL/pagination.php +++ b/resources/lang/pl_PL/pagination.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'previous' => '« Poprzednia', diff --git a/resources/lang/pl_PL/passwords.php b/resources/lang/pl_PL/passwords.php index 519a6ccd14..1072d81fb6 100644 --- a/resources/lang/pl_PL/passwords.php +++ b/resources/lang/pl_PL/passwords.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'password' => 'HasÅ‚o musi zawierać przynajmniej 6 znaków i musi siÄ™ zgadzać z potwierdzeniem.', 'user' => 'Nie możemy znaleźć użytkownika o podanym adresie email.', diff --git a/resources/lang/pl_PL/validation.php b/resources/lang/pl_PL/validation.php index 84e7df6be6..09d67d720d 100644 --- a/resources/lang/pl_PL/validation.php +++ b/resources/lang/pl_PL/validation.php @@ -1,16 +1,25 @@ . */ +declare(strict_types=1); return [ 'iban' => 'To nie jest prawidÅ‚owy IBAN.', 'unique_account_number_for_user' => 'WyglÄ…da na to, że ten numer konta jest już w użyciu.', From 1b4edae4d9de8bd91170dec71266d8c776fee47a Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Dec 2017 18:22:29 +0100 Subject: [PATCH 032/142] Final code for #384 --- .../Chart/ExpenseReportController.php | 269 ++++++++++++++++++ .../Controllers/Report/ExpenseController.php | 81 +++++- config/firefly.php | 2 +- public/js/ff/reports/account/all.js | 57 ---- public/js/ff/reports/account/month.js | 3 +- public/js/ff/reports/all.js | 136 +++++++++ public/js/ff/reports/audit/all.js | 32 --- public/js/ff/reports/budget/all.js | 20 -- public/js/ff/reports/category/all.js | 20 -- public/js/ff/reports/default/all.js | 105 ------- public/js/ff/reports/index.js | 30 -- public/js/ff/reports/tag/all.js | 20 -- resources/views/reports/account/report.twig | 2 +- resources/views/reports/audit/report.twig | 1 + resources/views/reports/budget/month.twig | 2 +- resources/views/reports/category/month.twig | 2 +- resources/views/reports/default/month.twig | 1 + .../views/reports/default/multi-year.twig | 1 + resources/views/reports/default/year.twig | 2 +- resources/views/reports/index.twig | 1 + .../views/reports/partials/exp-budgets.twig | 4 +- .../reports/partials/exp-categories.twig | 6 +- .../reports/partials/exp-not-grouped.twig | 4 +- .../reports/partials/top-transactions.twig | 24 ++ resources/views/reports/tag/month.twig | 3 +- routes/web.php | 4 +- 26 files changed, 526 insertions(+), 306 deletions(-) create mode 100644 app/Http/Controllers/Chart/ExpenseReportController.php delete mode 100644 public/js/ff/reports/account/all.js create mode 100644 public/js/ff/reports/all.js delete mode 100644 public/js/ff/reports/budget/all.js delete mode 100644 public/js/ff/reports/category/all.js delete mode 100644 public/js/ff/reports/tag/all.js create mode 100644 resources/views/reports/partials/top-transactions.twig diff --git a/app/Http/Controllers/Chart/ExpenseReportController.php b/app/Http/Controllers/Chart/ExpenseReportController.php new file mode 100644 index 0000000000..acc7485d1c --- /dev/null +++ b/app/Http/Controllers/Chart/ExpenseReportController.php @@ -0,0 +1,269 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Chart; + +use Carbon\Carbon; +use FireflyIII\Generator\Chart\Basic\GeneratorInterface; +use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Support\CacheProperties; +use Illuminate\Support\Collection; +use Navigation; +use Response; + +/** + * Separate controller because many helper functions are shared. + * + * Class ExpenseReportController + */ +class ExpenseReportController extends Controller +{ + /** @var AccountRepositoryInterface */ + protected $accountRepository; + /** @var GeneratorInterface */ + protected $generator; + + /** + * + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->generator = app(GeneratorInterface::class); + $this->accountRepository = app(AccountRepositoryInterface::class); + + return $next($request); + } + ); + } + + + /** + * @param Collection $accounts + * @param Collection $expense + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function mainChart(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + + $cache = new CacheProperties; + $cache->addProperty('chart.expense.report.main'); + $cache->addProperty($accounts); + $cache->addProperty($expense); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + // return Response::json($cache->get()); // @codeCoverageIgnore + } + + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); + $function = Navigation::preferredEndOfPeriod($start, $end); + $chartData = []; + $currentStart = clone $start; + $combined = $this->combineAccounts($expense); + + // make "all" set: + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + + // prep chart data: + foreach ($combined as $name => $combi) { + // first is always expense account: + /** @var Account $exp */ + $exp = $combi->first(); + $chartData[$exp->id . '-in'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.income'))) . ')', + 'type' => 'bar', + 'yAxisID' => 'y-axis-0', + 'entries' => [], + ]; + $chartData[$exp->id . '-out'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')', + 'type' => 'bar', + 'yAxisID' => 'y-axis-0', + 'entries' => [], + ]; + // total in, total out: + $chartData[$exp->id . '-total-in'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')', + 'type' => 'line', + 'fill' => false, + 'yAxisID' => 'y-axis-1', + 'entries' => [], + ]; + $chartData[$exp->id . '-total-out'] = [ + 'label' => $name . ' (' . strtolower(strval(trans('firefly.sum_of_expenses'))) . ')', + 'type' => 'line', + 'fill' => false, + 'yAxisID' => 'y-axis-1', + 'entries' => [], + ]; + } + + $sumOfIncome = []; + $sumOfExpense = []; + + while ($currentStart < $end) { + $currentEnd = clone $currentStart; + $currentEnd = $currentEnd->$function(); + + // get expenses grouped by opposing name: + $expenses = $this->groupByName($this->getExpenses($accounts, $all, $currentStart, $currentEnd)); + $income = $this->groupByName($this->getIncome($accounts, $all, $currentStart, $currentEnd)); + $label = $currentStart->formatLocalized($format); + + foreach ($combined as $name => $combi) { + // first is always expense account: + /** @var Account $exp */ + $exp = $combi->first(); + $labelIn = $exp->id . '-in'; + $labelOut = $exp->id . '-out'; + $labelSumIn = $exp->id . '-total-in'; + $labelSumOut = $exp->id . '-total-out'; + $currentIncome = $income[$name] ?? '0'; + $currentExpense = $expenses[$name] ?? '0'; + + // add to sum: + $sumOfIncome[$exp->id] = $sumOfIncome[$exp->id] ?? '0'; + $sumOfExpense[$exp->id] = $sumOfExpense[$exp->id] ?? '0'; + $sumOfIncome[$exp->id] = bcadd($sumOfIncome[$exp->id], $currentIncome); + $sumOfExpense[$exp->id] = bcadd($sumOfExpense[$exp->id], $currentExpense); + + // add to chart: + $chartData[$labelIn]['entries'][$label] = $currentIncome; + $chartData[$labelOut]['entries'][$label] = $currentExpense; + $chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$exp->id]; + $chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$exp->id]; + } + $currentStart = clone $currentEnd; + $currentStart->addDay(); + } + // remove all empty entries to prevent cluttering: + $newSet = []; + foreach ($chartData as $key => $entry) { + if (0 === !array_sum($entry['entries'])) { + $newSet[$key] = $chartData[$key]; + } + } + if (0 === count($newSet)) { + $newSet = $chartData; // @codeCoverageIgnore + } + $data = $this->generator->multiSet($newSet); + $cache->store($data); + + return Response::json($data); + } + + /** + * @param Collection $accounts + * + * @return array + */ + protected function combineAccounts(Collection $accounts): array + { + $combined = []; + /** @var Account $expenseAccount */ + foreach ($accounts as $expenseAccount) { + $collection = new Collection; + $collection->push($expenseAccount); + + $revenue = $this->accountRepository->findByName($expenseAccount->name, [AccountType::REVENUE]); + if (!is_null($revenue->id)) { + $collection->push($revenue); + } + $combined[$expenseAccount->name] = $collection; + } + + return $combined; + } + + /** + * @param Collection $accounts + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getExpenses(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setOpposingAccounts($opposing); + + $transactions = $collector->getJournals(); + + return $transactions; + } + + /** + * @param Collection $accounts + * @param Collection $opposing + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getIncome(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): Collection + { + /** @var JournalCollectorInterface $collector */ + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setOpposingAccounts($opposing); + + $transactions = $collector->getJournals(); + + return $transactions; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByName(Collection $set): array + { + // group by opposing account name. + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $name = $transaction->opposing_account_name; + $grouped[$name] = $grouped[$name] ?? '0'; + $grouped[$name] = bcadd($transaction->transaction_amount, $grouped[$name]); + } + + return $grouped; + } +} diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index b23fbf8cf8..26566c82ac 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -28,6 +28,7 @@ use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\CacheProperties; @@ -88,13 +89,13 @@ class ExpenseController extends Controller $all = $all->merge($combi); } // now find spent / earned: - $spent = $this->spentByBudget($accounts, $all, $start, $end); + $spent = $this->spentByBudget($accounts, $all, $start, $end); // join arrays somehow: $together = []; foreach ($spent as $categoryId => $spentInfo) { if (!isset($together[$categoryId])) { $together[$categoryId]['spent'] = $spentInfo; - $together[$categoryId]['budget'] = $spentInfo['name']; + $together[$categoryId]['budget'] = $spentInfo['name']; $together[$categoryId]['grand_total'] = '0'; } $together[$categoryId]['grand_total'] = bcadd($spentInfo['grand_total'], $together[$categoryId]['grand_total']); @@ -150,7 +151,7 @@ class ExpenseController extends Controller unset($spentInfo); foreach ($earned as $categoryId => $earnedInfo) { if (!isset($together[$categoryId])) { - $together[$categoryId]['earned'] = $earnedInfo; + $together[$categoryId]['earned'] = $earnedInfo; $together[$categoryId]['category'] = $earnedInfo['name']; $together[$categoryId]['grand_total'] = '0'; } @@ -210,6 +211,76 @@ class ExpenseController extends Controller } + public function topExpense(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-budget'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + //return $cache->get(); // @codeCoverageIgnore + } + $combined = $this->combineAccounts($expense); + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + // get all expenses in period: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setAccounts($accounts); + $collector->setOpposingAccounts($all); + $set = $collector->getJournals(); + $sorted = $set->sortBy( + function (Transaction $transaction) { + return floatval($transaction->transaction_amount); + } + ); + $result = view('reports.partials.top-transactions', compact('sorted'))->render(); + $cache->store($result); + + return $result; + } + + + + public function topIncome(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) + { + // Properties for cache: + $cache = new CacheProperties; + $cache->addProperty($start); + $cache->addProperty($end); + $cache->addProperty('expense-budget'); + $cache->addProperty($accounts->pluck('id')->toArray()); + $cache->addProperty($expense->pluck('id')->toArray()); + if ($cache->has()) { + return $cache->get(); // @codeCoverageIgnore + } + $combined = $this->combineAccounts($expense); + $all = new Collection; + foreach ($combined as $name => $combi) { + $all = $all->merge($combi); + } + // get all expenses in period: + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setAccounts($accounts); + $collector->setOpposingAccounts($all); + $set = $collector->getJournals(); + $sorted = $set->sortByDesc( + function (Transaction $transaction) { + return floatval($transaction->transaction_amount); + } + ); + $result = view('reports.partials.top-transactions', compact('sorted'))->render(); + $cache->store($result); + + return $result; + } + /** * @param Collection $accounts * @@ -348,7 +419,7 @@ class ExpenseController extends Controller $sum = []; // loop to support multi currency foreach ($set as $transaction) { - $currencyId = $transaction->transaction_currency_id; + $currencyId = $transaction->transaction_currency_id; $budgetName = $transaction->transaction_budget_name; $budgetId = intval($transaction->transaction_budget_id); // if null, grab from journal: @@ -369,7 +440,7 @@ class ExpenseController extends Controller 'per_currency' => [ $currencyId => [ 'sum' => '0', - 'budget' => [ + 'budget' => [ 'id' => $budgetId, 'name' => $budgetName, ], diff --git a/config/firefly.php b/config/firefly.php index 0ecf8112a6..05c58b89d6 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -54,7 +54,7 @@ return [ 'import_pre' => [ 'bunq' => 'FireflyIII\Support\Import\Prerequisites\BunqPrerequisites', 'spectre' => 'FireflyIII\Support\Import\Prerequisites\SpectrePrerequisites', - 'plaid' => 'FireflyIII\Support\Import\Prerequisites\PlairPrerequisites', + 'plaid' => 'FireflyIII\Support\Import\Prerequisites\PlaidPrerequisites', ], 'import_info' => [ 'bunq' => 'FireflyIII\Support\Import\Information\BunqInformation', diff --git a/public/js/ff/reports/account/all.js b/public/js/ff/reports/account/all.js deleted file mode 100644 index 539b599256..0000000000 --- a/public/js/ff/reports/account/all.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - -function loadAjaxPartial(holder, uri) { - "use strict"; - $.get(uri).done(function (data) { - displayAjaxPartial(data, holder); - }).fail(function () { - failAjaxPartial(uri, holder); - }); -} - -function failAjaxPartial(uri, holder) { - "use strict"; - var holderObject = $('#' + holder); - holderObject.parent().find('.overlay').remove(); - holderObject.addClass('general-chart-error'); - -} - -function displayAjaxPartial(data, holder) { - "use strict"; - var obj = $('#' + holder); - obj.html(data); - obj.parent().find('.overlay').remove(); - - // call some often needed recalculations and what-not: - - // find a sortable table and make it sortable: - if (typeof $.bootstrapSortable === "function") { - $.bootstrapSortable(true); - } - - // find the info click things and respond to them: - triggerInfoClick(); - - // trigger list thing - listLengthInitial(); - -} \ No newline at end of file diff --git a/public/js/ff/reports/account/month.js b/public/js/ff/reports/account/month.js index 099cf9a906..a3068ce4ab 100644 --- a/public/js/ff/reports/account/month.js +++ b/public/js/ff/reports/account/month.js @@ -36,5 +36,6 @@ function drawChart() { // month view: // draw account chart - lineChart(mainUri, 'in-out-chart'); + // month view: + doubleYChart(mainUri, 'in-out-chart'); } \ No newline at end of file diff --git a/public/js/ff/reports/all.js b/public/js/ff/reports/all.js new file mode 100644 index 0000000000..493a82fe4d --- /dev/null +++ b/public/js/ff/reports/all.js @@ -0,0 +1,136 @@ +function loadAjaxPartial(holder, uri) { + "use strict"; + $.get(uri).done(function (data) { + displayAjaxPartial(data, holder); + }).fail(function () { + failAjaxPartial(uri, holder); + }); +} + +function failAjaxPartial(uri, holder) { + "use strict"; + var holderObject = $('#' + holder); + holderObject.parent().find('.overlay').remove(); + holderObject.addClass('general-chart-error'); + +} + + +function createCookie(name, value, days) { + "use strict"; + var expires; + + if (days) { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ""; + } + document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; +} + +function readCookie(name) { + "use strict"; + var nameEQ = encodeURIComponent(name) + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) === ' ') { + c = c.substring(1, c.length); + } + if (c.indexOf(nameEQ) === 0) { + return decodeURIComponent(c.substring(nameEQ.length, c.length)); + } + } + return null; +} + + +function triggerInfoClick() { + "use strict"; + // find the little info buttons and respond to them. + $('.firefly-info-button').unbind('click').click(clickInfoButton); +} + +function clickInfoButton(e) { + "use strict"; + // find all data tags, regardless of what they are: + var element = $(e.target); + var attributes = element.data(); + + // set wait cursor + $('body').addClass('waiting'); + + // add some more elements: + attributes.startDate = startDate; + attributes.endDate = endDate; + attributes.accounts = accountIds; + + $.getJSON('popup/general', {attributes: attributes}).done(respondInfoButton).fail(errorInfoButton); +} + +function errorInfoButton() { + "use strict"; + // remove wait cursor + $('body').removeClass('waiting'); + alert('Apologies. The requested data is not (yet) available.'); +} + +function respondInfoButton(data) { + "use strict"; + // remove wait cursor + $('body').removeClass('waiting'); + $('#defaultModal').empty().html(data.html).modal('show'); + +} + +function displayAjaxPartial(data, holder) { + "use strict"; + var obj = $('#' + holder); + obj.html(data); + obj.parent().find('.overlay').remove(); + + // call some often needed recalculations and what-not: + + // find a sortable table and make it sortable: + if (typeof $.bootstrapSortable === "function") { + $.bootstrapSortable(true); + } + + // find the info click things and respond to them: + triggerInfoClick(); + + // trigger list thing + listLengthInitial(); + + // budget thing in year and multi year report: + $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); + + // category thing in year and multi year report: + $('.category-chart-activate').unbind('click').on('click', clickCategoryChart); +} + +function clickCategoryChart(e) { + "use strict"; + var link = $(e.target); + var categoryId = link.data('category'); + $('#category_help').remove(); + + var URL = 'chart/category/report-period/' + categoryId + '/' + accountIds + '/' + startDate + '/' + endDate; + var container = 'category_chart'; + columnChart(URL, container); + return false; +} + +function clickBudgetChart(e) { + "use strict"; + var link = $(e.target); + var budgetId = link.data('budget'); + $('#budget_help').remove(); + + var URL = 'chart/budget/period/' + budgetId + '/' + accountIds + '/' + startDate + '/' + endDate; + var container = 'budget_chart'; + columnChart(URL, container); + return false; +} \ No newline at end of file diff --git a/public/js/ff/reports/audit/all.js b/public/js/ff/reports/audit/all.js index 52f5dc9320..cfc4913105 100644 --- a/public/js/ff/reports/audit/all.js +++ b/public/js/ff/reports/audit/all.js @@ -86,35 +86,3 @@ function showOnlyColumns(checkboxes) { } } } - - -function createCookie(name, value, days) { - "use strict"; - var expires; - - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; -} - -function readCookie(name) { - "use strict"; - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return decodeURIComponent(c.substring(nameEQ.length, c.length)); - } - } - return null; -} - diff --git a/public/js/ff/reports/budget/all.js b/public/js/ff/reports/budget/all.js deleted file mode 100644 index 5a99dc19ab..0000000000 --- a/public/js/ff/reports/budget/all.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - diff --git a/public/js/ff/reports/category/all.js b/public/js/ff/reports/category/all.js deleted file mode 100644 index 5a99dc19ab..0000000000 --- a/public/js/ff/reports/category/all.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - diff --git a/public/js/ff/reports/default/all.js b/public/js/ff/reports/default/all.js index dd49e9ec04..fab470e48e 100644 --- a/public/js/ff/reports/default/all.js +++ b/public/js/ff/reports/default/all.js @@ -33,108 +33,3 @@ $(function () { loadAjaxPartial('incomeVsExpenseReport', incExpReportUri); }); - -function triggerInfoClick() { - "use strict"; - // find the little info buttons and respond to them. - $('.firefly-info-button').unbind('click').click(clickInfoButton); -} - -function clickInfoButton(e) { - "use strict"; - // find all data tags, regardless of what they are: - var element = $(e.target); - var attributes = element.data(); - - // set wait cursor - $('body').addClass('waiting'); - - // add some more elements: - attributes.startDate = startDate; - attributes.endDate = endDate; - attributes.accounts = accountIds; - - $.getJSON('popup/general', {attributes: attributes}).done(respondInfoButton).fail(errorInfoButton); -} - -function errorInfoButton() { - "use strict"; - // remove wait cursor - $('body').removeClass('waiting'); - alert('Apologies. The requested data is not (yet) available.'); -} - -function respondInfoButton(data) { - "use strict"; - // remove wait cursor - $('body').removeClass('waiting'); - $('#defaultModal').empty().html(data.html).modal('show'); - -} - -function loadAjaxPartial(holder, uri) { - "use strict"; - $.get(uri).done(function (data) { - displayAjaxPartial(data, holder); - }).fail(function () { - failAjaxPartial(uri, holder); - }); -} - -function displayAjaxPartial(data, holder) { - "use strict"; - var obj = $('#' + holder); - obj.html(data); - obj.parent().find('.overlay').remove(); - - // call some often needed recalculations and what-not: - - // find a sortable table and make it sortable: - if (typeof $.bootstrapSortable === "function") { - $.bootstrapSortable(true); - } - - // find the info click things and respond to them: - triggerInfoClick(); - - // trigger list thing - listLengthInitial(); - - // budget thing in year and multi year report: - $('.budget-chart-activate').unbind('click').on('click', clickBudgetChart); - - // category thing in year and multi year report: - $('.category-chart-activate').unbind('click').on('click', clickCategoryChart); -} - -function failAjaxPartial(uri, holder) { - "use strict"; - var holderObject = $('#' + holder); - holderObject.parent().find('.overlay').remove(); - holderObject.addClass('general-chart-error'); - -} - -function clickCategoryChart(e) { - "use strict"; - var link = $(e.target); - var categoryId = link.data('category'); - $('#category_help').remove(); - - var URL = 'chart/category/report-period/' + categoryId + '/' + accountIds + '/' + startDate + '/' + endDate; - var container = 'category_chart'; - columnChart(URL, container); - return false; -} - -function clickBudgetChart(e) { - "use strict"; - var link = $(e.target); - var budgetId = link.data('budget'); - $('#budget_help').remove(); - - var URL = 'chart/budget/period/' + budgetId + '/' + accountIds + '/' + startDate + '/' + endDate; - var container = 'budget_chart'; - columnChart(URL, container); - return false; -} \ No newline at end of file diff --git a/public/js/ff/reports/index.js b/public/js/ff/reports/index.js index d15de74232..ff041cbef9 100644 --- a/public/js/ff/reports/index.js +++ b/public/js/ff/reports/index.js @@ -179,33 +179,3 @@ function preSelectDate(e) { } -function createCookie(name, value, days) { - "use strict"; - var expires; - - if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; - } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; -} - -function readCookie(name) { - "use strict"; - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) === ' ') { - c = c.substring(1, c.length); - } - if (c.indexOf(nameEQ) === 0) { - return decodeURIComponent(c.substring(nameEQ.length, c.length)); - } - } - return null; -} - diff --git a/public/js/ff/reports/tag/all.js b/public/js/ff/reports/tag/all.js deleted file mode 100644 index 5a99dc19ab..0000000000 --- a/public/js/ff/reports/tag/all.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * all.js - * Copyright (c) 2017 thegrumpydictator@gmail.com - * - * This file is part of Firefly III. - * - * Firefly III is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Firefly III is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Firefly III. If not, see . - */ - diff --git a/resources/views/reports/account/report.twig b/resources/views/reports/account/report.twig index 4dbaf2b5f1..b03f054604 100644 --- a/resources/views/reports/account/report.twig +++ b/resources/views/reports/account/report.twig @@ -127,7 +127,7 @@ - + {% endblock %} diff --git a/resources/views/reports/audit/report.twig b/resources/views/reports/audit/report.twig index 5716f4dc08..740b28913a 100644 --- a/resources/views/reports/audit/report.twig +++ b/resources/views/reports/audit/report.twig @@ -86,5 +86,6 @@ + {% endblock %} diff --git a/resources/views/reports/budget/month.twig b/resources/views/reports/budget/month.twig index e79e09b4af..a52cd09552 100644 --- a/resources/views/reports/budget/month.twig +++ b/resources/views/reports/budget/month.twig @@ -260,7 +260,7 @@ - + {% endblock %} diff --git a/resources/views/reports/category/month.twig b/resources/views/reports/category/month.twig index bb4506523e..55ba6f5f3d 100644 --- a/resources/views/reports/category/month.twig +++ b/resources/views/reports/category/month.twig @@ -397,7 +397,7 @@ - + {% endblock %} diff --git a/resources/views/reports/default/month.twig b/resources/views/reports/default/month.twig index b6b0aa5b50..9b0bddad9e 100644 --- a/resources/views/reports/default/month.twig +++ b/resources/views/reports/default/month.twig @@ -171,6 +171,7 @@ var accountChartUri = '{{ route('chart.account.report', [accountIds, start.format('Ymd'), end.format('Ymd')]) }}'; + {% endblock %} diff --git a/resources/views/reports/default/multi-year.twig b/resources/views/reports/default/multi-year.twig index 6b94ad6ebe..96acae16a0 100644 --- a/resources/views/reports/default/multi-year.twig +++ b/resources/views/reports/default/multi-year.twig @@ -222,6 +222,7 @@ var categoryIncomeUri = '{{ route('report-data.category.income', [accountIds, start.format('Ymd'), end.format('Ymd')]) }}'; + {% endblock %} diff --git a/resources/views/reports/default/year.twig b/resources/views/reports/default/year.twig index 802152bdc2..821fb90e8c 100644 --- a/resources/views/reports/default/year.twig +++ b/resources/views/reports/default/year.twig @@ -217,7 +217,7 @@ - + diff --git a/resources/views/reports/index.twig b/resources/views/reports/index.twig index fd1bcd0b0c..aa2b563864 100644 --- a/resources/views/reports/index.twig +++ b/resources/views/reports/index.twig @@ -179,5 +179,6 @@ var filterPlaceholder = "{{ trans('firefly.multi_select_filter_placeholder')|escape('js') }}"; + {% endblock %} diff --git a/resources/views/reports/partials/exp-budgets.twig b/resources/views/reports/partials/exp-budgets.twig index de59cc258e..992570d1dc 100644 --- a/resources/views/reports/partials/exp-budgets.twig +++ b/resources/views/reports/partials/exp-budgets.twig @@ -1,8 +1,8 @@ - - + + diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig index aff8d62089..e700cf4268 100644 --- a/resources/views/reports/partials/exp-categories.twig +++ b/resources/views/reports/partials/exp-categories.twig @@ -1,9 +1,9 @@
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'category'|_ }}{{ 'spent'|_ }}
- - - + + + diff --git a/resources/views/reports/partials/exp-not-grouped.twig b/resources/views/reports/partials/exp-not-grouped.twig index bc454b7d44..85a5a667c8 100644 --- a/resources/views/reports/partials/exp-not-grouped.twig +++ b/resources/views/reports/partials/exp-not-grouped.twig @@ -3,8 +3,8 @@ - - + + diff --git a/resources/views/reports/partials/top-transactions.twig b/resources/views/reports/partials/top-transactions.twig new file mode 100644 index 0000000000..e068286fd1 --- /dev/null +++ b/resources/views/reports/partials/top-transactions.twig @@ -0,0 +1,24 @@ +
{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}{{ 'category'|_ }}{{ 'spent'|_ }}{{ 'earned'|_ }}
{{ 'name'|_ }}
+ + + + + + + + + + {% for transaction in sorted %} + + + + + + + {% endfor %} + +
{{ 'account'|_ }}{{ 'description'|_ }}{{ 'date'|_ }}
+ {{ transaction.opposing_account_name }} + {{ transaction.description }} + {{ transaction.date.formatLocalized(monthAndDayFormat) }} + {{ transaction|transactionAmount }}
diff --git a/resources/views/reports/tag/month.twig b/resources/views/reports/tag/month.twig index 54153b91ba..e4139f5dbb 100644 --- a/resources/views/reports/tag/month.twig +++ b/resources/views/reports/tag/month.twig @@ -422,8 +422,7 @@ var mainUri = '{{ route('chart.tag.main', [accountIds, tagTags, start.format('Ymd'), end.format('Ymd')]) }}'; - - + {% endblock %} diff --git a/routes/web.php b/routes/web.php index 3e46cf739e..5cb2d597d6 100755 --- a/routes/web.php +++ b/routes/web.php @@ -623,8 +623,8 @@ Route::group( Route::get('budget/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@budget', 'as' => 'budget']); //expense earned top X - Route::get('expenses/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topX', 'as' => 'expenses']); - Route::get('income/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topXPeriod', 'as' => 'income']); + Route::get('expenses/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topExpense', 'as' => 'expenses']); + Route::get('income/{accountList}/{expenseList}/{start_date}/{end_date}', ['uses' => 'ExpenseController@topIncome', 'as' => 'income']); } ); From bad889d450918b7611b2765061cb34e1f2493b98 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Dec 2017 20:53:16 +0100 Subject: [PATCH 033/142] Update ING description for #1015 --- app/Import/FileProcessor/CsvProcessor.php | 8 +++++--- app/Import/Specifics/IngDescription.php | 17 +++++++++++++++-- app/Support/Import/Configuration/Csv/Map.php | 6 ++++-- app/Support/Import/Configuration/Csv/Roles.php | 14 ++++++++------ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/app/Import/FileProcessor/CsvProcessor.php b/app/Import/FileProcessor/CsvProcessor.php index 0b547eb026..9a21cfc9da 100644 --- a/app/Import/FileProcessor/CsvProcessor.php +++ b/app/Import/FileProcessor/CsvProcessor.php @@ -169,9 +169,11 @@ class CsvProcessor implements FileProcessorInterface $delimiter = "\t"; } $reader->setDelimiter($delimiter); - $start = $config['has-headers'] ? 1 : 0; - $results = $reader->setOffset($start)->fetch(); - Log::debug(sprintf('Created a CSV reader starting at offset %d', $start)); + if($config['has-headers']) { + $reader->setHeaderOffset(0); + } + $results = $reader->getRecords(); + Log::debug('Created a CSV reader.'); return $results; } diff --git a/app/Import/Specifics/IngDescription.php b/app/Import/Specifics/IngDescription.php index 010b93801d..c34cf61cde 100644 --- a/app/Import/Specifics/IngDescription.php +++ b/app/Import/Specifics/IngDescription.php @@ -60,7 +60,7 @@ class IngDescription implements SpecificInterface */ public function run(array $row): array { - $this->row = $row; + $this->row = array_values($row); if (count($this->row) >= 8) { // check if the array is correct switch ($this->row[4]) { // Get value for the mutation type case 'GT': // InternetBankieren @@ -69,6 +69,8 @@ class IngDescription implements SpecificInterface case 'IC': // Incasso $this->removeIBANIngDescription(); $this->removeNameIngDescription(); + // if "tegenrekening" empty, copy the description. Primitive, but it works. + $this->copyDescriptionToOpposite(); break; case 'BA': // Betaalautomaat $this->addNameIngDescription(); @@ -113,9 +115,20 @@ class IngDescription implements SpecificInterface */ protected function removeNameIngDescription() { - // Try remove everything bevore the 'Omschrijving' + // Try remove everything before the 'Omschrijving' $this->row[8] = preg_replace('/.+Omschrijving: /', '', $this->row[8]); return true; } + + /** + * + */ + private function copyDescriptionToOpposite(): void + { + $search = ['Naar Oranje Spaarrekening ', 'Afschrijvingen']; + if (strlen($this->row[3]) === 0) { + $this->row[3] = trim(str_ireplace($search, '', $this->row[8])); + } + } } diff --git a/app/Support/Import/Configuration/Csv/Map.php b/app/Support/Import/Configuration/Csv/Map.php index 7aa35f62b0..449124bbb8 100644 --- a/app/Support/Import/Configuration/Csv/Map.php +++ b/app/Support/Import/Configuration/Csv/Map.php @@ -60,8 +60,10 @@ class Map implements ConfigurationInterface /** @var Reader $reader */ $reader = Reader::createFromString($content); $reader->setDelimiter($this->configuration['delimiter']); - $offset = $this->configuration['has-headers'] ? 1 : 0; - $results = $reader->setOffset($offset)->fetch(); + if($this->configuration['has-headers']) { + $reader->setHeaderOffset(0); + } + $results = $reader->getRecords(); $this->validSpecifics = array_keys(config('csv.import_specifics')); $indexes = array_keys($this->data); $rowIndex = 0; diff --git a/app/Support/Import/Configuration/Csv/Roles.php b/app/Support/Import/Configuration/Csv/Roles.php index 97160b430e..f0d1f206c5 100644 --- a/app/Support/Import/Configuration/Csv/Roles.php +++ b/app/Support/Import/Configuration/Csv/Roles.php @@ -26,6 +26,7 @@ use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; use League\Csv\Reader; +use League\Csv\Statement; use Log; /** @@ -53,9 +54,10 @@ class Roles implements ConfigurationInterface // create CSV reader. $reader = Reader::createFromString($content); $reader->setDelimiter($config['delimiter']); - $start = $config['has-headers'] ? 1 : 0; - $end = $start + config('csv.example_rows'); - + if ($config['has-headers']) { + $reader->setHeaderOffset(0); + } + $stmt = (new Statement)->limit(intval(config('csv.example_rows', 5))); // set data: $roles = $this->getRoles(); asort($roles); @@ -66,13 +68,13 @@ class Roles implements ConfigurationInterface 'headers' => $config['has-headers'] ? $reader->fetchOne(0) : [], ]; - while ($start < $end) { - $row = $reader->fetchOne($start); + + $records = $stmt->process($reader); + foreach ($records as $row) { $row = $this->processSpecifics($row); $count = count($row); $this->data['total'] = $count > $this->data['total'] ? $count : $this->data['total']; $this->processRow($row); - ++$start; } $this->updateColumCount(); From 44496707fb28de585b2138944f7941202a1ca356 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Dec 2017 20:53:40 +0100 Subject: [PATCH 034/142] Remove some comments [skip ci] --- public/js/ff/reports/account/month.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/js/ff/reports/account/month.js b/public/js/ff/reports/account/month.js index a3068ce4ab..e457847a9c 100644 --- a/public/js/ff/reports/account/month.js +++ b/public/js/ff/reports/account/month.js @@ -34,8 +34,6 @@ $(function () { function drawChart() { "use strict"; - // month view: - // draw account chart // month view: doubleYChart(mainUri, 'in-out-chart'); } \ No newline at end of file From fc4165b800ea31597554335c4bcdb9cddd9ebb26 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Dec 2017 21:05:16 +0100 Subject: [PATCH 035/142] Make sure new CSV reader is compatible with specifics #1015 --- app/Import/Specifics/AbnAmroDescription.php | 2 +- app/Import/Specifics/PresidentsChoice.php | 1 + app/Import/Specifics/RabobankDescription.php | 1 + app/Import/Specifics/SnsDescription.php | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php index cd884c7c29..8f509cbf84 100644 --- a/app/Import/Specifics/AbnAmroDescription.php +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -58,7 +58,7 @@ class AbnAmroDescription implements SpecificInterface */ public function run(array $row): array { - $this->row = $row; + $this->row = array_values($row); if (!isset($row[7])) { return $row; diff --git a/app/Import/Specifics/PresidentsChoice.php b/app/Import/Specifics/PresidentsChoice.php index 1c0d5ca1ba..c25a17ab89 100644 --- a/app/Import/Specifics/PresidentsChoice.php +++ b/app/Import/Specifics/PresidentsChoice.php @@ -50,6 +50,7 @@ class PresidentsChoice implements SpecificInterface */ public function run(array $row): array { + $row = array_values($row); // first, if column 2 is empty and 3 is not, do nothing. // if column 3 is empty and column 2 is not, move amount to column 3, *-1 if (isset($row[3]) && 0 === strlen($row[3])) { diff --git a/app/Import/Specifics/RabobankDescription.php b/app/Import/Specifics/RabobankDescription.php index 94a9e1f559..1a1caa8f4c 100644 --- a/app/Import/Specifics/RabobankDescription.php +++ b/app/Import/Specifics/RabobankDescription.php @@ -52,6 +52,7 @@ class RabobankDescription implements SpecificInterface */ public function run(array $row): array { + $row = array_values($row); Log::debug(sprintf('Now in RabobankSpecific::run(). Row has %d columns', count($row))); $oppositeAccount = isset($row[5]) ? trim($row[5]) : ''; $oppositeName = isset($row[6]) ? trim($row[6]) : ''; diff --git a/app/Import/Specifics/SnsDescription.php b/app/Import/Specifics/SnsDescription.php index c4fa347421..131823d1d9 100644 --- a/app/Import/Specifics/SnsDescription.php +++ b/app/Import/Specifics/SnsDescription.php @@ -51,6 +51,7 @@ class SnsDescription implements SpecificInterface */ public function run(array $row): array { + $row = array_values($row); $row[17] = ltrim($row[17], "'"); $row[17] = rtrim($row[17], "'"); From df458b3913481a5e2e463d7bb401a3d257da16d8 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 12 Dec 2017 21:13:50 +0100 Subject: [PATCH 036/142] Update composer file and version info. --- composer.lock | 49 +++++++++++++++++++++++----------------------- config/firefly.php | 2 +- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 7d197e7c1a..67ba3bcce4 100644 --- a/composer.lock +++ b/composer.lock @@ -156,16 +156,16 @@ }, { "name": "doctrine/annotations", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f" + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/5beebb01b025c94e93686b7a0ed3edae81fe3e7f", - "reference": "5beebb01b025c94e93686b7a0ed3edae81fe3e7f", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", "shasum": "" }, "require": { @@ -174,12 +174,12 @@ }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -220,7 +220,7 @@ "docblock", "parser" ], - "time": "2017-07-22T10:58:02+00:00" + "time": "2017-12-06T07:11:42+00:00" }, { "name": "doctrine/cache", @@ -791,16 +791,16 @@ }, { "name": "laravel/framework", - "version": "v5.5.24", + "version": "v5.5.25", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "06135405bb1f736dac5e9529ed1541fc446c9c0f" + "reference": "0a5b6112f325c56ae5a6679c08a0a10723153fe0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/06135405bb1f736dac5e9529ed1541fc446c9c0f", - "reference": "06135405bb1f736dac5e9529ed1541fc446c9c0f", + "url": "https://api.github.com/repos/laravel/framework/zipball/0a5b6112f325c56ae5a6679c08a0a10723153fe0", + "reference": "0a5b6112f325c56ae5a6679c08a0a10723153fe0", "shasum": "" }, "require": { @@ -880,6 +880,7 @@ "guzzlehttp/guzzle": "Required to use the Mailgun and Mandrill mail drivers and the ping methods on schedules (~6.0).", "laravel/tinker": "Required to use the tinker console command (~1.0).", "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (~1.0).", + "league/flysystem-cached-adapter": "Required to use Flysystem caching (~1.0).", "league/flysystem-rackspace": "Required to use the Flysystem Rackspace driver (~1.0).", "nexmo/client": "Required to use the Nexmo transport (~1.0).", "pda/pheanstalk": "Required to use the beanstalk queue driver (~3.0).", @@ -920,7 +921,7 @@ "framework", "laravel" ], - "time": "2017-12-07T01:28:21+00:00" + "time": "2017-12-11T14:59:28+00:00" }, { "name": "laravelcollective/html", @@ -4284,16 +4285,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.3", + "version": "6.5.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "882e886cc928a0abd3c61282b2a64026237d14a4" + "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/882e886cc928a0abd3c61282b2a64026237d14a4", - "reference": "882e886cc928a0abd3c61282b2a64026237d14a4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b2f933d5775f9237369deaa2d2bfbf9d652be4c", + "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c", "shasum": "" }, "require": { @@ -4311,7 +4312,7 @@ "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.4", + "phpunit/phpunit-mock-objects": "^5.0.5", "sebastian/comparator": "^2.1", "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", @@ -4364,27 +4365,27 @@ "testing", "xunit" ], - "time": "2017-12-06T09:42:03+00:00" + "time": "2017-12-10T08:06:19+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.4", + "version": "5.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "16b50f4167e5e85e81ca8a3dd105d0a5fd32009a" + "reference": "283b9f4f670e3a6fd6c4ff95c51a952eb5c75933" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/16b50f4167e5e85e81ca8a3dd105d0a5fd32009a", - "reference": "16b50f4167e5e85e81ca8a3dd105d0a5fd32009a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/283b9f4f670e3a6fd6c4ff95c51a952eb5c75933", + "reference": "283b9f4f670e3a6fd6c4ff95c51a952eb5c75933", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", "php": "^7.0", "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.0" + "sebastian/exporter": "^3.1" }, "conflict": { "phpunit/phpunit": "<6.0" @@ -4423,7 +4424,7 @@ "mock", "xunit" ], - "time": "2017-12-02T05:31:19+00:00" + "time": "2017-12-10T08:01:53+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/config/firefly.php b/config/firefly.php index 05c58b89d6..3552a917ee 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -33,7 +33,7 @@ return [ 'is_demo_site' => false, ], 'encryption' => (is_null(env('USE_ENCRYPTION')) || env('USE_ENCRYPTION') === true), - 'version' => '4.6.11.1', + 'version' => '4.6.12', 'maxUploadSize' => 15242880, 'allowedMimes' => ['image/png', 'image/jpeg', 'application/pdf', 'text/plain'], 'list_length' => 10, From 06ddfe3d415f3f157de31d699f8f1c6c759c24c9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Dec 2017 10:59:48 +0100 Subject: [PATCH 037/142] Limit routes for demo user. --- app/Http/Controllers/HomeController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 1dd55b1cc9..e39a5ca9fc 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -54,6 +54,7 @@ class HomeController extends Controller parent::__construct(); View::share('title', 'Firefly III'); View::share('mainTitleIcon', 'fa-fire'); + $this->middleware(IsLimitedUser::class)->except(['dateRange', 'index']); } /** From e60bbc78e6cb185ebe125eae5a16df16684558fb Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Dec 2017 11:07:05 +0100 Subject: [PATCH 038/142] Forgot to include include command. --- app/Http/Controllers/HomeController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index e39a5ca9fc..f12681ed65 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -28,6 +28,7 @@ use DB; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Models\AccountType; +use FireflyIII\Http\Middleware\IsLimitedUser; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use Illuminate\Http\Request; From 044af254f3dcdeac96462f3fb89cefaa8c4b2045 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Dec 2017 14:15:44 +0100 Subject: [PATCH 039/142] Update links to new site. --- .github/CONTRIBUTING.md | 2 +- .github/SUPPORT.md | 4 ++-- .sandstorm/sandstorm-pkgdef.capnp | 2 +- README.md | 8 ++++---- app.json | 2 +- resources/views/emails/registered-html.twig | 2 +- resources/views/emails/registered-text.twig | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f715742ead..3e3a8de51c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,7 +4,7 @@ ## Feature requests -I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.github.io/requested-features/). +I am always interested in expanding Firefly III's many features. If you are requesting a new feature, please check out the list of [often requested features](https://firefly-iii.org/requested-features/). ## Pull requests diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index 7b7759dcb8..8538d150bd 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -4,8 +4,8 @@ ## Bugs -First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://firefly-iii.nder.be/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority! +First of all: thank you for reporting a bug instead of ditching the tool altogether. If you find a bug, please take the time and see if the [demo site](https://demo.firefly-iii.org/) is also suffering from this bug. Include as many log files and details as you think are necessary. Bugs have a lot of priority! ## Installation problems -Please take the time to read the [installation guide FAQ](https://firefly-iii.github.io/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can. \ No newline at end of file +Please take the time to read the [installation guide FAQ](https://firefly-iii.org/installation-guide-faq/) and make sure you search through closed issues for the problems other people have had. Your problem may be among them! If not, open an issue and I will help where I can. \ No newline at end of file diff --git a/.sandstorm/sandstorm-pkgdef.capnp b/.sandstorm/sandstorm-pkgdef.capnp index aa56b258ea..85220dd1bf 100644 --- a/.sandstorm/sandstorm-pkgdef.capnp +++ b/.sandstorm/sandstorm-pkgdef.capnp @@ -41,7 +41,7 @@ const pkgdef :Spk.PackageDefinition = ( market = (png = (dpi1x = embed "app-graphics/firefly-iii-150.png")) ), - website = "https://firefly-iii.github.io/", + website = "https://firefly-iii.org/", codeUrl = "https://github.com/firefly-iii/firefly-iii", license = (openSource = gpl3), # The license this package is distributed under. See diff --git a/README.md b/README.md index e6f1882923..42ed976bcb 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,17 @@ There are many financial reports available. ## Want to try Firefly III? -There is a **[demo site](https://firefly-iii.nder.be)** with an example financial administration already present. You can use Docker, Heroku or Sandstorm.io (see below) to quickly setup your own instance. +There is a **[demo site](https://demo.firefly-iii.org)** with an example financial administration already present. You can use Docker, Heroku or Sandstorm.io (see below) to quickly setup your own instance. ## Install Firefly III ### Using docker -You can use docker-compose to [set up your personal secure](https://firefly-iii.github.io/using-docker.html) Firefly III environment. Advanced users can use the Dockerfile directly. +You can use docker-compose to [set up your personal secure](https://firefly-iii.org/using-docker.html) Firefly III environment. Advanced users can use the Dockerfile directly. ### Using vagrant (or other VMs) -You can install Firefly III on any Linux or Windows machine. You'll need a web server (preferrably on Linux) and access to the command line. Please read the [installation guide](https://firefly-iii.github.io/using-installing.html). +You can install Firefly III on any Linux or Windows machine. You'll need a web server (preferrably on Linux) and access to the command line. Please read the [installation guide](https://firefly-iii.org/using-installing.html). ### Using Heroku @@ -53,7 +53,7 @@ Firefly works on the principle that if you know where you're money is going, you - If you feel you're missing something you can just ask me and I'll add it! Firefly III has become pretty awesome over the years! (but please excuse me for bragging, it's just that I'm proud of it). -[You can read more about Firefly III, and its features, on the Github Pages](https://firefly-iii.github.io/). +[You can read more about Firefly III, and its features, on the website](https://firefly-iii.org/). ### Contributing diff --git a/app.json b/app.json index 4843baa8d6..82677845eb 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "name": "Firefly III", "description": "A free and open source personal finances manager", "repository": "https://github.com/firefly-iii/firefly-iii", - "website": "https://firefly-iii.github.io/", + "website": "https://firefly-iii.org/", "logo": "https://raw.githubusercontent.com/firefly-iii/firefly-iii/master/public/mstile-150x150.png", "keywords": [ "finance", diff --git a/resources/views/emails/registered-html.twig b/resources/views/emails/registered-html.twig index 4aec81c009..5274b801b7 100644 --- a/resources/views/emails/registered-html.twig +++ b/resources/views/emails/registered-html.twig @@ -12,7 +12,7 @@ There is a help-icon in the top right corner of each page. If you need help, click it!
  • - If you haven't already, please read the full description. + If you haven't already, please read the full description.
  • {% include 'emails.footer-html' %} diff --git a/resources/views/emails/registered-text.twig b/resources/views/emails/registered-text.twig index 4877bf52fd..c750538f65 100644 --- a/resources/views/emails/registered-text.twig +++ b/resources/views/emails/registered-text.twig @@ -15,6 +15,6 @@ Password reset: Documentation: https://github.com/firefly-iii/firefly-iii -https://firefly-iii.github.io/ +https://firefly-iii.org/ {% include 'emails.footer-text' %} From 9264f1e9b9ceedd4dbe8854542d68227b014f1b4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Dec 2017 17:09:55 +0100 Subject: [PATCH 040/142] Fix #1051 --- app/Import/FileProcessor/CsvProcessor.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Import/FileProcessor/CsvProcessor.php b/app/Import/FileProcessor/CsvProcessor.php index 9a21cfc9da..daa985f973 100644 --- a/app/Import/FileProcessor/CsvProcessor.php +++ b/app/Import/FileProcessor/CsvProcessor.php @@ -80,6 +80,7 @@ class CsvProcessor implements FileProcessorInterface Log::debug(sprintf('Number of entries: %d', $entries->count())); $notImported = $entries->filter( function (array $row, int $index) { + $row = array_values($row); if ($this->rowAlreadyImported($row)) { $message = sprintf('Row #%d has already been imported.', $index); $this->job->addError($index, $message); @@ -241,6 +242,7 @@ class CsvProcessor implements FileProcessorInterface */ private function importRow(int $index, array $row): ImportJournal { + $row = array_values($row); Log::debug(sprintf('Now at row %d', $index)); $row = $this->specifics($row); $hash = $this->getRowHash($row); From 11b5a0294e178b60e55463800f76e7a37f715b78 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 13 Dec 2017 18:46:30 +0100 Subject: [PATCH 041/142] Small changes in templates [skip ci] --- resources/views/accounts/edit.twig | 2 +- resources/views/partials/flashes.twig | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/views/accounts/edit.twig b/resources/views/accounts/edit.twig index 2384ee57fb..37ebc3b9c0 100644 --- a/resources/views/accounts/edit.twig +++ b/resources/views/accounts/edit.twig @@ -53,7 +53,7 @@ - {% if Session.get('preFilled').accountRole == 'ccAsset' %} + {% if preFilled.accountRole == 'ccAsset' %}

    {{ 'credit_card_options'|_ }}

    diff --git a/resources/views/partials/flashes.twig b/resources/views/partials/flashes.twig index 6e428ccdb0..77c03557fe 100644 --- a/resources/views/partials/flashes.twig +++ b/resources/views/partials/flashes.twig @@ -9,7 +9,7 @@ {% endif %} {# SUCCESS MESSAGE (ALWAYS SINGULAR) #} -{% if Session.has('success') %} +{% if session_has('success') %}
    -
    +
    diff --git a/resources/views/import/csv/map.twig b/resources/views/import/file/map.twig similarity index 96% rename from resources/views/import/csv/map.twig rename to resources/views/import/file/map.twig index 8f25f984f4..edc4b4b5bb 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/file/map.twig @@ -22,7 +22,7 @@
    - + {% for field in data %} diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/file/roles.twig similarity index 97% rename from resources/views/import/csv/roles.twig rename to resources/views/import/file/roles.twig index f87ae66ec9..f35b92bfe4 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/file/roles.twig @@ -21,7 +21,7 @@ - + diff --git a/resources/views/import/file/status.twig b/resources/views/import/file/status.twig index 51858bf20c..b8b89ef64b 100644 --- a/resources/views/import/file/status.twig +++ b/resources/views/import/file/status.twig @@ -53,12 +53,17 @@

    - {{ 'import_status_ready_text'|_ }} + {% if job.configuration['has-config-file'] == false %} + This should only be visible momentarily. + {% else %} + {{ 'import_status_ready_text'|_ }} + {% endif %}

    php artisan firefly:start-import {{ job.key }}

    + {% if job.configuration['has-config-file'] != false %}
    {{ 'import_status_ready_config'|_ }} @@ -66,13 +71,16 @@
    + {% endif %}
    + {% if job.configuration['has-config-file'] != false %}

     

    {{ 'import_status_ready_share'|_ }}

    + {% endif %}
    @@ -145,6 +153,11 @@ var jobImportUrl = '{{ route('import.file.json', [job.key]) }}'; var jobStartUrl = '{{ route('import.file.start', [job.key]) }}'; var token = '{{ csrf_token() }}'; + {% if job.configuration['auto-start'] == true %} + var autoStart = true; + {% else %} + var autoStart = false; + {% endif %} {% endblock %} diff --git a/resources/views/import/file/upload.twig b/resources/views/import/file/upload.twig new file mode 100644 index 0000000000..50c184e58d --- /dev/null +++ b/resources/views/import/file/upload.twig @@ -0,0 +1,53 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} +
    +
    +
    +
    +

    {{ 'import_index_title'|_ }}

    +
    +
    +
    +
    +

    + {{ 'import_index_intro'|_ }} +

    +
    +
    + +
    + + + + +
    + + {{ ExpandedForm.file('import_file', {helpText: 'import_index_file'|_}) }} + {{ ExpandedForm.file('configuration_file', {helpText: 'import_index_config'|_|raw}) }} + {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_index_type'|_}) }} + +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index e83cef961b..73cfa3cad4 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -21,39 +21,50 @@ + {% endif %} + + {# import from Plaid #} + {% if routines.plaid == true %} + + {% endif %} + {% endblock %} {% block scripts %} {% endblock %} diff --git a/resources/views/import/spectre/input-fields.twig b/resources/views/import/spectre/input-fields.twig index de77ba5b3f..03aae3faf2 100644 --- a/resources/views/import/spectre/input-fields.twig +++ b/resources/views/import/spectre/input-fields.twig @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
    -
    +
    diff --git a/resources/views/import/spectre/select-country.twig b/resources/views/import/spectre/select-country.twig index ba4f4a0c59..6f27099bba 100644 --- a/resources/views/import/spectre/select-country.twig +++ b/resources/views/import/spectre/select-country.twig @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
    - +
    diff --git a/resources/views/import/spectre/select-provider.twig b/resources/views/import/spectre/select-provider.twig index 5be257d082..d4c554d379 100644 --- a/resources/views/import/spectre/select-provider.twig +++ b/resources/views/import/spectre/select-provider.twig @@ -5,12 +5,12 @@ {% endblock %} {% block content %}
    - +
    -

    {{ trans('bank.spectre_select_bank_title') }}

    +

    {{ trans('bank.spectre_select_provider_title') }}

    diff --git a/routes/web.php b/routes/web.php index 5cb2d597d6..ee79111ec4 100755 --- a/routes/web.php +++ b/routes/web.php @@ -427,28 +427,30 @@ Route::group( Route::group( ['middleware' => 'user-full-auth', 'prefix' => 'import', 'as' => 'import.'], function () { - Route::get('', ['uses' => 'ImportController@index', 'as' => 'index']); + Route::get('', ['uses' => 'Import\IndexController@index', 'as' => 'index']); + + // import method prerequisites: + Route::get('prerequisites/{bank}', ['uses' => 'Import\PrerequisitesController@index', 'as' => 'prerequisites']); + Route::post('prerequisites/{bank}', ['uses' => 'Import\PrerequisitesController@post', 'as' => 'prerequisites.post']); + + // create the job: + Route::get('create/{bank}', ['uses' => 'Import\IndexController@create', 'as' => 'create-job']); + + // configure the job: + Route::get('configure/{importJob}', ['uses' => 'Import\ConfigurationController@index', 'as' => 'configure']); + Route::post('configure/{importJob}', ['uses' => 'Import\ConfigurationController@post', 'as' => 'configure.post']); // file import - Route::get('file', ['uses' => 'Import\FileController@index', 'as' => 'file.index']); - Route::post('file/initialize', ['uses' => 'Import\FileController@initialize', 'as' => 'file.initialize']); + // Route::get('file', ['uses' => 'Import\FileController@index', 'as' => 'file.index']); + // Route::post('file/initialize', ['uses' => 'Import\FileController@initialize', 'as' => 'file.initialize']); - Route::get('file/configure/{importJob}', ['uses' => 'Import\FileController@configure', 'as' => 'file.configure']); - Route::post('file/configure/{importJob}', ['uses' => 'Import\FileController@postConfigure', 'as' => 'file.process-configuration']); - - Route::get('file/download/{importJob}', ['uses' => 'Import\FileController@download', 'as' => 'file.download']); - Route::get('file/status/{importJob}', ['uses' => 'Import\FileController@status', 'as' => 'file.status']); - Route::get('file/json/{importJob}', ['uses' => 'Import\FileController@json', 'as' => 'file.json']); - Route::post('file/start/{importJob}', ['uses' => 'Import\FileController@start', 'as' => 'file.start']); - - // banks: - Route::get('bank/{bank}/prerequisites', ['uses' => 'Import\BankController@prerequisites', 'as' => 'bank.prerequisites']); - Route::post('bank/{bank}/prerequisites', ['uses' => 'Import\BankController@postPrerequisites', 'as' => 'bank.prerequisites.post']); - - Route::get('bank/{bank}/create', ['uses' => 'Import\BankController@createJob', 'as' => 'bank.create-job']); - Route:: get('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@configure', 'as' => 'bank.configure']); - Route::post('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@postConfigure', 'as' => 'bank.configure.post']); + // Route::get('file/download/{importJob}', ['uses' => 'Import\FileController@download', 'as' => 'file.download']); + // Route::get('file/status/{importJob}', ['uses' => 'Import\FileController@status', 'as' => 'file.status']); + // Route::get('file/json/{importJob}', ['uses' => 'Import\FileController@json', 'as' => 'file.json']); + // Route::any('file/start/{importJob}', ['uses' => 'Import\FileController@start', 'as' => 'file.start']); + // Route:: get('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@configure', 'as' => 'bank.configure']); + // Route::post('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@postConfigure', 'as' => 'bank.configure.post']); } ); From 66ee382da0267960f1865528651313801c9d7d63 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Dec 2017 17:19:10 +0100 Subject: [PATCH 053/142] Lots of new code for the import routine part 2 --- .../Import/ConfigurationController.php | 37 ++++- .../Controllers/Import/FileController.php | 9 +- .../Controllers/Import/IndexController.php | 1 + .../Controllers/Import/StatusController.php | 15 +++ app/Http/breadcrumbs.php | 21 +-- app/Import/Mapper/AssetAccountIbans.php | 2 +- app/Import/Mapper/AssetAccounts.php | 2 +- app/Import/Mapper/Bills.php | 2 +- app/Import/Mapper/Budgets.php | 2 +- app/Import/Mapper/Categories.php | 2 +- app/Import/Mapper/OpposingAccountIbans.php | 2 +- app/Import/Mapper/OpposingAccounts.php | 2 +- app/Import/Mapper/Tags.php | 2 +- app/Import/Mapper/TransactionCurrencies.php | 2 +- .../ImportJob/ImportJobRepository.php | 20 ++- .../ImportJobRepositoryInterface.php | 8 +- app/Support/Import/Configuration/File/Map.php | 13 +- .../Import/Configuration/File/Roles.php | 28 ++-- .../Import/Configuration/File/Upload.php | 40 +++++- config/import.php | 6 + resources/lang/en_US/bank.php | 14 -- resources/lang/en_US/csv.php | 75 ----------- resources/lang/en_US/firefly.php | 18 +-- resources/lang/en_US/import.php | 126 ++++++++++++++++++ .../views/import/{file => _old}/finished.twig | 0 .../views/import/{file => _old}/index.twig | 0 .../views/import/{file => _old}/status.twig | 0 resources/views/import/file/initial.twig | 24 ++-- resources/views/import/file/map.twig | 12 +- resources/views/import/file/roles.twig | 20 +-- resources/views/import/file/upload.twig | 70 +++++----- routes/web.php | 2 + 32 files changed, 351 insertions(+), 226 deletions(-) create mode 100644 app/Http/Controllers/Import/StatusController.php create mode 100644 resources/lang/en_US/import.php rename resources/views/import/{file => _old}/finished.twig (100%) rename resources/views/import/{file => _old}/index.twig (100%) rename resources/views/import/{file => _old}/status.twig (100%) diff --git a/app/Http/Controllers/Import/ConfigurationController.php b/app/Http/Controllers/Import/ConfigurationController.php index 79ea3ae368..428ffc8c9e 100644 --- a/app/Http/Controllers/Import/ConfigurationController.php +++ b/app/Http/Controllers/Import/ConfigurationController.php @@ -9,6 +9,8 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Import\Configuration\ConfiguratorInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Http\Request; +use Log; /** * Class ConfigurationController @@ -53,8 +55,9 @@ class ConfigurationController extends Controller if ($configurator->isJobConfigured()) { $this->repository->updateStatus($job, 'configured'); - return redirect(route('import.file.status', [$job->key])); + return redirect(route('import.status', [$job->key])); } + $this->repository->updateStatus($job, 'configuring'); $view = $configurator->getNextView(); $data = $configurator->getNextData(); $subTitle = trans('firefly.import_config_bread_crumb'); @@ -63,6 +66,36 @@ class ConfigurationController extends Controller return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon')); } + /** + * @param Request $request + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function post(Request $request, ImportJob $job) + { + Log::debug('Now in postConfigure()', ['job' => $job->key]); + $configurator = $this->makeConfigurator($job); + + // is the job already configured? + if ($configurator->isJobConfigured()) { + return redirect(route('import.status', [$job->key])); + } + $data = $request->all(); + $configurator->configureJob($data); + + // get possible warning from configurator: + $warning = $configurator->getWarningMessage(); + + if (strlen($warning) > 0) { + $request->session()->flash('warning', $warning); + } + + // return to configure + return redirect(route('import.configure', [$job->key])); + } + /** * @param ImportJob $job * @@ -76,7 +109,7 @@ class ConfigurationController extends Controller $key = sprintf('import.configuration.%s', $type); $className = config($key); if (null === $className || !class_exists($className)) { - throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".',$type)); // @codeCoverageIgnore + throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".', $type)); // @codeCoverageIgnore } /** @var ConfiguratorInterface $configurator */ $configurator = app($className); diff --git a/app/Http/Controllers/Import/FileController.php b/app/Http/Controllers/Import/FileController.php index 575c47ae1f..5e13cc9ed3 100644 --- a/app/Http/Controllers/Import/FileController.php +++ b/app/Http/Controllers/Import/FileController.php @@ -81,7 +81,7 @@ class FileController extends Controller if ($configurator->isJobConfigured()) { $this->repository->updateStatus($job, 'configured'); - return redirect(route('import.file.status', [$job->key])); + return redirect(route('import.status', [$job->key])); } $view = $configurator->getNextView(); $data = $configurator->getNextData(); @@ -227,6 +227,7 @@ class FileController extends Controller * @param ImportJob $job * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException */ public function postConfigure(Request $request, ImportJob $job) { @@ -235,7 +236,7 @@ class FileController extends Controller // is the job already configured? if ($configurator->isJobConfigured()) { - return redirect(route('import.file.status', [$job->key])); + return redirect(route('import.status', [$job->key])); } $data = $request->all(); $configurator->configureJob($data); @@ -248,7 +249,7 @@ class FileController extends Controller } // return to configure - return redirect(route('import.file.configure', [$job->key])); + return redirect(route('import.configure', [$job->key])); } /** @@ -285,7 +286,7 @@ class FileController extends Controller $subTitle = trans('firefly.import_status_sub_title'); $subTitleIcon = 'fa-star'; - return view('import.file.status', compact('job', 'subTitle', 'subTitleIcon')); + return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); } /** diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index efaf516f35..866f0b1557 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -40,6 +40,7 @@ class IndexController extends Controller * * @param string $bank * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @throws FireflyException */ public function create(string $bank) diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php new file mode 100644 index 0000000000..f4f11d9fda --- /dev/null +++ b/app/Http/Controllers/Import/StatusController.php @@ -0,0 +1,15 @@ +parent('import.index'); - $breadcrumbs->push(trans('firefly.import_file'), route('import.file.index')); - } -); - -Breadcrumbs::register( - 'import.file.configure', - function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) { - $breadcrumbs->parent('import.file.index'); - $breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.file.configure', [$job->key])); + $breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key])); } ); Breadcrumbs::register( - 'import.file.status', + 'import.status', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) { - $breadcrumbs->parent('import.file.index'); - $breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.file.status', [$job->key])); + $breadcrumbs->parent('import.index'); + $breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key])); } ); diff --git a/app/Import/Mapper/AssetAccountIbans.php b/app/Import/Mapper/AssetAccountIbans.php index 6a53db8941..9e2c78f277 100644 --- a/app/Import/Mapper/AssetAccountIbans.php +++ b/app/Import/Mapper/AssetAccountIbans.php @@ -56,7 +56,7 @@ class AssetAccountIbans implements MapperInterface asort($list); $list = $topList + $list; - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/AssetAccounts.php b/app/Import/Mapper/AssetAccounts.php index 43188a6833..1df9b2dca7 100644 --- a/app/Import/Mapper/AssetAccounts.php +++ b/app/Import/Mapper/AssetAccounts.php @@ -53,7 +53,7 @@ class AssetAccounts implements MapperInterface asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Bills.php b/app/Import/Mapper/Bills.php index b758b52dd1..90e22e2558 100644 --- a/app/Import/Mapper/Bills.php +++ b/app/Import/Mapper/Bills.php @@ -46,7 +46,7 @@ class Bills implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Budgets.php b/app/Import/Mapper/Budgets.php index eb1b73c9d8..286a6e3879 100644 --- a/app/Import/Mapper/Budgets.php +++ b/app/Import/Mapper/Budgets.php @@ -46,7 +46,7 @@ class Budgets implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Categories.php b/app/Import/Mapper/Categories.php index d6e0bd3d0f..10155d77a6 100644 --- a/app/Import/Mapper/Categories.php +++ b/app/Import/Mapper/Categories.php @@ -46,7 +46,7 @@ class Categories implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/OpposingAccountIbans.php b/app/Import/Mapper/OpposingAccountIbans.php index 894ff7f509..35d4178a88 100644 --- a/app/Import/Mapper/OpposingAccountIbans.php +++ b/app/Import/Mapper/OpposingAccountIbans.php @@ -62,7 +62,7 @@ class OpposingAccountIbans implements MapperInterface asort($list); $list = $topList + $list; - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/OpposingAccounts.php b/app/Import/Mapper/OpposingAccounts.php index c80c1753ab..7269fca8d9 100644 --- a/app/Import/Mapper/OpposingAccounts.php +++ b/app/Import/Mapper/OpposingAccounts.php @@ -59,7 +59,7 @@ class OpposingAccounts implements MapperInterface asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Tags.php b/app/Import/Mapper/Tags.php index 8a696ae082..0c2532b1c9 100644 --- a/app/Import/Mapper/Tags.php +++ b/app/Import/Mapper/Tags.php @@ -46,7 +46,7 @@ class Tags implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/TransactionCurrencies.php b/app/Import/Mapper/TransactionCurrencies.php index 0a52533aee..fc2ee8d395 100644 --- a/app/Import/Mapper/TransactionCurrencies.php +++ b/app/Import/Mapper/TransactionCurrencies.php @@ -42,7 +42,7 @@ class TransactionCurrencies implements MapperInterface asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index c2e3bd81d6..e40e541410 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -125,18 +125,22 @@ class ImportJobRepository implements ImportJobRepositoryInterface } /** - * @param ImportJob $job - * @param UploadedFile $file + * @param ImportJob $job + * @param null|UploadedFile $file * - * @return mixed + * @return bool + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException */ - public function processFile(ImportJob $job, UploadedFile $file): bool + public function processFile(ImportJob $job, ?UploadedFile $file): bool { + if (is_null($file)) { + return false; + } /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $newName = sprintf('%s.upload', $job->key); $uploaded = new SplFileObject($file->getRealPath()); - $content = $uploaded->fread($uploaded->getSize()); + $content = trim($uploaded->fread($uploaded->getSize())); $contentEncrypted = Crypt::encrypt($content); $disk = Storage::disk('upload'); @@ -171,8 +175,12 @@ class ImportJobRepository implements ImportJobRepositoryInterface */ public function setConfiguration(ImportJob $job, array $configuration): ImportJob { - $job->configuration = $configuration; + Log::debug(sprintf('Incoming config for job "%s" is: ', $job->key), $configuration); + $currentConfig = $job->configuration; + $newConfig = array_merge($currentConfig, $configuration); + $job->configuration = $newConfig; $job->save(); + Log::debug(sprintf('Set config of job "%s" to: ', $job->key), $newConfig); return $job; } diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php index a2a9869eaf..31067db0e6 100644 --- a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -54,12 +54,12 @@ interface ImportJobRepositoryInterface public function processConfiguration(ImportJob $job, UploadedFile $file): bool; /** - * @param ImportJob $job - * @param UploadedFile $file + * @param ImportJob $job + * @param null|UploadedFile $file * - * @return mixed + * @return bool */ - public function processFile(ImportJob $job, UploadedFile $file): bool; + public function processFile(ImportJob $job, ?UploadedFile $file): bool; /** * @param ImportJob $job diff --git a/app/Support/Import/Configuration/File/Map.php b/app/Support/Import/Configuration/File/Map.php index fa7b7428e2..20d84bdfb7 100644 --- a/app/Support/Import/Configuration/File/Map.php +++ b/app/Support/Import/Configuration/File/Map.php @@ -29,6 +29,7 @@ use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; use League\Csv\Reader; +use League\Csv\Statement; use Log; /** @@ -49,6 +50,7 @@ class Map implements ConfigurationInterface * @return array * * @throws FireflyException + * @throws \League\Csv\Exception */ public function getData(): array { @@ -57,13 +59,15 @@ class Map implements ConfigurationInterface // in order to actually map we also need all possible values from the CSV file. $content = $this->job->uploadFileContents(); + $offset = 0; /** @var Reader $reader */ $reader = Reader::createFromString($content); $reader->setDelimiter($this->configuration['delimiter']); - if($this->configuration['has-headers']) { - $reader->setHeaderOffset(0); + if ($this->configuration['has-headers']) { + $offset = 1; } - $results = $reader->getRecords(); + $stmt = (new Statement)->offset($offset); + $results = $stmt->process($reader); $this->validSpecifics = array_keys(config('csv.import_specifics')); $indexes = array_keys($this->data); $rowIndex = 0; @@ -71,13 +75,12 @@ class Map implements ConfigurationInterface $row = $this->runSpecifics($row); //do something here - foreach ($indexes as $index) { // this is simply 1, 2, 3, etc. if (!isset($row[$index])) { // don't really know how to handle this. Just skip, for now. continue; } - $value = $row[$index]; + $value = trim($row[$index]); if (strlen($value) > 0) { // we can do some preprocessing here, // which is exclusively to fix the tags: diff --git a/app/Support/Import/Configuration/File/Roles.php b/app/Support/Import/Configuration/File/Roles.php index f39f698d2b..a06785757c 100644 --- a/app/Support/Import/Configuration/File/Roles.php +++ b/app/Support/Import/Configuration/File/Roles.php @@ -45,19 +45,28 @@ class Roles implements ConfigurationInterface * Get the data necessary to show the configuration screen. * * @return array + * @throws \League\Csv\Exception */ public function getData(): array { - $config = $this->job->configuration; - $content = $this->job->uploadFileContents(); - + $config = $this->job->configuration; + $content = $this->job->uploadFileContents(); + $config['has-headers'] = true; + $headers = []; + $offset = 0; // create CSV reader. $reader = Reader::createFromString($content); $reader->setDelimiter($config['delimiter']); + if ($config['has-headers']) { - $reader->setHeaderOffset(0); + $offset = 1; + // get headers: + $stmt = (new Statement)->limit(1)->offset(0); + $records = $stmt->process($reader); + $headers = $records->fetchOne(); } - $stmt = (new Statement)->limit(intval(config('csv.example_rows', 5))); + // example rows: + $stmt = (new Statement)->limit(intval(config('csv.example_rows', 5)))->offset($offset); // set data: $roles = $this->getRoles(); asort($roles); @@ -65,10 +74,9 @@ class Roles implements ConfigurationInterface 'examples' => [], 'roles' => $roles, 'total' => 0, - 'headers' => $config['has-headers'] ? $reader->fetchOne(0) : [], + 'headers' => $headers, ]; - $records = $stmt->process($reader); foreach ($records as $row) { $row = $this->processSpecifics($row); @@ -142,7 +150,7 @@ class Roles implements ConfigurationInterface { $roles = []; foreach (array_keys(config('csv.import_roles')) as $role) { - $roles[$role] = trans('csv.column_' . $role); + $roles[$role] = trans('import.column_' . $role); } return $roles; @@ -236,7 +244,7 @@ class Roles implements ConfigurationInterface */ private function processSpecifics(array $row): array { - $names = array_keys($this->job->configuration['specifics']); + $names = array_keys($this->job->configuration['specifics'] ?? []); foreach ($names as $name) { /** @var SpecificInterface $specific */ $specific = app('FireflyIII\Import\Specifics\\' . $name); @@ -271,7 +279,7 @@ class Roles implements ConfigurationInterface $this->warning = ''; } if (0 === $assigned || !$hasAmount) { - $this->warning = strval(trans('csv.roles_warning')); + $this->warning = strval(trans('import.roles_warning')); } return true; diff --git a/app/Support/Import/Configuration/File/Upload.php b/app/Support/Import/Configuration/File/Upload.php index b31e2915b9..133e356eb9 100644 --- a/app/Support/Import/Configuration/File/Upload.php +++ b/app/Support/Import/Configuration/File/Upload.php @@ -22,11 +22,9 @@ declare(strict_types=1); namespace FireflyIII\Support\Import\Configuration\File; -use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; -use League\Csv\Reader; -use League\Csv\Statement; use Log; /** @@ -47,7 +45,17 @@ class Upload implements ConfigurationInterface */ public function getData(): array { - return []; + $importFileTypes = []; + $defaultImportType = config('import.options.file.default_import_format'); + + foreach (config('import.options.file.import_formats') as $type) { + $importFileTypes[$type] = trans('import.import_file_type_' . $type); + } + + return [ + 'default_type' => $defaultImportType, + 'file_types' => $importFileTypes, + ]; } /** @@ -81,8 +89,28 @@ class Upload implements ConfigurationInterface */ public function storeConfiguration(array $data): bool { - echo 'do something with data.'; - exit; + Log::debug('Now in storeConfiguration for file Upload.'); + /** @var ImportJobRepositoryInterface $repository */ + $repository = app(ImportJobRepositoryInterface::class); + $type = $data['import_file_type'] ?? 'unknown'; + $config = $this->job->configuration; + $config['file-type'] = in_array($type, config('import.options.file.import_formats')) ? $type : 'unknown'; + $repository->setConfiguration($this->job, $config); + $uploaded = $repository->processFile($this->job, $data['import_file'] ?? null); + $this->job->save(); + Log::debug(sprintf('Result of upload is %s', var_export($uploaded, true))); + // process config, if present: + if (isset($data['configuration_file'])) { + $repository->processConfiguration($this->job, $data['configuration_file']); + } + $config = $this->job->configuration; + $config['has-file-upload'] = $uploaded; + $repository->setConfiguration($this->job, $config); + + if ($uploaded === false) { + $this->warning = 'No valid upload.'; + } + return true; } diff --git a/config/import.php b/config/import.php index ad8a9d1ff3..4181666ea5 100644 --- a/config/import.php +++ b/config/import.php @@ -21,6 +21,12 @@ return [ 'spectre' => 'FireflyIII\Import\Configuration\SpectreConfigurator', 'plaid' => 'FireflyIII\Import\Configuration\PlaidConfigurator', ], + 'options' => [ + 'file' => [ + 'import_formats' => ['csv'], // mt940 + 'default_import_format' => 'csv', + ], + ], 'default_config' => [ 'file' => [ 'has-config-file' => true, diff --git a/resources/lang/en_US/bank.php b/resources/lang/en_US/bank.php index b9187230d5..ee3504e6f3 100644 --- a/resources/lang/en_US/bank.php +++ b/resources/lang/en_US/bank.php @@ -23,19 +23,5 @@ declare(strict_types=1); return [ - '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.', - // Spectre: - 'spectre_title' => 'Import using Spectre', - 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', - 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', - 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', - 'spectre_select_country_title' => 'Select a country', - 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', - 'spectre_select_provider_title' => 'Select a bank', - 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', - 'spectre_input_fields_title' => 'Input mandatory fields', - 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', - 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', ]; diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 8889ca3aab..724ca562c1 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -23,79 +23,4 @@ declare(strict_types=1); return [ - // initial config - 'initial_title' => 'Import setup (1/3) - Basic CSV import setup', - 'initial_text' => 'To be able to import your file correctly, please validate the options below.', - 'initial_box' => 'Basic CSV import setup', - 'initial_box_title' => 'Basic CSV import setup options', - 'initial_header_help' => 'Check this box if the first row of your CSV file are the column titles.', - 'initial_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'initial_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'initial_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'initial_submit' => 'Continue with step 2/3', - - // new options: - 'apply_rules_title' => 'Apply rules', - 'apply_rules_description' => 'Apply your rules. Note that this slows the import significantly.', - 'match_bills_title' => 'Match bills', - 'match_bills_description' => 'Match your bills to newly created withdrawals. Note that this slows the import significantly.', - - // roles config - 'roles_title' => 'Import setup (2/3) - Define each column\'s role', - 'roles_text' => 'Each column in your CSV file contains certain data. Please indicate what kind of data the importer should expect. The option to "map" data means that you will link each entry found in the column to a value in your database. An often mapped column is the column that contains the IBAN of the opposing account. That can be easily matched to IBAN\'s present in your database already.', - 'roles_table' => 'Table', - 'roles_column_name' => 'Name of column', - 'roles_column_example' => 'Column example data', - 'roles_column_role' => 'Column data meaning', - 'roles_do_map_value' => 'Map these values', - 'roles_column' => 'Column', - 'roles_no_example_data' => 'No example data available', - 'roles_submit' => 'Continue with step 3/3', - 'roles_warning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', - - // map data - 'map_title' => 'Import setup (3/3) - Connect import data to Firefly III data', - 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', - 'map_field_value' => 'Field value', - 'map_field_mapped_to' => 'Mapped to', - 'map_do_not_map' => '(do not map)', - 'map_submit' => 'Start the import', - - // map things. - 'column__ignore' => '(ignore this column)', - 'column_account-iban' => 'Asset account (IBAN)', - 'column_account-id' => 'Asset account ID (matching Firefly)', - 'column_account-name' => 'Asset account (name)', - 'column_amount' => 'Amount', - 'column_amount_debet' => 'Amount (debet column)', - 'column_amount_credit' => 'Amount (credit column)', - 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'column_bill-id' => 'Bill ID (matching Firefly)', - 'column_bill-name' => 'Bill name', - 'column_budget-id' => 'Budget ID (matching Firefly)', - 'column_budget-name' => 'Budget name', - 'column_category-id' => 'Category ID (matching Firefly)', - 'column_category-name' => 'Category name', - 'column_currency-code' => 'Currency code (ISO 4217)', - 'column_currency-id' => 'Currency ID (matching Firefly)', - 'column_currency-name' => 'Currency name (matching Firefly)', - 'column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'column_date-interest' => 'Interest calculation date', - 'column_date-book' => 'Transaction booking date', - 'column_date-process' => 'Transaction process date', - 'column_date-transaction' => 'Date', - 'column_description' => 'Description', - 'column_opposing-iban' => 'Opposing account (IBAN)', - 'column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'column_external-id' => 'External ID', - 'column_opposing-name' => 'Opposing account (name)', - 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'column_sepa-db' => 'SEPA Direct Debet', - 'column_tags-comma' => 'Tags (comma separated)', - 'column_tags-space' => 'Tags (space separated)', - 'column_account-number' => 'Asset account (account number)', - 'column_opposing-number' => 'Opposing account (account number)', ]; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 2e0cda0d22..9c4d3e3a49 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1070,29 +1070,17 @@ return [ // import bread crumbs and titles: 'import' => 'Import', 'import_data' => 'Import data', + 'import_general_index_file' => 'Import a file', 'import_from_bunq' => 'Import from bunq', 'import_using_spectre' => 'Import using Spectre', 'import_using_plaid' => 'Import using Plaid', - + 'import_config_sub_title' => 'Set up your import', + 'import_config_bread_crumb' => 'Set up your import', // import index page: 'import_index_title' => 'Import data into Firefly III', 'import_index_sub_title' => 'Index', 'import_general_index_intro' => 'Welcome to Firefly\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.', - 'import_general_index_file' => 'Import a file', - 'import_index_intro' => 'This routine will help you import files from your bank into Firefly III. Please check out the help pages in the top right corner.', - 'import_index_file' => 'Select your file', - 'import_index_config' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file.', - 'import_index_type' => 'Select the type of file you will upload', - 'import_index_start' => 'Start importing', - 'import_file' => 'Import a file', - - // supported file types: - 'import_file_type_csv' => 'CSV (comma separated values)', - - // import configuration routine: - 'import_config_sub_title' => 'Set up your import file', - 'import_config_bread_crumb' => 'Set up your import file', // import status page: 'import_status_bread_crumb' => 'Import status', diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php new file mode 100644 index 0000000000..d1135a4198 --- /dev/null +++ b/resources/lang/en_US/import.php @@ -0,0 +1,126 @@ + 'This routine will help you import files from your bank into Firefly III. Please check out the help pages in the top right corner.', + // 'import_index_file' => 'Select your file', + // 'import_index_config' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file.', + // + // 'import_index_start' => 'Start importing', + // 'import_file' => 'Import a file', + // + // // supported file types: + // + // + // // import configuration routine: + + + // file: upload something: + 'file_upload_title' => 'Import setup (1/4) - Upload your file', + 'file_upload_text' => 'This routine will help you import files from your bank into Firefly III. Please check out the help pages in the top right corner.', + 'file_upload_fields' => 'Fields', + 'file_upload_help' => 'Select your file', + 'file_upload_config_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their configuration file', + 'file_upload_type_help' => 'Select the type of file you will upload', + 'file_upload_submit' => 'Upload files', + + // file: upload types + 'import_file_type_csv' => 'CSV (comma separated values)', + + // file: initial config for CSV + 'csv_initial_title' => 'Import setup (2/4) - Basic CSV import setup', + 'csv_initial_text' => 'To be able to import your file correctly, please validate the options below.', + 'csv_initial_box' => 'Basic CSV import setup', + 'csv_initial_box_title' => 'Basic CSV import setup options', + 'csv_initial_header_help' => 'Check this box if the first row of your CSV file are the column titles.', + 'csv_initial_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'csv_initial_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_initial_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'csv_initial_submit' => 'Continue with step 3/4', + + // file: new options: + 'file_apply_rules_title' => 'Apply rules', + 'file_apply_rules_description' => 'Apply your rules. Note that this slows the import significantly.', + 'file_match_bills_title' => 'Match bills', + 'file_match_bills_description' => 'Match your bills to newly created withdrawals. Note that this slows the import significantly.', + + // file: roles config + 'csv_roles_title' => 'Import setup (3/4) - Define each column\'s role', + 'csv_roles_text' => 'Each column in your CSV file contains certain data. Please indicate what kind of data the importer should expect. The option to "map" data means that you will link each entry found in the column to a value in your database. An often mapped column is the column that contains the IBAN of the opposing account. That can be easily matched to IBAN\'s present in your database already.', + 'csv_roles_table' => 'Table', + 'csv_roles_column_name' => 'Name of column', + 'csv_roles_column_example' => 'Column example data', + 'csv_roles_column_role' => 'Column data meaning', + 'csv_roles_do_map_value' => 'Map these values', + 'csv_roles_column' => 'Column', + 'csv_roles_no_example_data' => 'No example data available', + 'csv_roles_submit' => 'Continue with step 4/4', + 'csv_roles_warning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', + + + // map data + 'file_map_title' => 'Import setup (4/4) - Connect import data to Firefly III data', + 'file_map_text' => 'In the following tables, the left value shows you information found in your uploaded file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + 'file_map_field_value' => 'Field value', + 'file_map_field_mapped_to' => 'Mapped to', + 'map_do_not_map' => '(do not map)', + 'file_map_submit' => 'Start the import', + + // map things. + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount_debet' => 'Amount (debet column)', + 'column_amount_credit' => 'Amount (credit column)', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', + + // 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.', + + // Spectre: + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_select_provider_title' => 'Select a bank', + 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', + 'spectre_input_fields_title' => 'Input mandatory fields', + 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', + 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', +]; \ No newline at end of file diff --git a/resources/views/import/file/finished.twig b/resources/views/import/_old/finished.twig similarity index 100% rename from resources/views/import/file/finished.twig rename to resources/views/import/_old/finished.twig diff --git a/resources/views/import/file/index.twig b/resources/views/import/_old/index.twig similarity index 100% rename from resources/views/import/file/index.twig rename to resources/views/import/_old/index.twig diff --git a/resources/views/import/file/status.twig b/resources/views/import/_old/status.twig similarity index 100% rename from resources/views/import/file/status.twig rename to resources/views/import/_old/status.twig diff --git a/resources/views/import/file/initial.twig b/resources/views/import/file/initial.twig index fbefb1b37c..bf7a5e2ea6 100644 --- a/resources/views/import/file/initial.twig +++ b/resources/views/import/file/initial.twig @@ -10,11 +10,11 @@
    -

    {{ trans('csv.initial_title') }}

    +

    {{ trans('import.csv_initial_title') }}

    - {{ trans('csv.initial_text') }} + {{ trans('import.csv_initial_text') }}

    @@ -29,42 +29,42 @@
    -

    {{ trans('csv.initial_box_title') }}

    +

    {{ trans('import.csv_initial_box_title') }}

    {{ 'mandatoryFields'|_ }}

    - {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.initial_header_help')}) }} - {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.initial_date_help', {dateExample: phpdate('Ymd')}) }) }} - {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.initial_delimiter_help') } ) }} - {{ ExpandedForm.select('csv_import_account', data.accounts, job.configuration['import-account'], {helpText: trans('csv.initial_import_account_help')} ) }} + {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('import.csv_initial_header_help')}) }} + {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('import.csv_initial_date_help', {dateExample: phpdate('Ymd')}) }) }} + {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('import.csv_initial_delimiter_help') } ) }} + {{ ExpandedForm.select('csv_import_account', data.accounts, job.configuration['import-account'], {helpText: trans('import.csv_initial_import_account_help')} ) }}

    {{ 'optionalFields'|_ }}

    @@ -97,7 +97,7 @@
    diff --git a/resources/views/import/file/map.twig b/resources/views/import/file/map.twig index edc4b4b5bb..31d309d7f2 100644 --- a/resources/views/import/file/map.twig +++ b/resources/views/import/file/map.twig @@ -11,11 +11,11 @@
    -

    {{ trans('csv.map_title') }}

    +

    {{ trans('import.file_map_title') }}

    - {{ trans('csv.map_text') }} + {{ trans('import.file_map_text') }}

    @@ -30,14 +30,14 @@
    -

    {{ trans('csv.column_'~field.name) }}

    +

    {{ trans('import.column_'~field.name) }}

    - - + + @@ -67,7 +67,7 @@
    diff --git a/resources/views/import/file/roles.twig b/resources/views/import/file/roles.twig index f35b92bfe4..e5861329e2 100644 --- a/resources/views/import/file/roles.twig +++ b/resources/views/import/file/roles.twig @@ -10,11 +10,11 @@
    -

    {{ trans('csv.roles_title') }}

    +

    {{ trans('import.csv_roles_title') }}

    - {{ trans('csv.roles_text') }} + {{ trans('import.csv_roles_text') }}

    @@ -29,17 +29,17 @@
    -

    {{ trans('csv.roles_table') }}

    +

    {{ trans('import.csv_roles_table') }}

    {{ trans('csv.map_field_value') }}{{ trans('csv.map_field_mapped_to') }}{{ trans('import.file_map_field_value') }}{{ trans('import.file_map_field_mapped_to') }}
    - - - - + + + + {% for i in 0..(data.total -1) %} @@ -47,14 +47,14 @@ {% if limit.spent > 0 %} - - + - + {% if overspent %} + {% set pct = (limit.spent != 0 ? (limit.amount / (limit.spent*-1))*100 : 0) %} +
    +
    +
    +
    + {% else %} + {% set pct = (limit.amount != 0 ? (((limit.spent*-1) / limit.amount)*100) : 0) %} +
    +
    +
    + {% endif %} + + {% endif %}
    {{ trans('csv.roles_column_name') }}{{ trans('csv.roles_column_example') }}{{ trans('csv.roles_column_role') }}{{ trans('csv.roles_do_map_value') }}{{ trans('import.csv_roles_column_name') }}{{ trans('import.csv_roles_column_example') }}{{ trans('import.csv_roles_column_role') }}{{ trans('import.csv_roles_do_map_value') }}
    {% if data.headers[i] == '' %} - {{ trans('csv.roles_column') }} #{{ loop.index }} + {{ trans('import.csv_roles_column') }} #{{ loop.index }} {% else %} {{ data.headers[i] }} {% endif %} {% if data.examples[i]|length == 0 %} - {{ trans('csv.roles_no_example_data') }} + {{ trans('import.csv_roles_no_example_data') }} {% else %} {% for example in data.examples[i] %} {{ example }}
    @@ -91,7 +91,7 @@
    diff --git a/resources/views/import/file/upload.twig b/resources/views/import/file/upload.twig index 50c184e58d..4f4414db79 100644 --- a/resources/views/import/file/upload.twig +++ b/resources/views/import/file/upload.twig @@ -5,47 +5,51 @@ {% endblock %} {% block content %}
    -
    -
    +
    +
    -

    {{ 'import_index_title'|_ }}

    +

    {{ trans('import.file_upload_title') }}

    -
    -
    -

    - {{ 'import_index_intro'|_ }} -

    -
    +

    + {{ trans('import.file_upload_text') }} +

    +
    +
    + +
    +
    + + + + + +
    +
    +
    +
    +

    {{ trans('import.file_upload_fields') }}

    - -
    - - - - -
    - - {{ ExpandedForm.file('import_file', {helpText: 'import_index_file'|_}) }} - {{ ExpandedForm.file('configuration_file', {helpText: 'import_index_config'|_|raw}) }} - {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_index_type'|_}) }} - -
    - -
    - -
    -
    -
    - +
    + {{ ExpandedForm.file('import_file', {helpText: trans('import.file_upload_help')}) }} + {{ ExpandedForm.file('configuration_file', {helpText: trans('import.file_upload_config_help')|raw}) }} + {{ ExpandedForm.select('import_file_type', data.file_types, data.default_type, {'helpText' : trans('import.file_upload_type_help')}) }}
    -
    +
    +
    +
    +
    + +
    +
    +
    +
    + {% endblock %} {% block scripts %} {% endblock %} diff --git a/routes/web.php b/routes/web.php index ee79111ec4..42ca3e5acc 100755 --- a/routes/web.php +++ b/routes/web.php @@ -440,6 +440,8 @@ Route::group( Route::get('configure/{importJob}', ['uses' => 'Import\ConfigurationController@index', 'as' => 'configure']); Route::post('configure/{importJob}', ['uses' => 'Import\ConfigurationController@post', 'as' => 'configure.post']); + // get status of any job: + Route::get('status/{importJob}', ['uses' => 'Import\StatusController@index', 'as' => 'status']); // file import // Route::get('file', ['uses' => 'Import\FileController@index', 'as' => 'file.index']); From 9086259a65c741e25f115d59fab05ee93ae29b84 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Dec 2017 19:46:36 +0100 Subject: [PATCH 054/142] Small code change for controllers. --- app/Http/Controllers/Account/ReconcileController.php | 4 ++-- app/Http/Controllers/AccountController.php | 4 ++-- app/Http/Controllers/Admin/ConfigurationController.php | 4 ++-- app/Http/Controllers/Admin/LinkController.php | 4 ++-- app/Http/Controllers/Admin/UserController.php | 4 ++-- app/Http/Controllers/AttachmentController.php | 4 ++-- app/Http/Controllers/BillController.php | 4 ++-- app/Http/Controllers/BudgetController.php | 4 ++-- app/Http/Controllers/CategoryController.php | 4 ++-- app/Http/Controllers/CurrencyController.php | 4 ++-- app/Http/Controllers/ExportController.php | 4 ++-- app/Http/Controllers/HomeController.php | 4 ++-- app/Http/Controllers/NewUserController.php | 4 ++-- app/Http/Controllers/PiggyBankController.php | 4 ++-- app/Http/Controllers/PreferencesController.php | 4 ++-- app/Http/Controllers/ProfileController.php | 4 ++-- app/Http/Controllers/ReportController.php | 4 ++-- app/Http/Controllers/RuleController.php | 4 ++-- app/Http/Controllers/RuleGroupController.php | 4 ++-- app/Http/Controllers/SearchController.php | 4 ++-- app/Http/Controllers/TagController.php | 4 ++-- app/Http/Controllers/Transaction/ConvertController.php | 4 ++-- app/Http/Controllers/Transaction/LinkController.php | 4 ++-- app/Http/Controllers/Transaction/MassController.php | 4 ++-- app/Http/Controllers/Transaction/SingleController.php | 4 ++-- app/Http/Controllers/Transaction/SplitController.php | 4 ++-- app/Http/Controllers/TransactionController.php | 4 ++-- app/Import/Routine/{ImportRoutine.php => FileRoutine.php} | 0 28 files changed, 54 insertions(+), 54 deletions(-) rename app/Import/Routine/{ImportRoutine.php => FileRoutine.php} (100%) diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index 06fc158fcc..1125a79472 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -59,8 +59,8 @@ class ReconcileController extends Controller // translations: $this->middleware( function ($request, $next) { - View::share('mainTitleIcon', 'fa-credit-card'); - View::share('title', trans('firefly.accounts')); + app('view')->share('mainTitleIcon', 'fa-credit-card'); + app('view')->share('title', trans('firefly.accounts')); return $next($request); } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index aa85f922c2..0b57e0555c 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -60,8 +60,8 @@ class AccountController extends Controller // translations: $this->middleware( function ($request, $next) { - View::share('mainTitleIcon', 'fa-credit-card'); - View::share('title', trans('firefly.accounts')); + app('view')->share('mainTitleIcon', 'fa-credit-card'); + app('view')->share('title', trans('firefly.accounts')); return $next($request); } diff --git a/app/Http/Controllers/Admin/ConfigurationController.php b/app/Http/Controllers/Admin/ConfigurationController.php index 3dba4e77b4..1629aa7677 100644 --- a/app/Http/Controllers/Admin/ConfigurationController.php +++ b/app/Http/Controllers/Admin/ConfigurationController.php @@ -45,8 +45,8 @@ class ConfigurationController extends Controller $this->middleware( function ($request, $next) { - View::share('title', strval(trans('firefly.administration'))); - View::share('mainTitleIcon', 'fa-hand-spock-o'); + app('view')->share('title', strval(trans('firefly.administration'))); + app('view')->share('mainTitleIcon', 'fa-hand-spock-o'); return $next($request); } diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index ec66da1a36..ada71fbcda 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -45,8 +45,8 @@ class LinkController extends Controller $this->middleware( function ($request, $next) { - View::share('title', strval(trans('firefly.administration'))); - View::share('mainTitleIcon', 'fa-hand-spock-o'); + app('view')->share('title', strval(trans('firefly.administration'))); + app('view')->share('mainTitleIcon', 'fa-hand-spock-o'); return $next($request); } diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index e23f91e012..2ee4cb7684 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -46,8 +46,8 @@ class UserController extends Controller $this->middleware( function ($request, $next) { - View::share('title', strval(trans('firefly.administration'))); - View::share('mainTitleIcon', 'fa-hand-spock-o'); + app('view')->share('title', strval(trans('firefly.administration'))); + app('view')->share('mainTitleIcon', 'fa-hand-spock-o'); return $next($request); } diff --git a/app/Http/Controllers/AttachmentController.php b/app/Http/Controllers/AttachmentController.php index dc156b49e4..4261310574 100644 --- a/app/Http/Controllers/AttachmentController.php +++ b/app/Http/Controllers/AttachmentController.php @@ -50,8 +50,8 @@ class AttachmentController extends Controller // translations: $this->middleware( function ($request, $next) { - View::share('mainTitleIcon', 'fa-paperclip'); - View::share('title', trans('firefly.attachments')); + app('view')->share('mainTitleIcon', 'fa-paperclip'); + app('view')->share('title', trans('firefly.attachments')); return $next($request); } diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index a433b292ac..389c0b689c 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -58,8 +58,8 @@ class BillController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.bills')); - View::share('mainTitleIcon', 'fa-calendar-o'); + app('view')->share('title', trans('firefly.bills')); + app('view')->share('mainTitleIcon', 'fa-calendar-o'); $this->attachments = app(AttachmentHelperInterface::class); return $next($request); diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index f7bce98240..848d8df320 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -64,8 +64,8 @@ class BudgetController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.budgets')); - View::share('mainTitleIcon', 'fa-tasks'); + app('view')->share('title', trans('firefly.budgets')); + app('view')->share('mainTitleIcon', 'fa-tasks'); $this->repository = app(BudgetRepositoryInterface::class); return $next($request); diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 1773902ee8..b447f9c614 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -55,8 +55,8 @@ class CategoryController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.categories')); - View::share('mainTitleIcon', 'fa-bar-chart'); + app('view')->share('title', trans('firefly.categories')); + app('view')->share('mainTitleIcon', 'fa-bar-chart'); return $next($request); } diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php index ca7cf7b9f3..9524c971b9 100644 --- a/app/Http/Controllers/CurrencyController.php +++ b/app/Http/Controllers/CurrencyController.php @@ -52,8 +52,8 @@ class CurrencyController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.currencies')); - View::share('mainTitleIcon', 'fa-usd'); + app('view')->share('title', trans('firefly.currencies')); + app('view')->share('mainTitleIcon', 'fa-usd'); $this->repository = app(CurrencyRepositoryInterface::class); $this->userRepository = app(UserRepositoryInterface::class); diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index fa746b8145..33df4d9f0a 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -50,8 +50,8 @@ class ExportController extends Controller $this->middleware( function ($request, $next) { - View::share('mainTitleIcon', 'fa-file-archive-o'); - View::share('title', trans('firefly.export_and_backup_data')); + app('view')->share('mainTitleIcon', 'fa-file-archive-o'); + app('view')->share('title', trans('firefly.export_and_backup_data')); return $next($request); } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 01dad0f145..5167168692 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -53,8 +53,8 @@ class HomeController extends Controller public function __construct() { parent::__construct(); - View::share('title', 'Firefly III'); - View::share('mainTitleIcon', 'fa-fire'); + app('view')->share('title', 'Firefly III'); + app('view')->share('mainTitleIcon', 'fa-fire'); $this->middleware(IsLimitedUser::class)->except(['dateRange', 'index']); } diff --git a/app/Http/Controllers/NewUserController.php b/app/Http/Controllers/NewUserController.php index b8a88a0289..2a703340b0 100644 --- a/app/Http/Controllers/NewUserController.php +++ b/app/Http/Controllers/NewUserController.php @@ -56,8 +56,8 @@ class NewUserController extends Controller */ public function index(AccountRepositoryInterface $repository) { - View::share('title', trans('firefly.welcome')); - View::share('mainTitleIcon', 'fa-fire'); + app('view')->share('title', trans('firefly.welcome')); + app('view')->share('mainTitleIcon', 'fa-fire'); $types = config('firefly.accountTypesByIdentifier.asset'); $count = $repository->count($types); diff --git a/app/Http/Controllers/PiggyBankController.php b/app/Http/Controllers/PiggyBankController.php index 5d150b6193..b281c64753 100644 --- a/app/Http/Controllers/PiggyBankController.php +++ b/app/Http/Controllers/PiggyBankController.php @@ -52,8 +52,8 @@ class PiggyBankController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.piggyBanks')); - View::share('mainTitleIcon', 'fa-sort-amount-asc'); + app('view')->share('title', trans('firefly.piggyBanks')); + app('view')->share('mainTitleIcon', 'fa-sort-amount-asc'); return $next($request); } diff --git a/app/Http/Controllers/PreferencesController.php b/app/Http/Controllers/PreferencesController.php index f97f87bde5..2f19ed02ab 100644 --- a/app/Http/Controllers/PreferencesController.php +++ b/app/Http/Controllers/PreferencesController.php @@ -46,8 +46,8 @@ class PreferencesController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.preferences')); - View::share('mainTitleIcon', 'fa-gear'); + app('view')->share('title', trans('firefly.preferences')); + app('view')->share('mainTitleIcon', 'fa-gear'); return $next($request); } diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 88532a8d74..463a3cbae0 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -56,8 +56,8 @@ class ProfileController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.profile')); - View::share('mainTitleIcon', 'fa-user'); + app('view')->share('title', trans('firefly.profile')); + app('view')->share('mainTitleIcon', 'fa-user'); return $next($request); } diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index c1d7be7e41..cc1b2bb6fb 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -59,8 +59,8 @@ class ReportController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.reports')); - View::share('mainTitleIcon', 'fa-line-chart'); + app('view')->share('title', trans('firefly.reports')); + app('view')->share('mainTitleIcon', 'fa-line-chart'); View::share('subTitleIcon', 'fa-calendar'); return $next($request); diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 82f184600b..a4b259cc49 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -57,8 +57,8 @@ class RuleController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.rules')); - View::share('mainTitleIcon', 'fa-random'); + app('view')->share('title', trans('firefly.rules')); + app('view')->share('mainTitleIcon', 'fa-random'); return $next($request); } diff --git a/app/Http/Controllers/RuleGroupController.php b/app/Http/Controllers/RuleGroupController.php index 9528ee4178..c30d8e80fb 100644 --- a/app/Http/Controllers/RuleGroupController.php +++ b/app/Http/Controllers/RuleGroupController.php @@ -50,8 +50,8 @@ class RuleGroupController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.rules')); - View::share('mainTitleIcon', 'fa-random'); + app('view')->share('title', trans('firefly.rules')); + app('view')->share('mainTitleIcon', 'fa-random'); return $next($request); } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index aebb061620..c6e1ace145 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -43,8 +43,8 @@ class SearchController extends Controller $this->middleware( function ($request, $next) { - View::share('mainTitleIcon', 'fa-search'); - View::share('title', trans('firefly.search')); + app('view')->share('mainTitleIcon', 'fa-search'); + app('view')->share('title', trans('firefly.search')); return $next($request); } diff --git a/app/Http/Controllers/TagController.php b/app/Http/Controllers/TagController.php index 3d1bb319d6..adf20010fa 100644 --- a/app/Http/Controllers/TagController.php +++ b/app/Http/Controllers/TagController.php @@ -64,8 +64,8 @@ class TagController extends Controller $this->middleware( function ($request, $next) { $this->repository = app(TagRepositoryInterface::class); - View::share('title', strval(trans('firefly.tags'))); - View::share('mainTitleIcon', 'fa-tags'); + app('view')->share('title', strval(trans('firefly.tags'))); + app('view')->share('mainTitleIcon', 'fa-tags'); return $next($request); } diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index c6c2b62b1f..393c4d1ce6 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -55,8 +55,8 @@ class ConvertController extends Controller function ($request, $next) { $this->accounts = app(AccountRepositoryInterface::class); - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-exchange'); + app('view')->share('title', trans('firefly.transactions')); + app('view')->share('mainTitleIcon', 'fa-exchange'); return $next($request); } diff --git a/app/Http/Controllers/Transaction/LinkController.php b/app/Http/Controllers/Transaction/LinkController.php index 866924014a..cb4bfe5e8e 100644 --- a/app/Http/Controllers/Transaction/LinkController.php +++ b/app/Http/Controllers/Transaction/LinkController.php @@ -48,8 +48,8 @@ class LinkController extends Controller // some useful repositories: $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); + app('view')->share('title', trans('firefly.transactions')); + app('view')->share('mainTitleIcon', 'fa-repeat'); return $next($request); } diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index ae7a3dabcc..fb512aa3dc 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -51,8 +51,8 @@ class MassController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); + app('view')->share('title', trans('firefly.transactions')); + app('view')->share('mainTitleIcon', 'fa-repeat'); return $next($request); } diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index 13a0578616..36d04b6dda 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -89,8 +89,8 @@ class SingleController extends Controller $this->currency = app(CurrencyRepositoryInterface::class); $this->repository = app(JournalRepositoryInterface::class); - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); + app('view')->share('title', trans('firefly.transactions')); + app('view')->share('mainTitleIcon', 'fa-repeat'); return $next($request); } diff --git a/app/Http/Controllers/Transaction/SplitController.php b/app/Http/Controllers/Transaction/SplitController.php index 9b141fe5e8..865e086493 100644 --- a/app/Http/Controllers/Transaction/SplitController.php +++ b/app/Http/Controllers/Transaction/SplitController.php @@ -78,8 +78,8 @@ class SplitController extends Controller $this->tasker = app(JournalTaskerInterface::class); $this->attachments = app(AttachmentHelperInterface::class); $this->currencies = app(CurrencyRepositoryInterface::class); - View::share('mainTitleIcon', 'fa-share-alt'); - View::share('title', trans('firefly.split-transactions')); + app('view')->share('mainTitleIcon', 'fa-share-alt'); + app('view')->share('title', trans('firefly.split-transactions')); return $next($request); } diff --git a/app/Http/Controllers/TransactionController.php b/app/Http/Controllers/TransactionController.php index 0f48c39b0a..bc86d5e35c 100644 --- a/app/Http/Controllers/TransactionController.php +++ b/app/Http/Controllers/TransactionController.php @@ -55,8 +55,8 @@ class TransactionController extends Controller $this->middleware( function ($request, $next) { - View::share('title', trans('firefly.transactions')); - View::share('mainTitleIcon', 'fa-repeat'); + app('view')->share('title', trans('firefly.transactions')); + app('view')->share('mainTitleIcon', 'fa-repeat'); return $next($request); } diff --git a/app/Import/Routine/ImportRoutine.php b/app/Import/Routine/FileRoutine.php similarity index 100% rename from app/Import/Routine/ImportRoutine.php rename to app/Import/Routine/FileRoutine.php From 81763565099c39d226ba1fc3ce484312473315b1 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Dec 2017 19:47:14 +0100 Subject: [PATCH 055/142] New code for import routine, part 3 --- .../Controllers/Import/FileController.php | 62 +------ .../Controllers/Import/IndexController.php | 71 ++++++++ .../Controllers/Import/StatusController.php | 82 +++++++++ app/Import/Routine/FileRoutine.php | 7 +- app/Import/Routine/RoutineInterface.php | 48 ++++++ config/import.php | 13 +- public/js/ff/import/status.js | 47 ++++-- resources/lang/en_US/firefly.php | 7 - resources/lang/en_US/import.php | 13 +- resources/views/import/status.twig | 158 ++++++++++++++++++ routes/web.php | 10 +- 11 files changed, 428 insertions(+), 90 deletions(-) create mode 100644 app/Import/Routine/RoutineInterface.php create mode 100644 resources/views/import/status.twig diff --git a/app/Http/Controllers/Import/FileController.php b/app/Http/Controllers/Import/FileController.php index 5e13cc9ed3..d6ad685aa2 100644 --- a/app/Http/Controllers/Import/FileController.php +++ b/app/Http/Controllers/Import/FileController.php @@ -175,50 +175,7 @@ class FileController extends Controller return redirect(route('import.file.configure', [$job->key])); } - /** - * Show status of import job in JSON. - * - * @param ImportJob $job - * - * @return \Illuminate\Http\JsonResponse - */ - public function json(ImportJob $job) - { - $result = [ - 'started' => false, - 'finished' => false, - 'running' => false, - 'errors' => array_values($job->extended_status['errors']), - 'percentage' => 0, - 'show_percentage' => false, - 'steps' => $job->extended_status['steps'], - 'done' => $job->extended_status['done'], - 'statusText' => trans('firefly.import_status_job_' . $job->status), - 'status' => $job->status, - 'finishedText' => '', - ]; - if (0 !== $job->extended_status['steps']) { - $result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0); - $result['show_percentage'] = true; - } - - if ('finished' === $job->status) { - $tagId = $job->extended_status['tag']; - /** @var TagRepositoryInterface $repository */ - $repository = app(TagRepositoryInterface::class); - $tag = $repository->find($tagId); - $result['finished'] = true; - $result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]); - } - - if ('running' === $job->status) { - $result['started'] = true; - $result['running'] = true; - } - - return Response::json($result); - } /** * Step 4. Save the configuration. @@ -269,24 +226,7 @@ class FileController extends Controller return Response::json(['run' => 'ok']); } - throw new FireflyException('Job did not complete succesfully.'); - } - - /** - * @param ImportJob $job - * - * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View - */ - public function status(ImportJob $job) - { - $statuses = ['configured', 'running', 'finished']; - if (!in_array($job->status, $statuses)) { - return redirect(route('import.file.configure', [$job->key])); - } - $subTitle = trans('firefly.import_status_sub_title'); - $subTitleIcon = 'fa-star'; - - return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); + throw new FireflyException('Job did not complete successfully.'); } /** diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index 866f0b1557..b644e561e3 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -6,7 +6,12 @@ namespace FireflyIII\Http\Controllers\Import; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Import\Routine\ImportRoutine; +use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Http\Response as LaravelResponse; +use Log; +use Response; use View; /** @@ -56,6 +61,42 @@ class IndexController extends Controller } + /** + * Generate a JSON file of the job's configuration and send it to the user. + * + * @param ImportJob $job + * + * @return LaravelResponse + */ + public function download(ImportJob $job) + { + Log::debug('Now in download()', ['job' => $job->key]); + $config = $job->configuration; + + // This is CSV import specific: + $config['column-roles-complete'] = false; + $config['column-mapping-complete'] = false; + $config['initial-config-complete'] = false; + $config['has-file-upload'] = false; + $config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter']; + + $result = json_encode($config, JSON_PRETTY_PRINT); + $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\')); + + /** @var LaravelResponse $response */ + $response = response($result, 200); + $response->header('Content-disposition', 'attachment; filename=' . $name) + ->header('Content-Type', 'application/json') + ->header('Content-Description', 'File Transfer') + ->header('Connection', 'Keep-Alive') + ->header('Expires', '0') + ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') + ->header('Pragma', 'public') + ->header('Content-Length', strlen($result)); + + return $response; + } + /** * General import index. * @@ -70,4 +111,34 @@ class IndexController extends Controller return view('import.index', compact('subTitle', 'subTitleIcon', 'routines')); } + /** + * @param ImportJob $job + * + * @return \Illuminate\Http\JsonResponse + * @throws FireflyException + */ + public function start(ImportJob $job) + { + + $type = $job->file_type; + $key = sprintf('import.routine.%s', $type); + $className = config($key); + if (null === $className || !class_exists($className)) { + throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore + } + var_dump($className); + exit; + + /** @var ImportRoutine $routine */ + $routine = app(ImportRoutine::class); + $routine->setJob($job); + $result = $routine->run(); + + if ($result) { + return Response::json(['run' => 'ok']); + } + + throw new FireflyException('Job did not complete successfully. Please review the log files.'); + } + } \ No newline at end of file diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php index f4f11d9fda..d04d4edb00 100644 --- a/app/Http/Controllers/Import/StatusController.php +++ b/app/Http/Controllers/Import/StatusController.php @@ -5,6 +5,9 @@ namespace FireflyIII\Http\Controllers\Import; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Response; /** * Class StatusController @@ -12,4 +15,83 @@ use FireflyIII\Http\Controllers\Controller; class StatusController extends Controller { + /** + * + */ + public function __construct() + { + parent::__construct(); + + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-archive'); + app('view')->share('title', trans('firefly.import_index_title')); + + return $next($request); + } + ); + } + /** + * @param ImportJob $job + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + */ + public function index(ImportJob $job) + { + $statuses = ['configured', 'running', 'finished', 'errored']; + if (!in_array($job->status, $statuses)) { + return redirect(route('import.file.configure', [$job->key])); + } + $subTitle = trans('firefly.import_status_sub_title'); + $subTitleIcon = 'fa-star'; + + return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); + } + + /** + * Show status of import job in JSON. + * + * @param ImportJob $job + * + * @return \Illuminate\Http\JsonResponse + */ + public function json(ImportJob $job) + { + $result = [ + 'started' => false, + 'finished' => false, + 'running' => false, + 'errors' => array_values($job->extended_status['errors']), + 'percentage' => 0, + 'show_percentage' => false, + 'steps' => $job->extended_status['steps'], + 'done' => $job->extended_status['done'], + 'statusText' => trans('firefly.import_status_job_' . $job->status), + 'status' => $job->status, + 'finishedText' => '', + ]; + + if (0 !== $job->extended_status['steps']) { + $result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0); + $result['show_percentage'] = true; + } + + if ('finished' === $job->status) { + $tagId = $job->extended_status['tag']; + /** @var TagRepositoryInterface $repository */ + $repository = app(TagRepositoryInterface::class); + $tag = $repository->find($tagId); + $result['finished'] = true; + $result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]); + } + + if ('running' === $job->status) { + $result['started'] = true; + $result['running'] = true; + } + // TODO cannot handle 'errored' + + return Response::json($result); + } + } \ No newline at end of file diff --git a/app/Import/Routine/FileRoutine.php b/app/Import/Routine/FileRoutine.php index 74dc52150c..eb5ea1cd9c 100644 --- a/app/Import/Routine/FileRoutine.php +++ b/app/Import/Routine/FileRoutine.php @@ -1,6 +1,6 @@ . + */ +declare(strict_types=1); + +namespace FireflyIII\Import\Routine; + +use Carbon\Carbon; +use DB; +use FireflyIII\Import\FileProcessor\FileProcessorInterface; +use FireflyIII\Import\Storage\ImportStorage; +use FireflyIII\Models\ImportJob; +use FireflyIII\Models\Tag; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Support\Collection; +use Log; + +interface RoutineInterface +{ + /** + * @return bool + */ + public function run(): bool; + + /** + * @param ImportJob $job + * + * @return mixed + */ + public function setJob(ImportJob $job); +} diff --git a/config/import.php b/config/import.php index 4181666ea5..877eeb4711 100644 --- a/config/import.php +++ b/config/import.php @@ -2,25 +2,32 @@ declare(strict_types=1); return [ - 'enabled' => [ + 'enabled' => [ 'file' => true, 'bunq' => true, 'spectre' => true, 'plaid' => true, ], - 'prerequisites' => [ + 'prerequisites' => [ 'file' => 'FireflyIII\Import\Prerequisites\FilePrerequisites', 'bunq' => 'FireflyIII\Import\Prerequisites\BunqPrerequisites', 'spectre' => 'FireflyIII\Import\Prerequisites\SpectrePrerequisites', 'plaid' => 'FireflyIII\Import\Prerequisites\PlaidPrerequisites', ], - 'configuration' => [ + 'configuration' => [ 'file' => 'FireflyIII\Import\Configuration\FileConfigurator', 'bunq' => 'FireflyIII\Import\Configuration\BunqConfigurator', 'spectre' => 'FireflyIII\Import\Configuration\SpectreConfigurator', 'plaid' => 'FireflyIII\Import\Configuration\PlaidConfigurator', ], + 'routine' => [ + 'file' => 'FireflyIII\Import\Routine\FileRoutine', + 'bunq' => 'FireflyIII\Import\Routine\BunqRoutine', + 'spectre' => 'FireflyIII\Import\Routine\SpectreRoutine', + 'plaid' => 'FireflyIII\Import\Routine\PlaidRoutine', + ], + 'options' => [ 'file' => [ 'import_formats' => ['csv'], // mt940 diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index 02c3bbd073..d864c2cd14 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -18,7 +18,7 @@ * along with Firefly III. If not, see . */ -/** global: jobImportUrl, langImportSingleError, langImportMultiError, jobStartUrl, langImportTimeOutError, langImportFinished, langImportFatalError */ +/** global: langImportSingleError, langImportMultiError, jobStartUrl, langImportTimeOutError, langImportFinished, langImportFatalError */ var timeOutId; var startInterval = 1000; @@ -34,9 +34,10 @@ var knownErrors = 0; $(function () { "use strict"; - timeOutId = setTimeout(checkImportStatus, startInterval); + timeOutId = setTimeout(checkJobStatus, startInterval); + $('.start-job').click(startJob); - if(autoStart) { + if (job.configuration['auto-start']) { startJob(); } }); @@ -44,14 +45,14 @@ $(function () { /** * Downloads some JSON and responds to its content to see what the status is of the current import. */ -function checkImportStatus() { - $.getJSON(jobImportUrl).done(reportOnJobImport).fail(failedJobImport); +function checkJobStatus() { + $.getJSON(jobStatusUri).done(reportOnJobStatus).fail(reportFailedJob); } /** * This method is called when the JSON query returns an error. If possible, this error is relayed to the user. */ -function failedJobImport(jqxhr, textStatus, error) { +function reportFailedJob(jqxhr, textStatus, error) { // hide all possible boxes: $('.statusbox').hide(); @@ -70,13 +71,15 @@ function failedJobImport(jqxhr, textStatus, error) { * * @param data */ -function reportOnJobImport(data) { +function reportOnJobStatus(data) { switch (data.status) { case "configured": // job is ready. Do not check again, just show the start-box. Hide the rest. - $('.statusbox').hide(); - $('.status_configured').show(); + if (!job.configuration['auto-start']) { + $('.statusbox').hide(); + $('.status_configured').show(); + } break; case "running": // job is running! Show the running box: @@ -97,7 +100,7 @@ function reportOnJobImport(data) { showStalledBox(); } else { // check again in 500ms - timeOutId = setTimeout(checkImportStatus, interval); + timeOutId = setTimeout(checkJobStatus, interval); } break; case "finished": @@ -106,6 +109,19 @@ function reportOnJobImport(data) { // show text: $('#import-status-more-info').html(data.finishedText); break; + case "errored": + // TODO this view is not yet used. + // hide all possible boxes: + $('.statusbox').hide(); + + // fill in some details: + var errorMessage = data.error_message; + + $('.fatal_error_txt').text(errorMessage); + + // show the fatal error box: + $('.fatal_error').show(); + break; default: break; @@ -147,13 +163,16 @@ function jobIsStalled(data) { function startJob() { // disable the button, add loading thing. $('.start-job').prop('disabled', true).text('...'); - $.post(jobStartUrl).fail(reportOnSubmitError); + $.post(jobStartUri).fail(reportOnSubmitError); // check status, every 500 ms. - timeOutId = setTimeout(checkImportStatus, startInterval); + timeOutId = setTimeout(checkJobStatus, startInterval); } -function reportOnSubmitError() { +/** + * When the start button fails (returns error code) this function reports. It assumes a time out. + */ +function reportOnSubmitError(jqxhr, textStatus, error) { // stop the refresh thing clearTimeout(timeOutId); @@ -161,7 +180,7 @@ function reportOnSubmitError() { $('.statusbox').hide(); // fill in some details: - var errorMessage = "Time out while waiting for job to finish."; + var errorMessage = "Submitting the job returned an error: " + textStatus + ' ' + error; $('.fatal_error_txt').text(errorMessage); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 9c4d3e3a49..ef97fb4956 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1085,10 +1085,6 @@ return [ // import status page: 'import_status_bread_crumb' => 'Import status', 'import_status_sub_title' => 'Import status', - 'import_status_wait_title' => 'Please hold...', - 'import_status_wait_text' => 'This box will disappear in a moment.', - 'import_status_ready_title' => 'Import is ready to start', - 'import_status_ready_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'import_status_ready_config' => 'Download configuration', 'import_status_ready_start' => 'Start the import', 'import_status_ready_share' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', @@ -1097,9 +1093,6 @@ return [ 'import_status_errors_title' => 'Errors during the import', 'import_status_errors_single' => 'An error has occured during the import. It does not appear to be fatal.', 'import_status_errors_multi' => 'Some errors occured during the import. These do not appear to be fatal.', - 'import_status_fatal_title' => 'A fatal error occurred', - 'import_status_fatal_text' => 'A fatal error occurred, which the import-routine cannot recover from. Please see the explanation in red below.', - 'import_status_fatal_more' => 'If the error is a time-out, the import will have stopped half-way. For some server configurations, it is merely the server that stopped while the import keeps running in the background. To verify this, check out the log files. If the problem persists, consider importing over the command line instead.', 'import_status_finished_title' => 'Import routine finished', 'import_status_finished_text' => 'The import routine has imported your file.', 'import_status_finished_job' => 'The transactions imported can be found in tag :tag.', diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php index d1135a4198..baf69809a6 100644 --- a/resources/lang/en_US/import.php +++ b/resources/lang/en_US/import.php @@ -17,6 +17,15 @@ return [ // // // import configuration routine: + // status of import: + 'status_wait_title' => 'Please hold...', + 'status_wait_text' => 'This box will disappear in a moment.', + 'status_fatal_title' => 'A fatal error occurred', + 'status_fatal_text' => 'A fatal error occurred, which the import-routine cannot recover from. Please see the explanation in red below.', + 'status_fatal_more' => 'If the error is a time-out, the import will have stopped half-way. For some server configurations, it is merely the server that stopped while the import keeps running in the background. To verify this, check out the log files. If the problem persists, consider importing over the command line instead.', + 'status_ready_title' => 'Import is ready to start', + 'status_ready_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', + 'status_ready_noconfig_text' => 'The import is ready to start. All the configuration you needed to do has been done. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', // file: upload something: 'file_upload_title' => 'Import setup (1/4) - Upload your file', @@ -61,12 +70,12 @@ return [ 'csv_roles_warning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', - // map data + // file: map data 'file_map_title' => 'Import setup (4/4) - Connect import data to Firefly III data', 'file_map_text' => 'In the following tables, the left value shows you information found in your uploaded file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', 'file_map_field_value' => 'Field value', 'file_map_field_mapped_to' => 'Mapped to', - 'map_do_not_map' => '(do not map)', + 'map_do_not_map' => '(do not map)', 'file_map_submit' => 'Start the import', // map things. diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig new file mode 100644 index 0000000000..2e69c94506 --- /dev/null +++ b/resources/views/import/status.twig @@ -0,0 +1,158 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists }} +{% endblock %} +{% block content %} + + {# Initial display. Will refresh (and disappear almost immediately. #} +
    +
    +
    +
    +

    {{ trans('import.status_wait_title') }}

    +
    +
    +

    + {{ trans('import.status_wait_text') }} +

    +
    +
    +
    +
    + + {# Fatal error display. Will be shown (duh) when something goes horribly wrong. #} + + + {# Box for when the job is ready to start #} + + + {# Box for when the job is running! #} + + + {# displays the finished status of the import #} + + + {# box to show error information. #} + + +{% endblock %} +{% block scripts %} + + +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/routes/web.php b/routes/web.php index 42ca3e5acc..3daf730bc5 100755 --- a/routes/web.php +++ b/routes/web.php @@ -442,6 +442,14 @@ Route::group( // get status of any job: Route::get('status/{importJob}', ['uses' => 'Import\StatusController@index', 'as' => 'status']); + Route::get('json/{importJob}', ['uses' => 'Import\StatusController@json', 'as' => 'status.json']); + + // start a job + Route::any('start/{importJob}', ['uses' => 'Import\IndexController@start', 'as' => 'start']); + + // download config + Route::get('download/{importJob}', ['uses' => 'Import\IndexController@download', 'as' => 'download']); + // file import // Route::get('file', ['uses' => 'Import\FileController@index', 'as' => 'file.index']); @@ -449,7 +457,7 @@ Route::group( // Route::get('file/download/{importJob}', ['uses' => 'Import\FileController@download', 'as' => 'file.download']); // Route::get('file/status/{importJob}', ['uses' => 'Import\FileController@status', 'as' => 'file.status']); - // Route::get('file/json/{importJob}', ['uses' => 'Import\FileController@json', 'as' => 'file.json']); + // // Route::any('file/start/{importJob}', ['uses' => 'Import\FileController@start', 'as' => 'file.start']); // Route:: get('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@configure', 'as' => 'bank.configure']); // Route::post('bank/{bank}/configure/{importJob}', ['uses' => 'Import\BankController@postConfigure', 'as' => 'bank.configure.post']); From 1d58d519a0e8bca9385f943d3375b1197c0465dc Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Dec 2017 19:48:31 +0100 Subject: [PATCH 056/142] Unrelated code cleanup. --- .../Account/ReconcileController.php | 16 +++++-- app/Http/Controllers/Admin/LinkController.php | 2 +- app/Http/Controllers/Admin/UserController.php | 2 +- app/Http/Controllers/Auth/LoginController.php | 2 +- app/Http/Controllers/BillController.php | 2 +- app/Http/Controllers/BudgetController.php | 2 +- app/Http/Controllers/CategoryController.php | 6 +-- .../Controllers/Import/BankController.php | 4 -- .../Import/ConfigurationController.php | 2 +- .../Controllers/Import/FileController.php | 2 - .../Controllers/Import/StatusController.php | 2 + app/Http/Controllers/ProfileController.php | 2 +- .../Controllers/Report/ExpenseController.php | 1 - app/Http/Controllers/ReportController.php | 5 ++- .../Transaction/LinkController.php | 1 - app/Http/Middleware/StartFireflySession.php | 1 - app/Http/Requests/PiggyBankFormRequest.php | 2 - app/Http/Requests/ReportFormRequest.php | 44 +++++++++---------- app/Http/breadcrumbs.php | 4 +- 19 files changed, 52 insertions(+), 50 deletions(-) diff --git a/app/Http/Controllers/Account/ReconcileController.php b/app/Http/Controllers/Account/ReconcileController.php index 1125a79472..d508927317 100644 --- a/app/Http/Controllers/Account/ReconcileController.php +++ b/app/Http/Controllers/Account/ReconcileController.php @@ -41,7 +41,6 @@ use Navigation; use Preferences; use Response; use Session; -use View; /** * Class ReconcileController. @@ -147,9 +146,14 @@ class ReconcileController extends Controller } $difference = bcadd(bcadd(bcsub($startBalance, $endBalance), $clearedAmount), $amount); $diffCompare = bccomp($difference, '0'); - $return = [ + $return = [ 'post_uri' => $route, - 'html' => view('accounts.reconcile.overview', compact('account', 'start', 'diffCompare', 'difference', 'end', 'clearedIds', 'transactionIds', 'clearedAmount', 'startBalance', 'endBalance', 'amount', 'route', 'countCleared'))->render(), + 'html' => view( + 'accounts.reconcile.overview', compact( + 'account', 'start', 'diffCompare', 'difference', 'end', 'clearedIds', 'transactionIds', 'clearedAmount', 'startBalance', 'endBalance', 'amount', + 'route', 'countCleared' + ) + )->render(), ]; return Response::json($return); @@ -204,7 +208,11 @@ class ReconcileController extends Controller $overviewUri = route('accounts.reconcile.overview', [$account->id, '%start%', '%end%']); $indexUri = route('accounts.reconcile', [$account->id, '%start%', '%end%']); - return view('accounts.reconcile.index', compact('account', 'currency', 'subTitleIcon', 'start', 'end', 'subTitle', 'startBalance', 'endBalance', 'transactionsUri', 'overviewUri', 'indexUri')); + return view( + 'accounts.reconcile.index', compact( + 'account', 'currency', 'subTitleIcon', 'start', 'end', 'subTitle', 'startBalance', 'endBalance', 'transactionsUri', 'overviewUri', 'indexUri' + ) + ); } /** diff --git a/app/Http/Controllers/Admin/LinkController.php b/app/Http/Controllers/Admin/LinkController.php index ada71fbcda..38b14bae8d 100644 --- a/app/Http/Controllers/Admin/LinkController.php +++ b/app/Http/Controllers/Admin/LinkController.php @@ -51,7 +51,7 @@ class LinkController extends Controller return $next($request); } ); - $this->middleware(IsLimitedUser::class)->except(['index','show']); + $this->middleware(IsLimitedUser::class)->except(['index', 'show']); } /** diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 2ee4cb7684..77e08b0a47 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -52,7 +52,7 @@ class UserController extends Controller return $next($request); } ); - $this->middleware(IsLimitedUser::class)->except(['index','show']); + $this->middleware(IsLimitedUser::class)->except(['index', 'show']); } /** diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 57f16dfef1..231e216233 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -118,7 +118,7 @@ class LoginController extends Controller /** * Show the application's login form. * - * @param Request $request + * @param Request $request * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 389c0b689c..bffa80b359 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -229,7 +229,7 @@ class BillController extends Controller public function show(Request $request, BillRepositoryInterface $repository, Bill $bill) { /** @var Carbon $date */ - $date = session('start'); + $date = session('start'); /** @var Carbon $end */ $end = session('end'); $year = $date->year; diff --git a/app/Http/Controllers/BudgetController.php b/app/Http/Controllers/BudgetController.php index 848d8df320..c1a646f55c 100644 --- a/app/Http/Controllers/BudgetController.php +++ b/app/Http/Controllers/BudgetController.php @@ -85,7 +85,7 @@ class BudgetController extends Controller $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); $end = Carbon::createFromFormat('Y-m-d', $request->get('end')); $budgetLimit = $this->repository->updateLimitAmount($budget, $start, $end, $amount); - if (bccomp($amount,'0') === 0) { + if (bccomp($amount, '0') === 0) { $budgetLimit = null; } diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index b447f9c614..43259f84fc 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -227,7 +227,7 @@ class CategoryController extends Controller $start = null; $end = null; $periods = new Collection; - $path = route('categories.show', [$category->id]); + $path = route('categories.show', [$category->id]); // prep for "all" view. if ('all' === $moment) { @@ -236,7 +236,7 @@ class CategoryController extends Controller /** @var Carbon $start */ $start = null === $first ? new Carbon : $first; $end = new Carbon; - $path = route('categories.show', [$category->id,'all']); + $path = route('categories.show', [$category->id, 'all']); } // prep for "specific date" view. @@ -249,7 +249,7 @@ class CategoryController extends Controller 'start' => $start->formatLocalized($this->monthAndDayFormat), 'end' => $end->formatLocalized($this->monthAndDayFormat),] ); $periods = $this->getPeriodOverview($category); - $path = route('categories.show', [$category->id, $moment]); + $path = route('categories.show', [$category->id, $moment]); } // prep for current period diff --git a/app/Http/Controllers/Import/BankController.php b/app/Http/Controllers/Import/BankController.php index 1b9358eed9..c911ffc2e8 100644 --- a/app/Http/Controllers/Import/BankController.php +++ b/app/Http/Controllers/Import/BankController.php @@ -26,9 +26,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Support\Import\Prerequisites\PrerequisitesInterface; -use Illuminate\Http\Request; -use Log; -use Session; class BankController extends Controller { @@ -61,5 +58,4 @@ class BankController extends Controller } - } diff --git a/app/Http/Controllers/Import/ConfigurationController.php b/app/Http/Controllers/Import/ConfigurationController.php index 428ffc8c9e..c51b2e432d 100644 --- a/app/Http/Controllers/Import/ConfigurationController.php +++ b/app/Http/Controllers/Import/ConfigurationController.php @@ -82,7 +82,7 @@ class ConfigurationController extends Controller if ($configurator->isJobConfigured()) { return redirect(route('import.status', [$job->key])); } - $data = $request->all(); + $data = $request->all(); $configurator->configureJob($data); // get possible warning from configurator: diff --git a/app/Http/Controllers/Import/FileController.php b/app/Http/Controllers/Import/FileController.php index d6ad685aa2..6eb81d17d8 100644 --- a/app/Http/Controllers/Import/FileController.php +++ b/app/Http/Controllers/Import/FileController.php @@ -29,7 +29,6 @@ use FireflyIII\Import\Configurator\ConfiguratorInterface; use FireflyIII\Import\Routine\ImportRoutine; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; use Illuminate\Http\Request; use Illuminate\Http\Response as LaravelResponse; use Log; @@ -176,7 +175,6 @@ class FileController extends Controller } - /** * Step 4. Save the configuration. * diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php index d04d4edb00..eea28fea64 100644 --- a/app/Http/Controllers/Import/StatusController.php +++ b/app/Http/Controllers/Import/StatusController.php @@ -31,6 +31,7 @@ class StatusController extends Controller } ); } + /** * @param ImportJob $job * @@ -89,6 +90,7 @@ class StatusController extends Controller $result['started'] = true; $result['running'] = true; } + // TODO cannot handle 'errored' return Response::json($result); diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 463a3cbae0..a1dbfc93a8 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -62,7 +62,7 @@ class ProfileController extends Controller return $next($request); } ); - $this->middleware(IsLimitedUser::class)->except(['confirmEmailChange','index', 'undoEmailChange']); + $this->middleware(IsLimitedUser::class)->except(['confirmEmailChange', 'index', 'undoEmailChange']); } /** diff --git a/app/Http/Controllers/Report/ExpenseController.php b/app/Http/Controllers/Report/ExpenseController.php index 26566c82ac..2f39d2330f 100644 --- a/app/Http/Controllers/Report/ExpenseController.php +++ b/app/Http/Controllers/Report/ExpenseController.php @@ -246,7 +246,6 @@ class ExpenseController extends Controller } - public function topIncome(Collection $accounts, Collection $expense, Carbon $start, Carbon $end) { // Properties for cache: diff --git a/app/Http/Controllers/ReportController.php b/app/Http/Controllers/ReportController.php index cc1b2bb6fb..d7cf237f24 100644 --- a/app/Http/Controllers/ReportController.php +++ b/app/Http/Controllers/ReportController.php @@ -88,8 +88,9 @@ class ReportController extends Controller View::share( 'subTitle', trans( - 'firefly.report_default', ['start' => $start->formatLocalized($this->monthFormat), 'end' => $end->formatLocalized($this->monthFormat),] - ) + 'firefly.report_default', + ['start' => $start->formatLocalized($this->monthFormat), 'end' => $end->formatLocalized($this->monthFormat),] + ) ); $generator = ReportGeneratorFactory::reportGenerator('Account', $start, $end); diff --git a/app/Http/Controllers/Transaction/LinkController.php b/app/Http/Controllers/Transaction/LinkController.php index cb4bfe5e8e..9a2d316fbb 100644 --- a/app/Http/Controllers/Transaction/LinkController.php +++ b/app/Http/Controllers/Transaction/LinkController.php @@ -32,7 +32,6 @@ use Log; use Preferences; use Session; use URL; -use View; /** * Class LinkController. diff --git a/app/Http/Middleware/StartFireflySession.php b/app/Http/Middleware/StartFireflySession.php index 1d9ee97647..e8988208b9 100644 --- a/app/Http/Middleware/StartFireflySession.php +++ b/app/Http/Middleware/StartFireflySession.php @@ -22,7 +22,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Middleware; -use Closure; use Illuminate\Http\Request; use Illuminate\Session\Middleware\StartSession; diff --git a/app/Http/Requests/PiggyBankFormRequest.php b/app/Http/Requests/PiggyBankFormRequest.php index 38c209887a..bf1c068918 100644 --- a/app/Http/Requests/PiggyBankFormRequest.php +++ b/app/Http/Requests/PiggyBankFormRequest.php @@ -22,8 +22,6 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; -use Carbon\Carbon; - /** * Class PiggyBankFormRequest. */ diff --git a/app/Http/Requests/ReportFormRequest.php b/app/Http/Requests/ReportFormRequest.php index 187cf09f6d..41a0400d77 100644 --- a/app/Http/Requests/ReportFormRequest.php +++ b/app/Http/Requests/ReportFormRequest.php @@ -45,28 +45,6 @@ class ReportFormRequest extends Request return auth()->check(); } - /** - * @return Collection - */ - public function getExpenseList(): Collection - { - // fixed - /** @var AccountRepositoryInterface $repository */ - $repository = app(AccountRepositoryInterface::class); - $set = $this->get('exp_rev'); - $collection = new Collection; - if (is_array($set)) { - foreach ($set as $accountId) { - $account = $repository->find(intval($accountId)); - if (null !== $account->id) { - $collection->push($account); - } - } - } - - return $collection; - } - /** * @return Collection */ @@ -152,6 +130,28 @@ class ReportFormRequest extends Request return $date; } + /** + * @return Collection + */ + public function getExpenseList(): Collection + { + // fixed + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $set = $this->get('exp_rev'); + $collection = new Collection; + if (is_array($set)) { + foreach ($set as $accountId) { + $account = $repository->find(intval($accountId)); + if (null !== $account->id) { + $collection->push($account); + } + } + } + + return $collection; + } + /** * @return Carbon * diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index 0fa4484214..ae1b3bb1a4 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -882,7 +882,9 @@ Breadcrumbs::register( 'accounts.reconcile.edit', function (BreadCrumbGenerator $breadcrumbs, TransactionJournal $journal) { $breadcrumbs->parent('transactions.show', $journal); - $breadcrumbs->push(trans('breadcrumbs.edit_reconciliation', ['description' => $journal->description]), route('accounts.reconcile.edit', [$journal->id])); + $breadcrumbs->push( + trans('breadcrumbs.edit_reconciliation', ['description' => $journal->description]), route('accounts.reconcile.edit', [$journal->id]) + ); } ); From 22535c0e4356b6393f4d7564193bc6088f5df64f Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Dec 2017 20:21:05 +0100 Subject: [PATCH 057/142] Last steps for import, in user interface. --- .../Controllers/Import/IndexController.php | 7 +- .../Controllers/Import/StatusController.php | 6 +- app/Http/breadcrumbs.php | 4 +- app/Import/FileProcessor/CsvProcessor.php | 2 +- app/Import/Routine/FileRoutine.php | 10 +- config/firefly.php | 17 -- config/import.php | 3 + resources/lang/en_US/firefly.php | 18 -- resources/lang/en_US/import.php | 16 ++ resources/views/import/_old/finished.twig | 38 ---- resources/views/import/_old/index.twig | 53 ------ resources/views/import/_old/status.twig | 165 ------------------ resources/views/import/status.twig | 20 +-- 13 files changed, 44 insertions(+), 315 deletions(-) delete mode 100644 resources/views/import/_old/finished.twig delete mode 100644 resources/views/import/_old/index.twig delete mode 100644 resources/views/import/_old/status.twig diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index b644e561e3..1b6911a458 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -7,6 +7,7 @@ namespace FireflyIII\Http\Controllers\Import; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Import\Routine\ImportRoutine; +use FireflyIII\Import\Routine\RoutineInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use Illuminate\Http\Response as LaravelResponse; @@ -126,11 +127,9 @@ class IndexController extends Controller if (null === $className || !class_exists($className)) { throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore } - var_dump($className); - exit; - /** @var ImportRoutine $routine */ - $routine = app(ImportRoutine::class); + /** @var RoutineInterface $routine */ + $routine = app($className); $routine->setJob($job); $result = $routine->run(); diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php index eea28fea64..297b085e15 100644 --- a/app/Http/Controllers/Import/StatusController.php +++ b/app/Http/Controllers/Import/StatusController.php @@ -43,7 +43,7 @@ class StatusController extends Controller if (!in_array($job->status, $statuses)) { return redirect(route('import.file.configure', [$job->key])); } - $subTitle = trans('firefly.import_status_sub_title'); + $subTitle = trans('import.status_sub_title'); $subTitleIcon = 'fa-star'; return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); @@ -67,7 +67,7 @@ class StatusController extends Controller 'show_percentage' => false, 'steps' => $job->extended_status['steps'], 'done' => $job->extended_status['done'], - 'statusText' => trans('firefly.import_status_job_' . $job->status), + 'statusText' => trans('import.status_job_' . $job->status), 'status' => $job->status, 'finishedText' => '', ]; @@ -83,7 +83,7 @@ class StatusController extends Controller $repository = app(TagRepositoryInterface::class); $tag = $repository->find($tagId); $result['finished'] = true; - $result['finishedText'] = trans('firefly.import_status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]); + $result['finishedText'] = trans('import.status_finished_job', ['link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]); } if ('running' === $job->status) { diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index ae1b3bb1a4..78ef0b1e62 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -552,14 +552,14 @@ Breadcrumbs::register( 'import.configure', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) { $breadcrumbs->parent('import.index'); - $breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key])); + $breadcrumbs->push(trans('import.config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key])); } ); Breadcrumbs::register( 'import.status', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) { $breadcrumbs->parent('import.index'); - $breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key])); + $breadcrumbs->push(trans('import.status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key])); } ); diff --git a/app/Import/FileProcessor/CsvProcessor.php b/app/Import/FileProcessor/CsvProcessor.php index daa985f973..cdeb54805c 100644 --- a/app/Import/FileProcessor/CsvProcessor.php +++ b/app/Import/FileProcessor/CsvProcessor.php @@ -256,7 +256,7 @@ class CsvProcessor implements FileProcessorInterface * @var string $value */ foreach ($row as $rowIndex => $value) { - $value = trim($value); + $value = trim(strval($value)); if (strlen($value) > 0) { $annotated = $this->annotateValue($rowIndex, $value); Log::debug('Annotated value', $annotated); diff --git a/app/Import/Routine/FileRoutine.php b/app/Import/Routine/FileRoutine.php index eb5ea1cd9c..4d0ce8006c 100644 --- a/app/Import/Routine/FileRoutine.php +++ b/app/Import/Routine/FileRoutine.php @@ -109,9 +109,11 @@ class FileRoutine implements RoutineInterface */ protected function getImportObjects(): Collection { - $objects = new Collection; - $type = $this->job->file_type; - $class = config(sprintf('firefly.import_processors.%s', $type)); + $objects = new Collection; + $config = $this->job->configuration; + $fileType = $config['file-type']; + // will only respond to "file" + $class = config(sprintf('import.options.file.processors.%s', $fileType)); /** @var FileProcessorInterface $processor */ $processor = app($class); $processor->setJob($this->job); @@ -146,7 +148,7 @@ class FileRoutine implements RoutineInterface $repository = app(TagRepositoryInterface::class); $repository->setUser($this->job->user); $data = [ - 'tag' => trans('firefly.import_with_key', ['key' => $this->job->key]), + 'tag' => trans('import.import_with_key', ['key' => $this->job->key]), 'date' => new Carbon, 'description' => null, 'latitude' => null, diff --git a/config/firefly.php b/config/firefly.php index e389c8bc5d..265c3a5d32 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -40,23 +40,6 @@ return [ 'export_formats' => [ 'csv' => 'FireflyIII\Export\Exporter\CsvExporter', ], - 'import_formats' => [ - 'csv' => 'FireflyIII\Import\Configurator\CsvConfigurator', - 'spectre' => '', - ], - 'import_processors' => [ - 'csv' => 'FireflyIII\Import\FileProcessor\CsvProcessor', - ], - 'import_info' => [ - 'bunq' => 'FireflyIII\Support\Import\Information\BunqInformation', - 'spectre' => 'FireflyIII\Support\Import\Information\SpectreInformation', - 'plaid' => 'FireflyIII\Support\Import\Information\PlaidInformation', - ], - 'import_transactions' => [ - 'bunq' => 'FireflyIII\Support\Import\Transactions\BunqTransactions', - 'spectre' => 'FireflyIII\Support\Import\Transactions\SpectreTransactions', - 'plaid' => 'FireflyIII\Support\Import\Transactions\PlaidTransactions', - ], 'bunq' => [ 'server' => 'https://sandbox.public.api.bunq.com', ], diff --git a/config/import.php b/config/import.php index 877eeb4711..df1fcae827 100644 --- a/config/import.php +++ b/config/import.php @@ -32,6 +32,9 @@ return [ 'file' => [ 'import_formats' => ['csv'], // mt940 'default_import_format' => 'csv', + 'processors' => [ + 'csv' => 'FireflyIII\Import\FileProcessor\CsvProcessor', + ], ], ], 'default_config' => [ diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ef97fb4956..aba14a68b2 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1074,7 +1074,6 @@ return [ 'import_from_bunq' => 'Import from bunq', 'import_using_spectre' => 'Import using Spectre', 'import_using_plaid' => 'Import using Plaid', - 'import_config_sub_title' => 'Set up your import', 'import_config_bread_crumb' => 'Set up your import', // import index page: @@ -1082,23 +1081,6 @@ return [ 'import_index_sub_title' => 'Index', 'import_general_index_intro' => 'Welcome to Firefly\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.', - // import status page: - 'import_status_bread_crumb' => 'Import status', - 'import_status_sub_title' => 'Import status', - 'import_status_ready_config' => 'Download configuration', - 'import_status_ready_start' => 'Start the import', - 'import_status_ready_share' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', - 'import_status_running_title' => 'The import is running', - 'import_status_running_placeholder' => 'Please hold for an update...', - 'import_status_errors_title' => 'Errors during the import', - 'import_status_errors_single' => 'An error has occured during the import. It does not appear to be fatal.', - 'import_status_errors_multi' => 'Some errors occured during the import. These do not appear to be fatal.', - 'import_status_finished_title' => 'Import routine finished', - 'import_status_finished_text' => 'The import routine has imported your file.', - 'import_status_finished_job' => 'The transactions imported can be found in tag :tag.', - 'import_status_job_running' => 'The import is running...', - 'import_with_key' => 'Import with key \':key\'', - // sandstorm.io errors and messages: 'sandstorm_not_available' => 'This function is not available when you are using Firefly III within a Sandstorm.io environment.', diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php index baf69809a6..858daff129 100644 --- a/resources/lang/en_US/import.php +++ b/resources/lang/en_US/import.php @@ -26,6 +26,22 @@ return [ 'status_ready_title' => 'Import is ready to start', 'status_ready_text' => 'The import is ready to start. All the configuration you needed to do has been done. Please download the configuration file. It will help you with the import should it not go as planned. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', 'status_ready_noconfig_text' => 'The import is ready to start. All the configuration you needed to do has been done. To actually run the import, you can either execute the following command in your console, or run the web-based import. Depending on your configuration, the console import will give you more feedback.', + 'status_ready_config' => 'Download configuration', + 'status_ready_start' => 'Start the import', + 'status_ready_share' => 'Please consider downloading your configuration and sharing it at the import configuration center. This will allow other users of Firefly III to import their files more easily.', + 'status_job_running' => 'The import is running.. Please wait..', + 'status_running_title' => 'The import is running', + 'status_running_placeholder' => 'Please hold for an update...', + 'status_finished_title' => 'Import routine finished', + 'status_finished_text' => 'The import routine has imported your data.', + 'status_errors_title' => 'Errors during the import', + 'status_errors_single' => 'An error has occurred during the import. It does not appear to be fatal.', + 'status_errors_multi' => 'Some errors occurred during the import. These do not appear to be fatal.', + 'status_bread_crumb' => 'Import status', + 'status_sub_title' => 'Import status', + 'config_sub_title' => 'Set up your import', + 'status_finished_job' => 'The transactions imported can be found in tag :tag.', + 'import_with_key' => 'Import with key \':key\'', // file: upload something: 'file_upload_title' => 'Import setup (1/4) - Upload your file', diff --git a/resources/views/import/_old/finished.twig b/resources/views/import/_old/finished.twig deleted file mode 100644 index 9f981e3517..0000000000 --- a/resources/views/import/_old/finished.twig +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "./layout/default" %} - -{% block breadcrumbs %} - {{ Breadcrumbs.renderIfExists }} -{% endblock %} -{% block content %} -
    -
    -
    -
    -

    {{ 'import_finished'|_ }}

    -
    -
    -

    - {{ 'import_finished_intro'|_ }} -

    - {% if tagId > 0 %} -

    - {{ trans('firefly.import_finished_text_with_link', {tag: tagId})|raw }} -

    - {% else %} -

    - {{ 'import_finished_text_without_link'|_ }} -

    - {% endif %} - -

    - {{ 'import_share_configuration'|_ }} -

    -
    -
    -
    -
    -{% endblock %} -{% block scripts %} -{% endblock %} -{% block styles %} -{% endblock %} diff --git a/resources/views/import/_old/index.twig b/resources/views/import/_old/index.twig deleted file mode 100644 index f579b59eeb..0000000000 --- a/resources/views/import/_old/index.twig +++ /dev/null @@ -1,53 +0,0 @@ -{% extends "./layout/default" %} - -{% block breadcrumbs %} - {{ Breadcrumbs.renderIfExists }} -{% endblock %} -{% block content %} -
    -
    -
    -
    -

    {{ 'import_index_title'|_ }}

    -
    -
    -
    -
    -

    - {{ 'import_index_intro'|_ }} -

    -
    -
    - -
    -
    - - - -
    - - {{ ExpandedForm.file('import_file', {helpText: 'import_index_file'|_}) }} - {{ ExpandedForm.file('configuration_file', {helpText: 'import_index_config'|_|raw}) }} - {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_index_type'|_}) }} - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -{% endblock %} -{% block scripts %} -{% endblock %} -{% block styles %} -{% endblock %} diff --git a/resources/views/import/_old/status.twig b/resources/views/import/_old/status.twig deleted file mode 100644 index b8b89ef64b..0000000000 --- a/resources/views/import/_old/status.twig +++ /dev/null @@ -1,165 +0,0 @@ -{% extends "./layout/default" %} - -{% block breadcrumbs %} - {{ Breadcrumbs.renderIfExists }} -{% endblock %} -{% block content %} - - {# Initial display. Will refresh (and disappear almost immediately. #} - -
    -
    -
    -
    -

    {{ 'import_status_wait_title'|_ }}

    -
    -
    -

    - {{ 'import_status_wait_text'|_ }} -

    -
    -
    -
    -
    - - {# Fatal error display. Will be shown (duh) when something goes horribly wrong. #} - - - {# Box for when the job is ready to start #} - - - {# Box for when the job is running! #} - - - {# displays the finished status of the import #} - - - {# box to show error information. #} - - -{% endblock %} -{% block scripts %} - - -{% endblock %} -{% block styles %} -{% endblock %} diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index 2e69c94506..bc6e3867f0 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -65,11 +65,11 @@ {% if job.configuration['has-config-file'] != false %}
    {{ 'import_status_ready_config'|_ }} + class="fa fa-fw fa-download"> {{ trans('import.status_ready_config') }}
    {% endif %}
    - +
    {% if job.configuration['has-config-file'] != false %} @@ -77,7 +77,7 @@  

    - {{ 'import_status_ready_share'|_ }} + {{ trans('import.status_ready_share')|raw }}

    {% endif %}
    @@ -90,7 +90,7 @@
    -

    {{ 'import_status_running_title'|_ }}

    +

    {{ trans('import.status_running_title') }}

    @@ -100,7 +100,7 @@ aria-valuemax="100" style="width: 100%;min-width:40px;">
    -

    {{ 'import_status_running_placeholder'|_ }}

    +

    {{ trans('import.status_running_placeholder') }}

    @@ -112,11 +112,11 @@
    -

    {{ 'import_status_finished_title'|_ }}

    +

    {{ trans('import.status_finished_title') }}

    - {{ 'import_status_finished_text'|_ }} + {{ trans('import.status_finished_text') }}

    @@ -129,7 +129,7 @@
    -

    {{ 'import_status_errors_title'|_ }}

    +

    {{ trans('import.status_errors_title') }}

    @@ -145,8 +145,8 @@ + +{% endblock %} \ No newline at end of file diff --git a/routes/breadcrumbs.php b/routes/breadcrumbs.php index 757c2a548b..a027851e90 100644 --- a/routes/breadcrumbs.php +++ b/routes/breadcrumbs.php @@ -188,6 +188,13 @@ Breadcrumbs::register( $breadcrumbs->push(trans('firefly.instance_configuration'), route('admin.configuration.index')); } ); +Breadcrumbs::register( + 'admin.update-check', + function (BreadCrumbsGenerator $breadcrumbs) { + $breadcrumbs->parent('admin.index'); + $breadcrumbs->push(trans('firefly.update_check_title'), route('admin.update-check')); + } +); Breadcrumbs::register( 'admin.links.index', diff --git a/routes/web.php b/routes/web.php index 061c95f038..e368b4fc71 100755 --- a/routes/web.php +++ b/routes/web.php @@ -845,6 +845,11 @@ Route::group( Route::get('', ['uses' => 'HomeController@index', 'as' => 'index']); Route::post('test-message', ['uses' => 'HomeController@testMessage', 'as' => 'test-message']); + // check for updates? + Route::get('update-check', ['uses' => 'UpdateController@index', 'as' => 'update-check']); + Route::post('update-check/manual', ['uses' => 'UpdateController@updateCheck', 'as' => 'update-check.manual']); + Route::post('update-check', ['uses' => 'UpdateController@post', 'as' => 'update-check.post']); + // user manager Route::get('users', ['uses' => 'UserController@index', 'as' => 'users']); Route::get('users/edit/{user}', ['uses' => 'UserController@edit', 'as' => 'users.edit']); From ae7933c8c9335538999f075ece2c8cbbf2bb71e2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Thu, 28 Dec 2017 11:57:38 +0100 Subject: [PATCH 121/142] use config, not preferences, first tests. --- .../Events/VersionCheckEventHandler.php | 12 ++- .../Controllers/Admin/UpdateController.php | 8 +- .../Admin/UpdateControllerTest.php | 79 +++++++++++++++++++ 3 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 tests/Feature/Controllers/Admin/UpdateControllerTest.php diff --git a/app/Handlers/Events/VersionCheckEventHandler.php b/app/Handlers/Events/VersionCheckEventHandler.php index e22655c65c..4308d8da10 100644 --- a/app/Handlers/Events/VersionCheckEventHandler.php +++ b/app/Handlers/Events/VersionCheckEventHandler.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; +use FireflyConfig; use FireflyIII\User; use Illuminate\Auth\Events\Login; use Log; -use Preferences; /** * Class VersionCheckEventHandler @@ -45,9 +45,13 @@ class VersionCheckEventHandler } /** @var User $user */ - $user = $event->user; - $permission = Preferences::getForUser($user, 'permission_update_check', -1); - $lastCheckTime = Preferences::getForUser($user, 'last_update_check', time()); + $user = $event->user; + if (!$user->hasRole('owner')) { + return; + } + + $permission = FireflyConfig::get('permission_update_check', -1); + $lastCheckTime = FireflyConfig::get('last_update_check', time()); $now = time(); if ($now - $lastCheckTime->data < 604800) { Log::debug('Checked for updates less than a week ago.'); diff --git a/app/Http/Controllers/Admin/UpdateController.php b/app/Http/Controllers/Admin/UpdateController.php index 28127c4530..7142c43cde 100644 --- a/app/Http/Controllers/Admin/UpdateController.php +++ b/app/Http/Controllers/Admin/UpdateController.php @@ -30,7 +30,6 @@ use FireflyIII\Services\Github\Object\Release; use FireflyIII\Services\Github\Request\UpdateRequest; use Illuminate\Http\Request; use Log; -use Preferences; use Response; use Session; @@ -67,7 +66,7 @@ class UpdateController extends Controller { $subTitle = trans('firefly.update_check_title'); $subTitleIcon = 'fa-star'; - $permission = app('preferences')->get('permission_update_check', -1); + $permission = app('fireflyconfig')->get('permission_update_check', -1); $selected = $permission->data; $options = [ '-1' => trans('firefly.updates_ask_me_later'), @@ -86,9 +85,8 @@ class UpdateController extends Controller public function post(Request $request) { $checkForUpdates = intval($request->get('check_for_updates')); - Preferences::set('permission_update_check', $checkForUpdates); + app('fireflyconfig')->set('permission_update_check', $checkForUpdates); Session::flash('success', strval(trans('firefly.configuration_updated'))); - Preferences::mark(); return redirect(route('admin.update-check')); } @@ -109,7 +107,7 @@ class UpdateController extends Controller $first = reset($releases); $string = ''; $check = version_compare($current, $first->getTitle()); - Preferences::set('last_update_check', time()); + app('fireflyconfig')->set('last_update_check', time()); } catch (FireflyException $e) { Log::error(sprintf('Could not check for updates: %s', $e->getMessage())); } diff --git a/tests/Feature/Controllers/Admin/UpdateControllerTest.php b/tests/Feature/Controllers/Admin/UpdateControllerTest.php new file mode 100644 index 0000000000..a0e4bf69d1 --- /dev/null +++ b/tests/Feature/Controllers/Admin/UpdateControllerTest.php @@ -0,0 +1,79 @@ +. + */ +declare(strict_types=1); + +namespace Tests\Feature\Controllers\Admin; + +use FireflyConfig; +use FireflyIII\Models\Configuration; +use Tests\TestCase; + +/** + * Class UpdateControllerTest + * + * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConfigurationControllerTest extends TestCase +{ + /** + * @covers \FireflyIII\Http\Controllers\Admin\UpdateControllerTest::index + * @covers \FireflyIII\Http\Controllers\Admin\UpdateControllerTest::__construct + */ + public function testIndex() + { + $this->be($this->user()); + + $config = new Configuration; + $config->data = -1; + + $falseConfig = new Configuration; + $falseConfig->data = false; + + FireflyConfig::shouldReceive('get')->withArgs(['permission_update_check', true])->once()->andReturn($config); + FireflyConfig::shouldReceive('get')->withArgs(['is_demo_site', false])->once()->andReturn($falseConfig); + + $response = $this->get(route('admin.update-check')); + $response->assertStatus(200); + + // has bread crumb + $response->assertSee('

    diff --git a/resources/views/budgets/income.twig b/resources/views/budgets/income.twig index 50c80832f3..201e2f2a3c 100644 --- a/resources/views/budgets/income.twig +++ b/resources/views/budgets/income.twig @@ -5,7 +5,7 @@
    diff --git a/resources/views/budgets/index.twig b/resources/views/budgets/index.twig index f0b12e1493..b340a9ef02 100644 --- a/resources/views/budgets/index.twig +++ b/resources/views/budgets/index.twig @@ -17,10 +17,10 @@ {{ 'budgeted'|_ }}: {{ budgeted|formatAmountPlain }}
    - {{ trans('firefly.available_between',{start : periodStart, end: periodEnd }) }}: - {{ available|formatAmountPlain }} - - + {{ trans('firefly.available_between',{start : periodStart, end: periodEnd }) }}: + {{ available|formatAmountPlain }} + +
    diff --git a/resources/views/budgets/info.twig b/resources/views/budgets/info.twig index 25a8104b47..6bc6ff6582 100644 --- a/resources/views/budgets/info.twig +++ b/resources/views/budgets/info.twig @@ -5,50 +5,50 @@
    - - + + diff --git a/resources/views/budgets/show.twig b/resources/views/budgets/show.twig index f2a1c9e945..4a697a3c6e 100644 --- a/resources/views/budgets/show.twig +++ b/resources/views/budgets/show.twig @@ -122,27 +122,29 @@
    {{ limit.spent|formatAmount }}
    - {% set overspent = limit.amount + limit.spent < 0 %} +
    + {% set overspent = limit.amount + limit.spent < 0 %} - {% if overspent %} - {% set pct = (limit.spent != 0 ? (limit.amount / (limit.spent*-1))*100 : 0) %} -
    -
    -
    -
    - {% else %} - {% set pct = (limit.amount != 0 ? (((limit.spent*-1) / limit.amount)*100) : 0) %} -
    -
    -
    - {% endif %} -
    diff --git a/resources/views/categories/index.twig b/resources/views/categories/index.twig index 8f73c9b307..743d72eede 100644 --- a/resources/views/categories/index.twig +++ b/resources/views/categories/index.twig @@ -6,30 +6,30 @@ {% block content %} {% if categories.count > 0 %} -
    -
    -
    -
    -

    {{ 'categories'|_ }}

    +
    +
    +
    +
    +

    {{ 'categories'|_ }}

    - - -
    -
    - {% include 'list/categories' %} + + +
    +
    + {% include 'list/categories' %}
    - {% else %} +
    +
    +{% else %} {% include 'partials.empty' with {what: 'default', type: 'categories',route: route('categories.create')} %} {% endif %} {% endblock %} diff --git a/resources/views/form/location.twig b/resources/views/form/location.twig index c8201e641f..939bef8ff3 100644 --- a/resources/views/form/location.twig +++ b/resources/views/form/location.twig @@ -2,118 +2,120 @@
    {% if env('MAPBOX_API_KEY','') == '' %} -

    - {{ trans('firefly.mapbox_api_key')|raw }} -

    +

    + {{ trans('firefly.mapbox_api_key')|raw }} +

    {% else %}
    -
    +

    {{ 'press_tag_location'|_ }} {{ 'clear_location'|_ }}

    - {# latitude #} - {% if old(name~'_latitude') %} - - {% else %} - - {% endif %} + {# latitude #} + {% if old(name~'_latitude') %} + + {% else %} + + {% endif %} - {# longitude #} - {% if old('tag_position_longitude') %} - - {% else %} - - {% endif %} + {# longitude #} + {% if old('tag_position_longitude') %} + + {% else %} + + {% endif %} - {# zoomlevel #} - {% if old('tag_position_zoomlevel') %} - - {% else %} - - {% endif %} - {% if tag.zoomLevel and tag.longitude and tag.latitude %} - - {% else %} - - {% endif %} + {# zoomlevel #} + {% if old('tag_position_zoomlevel') %} + + {% else %} + + {% endif %} + {% if tag.zoomLevel and tag.longitude and tag.latitude %} + + {% else %} + + {% endif %} {% include 'form/feedback' %} {% endif %}
    {% if env('MAPBOX_API_KEY','') != '' %} -{% set latitudevar = name~'_latitude' %} -{% set longitudevar = name~'_longitude' %} -{% set zoomlevelvar = name~'_zoomlevel' %} -{% set hastagvar = name~'_has_tag' %} -{% set settagvar = name~'_set_tag' %} -{% set clearvar = name~'_clear_location' %} + {% set latitudevar = name~'_latitude' %} + {% set longitudevar = name~'_longitude' %} + {% set zoomlevelvar = name~'_zoomlevel' %} + {% set hastagvar = name~'_has_tag' %} + {% set settagvar = name~'_set_tag' %} + {% set clearvar = name~'_clear_location' %} - + if (typeof mapboxToken === 'undefined') { + var mapboxToken = 'invalid'; + } + // + document.getElementById('{{ clearvar }}').addEventListener('click', function () { + if (typeof marker !== 'undefined') { + marker.remove(); + $('input[name="{{ hastagvar }}"]').val('false'); + } + return false; + }); + + // set location thing: + function setTagLocation(e) { + $('input[name="{{ latitudevar }}"]').val(e.latlng.lat); + $('input[name="{{ longitudevar }}"]').val(e.latlng.lng); + $('input[name="{{ zoomlevelvar }}"]').val(mymap.getZoom()); + $('input[name="{{ hastagvar }}"]').val('true'); + + // remove existing marker: + if (typeof marker !== 'undefined') { + marker.remove(); + } + // new marker + marker = L.marker([e.latlng.lat, e.latlng.lng]).addTo(mymap); + } + + + console.log({{ longitudevar }}); + + document.addEventListener("DOMContentLoaded", function () { + "use strict"; + + // make map: + mymap = L.map('{{ name }}_map').setView([{{ latitudevar }}, {{ longitudevar }}], {{ zoomlevelvar }}); + + L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { + attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox', + maxZoom: 18, + id: 'mapbox.streets', + accessToken: mapboxToken + }).addTo(mymap); + + mymap.on('contextmenu', setTagLocation); + + // add marker + if (typeof {{ settagvar }} !== 'undefined' && {{ settagvar }} === true) { + marker = L.marker([{{ latitudevar }}, {{ longitudevar }}]).addTo(mymap); + } + + }); + {% endif %} diff --git a/resources/views/form/number.twig b/resources/views/form/number.twig index efffb5b8a0..6951b19328 100644 --- a/resources/views/form/number.twig +++ b/resources/views/form/number.twig @@ -2,7 +2,7 @@
    - {{ Form.input('number', name, value, options) }} + {{ Form.input('number', name, value, options) }} {% include 'form/feedback' %}
    diff --git a/resources/views/import/bank/form.twig b/resources/views/import/bank/form.twig index 70f99a6790..a7c6507a01 100644 --- a/resources/views/import/bank/form.twig +++ b/resources/views/import/bank/form.twig @@ -34,13 +34,13 @@ {% for remoteAccount in remoteAccounts %}
    - + {{ remoteAccount.name }} -
    {{ remoteAccount.number }} +
    {{ remoteAccount.number }}
    {{ remoteAccount.currency }} diff --git a/resources/views/import/file/initial.twig b/resources/views/import/file/initial.twig index fa2e6121ec..cff85df421 100644 --- a/resources/views/import/file/initial.twig +++ b/resources/views/import/file/initial.twig @@ -79,7 +79,7 @@
    diff --git a/resources/views/import/file/map.twig b/resources/views/import/file/map.twig index a2ad9d0010..3a347f8905 100644 --- a/resources/views/import/file/map.twig +++ b/resources/views/import/file/map.twig @@ -48,8 +48,8 @@
    {{ Form.select('mapping['~field.index~']['~option~']', - field.options, - job.configuration['column-mapping-config'][field.index][option], {class: 'form-control'}) }} + field.options, + job.configuration['column-mapping-config'][field.index][option], {class: 'form-control'}) }}
    {{ account.name }}
    {{ entry.name }} diff --git a/resources/views/list/journals.twig b/resources/views/list/journals.twig index 0e3f4ff036..e0e25e9383 100644 --- a/resources/views/list/journals.twig +++ b/resources/views/list/journals.twig @@ -46,12 +46,14 @@
    {{ 'select_transactions'|_ }} + > {{ 'stop_selection'|_ }} {% if showReconcile == true %} {% if moment == 'all' %} - {{ 'reconcile_this_account'|_ }} + {{ 'reconcile_this_account'|_ }} {% else %} - {{ 'reconcile_this_account'|_ }} + {{ 'reconcile_this_account'|_ }} {% endif %} {% endif %}
    diff --git a/resources/views/list/piggy-bank-events.twig b/resources/views/list/piggy-bank-events.twig index 89b20f1710..43c815b54a 100644 --- a/resources/views/list/piggy-bank-events.twig +++ b/resources/views/list/piggy-bank-events.twig @@ -1,7 +1,7 @@ {% if showPiggyBank %} - + {% endif %} diff --git a/resources/views/partials/menu-sidebar.twig b/resources/views/partials/menu-sidebar.twig index b73ae95836..205a887c99 100644 --- a/resources/views/partials/menu-sidebar.twig +++ b/resources/views/partials/menu-sidebar.twig @@ -159,7 +159,8 @@ class="fa fa-gear fa-fw"> {{ 'preferences'|_ }}
  • - {{ 'currencies'|_ }} + {{ 'currencies'|_ }} +
  • {% if Auth.user.hasRole('owner') %} diff --git a/resources/views/partials/transaction-row.twig b/resources/views/partials/transaction-row.twig index e969a07888..2b54a0bad0 100644 --- a/resources/views/partials/transaction-row.twig +++ b/resources/views/partials/transaction-row.twig @@ -1,11 +1,11 @@ - {# input buttons #} diff --git a/resources/views/reports/partials/exp-budgets.twig b/resources/views/reports/partials/exp-budgets.twig index 992570d1dc..d1cc577e1d 100644 --- a/resources/views/reports/partials/exp-budgets.twig +++ b/resources/views/reports/partials/exp-budgets.twig @@ -1,7 +1,7 @@
    {{ trans('list.piggy_bank') }}{{ trans('list.piggy_bank') }}{{ trans('list.date') }} {{ trans('list.amount') }}
    {{ line.getBill.name }} -
    +
    {{ trans('firefly.bill_expected_between', {start: line.getPayDate.formatLocalized(monthAndDayFormat), end: line.getEndOfPayDate.formatLocalized(monthAndDayFormat) }) }}
    - + @@ -20,7 +20,7 @@ {{ '0'|formatAmount }} {% else %} {% for expense in entry.spent.per_currency %} - {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
    + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
    {% endfor %} {% endif %} diff --git a/resources/views/reports/partials/exp-categories.twig b/resources/views/reports/partials/exp-categories.twig index e700cf4268..1ff99b927d 100644 --- a/resources/views/reports/partials/exp-categories.twig +++ b/resources/views/reports/partials/exp-categories.twig @@ -1,7 +1,7 @@
    {{ 'category'|_ }}{{ 'category'|_ }} {{ 'spent'|_ }}
    - + @@ -22,7 +22,7 @@ {{ '0'|formatAmount }} {% else %} {% for expense in entry.spent.per_currency %} - {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
    + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
    {% endfor %} {% endif %} @@ -31,7 +31,7 @@ {{ '0'|formatAmount }} {% else %} {% for income in entry.earned.per_currency %} - {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
    + {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
    {% endfor %} {% endif %} diff --git a/resources/views/reports/partials/exp-not-grouped.twig b/resources/views/reports/partials/exp-not-grouped.twig index 85a5a667c8..ab4250937c 100644 --- a/resources/views/reports/partials/exp-not-grouped.twig +++ b/resources/views/reports/partials/exp-not-grouped.twig @@ -1,4 +1,3 @@ -
    {{ 'category'|_ }}{{ 'category'|_ }} {{ 'spent'|_ }} {{ 'earned'|_ }}
    @@ -12,19 +11,19 @@ diff --git a/resources/views/reports/partials/top-transactions.twig b/resources/views/reports/partials/top-transactions.twig index e068286fd1..67e9c080a2 100644 --- a/resources/views/reports/partials/top-transactions.twig +++ b/resources/views/reports/partials/top-transactions.twig @@ -17,7 +17,8 @@ - + {% endfor %} diff --git a/resources/views/rules/index.twig b/resources/views/rules/index.twig index 3a8f16d1ce..58a749477f 100644 --- a/resources/views/rules/index.twig +++ b/resources/views/rules/index.twig @@ -107,7 +107,9 @@
    {{ name }} - {% if amounts.spent.per_currency|length == 0%} + {% if amounts.spent.per_currency|length == 0 %} {{ '0'|formatAmount }} {% endif %} {% for expense in amounts.spent.per_currency %} - {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
    + {{ formatAmountBySymbol(expense.sum, expense.currency.symbol, expense.currency.dp) }}
    {% endfor %}
    - {% if amounts.earned.per_currency|length == 0%} + {% if amounts.earned.per_currency|length == 0 %} {{ '0'|formatAmount }} {% endif %} {% for income in amounts.earned.per_currency %} - {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
    + {{ formatAmountBySymbol(income.sum, income.currency.symbol, income.currency.dp) }}
    {% endfor %}
    {{ transaction.date.formatLocalized(monthAndDayFormat) }} {{ transaction|transactionAmount }}{{ transaction|transactionAmount }}
    {# show which transactions would match #} - + {# actually execute rule #} {% endblock %} {% block styles %} - + {% endblock %} diff --git a/resources/views/tags/edit.twig b/resources/views/tags/edit.twig index 5eed6ff416..41d032f2d6 100644 --- a/resources/views/tags/edit.twig +++ b/resources/views/tags/edit.twig @@ -95,7 +95,7 @@ {% endblock %} {% block styles %} - + {% endblock %} diff --git a/resources/views/tags/index.twig b/resources/views/tags/index.twig index 08d8c4156e..e611b43801 100644 --- a/resources/views/tags/index.twig +++ b/resources/views/tags/index.twig @@ -10,25 +10,26 @@ {% else %} {% for period,entries in clouds %} {% if entries|length > 0 %} -
    -
    -
    -
    -

    - {% if period == 'no-date' %}{{ 'without_date'|_ }}{% else %}{{ period }}{% endif %} -

    -
    -
    -

    - {% for tagInfo in entries %} - {{ tagInfo.tag.tag }} - {% endfor %} -

    +
    +
    +
    +
    +

    + {% if period == 'no-date' %}{{ 'without_date'|_ }}{% else %}{{ period }}{% endif %} +

    +
    +
    +

    + {% for tagInfo in entries %} + {{ tagInfo.tag.tag }} + {% endfor %} +

    +
    -
    - {% endif %} + {% endif %} {% endfor %} {# diff --git a/resources/views/tags/show.twig b/resources/views/tags/show.twig index 3f5a81478d..c7820c6beb 100644 --- a/resources/views/tags/show.twig +++ b/resources/views/tags/show.twig @@ -29,22 +29,22 @@
    {% if tag.description %} - - - - + + + + {% endif %} {% if tag.date %} - - - - + + + + {% endif %} @@ -196,7 +196,7 @@ {% endblock %} {% block styles %} - + {% endblock %} {% block scripts %}
    - {{ trans('list.description') }} - {{ tag.description }}
    + {{ trans('list.description') }} + {{ tag.description }}
    - {{ trans('list.date') }} - - {{ tag.date.formatLocalized(monthAndDayFormat) }} -
    + {{ trans('list.date') }} + + {{ tag.date.formatLocalized(monthAndDayFormat) }} +
    {{ trans('list.sum') }}