Kundenentscheid 26.08. (D-5): Eingang und Ausgang haben eigene, pflegbare Gründe-Listen statt einer festen Liste im Controller. - Neues Modell/Tabelle stock_movement_reasons (label, direction in/out, pos, active, SoftDeletes) inkl. Seed der bisherigen Gründe sinnvoll auf Ein-/Ausgang aufgeteilt. - CRUD wie DisposalReason (SettingCrudController + FormRequest + Formular mit Richtungs-Auswahl); zwei Listen (Eingangs-/Ausgangs-Gründe) auf der Seite Einstellungen → Allgemeines, „Neu anlegen" mit vorbelegter Richtung. - Produktbestand-Maske: das Grund-Dropdown wird jetzt je nach Eingang/Ausgang per JS aus den passenden, gepflegten Gründen befüllt (manualReasons entfernt). - DeletionGuard: StockMovementReason immer löschbar. Migration create_stock_movement_reasons auf Dev-/Live-DB. 4 Tests, Suite 326 grün. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2.4 KiB
PHP
57 lines
2.4 KiB
PHP
<?php
|
|
|
|
use App\Models\StockMovementReason;
|
|
use App\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
function smrAdmin(): User
|
|
{
|
|
$user = User::query()->create(['email' => uniqid('smr_', true).'@test.example', 'password' => bcrypt('password')]);
|
|
$user->forceFill(['admin' => 9, 'confirmed' => true, 'active' => true, 'wizard' => 100, 'blocked' => false])->save();
|
|
|
|
return $user->fresh();
|
|
}
|
|
|
|
test('AP-48: seed liefert getrennte gründe für eingang und ausgang', function () {
|
|
expect(StockMovementReason::query()->where('direction', 'in')->count())->toBeGreaterThan(0)
|
|
->and(StockMovementReason::query()->where('direction', 'out')->count())->toBeGreaterThan(0)
|
|
// „Testervergabe" ist ein Ausgangs-, kein Eingangsgrund
|
|
->and(StockMovementReason::query()->where('direction', 'out')->where('label', 'Testervergabe')->exists())->toBeTrue()
|
|
->and(StockMovementReason::query()->where('direction', 'in')->where('label', 'Testervergabe')->exists())->toBeFalse();
|
|
});
|
|
|
|
test('AP-48: grund kann mit richtung angelegt werden', function () {
|
|
$this->actingAs(smrAdmin(), 'user');
|
|
|
|
$this->post(route('admin.inventory.stock-movement-reasons.store'), [
|
|
'label' => 'AP48-Sonderentnahme',
|
|
'direction' => 'out',
|
|
'active' => '1',
|
|
'pos' => '3',
|
|
])->assertRedirect(route('admin.inventory.general'));
|
|
|
|
$reason = StockMovementReason::query()->where('label', 'AP48-Sonderentnahme')->firstOrFail();
|
|
expect($reason->direction)->toBe('out')->and($reason->active)->toBeTrue();
|
|
});
|
|
|
|
test('AP-48: produktbestand-maske liefert die gründe je richtung', function () {
|
|
$this->actingAs(smrAdmin(), 'user');
|
|
StockMovementReason::query()->create(['label' => 'AP48-NurEingang', 'direction' => 'in', 'active' => true, 'pos' => 9]);
|
|
|
|
$this->get(route('admin.inventory.product-stock.index'))
|
|
->assertSuccessful()
|
|
->assertSee('AP48-NurEingang');
|
|
});
|
|
|
|
test('AP-48: grund ist immer löschbar', function () {
|
|
$this->actingAs(smrAdmin(), 'user');
|
|
$reason = StockMovementReason::query()->create(['label' => 'AP48-Weg', 'direction' => 'in', 'active' => true, 'pos' => 9]);
|
|
|
|
$this->delete(route('admin.inventory.stock-movement-reasons.destroy', $reason))
|
|
->assertRedirect(route('admin.inventory.general'));
|
|
|
|
expect(StockMovementReason::query()->find($reason->id))->toBeNull();
|
|
});
|