93 lines
6.5 KiB
PHP
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();
|
|
});
|