Files
compras/tests/Feature/RequisitionCatalogueTest.php
T
nala c6373365bf
tests / ci (push) Canceled after 0s
commit9
2026-10-30 20:02:30 +02:00

93 lines
6.5 KiB
PHP

<?php
use App\Enums\PurchasingRole;
use App\Models\Requisition;
use App\Models\User;
use App\Services\RequisitionCatalogue;
use App\Services\RequisitionWorkflow;
use Illuminate\Support\Facades\DB;
use Inertia\Testing\AssertableInertia as Assert;
require_once __DIR__.'/../Support/ReceiptFixture.php';
beforeEach(function () {
$this->f = receiptFixture();
$this->author = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'department_id' => $this->f['department']->id, 'purchasing_role' => PurchasingRole::Requester]);
$this->actingAs($this->f['receiver']);
});
function publishRequestArticle(object $test, bool $published = true, int $version = 0): object
{
$stock = $test->f['stock']->fresh();
$test->actingAs($test->f['receiver'])->postJson('/stock/catalogo/'.$stock->id, ['version' => $version, 'stock_basis' => app(RequisitionCatalogue::class)->basis($stock),
'category' => 'Material de produção', 'purchase_type' => 'consumable', 'published' => $published, 'reason' => 'Publicação fictícia para reutilizar dados.'])->assertRedirect();
return DB::table('requisition_catalogue')->where('stock_item_id', $stock->id)->sole();
}
function requestCataloguePayload(object $test, object $entry): array
{
return ['cost_center_id' => $test->f['r']->cost_center_id, 'priority' => 'normal', 'needed_by' => today()->addWeek()->toDateString(), 'justification' => 'Pedido fictício com dados do catálogo.',
'items' => [['catalogue_entry_id' => $entry->id, 'catalogue_version' => $entry->version, 'quantity' => '2.000', 'unit_price' => '10.00', 'tax_rate' => '16']]];
}
test('requesters see only published article facts without warehouse balances and the server owns copied descriptors', function () {
$this->actingAs($this->author)->get('/requisicoes/criar')->assertInertia(fn (Assert $p) => $p->where('catalogue.items', []));
$entry = publishRequestArticle($this);
$this->actingAs($this->author)->get('/requisicoes/criar')->assertInertia(fn (Assert $p) => $p->has('catalogue.items', 1)
->where('catalogue.items.0.stock_item_id', $this->f['stock']->id)->missing('catalogue.items.0.balance')->missing('catalogue.items.0.created_by'));
$payload = requestCataloguePayload($this, $entry);
$payload['items'][0] += ['description' => 'FORGED', 'unit' => 'kg', 'category' => 'FORGED', 'purchase_type' => 'other'];
$this->postJson('/requisicoes', $payload)->assertRedirect();
$line = Requisition::query()->where('requester_id', $this->author->id)->sole()->items()->sole();
expect($line->description)->toBe($this->f['stock']->name)->and($line->unit)->toBe('cx')->and($line->category)->toBe('Material de produção')
->and($line->catalogue_snapshot['version'])->toBe(1)->and($line->total_minor)->toBe(2320);
$this->get('/stock/catalogo')->assertForbidden();
});
test('a saved draft preserves its source when publication changes but new uses require the current version', function () {
$entry = publishRequestArticle($this);
$this->actingAs($this->author)->postJson('/requisicoes', requestCataloguePayload($this, $entry))->assertRedirect();
$request = Requisition::query()->where('requester_id', $this->author->id)->sole();
$old = $request->items()->sole();
$this->f['stock']->update(['name' => 'Nome corrigido do artigo']);
$this->get('/requisicoes/criar')->assertInertia(fn (Assert $p) => $p->where('catalogue.items', []));
publishRequestArticle($this, false, 1);
$payload = requestCataloguePayload($this, $entry);
$payload['items'][0] += ['source_line_id' => $old->id];
$payload['items'][0]['quantity'] = '3';
$this->actingAs($this->author)->putJson('/requisicoes/'.$request->id, $payload)->assertRedirect();
$saved = $request->items()->sole();
expect($saved->description)->toBe($old->description)->and($saved->catalogue_snapshot)->toBe($old->catalogue_snapshot)->and($saved->quantity)->toBe('3.000');
$this->postJson('/requisicoes', requestCataloguePayload($this, $entry))->assertUnprocessable();
$new = publishRequestArticle($this, true, 2);
$this->actingAs($this->author)->postJson('/requisicoes', requestCataloguePayload($this, $new))->assertRedirect();
expect(Requisition::query()->where('requester_id', $this->author->id)->latest('id')->first()->items()->sole()->description)->toBe('Nome corrigido do artigo');
});
test('catalogue selection never reuses another company entry or another requests frozen line', function () {
$entry = publishRequestArticle($this);
$foreign = receiptFixture();
$this->actingAs($foreign['receiver'])->postJson('/stock/catalogo/'.$foreign['stock']->id, ['version' => 0, 'stock_basis' => app(RequisitionCatalogue::class)->basis($foreign['stock']),
'category' => 'Material', 'purchase_type' => 'consumable', 'published' => true, 'reason' => 'Catálogo de outra empresa fictícia.'])->assertRedirect();
$other = DB::table('requisition_catalogue')->where('tenant_id', $foreign['tenant']->id)->sole();
$this->actingAs($this->author)->postJson('/requisicoes', requestCataloguePayload($this, $other))->assertNotFound();
$payload = requestCataloguePayload($this, $entry);
$payload['items'][0]['source_line_id'] = $foreign['line']->requisition_item_id;
publishRequestArticle($this, false, 1);
$this->actingAs($this->author)->postJson('/requisicoes', $payload)->assertUnprocessable();
});
test('catalogue publication validates version source basis and write authority and rolls back on audit failure', function () {
$entry = publishRequestArticle($this);
$body = ['version' => 0, 'stock_basis' => app(RequisitionCatalogue::class)->basis($this->f['stock']), 'category' => 'Material', 'purchase_type' => 'consumable', 'published' => false, 'reason' => 'Mudança fictícia para conferência.'];
$this->postJson('/stock/catalogo/'.$this->f['stock']->id, $body)->assertUnprocessable();
$this->actingAs($this->author)->postJson('/stock/catalogo/'.$this->f['stock']->id, [...$body, 'version' => 1])->assertForbidden();
$this->actingAs($this->f['receiver']);
$this->mock(RequisitionWorkflow::class)->shouldReceive('audit')->once()->andThrow(new RuntimeException('Injected catalogue audit failure'));
$this->withoutExceptionHandling();
expect(fn () => $this->postJson('/stock/catalogo/'.$this->f['stock']->id, [...$body, 'version' => 1]))->toThrow(RuntimeException::class);
expect(DB::table('requisition_catalogue')->where('id', $entry->id)->value('version'))->toBe(1)
->and((bool) DB::table('requisition_catalogue')->where('id', $entry->id)->value('published'))->toBeTrue();
});