create([ 'name' => $name, 'trans_name' => '', 'inci' => $name, 'trans_inci' => '', 'effect' => '', 'trans_effect' => '', 'active' => true, 'pos' => 0, ]); } function ap23MakeProduct(string $name): Product { return Product::query()->create([ 'name' => $name, 'title' => $name, 'active' => true, ]); } /** * @param array $extra */ function ap23SaveManufacturerRecipe(Product $product, array $extra): void { $repo = new ProductRepository($product); $repo->update(array_merge([ 'id' => (string) $product->id, 'name' => $product->name, 'title' => $product->title, 'active' => '1', 'manufacturer_inci_sync_sent' => '1', ], $extra)); } test('phasen mit zeilen, notiz und reihenfolge werden gespeichert', function () { $product = ap23MakeProduct('AP23-Phasen'); $a = ap23MakeIngredient('AP23-Sheabutter'); $b = ap23MakeIngredient('AP23-Lecithin'); $c = ap23MakeIngredient('AP23-Wasser'); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => ['Phase A', 'Phase B'], 'mfg_phase_note' => ['Fettphase auf 70 °C erhitzen', ''], 'mfg_ingredient_id' => [(string) $a->id, (string) $b->id, (string) $c->id], 'mfg_row_phase' => ['0', '0', '1'], 'mfg_gram' => ['61,456', '8,544', '30'], 'mfg_factor' => ['1,10', '1,10', '1,00'], ]); $phases = $product->recipePhases()->get(); expect($phases)->toHaveCount(2) ->and($phases[0]->name)->toBe('Phase A') ->and($phases[0]->note)->toBe('Fettphase auf 70 °C erhitzen') ->and($phases[0]->pos)->toBe(0) ->and($phases[1]->name)->toBe('Phase B') ->and($phases[1]->note)->toBeNull(); $rows = $product->manufacturer_ingredients()->get(); expect($rows)->toHaveCount(3) ->and($rows->firstWhere('id', $a->id)->pivot->recipe_phase_id)->toBe($phases[0]->id) ->and($rows->firstWhere('id', $b->id)->pivot->recipe_phase_id)->toBe($phases[0]->id) ->and($rows->firstWhere('id', $c->id)->pivot->recipe_phase_id)->toBe($phases[1]->id); }); test('leerer phasenname wird automatisch benannt', function () { $product = ap23MakeProduct('AP23-AutoName'); $a = ap23MakeIngredient('AP23-Auto-A'); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => ['', 'Wasserphase'], 'mfg_phase_note' => ['', ''], 'mfg_ingredient_id' => [(string) $a->id], 'mfg_row_phase' => ['1'], 'mfg_gram' => ['100'], 'mfg_factor' => ['1,00'], ]); expect($product->recipePhases()->pluck('name')->all())->toBe(['Phase A', 'Wasserphase']); }); test('einzelner unbenannter block bleibt phasenlos (altverhalten)', function () { $product = ap23MakeProduct('AP23-Implizit'); $a = ap23MakeIngredient('AP23-Implizit-A'); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => [''], 'mfg_phase_note' => [''], 'mfg_ingredient_id' => [(string) $a->id], 'mfg_row_phase' => ['0'], 'mfg_gram' => ['100'], 'mfg_factor' => ['1,00'], ]); expect(RecipePhase::query()->where('product_id', $product->id)->count())->toBe(0) ->and($product->manufacturer_ingredients()->first()->pivot->recipe_phase_id)->toBeNull(); }); test('erneutes speichern ersetzt phasen vollstaendig', function () { $product = ap23MakeProduct('AP23-Resave'); $a = ap23MakeIngredient('AP23-Resave-A'); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => ['Phase A', 'Phase B'], 'mfg_phase_note' => ['', ''], 'mfg_ingredient_id' => [(string) $a->id], 'mfg_row_phase' => ['0'], 'mfg_gram' => ['100'], 'mfg_factor' => ['1,00'], ]); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => ['Nur noch eine'], 'mfg_phase_note' => ['Neue Notiz'], 'mfg_ingredient_id' => [(string) $a->id], 'mfg_row_phase' => ['0'], 'mfg_gram' => ['100'], 'mfg_factor' => ['1,00'], ]); $phases = RecipePhase::query()->where('product_id', $product->id)->get(); expect($phases)->toHaveCount(1) ->and($phases[0]->name)->toBe('Nur noch eine') ->and($phases[0]->note)->toBe('Neue Notiz'); }); test('produkt-kopie uebernimmt phasen und zuordnung', function () { $product = ap23MakeProduct('AP23-Original'); $a = ap23MakeIngredient('AP23-Kopie-A'); $b = ap23MakeIngredient('AP23-Kopie-B'); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => ['Phase A', 'Phase B'], 'mfg_phase_note' => ['Notiz A', ''], 'mfg_ingredient_id' => [(string) $a->id, (string) $b->id], 'mfg_row_phase' => ['0', '1'], 'mfg_gram' => ['60', '40'], 'mfg_factor' => ['1,10', '1,00'], ]); $repo = new ProductRepository(new Product); $repo->copy($product->fresh()); $copy = Product::query()->where('name', 'Kopie: AP23-Original')->firstOrFail(); $copyPhases = $copy->recipePhases()->get(); expect($copyPhases)->toHaveCount(2) ->and($copyPhases[0]->name)->toBe('Phase A') ->and($copyPhases[0]->note)->toBe('Notiz A'); $copyRows = $copy->manufacturer_ingredients()->get(); expect($copyRows)->toHaveCount(2) ->and($copyRows->firstWhere('id', $a->id)->pivot->recipe_phase_id)->toBe($copyPhases[0]->id) ->and($copyRows->firstWhere('id', $b->id)->pivot->recipe_phase_id)->toBe($copyPhases[1]->id) // Kopie hat eigene Phasen, nicht die des Originals ->and($copyPhases[0]->id)->not->toBe($product->recipePhases()->first()->id); }); test('produktion rechnet unveraendert ueber alle phasen', function () { $product = ap23MakeProduct('AP23-Produktion'); $a = ap23MakeIngredient('AP23-Prod-A'); $b = ap23MakeIngredient('AP23-Prod-B'); ap23SaveManufacturerRecipe($product, [ 'mfg_phase_name' => ['Phase A', 'Phase B'], 'mfg_phase_note' => ['', ''], 'mfg_ingredient_id' => [(string) $a->id, (string) $b->id], 'mfg_row_phase' => ['0', '1'], 'mfg_gram' => ['60', '40'], 'mfg_factor' => ['1,00', '1,00'], ]); // 5 Stück à (60 g + 40 g) => Soll je Rohstoff phasenunabhängig $required = app(ProductionService::class)->requiredGramsByIngredient($product->fresh(), 5); expect($required[$a->id])->toBe(300.0) ->and($required[$b->id])->toBe(200.0); });