@accounter/server
Version:
Accounter GraphQL server
129 lines (106 loc) • 5.93 kB
text/typescript
import { describe, it, expect } from 'vitest';
import { expenseScenarioB } from './expense-scenario-b';
import { validateFixture } from '../../helpers/fixture-validation';
import { makeUUID } from '../../../demo-fixtures/helpers/deterministic-uuid.js';
import { Currency, CountryCode } from '../../../shared/enums.js';
describe('Expense Scenario B - USD Invoice', () => {
it('fixture should compile and be well-formed', () => {
expect(expenseScenarioB).toBeDefined();
expect(expenseScenarioB.businesses).toBeDefined();
expect(expenseScenarioB.taxCategories).toBeDefined();
expect(expenseScenarioB.accounts).toBeDefined();
expect(expenseScenarioB.accountTaxCategories).toBeDefined();
expect(expenseScenarioB.charges).toBeDefined();
expect(expenseScenarioB.transactions).toBeDefined();
expect(expenseScenarioB.documents).toBeDefined();
expect(expenseScenarioB.expectations).toBeDefined();
});
it('fixture should validate successfully', () => {
const result = validateFixture(expenseScenarioB);
if (!result.ok) {
console.error('Validation errors:', result.errors);
}
expect(result.ok).toBe(true);
});
it('should have correct structure counts', () => {
expect(expenseScenarioB.businesses?.businesses).toHaveLength(2); // Admin + US Vendor
expect(expenseScenarioB.taxCategories?.taxCategories).toHaveLength(2); // Consulting expense + USD account
expect(expenseScenarioB.accounts?.accounts).toHaveLength(1); // USD account
expect(expenseScenarioB.accountTaxCategories?.mappings).toHaveLength(1); // USD account → tax category
expect(expenseScenarioB.charges?.charges).toHaveLength(1); // Consulting charge
expect(expenseScenarioB.transactions?.transactions).toHaveLength(1); // Payment in USD
expect(expenseScenarioB.documents?.documents).toHaveLength(1); // Invoice in USD
expect(expenseScenarioB.expectations?.ledger).toHaveLength(1); // Ledger expectation
});
it('should have correct business entities', () => {
const businesses = expenseScenarioB.businesses!.businesses;
const admin = businesses.find(b => b.id === makeUUID('business', 'admin-business-usd'));
const supplier = businesses.find(b => b.id === makeUUID('business', 'supplier-us-vendor-llc'));
expect(admin).toBeDefined();
expect(admin?.country).toBe(CountryCode.Israel);
expect(supplier).toBeDefined();
expect(supplier?.country).toBe(CountryCode['United States of America (the)']);
expect(supplier?.isReceiptEnough).toBe(false); // Requires invoice
});
it('should have referential integrity between charge, transaction, and document', () => {
const chargeId = makeUUID('charge', 'charge-consulting-services');
const charge = expenseScenarioB.charges!.charges[0];
const transaction = expenseScenarioB.transactions!.transactions[0];
const document = expenseScenarioB.documents!.documents[0];
expect(charge.id).toBe(chargeId);
expect(transaction.charge_id).toBe(chargeId);
expect(document.charge_id).toBe(chargeId);
});
it('should use USD currency for transaction and document', () => {
const transaction = expenseScenarioB.transactions!.transactions[0];
const document = expenseScenarioB.documents!.documents[0];
expect(transaction.currency).toBe(Currency.Usd);
expect(document.currency_code).toBe(Currency.Usd);
});
it('should have matching USD amounts', () => {
const transaction = expenseScenarioB.transactions!.transactions[0];
const document = expenseScenarioB.documents!.documents[0];
// Transaction is negative (outflow), document is positive
expect(transaction.amount).toBe('-200.00');
expect(document.total_amount).toBe(200.0);
});
it('should have matching dates', () => {
const transaction = expenseScenarioB.transactions!.transactions[0];
const document = expenseScenarioB.documents!.documents[0];
expect(transaction.event_date).toBe('2024-01-20');
expect(document.date).toBe('2024-01-20');
});
it('should use INVOICE document type for foreign transaction', () => {
const document = expenseScenarioB.documents!.documents[0];
expect(document.type).toBe('INVOICE');
});
it('should have ledger expectations with exchange rate', () => {
const ledgerExpectation = expenseScenarioB.expectations!.ledger![0];
expect(ledgerExpectation.chargeId).toBe(makeUUID('charge', 'charge-consulting-services'));
expect(ledgerExpectation.recordCount).toBe(2);
expect(ledgerExpectation.balanced).toBe(true);
expect(ledgerExpectation.foreignCurrency).toBe('USD');
expect(ledgerExpectation.foreignAmount).toBe(200.0);
expect(ledgerExpectation.exchangeRate).toBe(3.5);
});
it('should have correct ILS conversion in expectations', () => {
const ledgerExpectation = expenseScenarioB.expectations!.ledger![0];
// 200 USD × 3.5 ILS/USD = 700 ILS per entry × 2 entries = 1400 ILS total
// Ledger processes document and transaction separately
expect(ledgerExpectation.totalDebitLocal).toBe(1400.0);
expect(ledgerExpectation.totalCreditLocal).toBe(1400.0);
});
it('should have correct tax categories', () => {
const ledgerExpectation = expenseScenarioB.expectations!.ledger![0];
const debitEntity = ledgerExpectation.debitEntities![0];
const creditEntity = ledgerExpectation.creditEntities![0];
expect(debitEntity).toBe(makeUUID('tax-category', 'expense-consulting'));
expect(creditEntity).toBe(makeUUID('tax-category', 'usd-account-tax-category'));
});
it('should have USD account mapped to tax category', () => {
const accountMapping = expenseScenarioB.accountTaxCategories?.mappings[0];
expect(accountMapping?.accountNumber).toBe('USD-ACCOUNT-001');
expect(accountMapping?.currency).toBe(Currency.Usd);
expect(accountMapping?.taxCategoryId).toBe(makeUUID('tax-category', 'usd-account-tax-category'));
});
});