108 lines
6.9 KiB
PHP
108 lines
6.9 KiB
PHP
<?php
|
|
|
|
use App\Enums\PurchasingRole;
|
|
use App\Models\Budget;
|
|
use App\Models\BudgetConsumption;
|
|
use App\Models\Department;
|
|
use App\Models\GoodsReceipt;
|
|
use App\Models\Invoice;
|
|
use App\Models\StockOperation;
|
|
use App\Models\Supplier;
|
|
use App\Models\SupplierBankDetail;
|
|
use App\Models\SupplierBankReview;
|
|
use App\Models\TechnicalReview;
|
|
use App\Models\User;
|
|
use App\Services\EquipmentWorkflow;
|
|
use App\Services\ReceiptWorkflow;
|
|
use App\Services\RequisitionWorkflow;
|
|
use App\Services\SupplierBankWorkflow;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
require_once __DIR__.'/../Support/InvoiceFixture.php';
|
|
|
|
beforeEach(function () {
|
|
$this->f = receiptFixture();
|
|
});
|
|
|
|
test('receipt revalidates a warehouse operator whose cached profile was revoked', function () {
|
|
$this->actingAs($this->f['receiver']);
|
|
DB::table('users')->where('id', $this->f['receiver']->id)->update(['purchasing_role' => 'requester']);
|
|
$this->postJson('/ordens/'.$this->f['order']->id.'/recepcoes', receiptPayload($this->f))->assertForbidden();
|
|
expect(GoodsReceipt::query()->count())->toBe(0);
|
|
});
|
|
|
|
test('stock issue revalidates warehouse role inside transaction', function () {
|
|
$this->actingAs($this->f['receiver']);
|
|
app(ReceiptWorkflow::class)->confirm($this->f['receiver'], $this->f['order'], receiptPayload($this->f, '1.000'));
|
|
DB::table('users')->where('id', $this->f['receiver']->id)->update(['purchasing_role' => 'requester']);
|
|
$this->post('/stock/operacoes', ['kind' => 'issue', 'reference' => 'STALE-STOCK', 'idempotency_key' => (string) Str::uuid(), 'responsible_id' => $this->f['issuer']->id, 'warehouse_id' => $this->f['warehouse']->id, 'stock_item_id' => $this->f['stock']->id, 'destination' => 'Destino fictício', 'quantity' => '0.100', 'reason' => 'Teste de perfil revogado'])->assertForbidden();
|
|
expect(StockOperation::query()->count())->toBe(0);
|
|
});
|
|
|
|
test('invoice registration rechecks finance role after the company lock', function () {
|
|
$f = invoiceFixture();
|
|
DB::table('users')->where('id', $f['author']->id)->update(['purchasing_role' => 'warehouse']);
|
|
$this->actingAs($f['author'])->post('/ordens/'.$f['order']->id.'/facturas', [...$f['data'], 'number' => 'FT-STALE'])->assertForbidden();
|
|
expect(Invoice::query()->count())->toBe(1);
|
|
});
|
|
|
|
test('invoice validation cannot consume budget with a revoked finance role', function () {
|
|
$f = invoiceFixture();
|
|
DB::table('users')->where('id', $f['validator']->id)->update(['purchasing_role' => 'warehouse']);
|
|
$this->actingAs($f['validator'])->post('/facturas/'.$f['invoice']->id.'/validar')->assertForbidden();
|
|
expect($f['invoice']->refresh()->status)->toBe('pending')->and(BudgetConsumption::query()->count())->toBe(0);
|
|
});
|
|
|
|
test('bank proposal rechecks procurement role in the transaction', function () {
|
|
$this->actingAs($this->f['issuer']);
|
|
DB::table('users')->where('id', $this->f['issuer']->id)->update(['purchasing_role' => 'warehouse']);
|
|
$this->post('/fornecedores/'.$this->f['order']->supplier_id.'/dados-bancarios', ['previous_id' => 0, 'bank_name' => 'Banco fictício', 'country' => 'MZ', 'currency' => 'MZN', 'beneficiary' => 'Titular fictício', 'account_identifier' => 'TEST-STALE-ACCOUNT', 'evidence_reference' => 'Fixture', 'reason' => 'Fixture'])->assertForbidden();
|
|
expect(SupplierBankDetail::query()->count())->toBe(0);
|
|
});
|
|
|
|
test('bank review rechecks finance role before accepting a signature', function () {
|
|
$this->actingAs($this->f['issuer']);
|
|
$supplier = Supplier::query()->findOrFail($this->f['order']->supplier_id);
|
|
$detail = app(SupplierBankWorkflow::class)->propose($this->f['issuer'], $supplier, ['previous_id' => 0, 'bank_name' => 'Banco fictício', 'country' => 'MZ', 'currency' => 'MZN', 'beneficiary' => 'Titular fictício', 'account_identifier' => 'TEST-STALE-ACCOUNT', 'evidence_reference' => 'Fixture', 'reason' => 'Fixture']);
|
|
$finance = User::factory()->create(['tenant_id' => $supplier->tenant_id, 'purchasing_role' => PurchasingRole::Finance]);
|
|
$this->actingAs($finance);
|
|
DB::table('users')->where('id', $finance->id)->update(['purchasing_role' => 'warehouse']);
|
|
$this->post('/fornecedores/'.$supplier->id.'/dados-bancarios/'.$detail->id.'/validar', ['decision' => 'approved', 'reason' => 'Teste', 'independently_verified' => true])->assertForbidden();
|
|
expect(SupplierBankReview::query()->count())->toBe(0);
|
|
});
|
|
|
|
test('budget creation rechecks current finance or administration role', function () {
|
|
$finance = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'purchasing_role' => PurchasingRole::Finance]);
|
|
$this->actingAs($finance);
|
|
DB::table('users')->where('id', $finance->id)->update(['purchasing_role' => 'warehouse']);
|
|
$this->post('/orcamentos', ['cost_center_id' => $this->f['r']->cost_center_id, 'year' => now()->year, 'amount' => '100.00', 'includes_tax' => true, 'reason' => 'Dotação fictícia'])->assertForbidden();
|
|
expect(Budget::query()->count())->toBe(0);
|
|
});
|
|
|
|
test('a moved requester cannot save or submit using the previous department', function () {
|
|
$actor = $this->f['issuer'];
|
|
$this->actingAs($actor);
|
|
$department = Department::query()->create(['tenant_id' => $actor->tenant_id, 'name' => 'Novo departamento fictício']);
|
|
DB::table('users')->where('id', $actor->id)->update(['department_id' => $department->id]);
|
|
$data = ['cost_center_id' => $this->f['r']->cost_center_id, 'priority' => 'normal', 'needed_by' => today()->addWeek()->format('Y-m-d'), 'justification' => 'Necessidade fictícia para teste de departamento.', 'items' => [['description' => 'Artigo', 'category' => 'Teste', 'purchase_type' => 'consumable', 'quantity' => '1', 'unit' => 'cx', 'unit_price' => '1.00', 'tax_rate' => '0']]];
|
|
$this->post('/requisicoes', $data)->assertSessionHasErrors('cost_center_id');
|
|
$request = $this->f['r'];
|
|
$request->update(['status' => 'draft']);
|
|
expect(fn () => app(RequisitionWorkflow::class)->submit($actor, $request))->toThrow(ValidationException::class);
|
|
expect($request->refresh()->status)->toBe('draft')->and($request->steps()->count())->toBe(0);
|
|
});
|
|
|
|
test('technical review revalidates the department head current profile', function () {
|
|
$request = $this->f['r'];
|
|
$request->items()->update(['purchase_type' => 'equipment', 'production_area' => 'Linha fictícia']);
|
|
$request->update(['status' => 'draft']);
|
|
$request->refresh();
|
|
$head = User::factory()->create(['tenant_id' => $request->tenant_id, 'department_id' => $request->department_id, 'purchasing_role' => PurchasingRole::DepartmentHead]);
|
|
$this->actingAs($head);
|
|
DB::table('users')->where('id', $head->id)->update(['purchasing_role' => 'warehouse']);
|
|
$this->post('/requisicoes/'.$request->id.'/validacao-tecnica', ['basis_hash' => app(EquipmentWorkflow::class)->basis($request), 'decision' => 'approved', 'reason' => 'Teste'])->assertForbidden();
|
|
expect(TechnicalReview::query()->count())->toBe(0);
|
|
});
|