diff --git a/app/Filament/Resources/DeliveryCalendarOverrides/DeliveryCalendarOverrideResource.php b/app/Filament/Resources/DeliveryCalendarOverrides/DeliveryCalendarOverrideResource.php new file mode 100644 index 0000000..10f3f61 --- /dev/null +++ b/app/Filament/Resources/DeliveryCalendarOverrides/DeliveryCalendarOverrideResource.php @@ -0,0 +1,68 @@ + ListDeliveryCalendarOverrides::route('/'), + 'create' => CreateDeliveryCalendarOverride::route('/create'), + 'edit' => EditDeliveryCalendarOverride::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/DeliveryCalendarOverrides/Pages/CreateDeliveryCalendarOverride.php b/app/Filament/Resources/DeliveryCalendarOverrides/Pages/CreateDeliveryCalendarOverride.php new file mode 100644 index 0000000..1207bd0 --- /dev/null +++ b/app/Filament/Resources/DeliveryCalendarOverrides/Pages/CreateDeliveryCalendarOverride.php @@ -0,0 +1,11 @@ +components([ + Select::make('supplier_id') + ->label('Beszállító') + ->relationship('supplier', 'name') + ->searchable() + ->required(), + DatePicker::make('date') + ->label('Dátum') + ->required() + ->native(false) + ->displayFormat('Y-m-d'), + Toggle::make('is_delivery_day') + ->label('Szállítási nap?') + ->default(true), + TextInput::make('description') + ->label('Indoklás / Megjegyzés') + ->maxLength(255), + ]); + } +} diff --git a/app/Filament/Resources/DeliveryCalendarOverrides/Tables/DeliveryCalendarOverridesTable.php b/app/Filament/Resources/DeliveryCalendarOverrides/Tables/DeliveryCalendarOverridesTable.php new file mode 100644 index 0000000..991121e --- /dev/null +++ b/app/Filament/Resources/DeliveryCalendarOverrides/Tables/DeliveryCalendarOverridesTable.php @@ -0,0 +1,48 @@ +columns([ + \Filament\Tables\Columns\TextColumn::make('supplier.name') + ->label('Beszállító') + ->searchable() + ->sortable(), + \Filament\Tables\Columns\TextColumn::make('date') + ->label('Dátum') + ->date() + ->sortable(), + \Filament\Tables\Columns\IconColumn::make('is_delivery_day') + ->boolean() + ->label('Szállítási nap'), + \Filament\Tables\Columns\TextColumn::make('description') + ->label('Megjegyzés') + ->limit(30), + ]) + ->filters([ + TrashedFilter::make(), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/DeliverySchedules/DeliveryScheduleResource.php b/app/Filament/Resources/DeliverySchedules/DeliveryScheduleResource.php new file mode 100644 index 0000000..cc07d0b --- /dev/null +++ b/app/Filament/Resources/DeliverySchedules/DeliveryScheduleResource.php @@ -0,0 +1,68 @@ + ListDeliverySchedules::route('/'), + 'create' => CreateDeliverySchedule::route('/create'), + 'edit' => EditDeliverySchedule::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/DeliverySchedules/Pages/CreateDeliverySchedule.php b/app/Filament/Resources/DeliverySchedules/Pages/CreateDeliverySchedule.php new file mode 100644 index 0000000..9842353 --- /dev/null +++ b/app/Filament/Resources/DeliverySchedules/Pages/CreateDeliverySchedule.php @@ -0,0 +1,11 @@ +components([ + Section::make('Sablon adatai') + ->schema([ + TextInput::make('name') + ->label('Sablon neve') + ->required() + ->maxLength(255), + Toggle::make('is_active') + ->label('Aktív') + ->default(true), + ]), + Section::make('Szállítási napok') + ->columns(7) + ->schema([ + Toggle::make('monday')->label('Hétfő')->default(false), + Toggle::make('tuesday')->label('Kedd')->default(false), + Toggle::make('wednesday')->label('Szerda')->default(false), + Toggle::make('thursday')->label('Csütörtök')->default(false), + Toggle::make('friday')->label('Péntek')->default(false), + Toggle::make('saturday')->label('Szombat')->default(false), + Toggle::make('sunday')->label('Vasárnap')->default(false), + ]), + ]); + } +} diff --git a/app/Filament/Resources/DeliverySchedules/Tables/DeliverySchedulesTable.php b/app/Filament/Resources/DeliverySchedules/Tables/DeliverySchedulesTable.php new file mode 100644 index 0000000..e810f92 --- /dev/null +++ b/app/Filament/Resources/DeliverySchedules/Tables/DeliverySchedulesTable.php @@ -0,0 +1,49 @@ +columns([ + \Filament\Tables\Columns\TextColumn::make('name') + ->label('Név') + ->searchable() + ->sortable(), + \Filament\Tables\Columns\IconColumn::make('monday')->boolean()->label('H'), + \Filament\Tables\Columns\IconColumn::make('tuesday')->boolean()->label('K'), + \Filament\Tables\Columns\IconColumn::make('wednesday')->boolean()->label('Sze'), + \Filament\Tables\Columns\IconColumn::make('thursday')->boolean()->label('Cs'), + \Filament\Tables\Columns\IconColumn::make('friday')->boolean()->label('P'), + \Filament\Tables\Columns\IconColumn::make('saturday')->boolean()->label('Szo'), + \Filament\Tables\Columns\IconColumn::make('sunday')->boolean()->label('V'), + \Filament\Tables\Columns\IconColumn::make('is_active') + ->boolean() + ->label('Aktív') + ->sortable(), + ]) + ->filters([ + TrashedFilter::make(), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/ProfitCenterSupplierSchedules/Pages/CreateProfitCenterSupplierSchedule.php b/app/Filament/Resources/ProfitCenterSupplierSchedules/Pages/CreateProfitCenterSupplierSchedule.php new file mode 100644 index 0000000..a13ccd3 --- /dev/null +++ b/app/Filament/Resources/ProfitCenterSupplierSchedules/Pages/CreateProfitCenterSupplierSchedule.php @@ -0,0 +1,11 @@ + ListProfitCenterSupplierSchedules::route('/'), + 'create' => CreateProfitCenterSupplierSchedule::route('/create'), + 'edit' => EditProfitCenterSupplierSchedule::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/ProfitCenterSupplierSchedules/Schemas/ProfitCenterSupplierScheduleForm.php b/app/Filament/Resources/ProfitCenterSupplierSchedules/Schemas/ProfitCenterSupplierScheduleForm.php new file mode 100644 index 0000000..7cee796 --- /dev/null +++ b/app/Filament/Resources/ProfitCenterSupplierSchedules/Schemas/ProfitCenterSupplierScheduleForm.php @@ -0,0 +1,31 @@ +components([ + Select::make('profit_center_id') + ->label('Profit Center') + ->relationship('profitCenter', 'name') + ->searchable() + ->required(), + Select::make('supplier_id') + ->label('Beszállító') + ->relationship('supplier', 'name') + ->searchable() + ->required(), + Select::make('delivery_schedule_id') + ->label('Szállítási sablon') + ->relationship('deliverySchedule', 'name') + ->searchable() + ->required(), + ]); + } +} diff --git a/app/Filament/Resources/ProfitCenterSupplierSchedules/Tables/ProfitCenterSupplierSchedulesTable.php b/app/Filament/Resources/ProfitCenterSupplierSchedules/Tables/ProfitCenterSupplierSchedulesTable.php new file mode 100644 index 0000000..4f17e79 --- /dev/null +++ b/app/Filament/Resources/ProfitCenterSupplierSchedules/Tables/ProfitCenterSupplierSchedulesTable.php @@ -0,0 +1,46 @@ +columns([ + \Filament\Tables\Columns\TextColumn::make('profitCenter.name') + ->label('Profit Center') + ->searchable() + ->sortable(), + \Filament\Tables\Columns\TextColumn::make('supplier.name') + ->label('Beszállító') + ->searchable() + ->sortable(), + \Filament\Tables\Columns\TextColumn::make('deliverySchedule.name') + ->label('Szállítási sablon') + ->searchable() + ->sortable(), + ]) + ->filters([ + TrashedFilter::make(), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/WorkCalendars/Pages/CreateWorkCalendar.php b/app/Filament/Resources/WorkCalendars/Pages/CreateWorkCalendar.php new file mode 100644 index 0000000..cf34bb9 --- /dev/null +++ b/app/Filament/Resources/WorkCalendars/Pages/CreateWorkCalendar.php @@ -0,0 +1,11 @@ +components([ + // + ]); + } +} diff --git a/app/Filament/Resources/WorkCalendars/Tables/WorkCalendarsTable.php b/app/Filament/Resources/WorkCalendars/Tables/WorkCalendarsTable.php new file mode 100644 index 0000000..deb1b1c --- /dev/null +++ b/app/Filament/Resources/WorkCalendars/Tables/WorkCalendarsTable.php @@ -0,0 +1,35 @@ +columns([ + // + ]) + ->filters([ + TrashedFilter::make(), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/WorkCalendars/WorkCalendarResource.php b/app/Filament/Resources/WorkCalendars/WorkCalendarResource.php new file mode 100644 index 0000000..7647297 --- /dev/null +++ b/app/Filament/Resources/WorkCalendars/WorkCalendarResource.php @@ -0,0 +1,58 @@ + ListWorkCalendars::route('/'), + 'create' => CreateWorkCalendar::route('/create'), + 'edit' => EditWorkCalendar::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Models/DeliveryCalendarOverride.php b/app/Models/DeliveryCalendarOverride.php new file mode 100644 index 0000000..c25a0f7 --- /dev/null +++ b/app/Models/DeliveryCalendarOverride.php @@ -0,0 +1,23 @@ + 'date', + 'is_delivery_day' => 'boolean', + ]; + + public function supplier(): BelongsTo + { + return $this->belongsTo(Supplier::class); + } +} diff --git a/app/Models/DeliverySchedule.php b/app/Models/DeliverySchedule.php new file mode 100644 index 0000000..9face54 --- /dev/null +++ b/app/Models/DeliverySchedule.php @@ -0,0 +1,23 @@ + 'boolean', + 'tuesday' => 'boolean', + 'wednesday' => 'boolean', + 'thursday' => 'boolean', + 'friday' => 'boolean', + 'saturday' => 'boolean', + 'sunday' => 'boolean', + 'is_active' => 'boolean', + ]; +} diff --git a/app/Models/ProfitCenterSupplierSchedule.php b/app/Models/ProfitCenterSupplierSchedule.php new file mode 100644 index 0000000..5a92b03 --- /dev/null +++ b/app/Models/ProfitCenterSupplierSchedule.php @@ -0,0 +1,28 @@ +belongsTo(ProfitCenter::class); + } + + public function supplier(): BelongsTo + { + return $this->belongsTo(Supplier::class); + } + + public function deliverySchedule(): BelongsTo + { + return $this->belongsTo(DeliverySchedule::class); + } +} diff --git a/app/Services/DeliveryCalendarService.php b/app/Services/DeliveryCalendarService.php new file mode 100644 index 0000000..6b9b13c --- /dev/null +++ b/app/Services/DeliveryCalendarService.php @@ -0,0 +1,76 @@ +startOfDay(); + + // 1. Prioritás: Beszállítói felülbírálás (Override) + $override = DeliveryCalendarOverride::where('supplier_id', $supplier->id) + ->whereDate('date', $date) + ->first(); + + if ($override) { + return $override->is_delivery_day; + } + + // 2. Prioritás: Hivatalos ünnepnap (WorkCalendar) + // Ha aznap nem hivatalos munkanap van, akkor nincs szállítás (kivéve ha az 1. pont felülbírálta). + if (! $this->workCalendarService->isWorkDay($date)) { + return false; + } + + // 3. Prioritás: Heti sablon (Schedule) + $assignment = ProfitCenterSupplierSchedule::where('profit_center_id', $pc->id) + ->where('supplier_id', $supplier->id) + ->with('deliverySchedule') + ->first(); + + if (! $assignment || ! $assignment->deliverySchedule || ! $assignment->deliverySchedule->is_active) { + return false; + } + + $dayOfWeek = strtolower($date->format('l')); // 'monday', 'tuesday', etc. + + return (bool) $assignment->deliverySchedule->{$dayOfWeek}; + } + + /** + * Visszaadja az elérhető szállítási napokat egy adott időszakban. + * + * @return Collection + */ + public function getAvailableDeliveryDates(ProfitCenter $pc, Supplier $supplier, Carbon $start, Carbon $end): Collection + { + $dates = collect(); + $current = $start->copy()->startOfDay(); + $end = $end->copy()->startOfDay(); + + while ($current <= $end) { + if ($this->isDeliveryDay($pc, $supplier, $current)) { + $dates->push($current->copy()); + } + $current->addDay(); + } + + return $dates; + } +} diff --git a/database/factories/DeliveryCalendarOverrideFactory.php b/database/factories/DeliveryCalendarOverrideFactory.php new file mode 100644 index 0000000..771d3a0 --- /dev/null +++ b/database/factories/DeliveryCalendarOverrideFactory.php @@ -0,0 +1,27 @@ + + */ +class DeliveryCalendarOverrideFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'supplier_id' => \App\Models\Supplier::factory(), + 'date' => $this->faker->date(), + 'is_delivery_day' => $this->faker->boolean(), + 'description' => $this->faker->sentence(), + ]; + } +} diff --git a/database/factories/DeliveryScheduleFactory.php b/database/factories/DeliveryScheduleFactory.php new file mode 100644 index 0000000..66407f0 --- /dev/null +++ b/database/factories/DeliveryScheduleFactory.php @@ -0,0 +1,32 @@ + + */ +class DeliveryScheduleFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->word(), + 'monday' => false, + 'tuesday' => false, + 'wednesday' => false, + 'thursday' => false, + 'friday' => false, + 'saturday' => false, + 'sunday' => false, + 'is_active' => true, + ]; + } +} diff --git a/database/factories/ProfitCenterFactory.php b/database/factories/ProfitCenterFactory.php new file mode 100644 index 0000000..c867685 --- /dev/null +++ b/database/factories/ProfitCenterFactory.php @@ -0,0 +1,29 @@ + + */ +class ProfitCenterFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'hooreycaId' => fake()->uuid(), + 'postCode' => '1234', + 'city' => fake()->city(), + 'street' => fake()->streetAddress(), + 'note' => fake()->sentence(), + ]; + } +} diff --git a/database/factories/ProfitCenterSupplierScheduleFactory.php b/database/factories/ProfitCenterSupplierScheduleFactory.php new file mode 100644 index 0000000..a85c5d9 --- /dev/null +++ b/database/factories/ProfitCenterSupplierScheduleFactory.php @@ -0,0 +1,26 @@ + + */ +class ProfitCenterSupplierScheduleFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'profit_center_id' => \App\Models\ProfitCenter::factory(), + 'supplier_id' => \App\Models\Supplier::factory(), + 'delivery_schedule_id' => \App\Models\DeliverySchedule::factory(), + ]; + } +} diff --git a/database/factories/SupplierFactory.php b/database/factories/SupplierFactory.php index 09102ad..cb3538a 100644 --- a/database/factories/SupplierFactory.php +++ b/database/factories/SupplierFactory.php @@ -12,7 +12,13 @@ class SupplierFactory extends Factory public function definition(): array { return [ - // + 'name' => fake()->company(), + 'hooreycaId' => fake()->uuid(), + 'nameId' => fake()->word(), + 'orderEmail' => fake()->safeEmail(), + 'address' => fake()->address(), + 'phone' => fake()->phoneNumber(), + 'note' => fake()->sentence(), ]; } } diff --git a/database/migrations/2026_04_08_223732_create_delivery_schedules_table.php b/database/migrations/2026_04_08_223732_create_delivery_schedules_table.php new file mode 100644 index 0000000..9da7207 --- /dev/null +++ b/database/migrations/2026_04_08_223732_create_delivery_schedules_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('name'); + $table->boolean('monday')->default(false); + $table->boolean('tuesday')->default(false); + $table->boolean('wednesday')->default(false); + $table->boolean('thursday')->default(false); + $table->boolean('friday')->default(false); + $table->boolean('saturday')->default(false); + $table->boolean('sunday')->default(false); + $table->boolean('is_active')->default(true); + $table->userIdFields(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('delivery_schedules'); + } +}; diff --git a/database/migrations/2026_04_08_223733_create_profit_center_supplier_schedules_table.php b/database/migrations/2026_04_08_223733_create_profit_center_supplier_schedules_table.php new file mode 100644 index 0000000..f600940 --- /dev/null +++ b/database/migrations/2026_04_08_223733_create_profit_center_supplier_schedules_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('profit_center_id')->constrained()->onDelete('cascade'); + $table->foreignId('supplier_id')->constrained()->onDelete('cascade'); + $table->foreignId('delivery_schedule_id')->constrained()->onDelete('cascade'); + $table->userIdFields(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('profit_center_supplier_schedules'); + } +}; diff --git a/database/migrations/2026_04_08_223735_create_delivery_calendar_overrides_table.php b/database/migrations/2026_04_08_223735_create_delivery_calendar_overrides_table.php new file mode 100644 index 0000000..fcce67b --- /dev/null +++ b/database/migrations/2026_04_08_223735_create_delivery_calendar_overrides_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('supplier_id')->constrained()->onDelete('cascade'); + $table->date('date'); + $table->boolean('is_delivery_day'); + $table->string('description')->nullable(); + $table->userIdFields(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('delivery_calendar_overrides'); + } +}; diff --git a/tests/Feature/DeliveryCalendarServiceTest.php b/tests/Feature/DeliveryCalendarServiceTest.php new file mode 100644 index 0000000..ada67bd --- /dev/null +++ b/tests/Feature/DeliveryCalendarServiceTest.php @@ -0,0 +1,166 @@ +create(); + $supplier = Supplier::factory()->create(); + $schedule = DeliverySchedule::factory()->create([ + 'monday' => true, + ]); + + ProfitCenterSupplierSchedule::factory()->create([ + 'profit_center_id' => $pc->id, + 'supplier_id' => $supplier->id, + 'delivery_schedule_id' => $schedule->id, + ]); + + $service = app(DeliveryCalendarService::class); + $monday = Carbon::parse('2026-04-13'); // Monday + + expect($service->isDeliveryDay($pc, $supplier, $monday))->toBeTrue(); +}); + +it('returns false for a non-scheduled delivery day', function () { + $pc = ProfitCenter::factory()->create(); + $supplier = Supplier::factory()->create(); + $schedule = DeliverySchedule::factory()->create([ + 'monday' => true, + 'tuesday' => false, + ]); + + ProfitCenterSupplierSchedule::factory()->create([ + 'profit_center_id' => $pc->id, + 'supplier_id' => $supplier->id, + 'delivery_schedule_id' => $schedule->id, + ]); + + $service = app(DeliveryCalendarService::class); + $tuesday = Carbon::parse('2026-04-14'); // Tuesday + + expect($service->isDeliveryDay($pc, $supplier, $tuesday))->toBeFalse(); +}); + +it('respects official holidays (WorkCalendar)', function () { + $pc = ProfitCenter::factory()->create(); + $supplier = Supplier::factory()->create(); + $schedule = DeliverySchedule::factory()->create(['monday' => true]); + + ProfitCenterSupplierSchedule::factory()->create([ + 'profit_center_id' => $pc->id, + 'supplier_id' => $supplier->id, + 'delivery_schedule_id' => $schedule->id, + ]); + + $monday = Carbon::parse('2026-04-13'); // Monday + + // Mark this Monday as a holiday + WorkCalendar::create([ + 'date' => '2026-04-13', + 'type' => WorkDayType::Holiday, + 'data_source' => DataSource::Manual, + ]); + + $service = app(DeliveryCalendarService::class); + + expect($service->isDeliveryDay($pc, $supplier, $monday))->toBeFalse(); +}); + +it('respects supplier overrides over holidays', function () { + $pc = ProfitCenter::factory()->create(); + $supplier = Supplier::factory()->create(); + $schedule = DeliverySchedule::factory()->create(['monday' => true]); + + ProfitCenterSupplierSchedule::factory()->create([ + 'profit_center_id' => $pc->id, + 'supplier_id' => $supplier->id, + 'delivery_schedule_id' => $schedule->id, + ]); + + $monday = Carbon::parse('2026-04-13'); // Monday + + // Mark this Monday as a holiday + WorkCalendar::create([ + 'date' => '2026-04-13', + 'type' => WorkDayType::Holiday, + 'data_source' => DataSource::Manual, + ]); + + // Create an override to allow delivery despite the holiday + DeliveryCalendarOverride::create([ + 'supplier_id' => $supplier->id, + 'date' => '2026-04-13', + 'is_delivery_day' => true, + ]); + + $service = app(DeliveryCalendarService::class); + + expect($service->isDeliveryDay($pc, $supplier, $monday))->toBeTrue(); +}); + +it('respects supplier overrides to block delivery on a normal day', function () { + $pc = ProfitCenter::factory()->create(); + $supplier = Supplier::factory()->create(); + $schedule = DeliverySchedule::factory()->create(['monday' => true]); + + ProfitCenterSupplierSchedule::factory()->create([ + 'profit_center_id' => $pc->id, + 'supplier_id' => $supplier->id, + 'delivery_schedule_id' => $schedule->id, + ]); + + $monday = Carbon::parse('2026-04-13'); // Monday + + // Create an override to block delivery + DeliveryCalendarOverride::create([ + 'supplier_id' => $supplier->id, + 'date' => '2026-04-13', + 'is_delivery_day' => false, + ]); + + $service = app(DeliveryCalendarService::class); + + expect($service->isDeliveryDay($pc, $supplier, $monday))->toBeFalse(); +}); + +it('calculates available delivery dates in a range', function () { + $pc = ProfitCenter::factory()->create(); + $supplier = Supplier::factory()->create(); + $schedule = DeliverySchedule::factory()->create([ + 'monday' => true, + 'wednesday' => true, + 'friday' => true, + ]); + + ProfitCenterSupplierSchedule::factory()->create([ + 'profit_center_id' => $pc->id, + 'supplier_id' => $supplier->id, + 'delivery_schedule_id' => $schedule->id, + ]); + + $start = Carbon::parse('2026-04-13'); // Monday + $end = Carbon::parse('2026-04-19'); // Sunday + + $service = app(DeliveryCalendarService::class); + $dates = $service->getAvailableDeliveryDates($pc, $supplier, $start, $end); + + // Should be Mon, Wed, Fri + expect($dates)->toHaveCount(3); + $dateStrings = $dates->map(fn($d) => $d->toDateString()); + expect($dateStrings)->toContain('2026-04-13'); + expect($dateStrings)->toContain('2026-04-15'); + expect($dateStrings)->toContain('2026-04-17'); +});