f = invoiceFixture(); $this->actingAs($this->f['author']); }); test('compact invoice registration reuses order facts and preserves independent totals and validation', function () { $data = [...$this->f['data'], 'number' => 'FT-COMPACT-2', 'subtotal' => '40.00', 'total' => '40.00', 'items' => [['purchase_order_item_id' => $this->f['line']->id, 'quantity' => '0.400']]]; $this->postJson('/ordens/'.$this->f['order']->id.'/facturas', $data)->assertRedirect(); $invoice = Invoice::query()->where('number', 'FT-COMPACT-2')->sole(); expect($invoice->status)->toBe('pending')->and($invoice->items()->sole()->snapshot['unit'])->toBe('cx') ->and((int) $invoice->items()->sole()->snapshot['unit_price_minor'])->toBe(10000)->and(BudgetConsumption::query()->count())->toBe(0); $this->actingAs($this->f['validator']); app(InvoiceWorkflow::class)->validate($this->f['validator'], $this->f['invoice']); app(InvoiceWorkflow::class)->validate($this->f['validator'], $invoice); expect((int) BudgetConsumption::query()->sum('amount_minor'))->toBe(10000); }); test('compact registration still refuses forged supplied facts quantity and documentary totals', function () { $base = [...$this->f['data'], 'number' => 'FORGED', 'items' => [['purchase_order_item_id' => $this->f['line']->id, 'quantity' => '0.600']]]; foreach ([['unit' => 'kg'], ['unit_price' => '99.00'], ['tax_rate' => '16'], ['quantity' => '2.000']] as $extra) { $this->postJson('/ordens/'.$this->f['order']->id.'/facturas', [...$base, 'items' => [[...$base['items'][0], ...$extra]]])->assertUnprocessable(); } $this->postJson('/ordens/'.$this->f['order']->id.'/facturas', [...$base, 'total' => '59.99'])->assertUnprocessable(); expect(Invoice::query()->count())->toBe(1)->and(BudgetConsumption::query()->count())->toBe(0); }); test('finance task belongs to an independent finance user and reaches the exact invoice', function () { $invoice = $this->f['invoice']; $this->get('/requisicoes/tarefas?module=finance')->assertInertia(fn (Assert $page) => $page->where('tasks.total', 0)); $this->actingAs($this->f['validator'])->get('/requisicoes/tarefas?module=finance')->assertInertia(fn (Assert $page) => $page->where('tasks.total', 1) ->where('tasks.data.0.href', '/facturas?invoice_id='.$invoice->id.'#invoice-'.$invoice->id)); $this->get('/facturas?invoice_id='.$invoice->id)->assertHeader('Cache-Control', 'max-age=0, no-store, private')->assertInertia(fn (Assert $page) => $page->where('focusInvoiceId', $invoice->id)->where('invoices.total', 1)->where('invoices.data.0.canValidate', true)); $this->post('/facturas/'.$invoice->id.'/validar')->assertRedirect(); $this->get('/requisicoes/tarefas?module=finance')->assertInertia(fn (Assert $page) => $page->where('tasks.total', 0)); $this->actingAs($this->f['author'])->get('/facturas?purchase_order_id='.$this->f['order']->id)->assertInertia(fn (Assert $page) => $page->where('focusOrderId', $this->f['order']->id)); }); test('finance tasks respect declared conflicts current permissions and maintenance without changing facts', function () { $actor = $this->f['validator']; $this->actingAs($actor); $before = [DB::table('invoices')->get()->toArray(), DB::table('budget_consumptions')->count(), DB::table('audit_logs')->count()]; $this->get('/requisicoes/tarefas?module=finance')->assertOk(); expect([DB::table('invoices')->get()->toArray(), DB::table('budget_consumptions')->count(), DB::table('audit_logs')->count()])->toEqual($before); $guard = Mockery::mock(ConflictGuard::class)->makePartial(); $guard->shouldReceive('processBlocked')->andReturn(true); app()->instance(ConflictGuard::class, $guard); $this->get('/requisicoes/tarefas?module=finance')->assertInertia(fn (Assert $page) => $page->where('tasks.total', 0)); $this->get('/facturas')->assertInertia(fn (Assert $page) => $page->where('invoices.data.0.canValidate', false)); }); test('non finance users cannot inherit invoice validation tasks or cross company invoice focus', function () { $admin = User::factory()->create(['tenant_id' => $this->f['tenant']->id, 'purchasing_role' => PurchasingRole::Admin]); $this->actingAs($admin)->get('/requisicoes/tarefas?module=finance')->assertInertia(fn (Assert $page) => $page->where('tasks.total', 0)); $this->get('/facturas?invoice_id='.$this->f['invoice']->id)->assertInertia(fn (Assert $page) => $page->where('invoices.data.0.canValidate', false)); $foreign = receiptFixture(); $foreignFinance = User::factory()->create(['tenant_id' => $foreign['tenant']->id, 'purchasing_role' => PurchasingRole::Finance]); $this->actingAs($foreignFinance)->getJson('/facturas?invoice_id='.$this->f['invoice']->id)->assertUnprocessable(); }); test('maintenance finance can consult but never inherits invoice execution', function () { $actor = $this->f['validator']; supplyGrant($actor, ['finance']); DB::table('organization_memberships')->where('user_id', $actor->id)->update(['maintenance' => true, 'expires_at' => now()->addHour()]); $this->actingAs($actor); $this->get('/requisicoes/tarefas?module=finance')->assertInertia(fn (Assert $page) => $page->where('tasks.total', 0)); $this->get('/facturas')->assertInertia(fn (Assert $page) => $page->where('canManage', false)->where('invoices.data.0.canValidate', false)); $this->post('/facturas/'.$this->f['invoice']->id.'/validar')->assertForbidden(); expect($this->f['invoice']->fresh()->status)->toBe('pending')->and(BudgetConsumption::query()->count())->toBe(0); });