From 9b907cf25df219731f1c479020d8dd0c1d388db3 Mon Sep 17 00:00:00 2001 From: Ondrej Vlach Date: Wed, 7 Aug 2024 12:55:57 +0200 Subject: [PATCH] feat(services): add database storage for public holidays --- .../PublicHolidaysStateStorageInterface.php | 20 +++++ .../Storage/DatabaseStorage.php | 61 +++++++++++++ .../PublicHolidaysStorageInterface.php | 47 ++++++++++ .../Storage/DatabaseStorageTest.php | 88 +++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 app/Services/WorkingDays/PublicHolidays/PublicHolidaysStateStorageInterface.php create mode 100644 app/Services/WorkingDays/PublicHolidays/Storage/DatabaseStorage.php create mode 100644 app/Services/WorkingDays/PublicHolidays/Storage/PublicHolidaysStorageInterface.php create mode 100644 tests/Unit/Services/WorkingDays/PublicHolidays/Storage/DatabaseStorageTest.php diff --git a/app/Services/WorkingDays/PublicHolidays/PublicHolidaysStateStorageInterface.php b/app/Services/WorkingDays/PublicHolidays/PublicHolidaysStateStorageInterface.php new file mode 100644 index 0000000..7afd1b4 --- /dev/null +++ b/app/Services/WorkingDays/PublicHolidays/PublicHolidaysStateStorageInterface.php @@ -0,0 +1,20 @@ +getCountryByCountryCode($countryCode); + + return NonWorkingDays::where('country_id', $country->id) + ->where('non_working_date', $holidayDate->format('Y-m-d')) + ->exists(); + } + + public function storePublicHoliday(string $countryCode, \DateTimeImmutable $holidayDate): ?NonWorkingDays + { + $country = $this->getCountryByCountryCode($countryCode); + + try { + return NonWorkingDays::create([ + 'country_id' => $country->id, + 'non_working_date' => $holidayDate->format('Y-m-d'), + ]); + } catch (UniqueConstraintViolationException $e) { + // safe to ignore ... combination exists + Log::warning("Non-working day already exists for country: " . $country . ", date: {$holidayDate->format('Y-m-d')}"); + } + + return null; + } + + public function getPublicHolidaysInInterval(string $countryCode, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): array + { + $country = $this->getCountryByCountryCode($countryCode); + return NonWorkingDays::where('country_id', $country->id) + ->whereBetween('non_working_date', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')]) + ->get()->all(); + } + + + public function countPublicHolidaysInInterval(string $countryCode, \DateTimeImmutable $startDate, \DateTimeImmutable $endDate): int + { + $country = $this->getCountryByCountryCode($countryCode); + return NonWorkingDays::where('country_id', $country->id) + ->whereBetween('non_working_date', [$startDate->format('Y-m-d'), $endDate->format('Y-m-d')]) + ->count(); + } + + protected function getCountryByCountryCode(string $countryCode): Country + { + return Country::where('country_code', $countryCode)->first() ?? throw new \InvalidArgumentException("Country code '$countryCode' does not exist."); + } +} diff --git a/app/Services/WorkingDays/PublicHolidays/Storage/PublicHolidaysStorageInterface.php b/app/Services/WorkingDays/PublicHolidays/Storage/PublicHolidaysStorageInterface.php new file mode 100644 index 0000000..8877110 --- /dev/null +++ b/app/Services/WorkingDays/PublicHolidays/Storage/PublicHolidaysStorageInterface.php @@ -0,0 +1,47 @@ + 'XX', 'name' => 'Czech Republic']); + $databaseStorage = new DatabaseStorage(); + $item1 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $item2 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-02')); + $this->assertEquals(2, NonWorkingDays::where('country_id', $country->id)->count()); + $this->assertEquals('2022-01-01', NonWorkingDays::where('id', $item1->id)->first()->non_working_date); // @phpstan-ignore property.nonObject + $this->assertEquals('2022-01-02', NonWorkingDays::where('id', $item2->id)->first()->non_working_date); // @phpstan-ignore property.nonObject + } + + public function testStoreWillIgnoreDuplicateDates(): void + { + $country = Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']); + $databaseStorage = new DatabaseStorage(); + $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $this->assertEquals(1, NonWorkingDays::where('country_id', $country->id)->count()); + } + + public function testStoreWillStoreDateIfUsedAnotherCountry(): void + { + $country = Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']); + $country2 = Country::create(['country_code' => 'YY', 'name' => 'second Czech republic']); + $databaseStorage = new DatabaseStorage(); + $item1 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $item2 = $databaseStorage->storePublicHoliday('YY', new \DateTimeImmutable('2022-01-01')); + $this->assertEquals(1, NonWorkingDays::where('country_id', $country->id)->count()); + $this->assertEquals(1, NonWorkingDays::where('country_id', $country2->id)->count()); + $this->assertEquals('2022-01-01', NonWorkingDays::where('id', $item1->id)->first()->non_working_date); // @phpstan-ignore property.nonObject + $this->assertEquals('2022-01-01', NonWorkingDays::where('id', $item2->id)->first()->non_working_date); // @phpstan-ignore property.nonObject + } + + public function testCountPublicHolidaysInIntervalWillReturnCorrectCount(): void + { + Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']); + $databaseStorage = new DatabaseStorage(); + $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-02')); + $this->assertEquals(2, $databaseStorage->countPublicHolidaysInInterval('XX', new \DateTimeImmutable('2022-01-01'), new \DateTimeImmutable('2022-01-07'))); + } + + public function testCountPublicHolidaysInIntervalWillReturnCorrectPublicHolidays(): void + { + Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']); + $databaseStorage = new DatabaseStorage(); + $item1 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $item2 = $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-02')); + $holidays = $databaseStorage->getPublicHolidaysInInterval('XX', new \DateTimeImmutable('2022-01-01'), new \DateTimeImmutable('2022-01-07')); + $this->assertCount(2, $holidays); + $this->assertEquals($item1->id, $holidays[0]->id); // @phpstan-ignore property.nonObject + $this->assertEquals($item2->id, $holidays[1]->id); // @phpstan-ignore property.nonObject + } + + public function testIsPublicHolidayWillReturnTrueIfDateIsPublicHoliday(): void + { + Country::create(['country_code' => 'XX', 'name' => 'Czech Republic']); + $databaseStorage = new DatabaseStorage(); + $databaseStorage->storePublicHoliday('XX', new \DateTimeImmutable('2022-01-01')); + $this->assertTrue($databaseStorage->isPublicHoliday('XX', new \DateTimeImmutable('2022-01-01'))); + $this->assertFalse($databaseStorage->isPublicHoliday('XX', new \DateTimeImmutable('2022-01-02'))); + } + + public function testStoreWillRaiseExceptionIfCountryCodeDoesNotExist(): void + { + $databaseStorage = new DatabaseStorage(); + $this->expectException(\InvalidArgumentException::class); + $databaseStorage->storePublicHoliday('ZZ', new \DateTimeImmutable('2022-01-01')); + } +}