diff --git a/app/Api/V1/Controllers/Controller.php b/app/Api/V1/Controllers/Controller.php index 8e9a8153e6..aa15ba0d17 100644 --- a/app/Api/V1/Controllers/Controller.php +++ b/app/Api/V1/Controllers/Controller.php @@ -104,7 +104,7 @@ class Controller extends BaseController $obj = null; if (null !== $date) { try { - $obj = new Carbon($date); + $obj = Carbon::parse($date); } catch (InvalidDateException $e) { // don't care Log::error(sprintf('Invalid date exception in API controller: %s', $e->getMessage())); diff --git a/app/Api/V1/Requests/TransactionRequest.php b/app/Api/V1/Requests/TransactionRequest.php index 9c6820dad7..a9b024cc37 100644 --- a/app/Api/V1/Requests/TransactionRequest.php +++ b/app/Api/V1/Requests/TransactionRequest.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests; use FireflyIII\Rules\BelongsUser; use FireflyIII\Rules\IsBoolean; +use FireflyIII\Rules\IsDateOrTime; use FireflyIII\Validation\TransactionValidation; use Illuminate\Validation\Validator; @@ -59,7 +60,7 @@ class TransactionRequest extends Request { $data = [ 'type' => $this->string('type'), - 'date' => $this->date('date'), + 'date' => $this->dateTime('date'), 'description' => $this->string('description'), 'piggy_bank_id' => $this->integer('piggy_bank_id'), 'piggy_bank_name' => $this->string('piggy_bank_name'), @@ -103,7 +104,7 @@ class TransactionRequest extends Request // basic fields for journal: 'type' => 'required|in:withdrawal,deposit,transfer,opening-balance,reconciliation', 'description' => 'between:1,255', - 'date' => 'required|date', + 'date' => ['required', new IsDateOrTime], 'piggy_bank_id' => ['numeric', 'nullable', 'mustExist:piggy_banks,id', new BelongsUser], 'piggy_bank_name' => ['between:1,255', 'nullable', new BelongsUser], 'bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser], diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 6c022a0271..9dc5c3c654 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Factory; +use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionJournal; use FireflyIII\Services\Internal\Support\JournalServiceTrait; @@ -70,14 +71,18 @@ class TransactionJournalFactory Log::debug(sprintf('Going to store a %s', $type->type)); $description = app('steam')->cleanString($data['description']); $description = str_replace(["\n", "\t", "\r"], "\x20", $description); - $journal = TransactionJournal::create( + /** @var Carbon $carbon */ + $carbon = $data['date']; + $carbon->setTimezone(config('app.timezone')); + + $journal = TransactionJournal::create( [ 'user_id' => $data['user'], 'transaction_type_id' => $type->id, 'bill_id' => null, 'transaction_currency_id' => $defaultCurrency->id, 'description' => $description, - 'date' => $data['date']->format('Y-m-d'), + 'date' => $carbon->format('Y-m-d H:i:s'), 'order' => 0, 'tag_count' => 0, 'completed' => 0, @@ -92,7 +97,7 @@ class TransactionJournalFactory /** @var TransactionFactory $factory */ $factory = app(TransactionFactory::class); $factory->setUser($this->user); - $totalAmount= '0'; + $totalAmount = '0'; Log::debug(sprintf('Found %d transactions in array.', \count($data['transactions']))); /** @var array $trData */ foreach ($data['transactions'] as $index => $trData) { diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 6bd87f2af6..be04cca2ba 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; use Carbon\Carbon; +use Carbon\Exceptions\InvalidDateException; use Illuminate\Foundation\Http\FormRequest; +use Log; /** * Class Request. @@ -130,4 +132,42 @@ class Request extends FormRequest { return $this->get($field) ? new Carbon($this->get($field)) : null; } + + /** + * Return date time or NULL. + * + * @param string $field + * + * @return Carbon|null + */ + protected function dateTime(string $field): ?Carbon + { + if (null === $this->get($field)) { + return null; + } + $value = (string)$this->get($field); + if (10 === \strlen($value)) { + // probably a date format. + try { + $result = Carbon::createFromFormat('Y-m-d', $value); + } catch (InvalidDateException $e) { + Log::error(sprintf('"%s" is not a valid date: %s', $value, $e->getMessage())); + + return null; + } + + return $result; + } + // is an atom string, I hope? + try { + $result = Carbon::parse($value); + } catch (InvalidDateException $e) { + Log::error(sprintf('"%s" is not a valid date or time: %s', $value, $e->getMessage())); + + return null; + } + + return $result; + } + } diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 1cba19f5ed..458b4c7c13 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -84,7 +84,7 @@ class TransactionJournal extends Model 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', - 'date' => 'date', + 'date' => 'datetime', 'interest_date' => 'date', 'book_date' => 'date', 'process_date' => 'date', diff --git a/app/Rules/IsDateOrTime.php b/app/Rules/IsDateOrTime.php new file mode 100644 index 0000000000..52c40b6ead --- /dev/null +++ b/app/Rules/IsDateOrTime.php @@ -0,0 +1,61 @@ +getMessage())); + + return false; + } + + return true; + } + // is an atom string, I hope? + try { + Carbon::parse($value); + } catch (InvalidDateException $e) { + Log::error(sprintf('"%s" is not a valid date or time: %s', $value, $e->getMessage())); + + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/app/Transformers/TransactionTransformer.php b/app/Transformers/TransactionTransformer.php index 80e9ff8e9f..353b9fffa6 100644 --- a/app/Transformers/TransactionTransformer.php +++ b/app/Transformers/TransactionTransformer.php @@ -77,7 +77,7 @@ class TransactionTransformer extends AbstractTransformer 'description' => $transaction->description, 'journal_description' => $transaction->description, 'transaction_description' => $transaction->transaction_description, - 'date' => $transaction->date->format('Y-m-d'), + 'date' => $transaction->date->toAtomString(), 'type' => $transaction->transaction_type_type, 'identifier' => $transaction->identifier, 'journal_id' => (int)$transaction->journal_id, diff --git a/database/migrations/2019_01_28_193833_changes_for_v4710.php b/database/migrations/2019_01_28_193833_changes_for_v4710.php index 011305f270..f1edfb3b6e 100644 --- a/database/migrations/2019_01_28_193833_changes_for_v4710.php +++ b/database/migrations/2019_01_28_193833_changes_for_v4710.php @@ -11,7 +11,7 @@ class ChangesForV4710 extends Migration * * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('group_journals'); Schema::dropIfExists('transaction_groups'); @@ -22,7 +22,7 @@ class ChangesForV4710 extends Migration * * @return void */ - public function up() + public function up(): void { if (!Schema::hasTable('transaction_groups')) { Schema::create( diff --git a/database/migrations/2019_02_05_055516_changes_for_v4711.php b/database/migrations/2019_02_05_055516_changes_for_v4711.php new file mode 100644 index 0000000000..365d67df24 --- /dev/null +++ b/database/migrations/2019_02_05_055516_changes_for_v4711.php @@ -0,0 +1,37 @@ +dateTimeTz('date')->change(); + } + ); + + Schema::table('preferences', function (Blueprint $table) { + $table->text('data')->nullable()->change(); + } + ); + } +} diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index f1aff6e459..6bbd33b028 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -25,6 +25,7 @@ declare(strict_types=1); return [ 'iban' => 'This is not a valid IBAN.', 'zero_or_more' => 'The value cannot be negative.', + 'date_or_time' => 'The value must be a valid date or time value (ISO 8601).', 'source_equals_destination' => 'The source account equals the destination account.', 'unique_account_number_for_user' => 'It looks like this account number is already in use.', 'unique_iban_for_user' => 'It looks like this IBAN is already in use.',