@accounter/server
Version:
Accounter GraphQL server
288 lines • 16.2 kB
JavaScript
import { describe, expect, it } from 'vitest';
import { normalizeDocumentAmount, } from '../helpers/document-amount.helper.js';
import { DocumentType } from '../../../shared/enums.js';
describe('Document Amount Normalization', () => {
describe('INVOICE - Business is Debtor (User is Creditor)', () => {
it('should return positive amount when business is debtor', () => {
// Business owes user (user is creditor, business is debtor)
const result = normalizeDocumentAmount(100, false, DocumentType.Invoice);
expect(result).toBe(100);
});
it('should handle decimal amounts', () => {
const result = normalizeDocumentAmount(150.75, false, DocumentType.Invoice);
expect(result).toBe(150.75);
});
it('should handle large amounts', () => {
const result = normalizeDocumentAmount(1000000, false, DocumentType.Invoice);
expect(result).toBe(1000000);
});
});
describe('INVOICE - Business is Creditor (User is Debtor)', () => {
it('should return negative amount when business is creditor', () => {
// User owes business (user is debtor, business is creditor)
const result = normalizeDocumentAmount(100, true, DocumentType.Invoice);
expect(result).toBe(-100);
});
it('should handle decimal amounts', () => {
const result = normalizeDocumentAmount(250.50, true, DocumentType.Invoice);
expect(result).toBe(-250.50);
});
it('should handle large amounts', () => {
const result = normalizeDocumentAmount(5000000, true, DocumentType.Invoice);
expect(result).toBe(-5000000);
});
});
describe('CREDIT_INVOICE - Business is Debtor (User is Creditor)', () => {
it('should return negative amount for credit invoice with business debtor', () => {
// Credit invoice: user owes refund to business (opposite of regular invoice)
const result = normalizeDocumentAmount(100, false, DocumentType.CreditInvoice);
expect(result).toBe(-100);
});
it('should handle decimal amounts', () => {
const result = normalizeDocumentAmount(75.25, false, DocumentType.CreditInvoice);
expect(result).toBe(-75.25);
});
it('should handle large amounts', () => {
const result = normalizeDocumentAmount(2000000, false, DocumentType.CreditInvoice);
expect(result).toBe(-2000000);
});
});
describe('CREDIT_INVOICE - Business is Creditor (User is Debtor)', () => {
it('should return positive amount for credit invoice with business creditor (double negation)', () => {
// Credit invoice: business owes refund to user (double negation: -1 * -1 = 1)
const result = normalizeDocumentAmount(100, true, DocumentType.CreditInvoice);
expect(result).toBe(100);
});
it('should handle decimal amounts', () => {
const result = normalizeDocumentAmount(125.99, true, DocumentType.CreditInvoice);
expect(result).toBe(125.99);
});
it('should handle large amounts', () => {
const result = normalizeDocumentAmount(3000000, true, DocumentType.CreditInvoice);
expect(result).toBe(3000000);
});
});
describe('RECEIPT - Business is Debtor (User is Creditor)', () => {
it('should return positive amount when business is debtor', () => {
const result = normalizeDocumentAmount(100, false, DocumentType.Receipt);
expect(result).toBe(100);
});
it('should handle typical receipt amounts', () => {
const result = normalizeDocumentAmount(49.99, false, DocumentType.Receipt);
expect(result).toBe(49.99);
});
});
describe('RECEIPT - Business is Creditor (User is Debtor)', () => {
it('should return negative amount when business is creditor', () => {
const result = normalizeDocumentAmount(100, true, DocumentType.Receipt);
expect(result).toBe(-100);
});
it('should handle typical receipt amounts', () => {
const result = normalizeDocumentAmount(89.50, true, DocumentType.Receipt);
expect(result).toBe(-89.50);
});
});
describe('INVOICE_RECEIPT - Business is Debtor (User is Creditor)', () => {
it('should return positive amount when business is debtor', () => {
const result = normalizeDocumentAmount(100, false, DocumentType.InvoiceReceipt);
expect(result).toBe(100);
});
it('should handle combined invoice-receipt amounts', () => {
const result = normalizeDocumentAmount(350.00, false, DocumentType.InvoiceReceipt);
expect(result).toBe(350.00);
});
});
describe('INVOICE_RECEIPT - Business is Creditor (User is Debtor)', () => {
it('should return negative amount when business is creditor', () => {
const result = normalizeDocumentAmount(100, true, DocumentType.InvoiceReceipt);
expect(result).toBe(-100);
});
it('should handle combined invoice-receipt amounts', () => {
const result = normalizeDocumentAmount(425.75, true, DocumentType.InvoiceReceipt);
expect(result).toBe(-425.75);
});
});
describe('OTHER Document Type', () => {
it('should handle OTHER type with business debtor', () => {
const result = normalizeDocumentAmount(100, false, DocumentType.Other);
expect(result).toBe(100);
});
it('should handle OTHER type with business creditor', () => {
const result = normalizeDocumentAmount(100, true, DocumentType.Other);
expect(result).toBe(-100);
});
});
describe('PROFORMA Document Type', () => {
it('should handle PROFORMA type with business debtor', () => {
const result = normalizeDocumentAmount(100, false, DocumentType.Proforma);
expect(result).toBe(100);
});
it('should handle PROFORMA type with business creditor', () => {
const result = normalizeDocumentAmount(100, true, DocumentType.Proforma);
expect(result).toBe(-100);
});
});
describe('UNPROCESSED Document Type', () => {
it('should handle UNPROCESSED type with business debtor', () => {
const result = normalizeDocumentAmount(100, false, DocumentType.Unprocessed);
expect(result).toBe(100);
});
it('should handle UNPROCESSED type with business creditor', () => {
const result = normalizeDocumentAmount(100, true, DocumentType.Unprocessed);
expect(result).toBe(-100);
});
});
describe('Negative Input Amounts (Absolute Value First)', () => {
it('should convert negative input to positive for INVOICE with business debtor', () => {
// Input is -100, should become |−100| = 100
const result = normalizeDocumentAmount(-100, false, DocumentType.Invoice);
expect(result).toBe(100);
});
it('should convert negative input to negative for INVOICE with business creditor', () => {
// Input is -100, should become |−100| * -1 = -100
const result = normalizeDocumentAmount(-100, true, DocumentType.Invoice);
expect(result).toBe(-100);
});
it('should convert negative input for CREDIT_INVOICE with business debtor', () => {
// Input is -100, should become |−100| * -1 = -100
const result = normalizeDocumentAmount(-100, false, DocumentType.CreditInvoice);
expect(result).toBe(-100);
});
it('should convert negative input for CREDIT_INVOICE with business creditor', () => {
// Input is -100, should become |−100| * -1 * -1 = 100
const result = normalizeDocumentAmount(-100, true, DocumentType.CreditInvoice);
expect(result).toBe(100);
});
it('should handle negative decimal amounts', () => {
const result = normalizeDocumentAmount(-250.50, false, DocumentType.Invoice);
expect(result).toBe(250.50);
});
it('should handle large negative amounts', () => {
const result = normalizeDocumentAmount(-1000000, true, DocumentType.Receipt);
expect(result).toBe(-1000000);
});
});
describe('Zero Amounts', () => {
it('should handle zero amount with business debtor', () => {
const result = normalizeDocumentAmount(0, false, DocumentType.Invoice);
expect(result).toBe(0);
});
it('should handle zero amount with business creditor', () => {
const result = normalizeDocumentAmount(0, true, DocumentType.Invoice);
expect(result).toBe(-0); // JavaScript has -0, but 0 === -0 is true
});
it('should handle zero for CREDIT_INVOICE', () => {
const result = normalizeDocumentAmount(0, false, DocumentType.CreditInvoice);
expect(result).toBe(-0);
});
it('should verify zero equality regardless of sign', () => {
const result1 = normalizeDocumentAmount(0, false, DocumentType.Invoice);
const result2 = normalizeDocumentAmount(0, true, DocumentType.Invoice);
expect(result1).toBe(0);
// In JavaScript, -0 and 0 are loosely equal but Object.is distinguishes them
// For our purposes, they are functionally equivalent
expect(result2 == 0).toBe(true); // Using == instead of === for -0 case
});
});
describe('Very Large Amounts', () => {
it('should handle very large positive amounts', () => {
const largeAmount = 999999999.99;
const result = normalizeDocumentAmount(largeAmount, false, DocumentType.Invoice);
expect(result).toBe(largeAmount);
});
it('should handle very large amounts with creditor negation', () => {
const largeAmount = 999999999.99;
const result = normalizeDocumentAmount(largeAmount, true, DocumentType.Invoice);
expect(result).toBe(-largeAmount);
});
it('should handle very large amounts with CREDIT_INVOICE', () => {
const largeAmount = 888888888.88;
const result = normalizeDocumentAmount(largeAmount, true, DocumentType.CreditInvoice);
expect(result).toBe(largeAmount);
});
it('should handle maximum safe integer', () => {
const maxSafe = Number.MAX_SAFE_INTEGER;
const result = normalizeDocumentAmount(maxSafe, false, DocumentType.Invoice);
expect(result).toBe(maxSafe);
});
});
describe('Small Decimal Amounts', () => {
it('should handle very small decimal amounts', () => {
const result = normalizeDocumentAmount(0.01, false, DocumentType.Invoice);
expect(result).toBe(0.01);
});
it('should handle fractional cents', () => {
const result = normalizeDocumentAmount(0.001, true, DocumentType.Receipt);
expect(result).toBe(-0.001);
});
it('should preserve precision for small decimals', () => {
const result = normalizeDocumentAmount(1.23456789, false, DocumentType.Invoice);
expect(result).toBe(1.23456789);
});
});
describe('All Document Types Summary Table', () => {
// This test documents the expected behavior for all combinations
it('should correctly normalize all document type and business role combinations', () => {
const testCases = [
// INVOICE
{ type: DocumentType.Invoice, isCreditor: false, input: 100, expected: 100, description: 'INVOICE, business debtor' },
{ type: DocumentType.Invoice, isCreditor: true, input: 100, expected: -100, description: 'INVOICE, business creditor' },
// CREDIT_INVOICE
{ type: DocumentType.CreditInvoice, isCreditor: false, input: 100, expected: -100, description: 'CREDIT_INVOICE, business debtor' },
{ type: DocumentType.CreditInvoice, isCreditor: true, input: 100, expected: 100, description: 'CREDIT_INVOICE, business creditor (double neg)' },
// RECEIPT
{ type: DocumentType.Receipt, isCreditor: false, input: 100, expected: 100, description: 'RECEIPT, business debtor' },
{ type: DocumentType.Receipt, isCreditor: true, input: 100, expected: -100, description: 'RECEIPT, business creditor' },
// INVOICE_RECEIPT
{ type: DocumentType.InvoiceReceipt, isCreditor: false, input: 100, expected: 100, description: 'INVOICE_RECEIPT, business debtor' },
{ type: DocumentType.InvoiceReceipt, isCreditor: true, input: 100, expected: -100, description: 'INVOICE_RECEIPT, business creditor' },
// OTHER
{ type: DocumentType.Other, isCreditor: false, input: 100, expected: 100, description: 'OTHER, business debtor' },
{ type: DocumentType.Other, isCreditor: true, input: 100, expected: -100, description: 'OTHER, business creditor' },
// PROFORMA
{ type: DocumentType.Proforma, isCreditor: false, input: 100, expected: 100, description: 'PROFORMA, business debtor' },
{ type: DocumentType.Proforma, isCreditor: true, input: 100, expected: -100, description: 'PROFORMA, business creditor' },
// UNPROCESSED
{ type: DocumentType.Unprocessed, isCreditor: false, input: 100, expected: 100, description: 'UNPROCESSED, business debtor' },
{ type: DocumentType.Unprocessed, isCreditor: true, input: 100, expected: -100, description: 'UNPROCESSED, business creditor' },
];
testCases.forEach(({ type, isCreditor, input, expected, description }) => {
const result = normalizeDocumentAmount(input, isCreditor, type);
expect(result).toBe(expected);
});
});
});
describe('Real-World Scenarios', () => {
it('should normalize customer invoice (business sends invoice to customer)', () => {
// Business is creditor (customer owes business)
const result = normalizeDocumentAmount(1500, true, DocumentType.Invoice);
expect(result).toBe(-1500); // Negative because user owes
});
it('should normalize vendor invoice (vendor sends invoice to business)', () => {
// Business is debtor (business owes vendor)
const result = normalizeDocumentAmount(750, false, DocumentType.Invoice);
expect(result).toBe(750); // Positive because vendor owes user
});
it('should normalize customer refund (credit invoice to customer)', () => {
// Business owes customer a refund
const result = normalizeDocumentAmount(200, true, DocumentType.CreditInvoice);
expect(result).toBe(200); // Positive because business owes refund
});
it('should normalize vendor credit note (credit invoice from vendor)', () => {
// Vendor gives credit note to business
const result = normalizeDocumentAmount(150, false, DocumentType.CreditInvoice);
expect(result).toBe(-150); // Negative because business owes vendor less now
});
it('should normalize payment receipt from customer', () => {
// Customer pays business
const result = normalizeDocumentAmount(500, true, DocumentType.Receipt);
expect(result).toBe(-500); // Negative because customer paid
});
it('should normalize payment receipt to vendor', () => {
// Business pays vendor
const result = normalizeDocumentAmount(300, false, DocumentType.Receipt);
expect(result).toBe(300); // Positive because business paid
});
});
});
//# sourceMappingURL=document-amount.test.js.map