Warenwirtschaft: F-08 — Füllgewicht mit Nachkommastellen

fill_weight decimal(10,3) + float-Cast; deutsche Eingabe normalisiert,
positiv validiert, Fehler im Formular sichtbar. Tests für F-07/F-08.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-08 10:59:48 +00:00
parent fd1572df35
commit 2796b1103e
5 changed files with 114 additions and 2 deletions

View file

@ -0,0 +1,74 @@
<?php
use App\Models\Location;
use App\Models\PackagingItem;
use App\Models\PackagingMaterial;
use App\Models\Product;
use App\Models\Supplier;
use App\Services\ProductionService;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
/**
* Prüfung 08.09.2026 F-08: Füllgewicht mit Nachkommastellen (Dezimalspalte, deutsche Eingabe, Validierung).
*/
function fwUser(): User
{
$user = User::query()->create(['email' => uniqid('fw_', true).'@test.example', 'password' => bcrypt('password')]);
$user->forceFill(['admin' => 7, 'confirmed' => true, 'active' => true, 'wizard' => 100, 'blocked' => false])->save();
return $user->fresh();
}
test('F-08: füllgewicht 39,5 wird mit nachkommastellen gespeichert und skaliert die rezeptur exakt', function () {
$this->actingAs(fwUser(), 'user');
$this->post(route('admin_product_store'), ['id' => 'new', 'name' => 'FW-Dezimal', 'fill_weight' => '39,5'])
->assertRedirect()->assertSessionHasNoErrors();
$product = Product::query()->where('name', 'FW-Dezimal')->firstOrFail();
expect($product->fill_weight)->toBe(39.5)
->and(app(ProductionService::class)->recipeGramScale($product))->toBe(0.395);
// Punkt mit 1–2 Nachkommastellen gilt als Dezimaltrenner (WaWi-Konvention), leer = null.
$this->post(route('admin_product_store'), ['id' => $product->id, 'name' => 'FW-Dezimal', 'fill_weight' => '4.5'])->assertSessionHasNoErrors();
expect($product->fresh()->fill_weight)->toBe(4.5);
$this->post(route('admin_product_store'), ['id' => $product->id, 'name' => 'FW-Dezimal', 'fill_weight' => ''])->assertSessionHasNoErrors();
expect($product->fresh()->fill_weight)->toBeNull();
});
test('F-08: ungültiges oder negatives füllgewicht wird abgelehnt', function () {
$this->actingAs(fwUser(), 'user');
$product = Product::query()->create(['name' => 'FW-Invalid', 'title' => 'x', 'active' => true, 'fill_weight' => 10]);
// Der Produkt-Controller rendert bei Fehlern das Formular direkt (kein Redirect).
foreach (['abc', '-5', '0'] as $bad) {
$this->post(route('admin_product_store'), ['id' => $product->id, 'name' => 'FW-Invalid', 'fill_weight' => $bad])
->assertOk()
->assertSee('is-invalid', false);
}
expect($product->fresh()->fill_weight)->toBe(10.0);
});
test('F-07: produktionsformular liefert die verpackungs-stückliste je ergebnisprodukt für die vorschau', function () {
$this->actingAs(fwUser(), 'user');
$supplier = Supplier::factory()->create();
$material = PackagingMaterial::factory()->create();
$pk = PackagingItem::factory()->create(['packaging_material_id' => $material->id, 'supplier_id' => $supplier->id, 'name' => 'F07-Tiegel', 'category' => 'packaging']);
$small = Product::query()->create(['name' => 'F07-5ml', 'title' => 'x', 'active' => true, 'no_recipe_required' => true, 'production_relevant' => false]);
$small->packagings()->sync([$pk->id => ['quantity' => 2, 'pos' => 0]]);
Location::factory()->create();
$html = $this->get(route('admin.inventory.productions.create'))->assertOk()->getContent();
$start = strpos($html, "data-packaging-bom='") + strlen("data-packaging-bom='");
$bom = json_decode(substr($html, $start, strpos($html, "'", $start) - $start), true);
expect($bom)->toHaveKey((string) $small->id)
->and($bom[$small->id][0]['name'])->toBe('F07-Tiegel')
->and($bom[$small->id][0]['per_unit'])->toEqual(2)
->and($html)->toContain('renderPackagingPreview');
});