Neue Anforderungen (docs/) interpretiert und als Entwicklungsplan V5.0 (AP-20 bis AP-28) aufgenommen; erste drei Pakete umgesetzt: AP-26 Ausschuss-Gründe konfigurierbar: - Stammdaten-Tabelle disposal_reasons + CRUD unter Einstellungen → Allgemein - StockDisposalController liest aktive DB-Gründe statt hartkodierter Liste - Seeder übernimmt die bisherigen 6 Gründe idempotent AP-25 Lieferbestand — Datum statt Tage: - "Nicht vorrätig" wird über Datepicker "Wieder lieferbar ab" gepflegt; Resttage-Hinweis zählt täglich automatisch herunter - Interne Bestellliste wieder kaufbar: Hinweis erscheint zusätzlich zu den Mengen-Buttons (VP entscheidet selbst) AP-22 Produktbestand-Erweiterungen: - Default-Sortierung nach Dringlichkeit, Status-Kopf toggelt - Alle vier Status-Kacheln als Filter klickbar - Neue Spalte "Verbrauch/Monat" (Ø Abgänge der letzten 6 Monate) - Produkt-Flag "Im Produktbestand anzeigen" (products.show_in_product_stock) Tests: 77 grün (DisposalReasonSettings 8, ProductOutOfStock 8, ProductStock 13 + Regression). Hinweise-Doku + Plan-Protokoll fortgeschrieben; nächster Schritt laut Plan: AP-21 (INCI-Erweiterungen). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
116 lines
4.1 KiB
PHP
116 lines
4.1 KiB
PHP
<?php
|
|
|
|
use App\Models\DisposalReason;
|
|
use App\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
function disposalReasonUser(int $adminLevel = 8): User
|
|
{
|
|
$user = User::query()->create([
|
|
'email' => uniqid('dr_', true).'@test.example',
|
|
'password' => bcrypt('password'),
|
|
]);
|
|
$user->forceFill([
|
|
'admin' => $adminLevel,
|
|
'confirmed' => true,
|
|
'active' => true,
|
|
'wizard' => 100,
|
|
'blocked' => false,
|
|
])->save();
|
|
|
|
return $user->fresh();
|
|
}
|
|
|
|
test('allgemein-seite zeigt ausschuss-gruende', function () {
|
|
$this->actingAs(disposalReasonUser(8), 'user');
|
|
|
|
DisposalReason::create(['label' => 'Bruch / Beschädigung', 'active' => true, 'pos' => 0]);
|
|
|
|
$response = $this->get(route('admin.inventory.general'));
|
|
|
|
$response->assertSuccessful();
|
|
$response->assertSee('Ausschuss-Gründe', false);
|
|
$response->assertSee('Bruch / Beschädigung', false);
|
|
$response->assertSee(route('admin.inventory.disposal-reasons.create'), false);
|
|
});
|
|
|
|
test('superadmin legt ausschuss-grund an', function () {
|
|
$this->actingAs(disposalReasonUser(8), 'user');
|
|
|
|
$this->get(route('admin.inventory.disposal-reasons.create'))->assertSuccessful();
|
|
|
|
$this->post(route('admin.inventory.disposal-reasons.store'), [
|
|
'label' => 'Fehlproduktion',
|
|
'active' => '1',
|
|
'pos' => 0,
|
|
])->assertRedirect(route('admin.inventory.general'));
|
|
|
|
$reason = DisposalReason::query()->where('label', 'Fehlproduktion')->firstOrFail();
|
|
expect($reason->active)->toBeTrue();
|
|
});
|
|
|
|
test('ausschuss-grund kann bearbeitet und deaktiviert werden', function () {
|
|
$this->actingAs(disposalReasonUser(8), 'user');
|
|
|
|
$reason = DisposalReason::create(['label' => 'Qualitätsmangel', 'active' => true, 'pos' => 1]);
|
|
|
|
$this->get(route('admin.inventory.disposal-reasons.edit', $reason))->assertSuccessful();
|
|
|
|
$this->put(route('admin.inventory.disposal-reasons.update', $reason), [
|
|
'label' => 'Qualitätsmangel (Rohstoff)',
|
|
'pos' => 1,
|
|
])->assertRedirect(route('admin.inventory.general'));
|
|
|
|
$reason->refresh();
|
|
expect($reason->label)->toBe('Qualitätsmangel (Rohstoff)');
|
|
expect($reason->active)->toBeFalse();
|
|
});
|
|
|
|
test('ausschuss-grund kann geloescht werden', function () {
|
|
$this->actingAs(disposalReasonUser(8), 'user');
|
|
|
|
$reason = DisposalReason::create(['label' => 'Testgrund', 'active' => true, 'pos' => 2]);
|
|
|
|
$this->delete(route('admin.inventory.disposal-reasons.destroy', $reason))
|
|
->assertRedirect(route('admin.inventory.general'));
|
|
|
|
expect(DisposalReason::query()->whereKey($reason->id)->exists())->toBeFalse();
|
|
});
|
|
|
|
test('bezeichnung ist pflicht', function () {
|
|
$this->actingAs(disposalReasonUser(8), 'user');
|
|
|
|
$this->post(route('admin.inventory.disposal-reasons.store'), [
|
|
'label' => '',
|
|
])->assertSessionHasErrors('label');
|
|
});
|
|
|
|
test('nur aktive gruende ueber active-scope', function () {
|
|
DisposalReason::create(['label' => 'Aktiv', 'active' => true, 'pos' => 0]);
|
|
DisposalReason::create(['label' => 'Inaktiv', 'active' => false, 'pos' => 1]);
|
|
|
|
expect(DisposalReason::query()->active()->pluck('label')->all())->toBe(['Aktiv']);
|
|
});
|
|
|
|
test('ausschuss-formular zeigt nur aktive gruende in sortierung', function () {
|
|
$this->actingAs(disposalReasonUser(7), 'user');
|
|
|
|
DisposalReason::create(['label' => 'Zweiter Grund', 'active' => true, 'pos' => 1]);
|
|
DisposalReason::create(['label' => 'Erster Grund', 'active' => true, 'pos' => 0]);
|
|
DisposalReason::create(['label' => 'Deaktivierter Grund', 'active' => false, 'pos' => 2]);
|
|
|
|
$response = $this->get(route('admin.inventory.stock-disposals.create'));
|
|
|
|
$response->assertSuccessful();
|
|
$response->assertSeeInOrder(['Erster Grund', 'Zweiter Grund'], false);
|
|
$response->assertDontSee('Deaktivierter Grund', false);
|
|
});
|
|
|
|
test('nicht-superadmin hat keinen zugriff auf grund-anlage', function () {
|
|
$this->actingAs(disposalReasonUser(7), 'user');
|
|
|
|
$this->get(route('admin.inventory.disposal-reasons.create'))->assertRedirect('/home');
|
|
});
|