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 System.ComponentModel.DataAnnotations.Schema;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using Books.Api.Domain.JournalEntryDrafts;
|
|
|
|
|
using Books.Api.Domain.JournalEntryDrafts.Events;
|
|
|
|
|
using EventFlow.Aggregates;
|
|
|
|
|
using EventFlow.PostgreSql.ReadStores.Attributes;
|
|
|
|
|
using EventFlow.ReadStores;
|
|
|
|
|
|
|
|
|
|
namespace Books.Api.EventFlow.ReadModels;
|
|
|
|
|
|
|
|
|
|
[Table("journal_entry_draft_read_models")]
|
|
|
|
|
public class JournalEntryDraftReadModel : IReadModel,
|
|
|
|
|
IAmReadModelFor<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftCreatedEvent>,
|
|
|
|
|
IAmReadModelFor<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftUpdatedEvent>,
|
|
|
|
|
IAmReadModelFor<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftPostedEvent>,
|
|
|
|
|
IAmReadModelFor<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftDiscardedEvent>
|
|
|
|
|
{
|
|
|
|
|
[PostgreSqlReadModelIdentityColumn]
|
|
|
|
|
public string AggregateId { get; set; } = string.Empty;
|
|
|
|
|
|
|
|
|
|
public DateTimeOffset CreateTime { get; set; }
|
|
|
|
|
public DateTimeOffset UpdatedTime { get; set; }
|
|
|
|
|
|
|
|
|
|
[PostgreSqlReadModelVersionColumn]
|
|
|
|
|
public int LastAggregateSequenceNumber { get; set; }
|
|
|
|
|
|
|
|
|
|
// Business fields
|
|
|
|
|
public string CompanyId { get; set; } = string.Empty;
|
|
|
|
|
public string Name { get; set; } = string.Empty;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bilagsnummer - unique document number per company (required by Bogføringsloven § 7)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string VoucherNumber { get; set; } = string.Empty;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bilagsdato - the date of the transaction/document (e.g., invoice date)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public DateTime? DocumentDate { get; set; }
|
|
|
|
|
public string? Description { get; set; }
|
|
|
|
|
public string? FiscalYearId { get; set; }
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// JSON array of posting lines with VAT codes
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Lines { get; set; } = "[]";
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// JSON array of attachment IDs (bilag references, required by Bogføringsloven § 6)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string AttachmentIds { get; set; } = "[]";
|
|
|
|
|
public string Status { get; set; } = "active";
|
|
|
|
|
public string? TransactionId { get; set; }
|
2026-02-05 21:35:26 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// The exact timestamp when the draft was posted to the ledger.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public DateTimeOffset? PostedAt { get; set; }
|
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
|
|
|
public string CreatedBy { get; set; } = string.Empty;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Full AI extraction data stored as JSON string.
|
|
|
|
|
/// Contains vendor CVR, amounts, VAT, due date, payment reference, line items, etc.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string? ExtractionData { get; set; }
|
|
|
|
|
|
|
|
|
|
public Task ApplyAsync(
|
|
|
|
|
IReadModelContext context,
|
|
|
|
|
IDomainEvent<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftCreatedEvent> domainEvent,
|
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var e = domainEvent.AggregateEvent;
|
|
|
|
|
|
|
|
|
|
AggregateId = domainEvent.AggregateIdentity.Value;
|
|
|
|
|
CreateTime = domainEvent.Timestamp;
|
|
|
|
|
UpdatedTime = domainEvent.Timestamp;
|
|
|
|
|
LastAggregateSequenceNumber = (int)domainEvent.AggregateSequenceNumber;
|
|
|
|
|
|
|
|
|
|
CompanyId = e.CompanyId;
|
|
|
|
|
Name = e.Name;
|
|
|
|
|
VoucherNumber = e.VoucherNumber;
|
|
|
|
|
CreatedBy = e.CreatedBy;
|
|
|
|
|
Status = "active";
|
|
|
|
|
Lines = "[]";
|
|
|
|
|
AttachmentIds = "[]";
|
|
|
|
|
ExtractionData = e.ExtractionData;
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task ApplyAsync(
|
|
|
|
|
IReadModelContext context,
|
|
|
|
|
IDomainEvent<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftUpdatedEvent> domainEvent,
|
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var e = domainEvent.AggregateEvent;
|
|
|
|
|
|
|
|
|
|
UpdatedTime = domainEvent.Timestamp;
|
|
|
|
|
LastAggregateSequenceNumber = (int)domainEvent.AggregateSequenceNumber;
|
|
|
|
|
|
|
|
|
|
if (e.Name != null)
|
|
|
|
|
Name = e.Name;
|
|
|
|
|
|
|
|
|
|
DocumentDate = e.DocumentDate?.ToDateTime(TimeOnly.MinValue);
|
|
|
|
|
Description = e.Description;
|
|
|
|
|
FiscalYearId = e.FiscalYearId;
|
|
|
|
|
Lines = JsonSerializer.Serialize(e.Lines);
|
|
|
|
|
AttachmentIds = JsonSerializer.Serialize(e.AttachmentIds);
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task ApplyAsync(
|
|
|
|
|
IReadModelContext context,
|
|
|
|
|
IDomainEvent<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftPostedEvent> domainEvent,
|
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
UpdatedTime = domainEvent.Timestamp;
|
|
|
|
|
LastAggregateSequenceNumber = (int)domainEvent.AggregateSequenceNumber;
|
|
|
|
|
|
|
|
|
|
Status = "posted";
|
|
|
|
|
TransactionId = domainEvent.AggregateEvent.TransactionId;
|
2026-02-05 21:35:26 +01:00
|
|
|
PostedAt = domainEvent.AggregateEvent.PostedAt;
|
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
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task ApplyAsync(
|
|
|
|
|
IReadModelContext context,
|
|
|
|
|
IDomainEvent<JournalEntryDraftAggregate, JournalEntryDraftId, JournalEntryDraftDiscardedEvent> domainEvent,
|
|
|
|
|
CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
UpdatedTime = domainEvent.Timestamp;
|
|
|
|
|
LastAggregateSequenceNumber = (int)domainEvent.AggregateSequenceNumber;
|
|
|
|
|
|
|
|
|
|
Status = "discarded";
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
}
|