From cb7851f91cb9f4ec56bfa670348884798ac686c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Pocrnji=C4=8D?= Date: Thu, 30 Oct 2025 18:21:47 +0100 Subject: [PATCH] datatype date fixed --- app/Http/Controllers/ClientCaseContoller.php | 4 +- app/Services/DateNormalizer.php | 56 ++++++++++++++++++++ app/Services/ImportProcessor.php | 30 ++--------- tests/Unit/DateNormalizerTest.php | 21 ++++++++ 4 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 app/Services/DateNormalizer.php create mode 100644 tests/Unit/DateNormalizerTest.php diff --git a/app/Http/Controllers/ClientCaseContoller.php b/app/Http/Controllers/ClientCaseContoller.php index efec603..fd4ef03 100644 --- a/app/Http/Controllers/ClientCaseContoller.php +++ b/app/Http/Controllers/ClientCaseContoller.php @@ -130,7 +130,7 @@ public function storeContract(ClientCase $clientCase, StoreContractRequest $requ // Create contract $contract = $clientCase->contracts()->create([ 'reference' => $request->input('reference'), - 'start_date' => date('Y-m-d', strtotime($request->input('start_date'))), + 'start_date' => \App\Services\DateNormalizer::toDate($request->input('start_date')), 'type_id' => $request->input('type_id'), 'description' => $request->input('description'), ]); @@ -169,7 +169,7 @@ public function updateContract(ClientCase $clientCase, string $uuid, UpdateContr 'reference' => $request->input('reference'), 'type_id' => $request->input('type_id'), 'description' => $request->input('description'), - 'start_date' => $request->filled('start_date') ? date('Y-m-d', strtotime($request->input('start_date'))) : $contract->start_date, + 'start_date' => $request->filled('start_date') ? \App\Services\DateNormalizer::toDate($request->input('start_date')) : $contract->start_date, ]); $initial = $request->input('initial_amount'); diff --git a/app/Services/DateNormalizer.php b/app/Services/DateNormalizer.php new file mode 100644 index 0000000..6cbb7a4 --- /dev/null +++ b/app/Services/DateNormalizer.php @@ -0,0 +1,56 @@ + 2000-2069, 70-99 => 1970-1999) + $year = (int) $dt->format('Y'); + if ($year < 100) { + $year += ($year <= 69) ? 2000 : 1900; + // Rebuild date with corrected year + $month = (int) $dt->format('m'); + $day = (int) $dt->format('d'); + return sprintf('%04d-%02d-%02d', $year, $month, $day); + } + return $dt->format('Y-m-d'); + } + } + } + + // Fallback: strtotime (permissive). If fails, return null. + $ts = @strtotime($raw); + if ($ts === false) { + return null; + } + + return date('Y-m-d', $ts); + } +} diff --git a/app/Services/ImportProcessor.php b/app/Services/ImportProcessor.php index a000b52..436552b 100644 --- a/app/Services/ImportProcessor.php +++ b/app/Services/ImportProcessor.php @@ -553,7 +553,8 @@ public function process(Import $import, ?Authenticatable $user = null): array $payload = [ 'account_id' => $accountIdForPayment, 'reference' => $p['reference'] ?? null, - 'paid_at' => $p['payment_date'] ?? ($p['paid_at'] ?? null), + // Normalize payment date to ISO (Y-m-d) to avoid DB parse errors + 'paid_at' => \App\Services\DateNormalizer::toDate((string) (($p['payment_date'] ?? ($p['paid_at'] ?? '')))), 'currency' => $p['currency'] ?? 'EUR', 'created_by' => $user?->getAuthIdentifier(), ]; @@ -1742,31 +1743,8 @@ private function sanitizeHeaderName(string $v): string */ private function normalizeDate(?string $raw): ?string { - if ($raw === null) { - return null; - } - $raw = trim($raw); - if ($raw === '') { - return null; - } - $candidates = ['d.m.Y', 'd.m.y', 'd/m/Y', 'd/m/y', 'Y-m-d']; - foreach ($candidates as $fmt) { - $dt = \DateTime::createFromFormat($fmt, $raw); - if ($dt instanceof \DateTime) { - // Reject invalid (createFromFormat returns false on mismatch; partial matches handled by checking errors) - $errors = \DateTime::getLastErrors(); - if (($errors['warning_count'] ?? 0) === 0 && ($errors['error_count'] ?? 0) === 0) { - return $dt->format('Y-m-d'); - } - } - } - // Fallback: strtotime (very permissive); if fails return null - $ts = @strtotime($raw); - if ($ts === false) { - return null; - } - - return date('Y-m-d', $ts); + // Delegate to shared normalizer for consistency across the app + return \App\Services\DateNormalizer::toDate($raw); } private function findSourceColumnFor($mappings, string $targetField): ?string diff --git a/tests/Unit/DateNormalizerTest.php b/tests/Unit/DateNormalizerTest.php new file mode 100644 index 0000000..ae5ee07 --- /dev/null +++ b/tests/Unit/DateNormalizerTest.php @@ -0,0 +1,21 @@ +toBe('2025-10-30'); + expect(DateNormalizer::toDate('30/10/2025'))->toBe('2025-10-30'); + expect(DateNormalizer::toDate('30-10-2025'))->toBe('2025-10-30'); + expect(DateNormalizer::toDate('1/2/25'))->toBe('2025-02-01'); + expect(DateNormalizer::toDate('2025-10-30'))->toBe('2025-10-30'); + expect(DateNormalizer::toDate('2025/10/30'))->toBe('2025-10-30'); + expect(DateNormalizer::toDate(' 30.10.2025 '))->toBe('2025-10-30'); +}); + +it('returns null for empty or invalid dates', function () { + expect(DateNormalizer::toDate(null))->toBeNull(); + expect(DateNormalizer::toDate(''))->toBeNull(); + expect(DateNormalizer::toDate('not-a-date'))->toBeNull(); + // invalid calendar date + expect(DateNormalizer::toDate('32.01.2025'))->toBeNull(); +});