books/backend/Books.Api/Domain/Accounts/AccountId.cs
Nicolaj Hartmann 1f75c5d791 Add all backend domain, commands, repositories, and tests
This commit includes all previously untracked backend files:

Domain:
- Accounts, Attachments, BankConnections, Customers
- FiscalYears, Invoices, JournalEntryDrafts
- Orders, Products, UserAccess

Commands & Handlers:
- Full CQRS command structure for all domains

Repositories:
- PostgreSQL repositories for all read models
- Bank transaction and ledger repositories

GraphQL:
- Input types, scalars, and types for all entities
- Mutations and queries

Infrastructure:
- Banking integration (Enable Banking client)
- File storage, Invoicing, Reporting, SAF-T export
- Database migrations (003-029)

Tests:
- Integration tests for GraphQL endpoints
- Domain tests
- Invoicing and reporting tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:19:42 +01:00

31 lines
1.2 KiB
C#

using System.Security.Cryptography;
using System.Text;
using EventFlow.Core;
namespace Books.Api.Domain.Accounts;
public class AccountId(string value) : Identity<AccountId>(value)
{
/// <summary>
/// Creates a deterministic AccountId based on company and account number.
/// This prevents duplicate accounts even with race conditions.
/// Uses SHA256 to generate a deterministic GUID from the input.
/// </summary>
public static AccountId FromCompanyAndNumber(string companyId, string accountNumber)
{
// Create a deterministic GUID from the company ID and account number
var input = $"{companyId}:{accountNumber}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(input));
// Use the first 16 bytes of the hash to create a GUID
var guidBytes = new byte[16];
Array.Copy(hash, guidBytes, 16);
// Set version (4) and variant bits to make it a valid UUID
guidBytes[6] = (byte)((guidBytes[6] & 0x0F) | 0x40); // Version 4
guidBytes[8] = (byte)((guidBytes[8] & 0x3F) | 0x80); // Variant 1
var deterministicGuid = new Guid(guidBytes);
return new AccountId($"account-{deterministicGuid:D}");
}
}