2026-02-05 21:35:26 +01:00
using Books.Api.Domain ;
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
using Books.Api.Domain.FiscalYears ;
2026-02-05 21:35:26 +01:00
using Books.Api.EventFlow.Repositories ;
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
using EventFlow.Commands ;
namespace Books.Api.Commands.FiscalYears ;
2026-02-05 21:35:26 +01:00
/// <summary>
/// Command handler for creating a new fiscal year.
/// Validates overlap with existing fiscal years and checks for gaps.
/// </summary>
public class CreateFiscalYearCommandHandler (
IFiscalYearRepository fiscalYearRepository )
: CommandHandler < FiscalYearAggregate , FiscalYearId , CreateFiscalYearCommand >
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
{
2026-02-05 21:35:26 +01:00
public override async Task ExecuteAsync (
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
FiscalYearAggregate aggregate ,
CreateFiscalYearCommand command ,
CancellationToken cancellationToken )
{
2026-02-05 21:35:26 +01:00
// Check for overlapping fiscal years
var hasOverlap = await fiscalYearRepository . HasOverlappingYearAsync (
command . CompanyId ,
command . StartDate ,
command . EndDate ,
excludeId : null ,
cancellationToken ) ;
if ( hasOverlap )
{
throw new DomainException (
"FISCAL_YEAR_OVERLAP" ,
"The fiscal year overlaps with an existing fiscal year" ,
"Regnskabsåret overlapper med et eksisterende regnskabsår" ) ;
}
// Check for gaps: verify the new start date follows the latest existing fiscal year
if ( ! command . IsFirstFiscalYear )
{
var existingYears = await fiscalYearRepository . GetByCompanyIdAsync (
command . CompanyId , cancellationToken ) ;
if ( existingYears . Count > 0 )
{
// Find the latest end date among existing fiscal years
var latestEndDate = existingYears
. Select ( fy = > DateOnly . FromDateTime ( fy . EndDate ) )
. Max ( ) ;
var expectedStartDate = latestEndDate . AddDays ( 1 ) ;
if ( command . StartDate ! = expectedStartDate )
{
throw new DomainException (
"FISCAL_YEAR_GAP" ,
$"Fiscal year must start on {expectedStartDate:yyyy-MM-dd} (day after previous year ends on {latestEndDate:yyyy-MM-dd}). No gaps are allowed between fiscal years." ,
$"Regnskabsåret skal starte den {expectedStartDate:yyyy-MM-dd} (dagen efter forrige år slutter den {latestEndDate:yyyy-MM-dd}). Der må ikke være huller mellem regnskabsår." ) ;
}
}
}
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
aggregate . Create (
command . CompanyId ,
command . Name ,
command . StartDate ,
command . EndDate ,
command . IsFirstFiscalYear ,
command . IsReorganization ) ;
}
}
public class CloseFiscalYearCommandHandler : CommandHandler < FiscalYearAggregate , FiscalYearId , CloseFiscalYearCommand >
{
public override Task ExecuteAsync (
FiscalYearAggregate aggregate ,
CloseFiscalYearCommand command ,
CancellationToken cancellationToken )
{
aggregate . Close ( command . ClosedBy ) ;
return Task . CompletedTask ;
}
}
public class ReopenFiscalYearCommandHandler : CommandHandler < FiscalYearAggregate , FiscalYearId , ReopenFiscalYearCommand >
{
public override Task ExecuteAsync (
FiscalYearAggregate aggregate ,
ReopenFiscalYearCommand command ,
CancellationToken cancellationToken )
{
aggregate . Reopen ( command . ReopenedBy ) ;
return Task . CompletedTask ;
}
}
public class LockFiscalYearCommandHandler : CommandHandler < FiscalYearAggregate , FiscalYearId , LockFiscalYearCommand >
{
public override Task ExecuteAsync (
FiscalYearAggregate aggregate ,
LockFiscalYearCommand command ,
CancellationToken cancellationToken )
{
aggregate . Lock ( command . LockedBy ) ;
return Task . CompletedTask ;
}
}
public class MarkOpeningBalancePostedCommandHandler : CommandHandler < FiscalYearAggregate , FiscalYearId , MarkOpeningBalancePostedCommand >
{
public override Task ExecuteAsync (
FiscalYearAggregate aggregate ,
MarkOpeningBalancePostedCommand command ,
CancellationToken cancellationToken )
{
aggregate . MarkOpeningBalancePosted ( ) ;
return Task . CompletedTask ;
}
}