Kandra.Application.Authorization.ClaimsCatalog
The one IClaimsCatalog implementation for every configuration — unlike IEntityClaimsBuilder (generated per-configuration, since the entity/behavior universe differs per configuration), the area-wildcard claims added here are expressed purely in terms of engine types (ApplicationArea/ApplicationAction), so there's nothing configuration-specific left to inject.
Constructors
| Constructor | Description |
|---|
ClaimsCatalog(IEntityClaimsBuilder) | The one IClaimsCatalog implementation for every configuration — unlike IEntityClaimsBuilder (generated per-configuration, since the entity/behavior universe differs per configuration), the area-wildcard claims added here are expressed purely in terms of engine types (ApplicationArea/ApplicationAction), so there's nothing configuration-specific left to inject. |
Kandra.Application.Authorization.PermissionsFromAuthorizationExtensions
Enforces every [PermissionsFrom] attribute declared on sourceType (typically a handler marker class) — lets a handler "borrow" an existing entity's permission (see PermissionsFromAttribute's own doc comment) instead of needing its own standalone claim. Multiple attributes on the same class all must pass.
Methods
| Method | Description |
|---|
ResolveObjectName(Type) | undocumented |
Methods
| Method | Description |
|---|
AddHandler<T0, T1, T2>(IServiceCollection, String) | Registers BEHAVIOR as a keyed service under key (the handler's own IHandler<TIn,TOut>.Key) — see IHandlerBehavior's doc comment for why keying (not just the closed generic type) is the DI discriminator here. |
AddKandraInterface(IServiceCollection, InterfaceDefinition) | Registers one Interface navigation tree (see InterfaceDefinition's own doc comment). Call order is significant: IEnumerable<InterfaceDefinition> resolves in registration order, and the first one registered is served as the default. |
AddKandraSubmitScope(IServiceCollection) | Registers ISubmitScope alone - the only legal way to obtain a register writer or the posting service (B03). Split out from AddKandraApplication so a test wanting a scoped DI container without the rest of that method's composition (AutoMapper/ FluentValidation assembly scans, Identity, etc.) can register just this, instead of needing the internal SubmitScope implementation type made friend-visible to it. |
AddRegisterWritingDataProcessor<T0, T1, T2>(IServiceCollection) | Mirrors AddDataProcessor for the register-writing opt-in (design §2.5) — BEHAVIOR gets an ISubmitScope on its OnProcessAsync hook instead of the plain data-processor contract. |
Methods
| Method | Description |
|---|
AddKandraScheduling<T0, T1>(IServiceCollection) | undocumented |
Kandra.Application.Scheduling.QuartzTriggerFactory
Kandra.Application.Scheduling.SchedulerAdminService<T0, T1>
Methods
| Method | Description |
|---|
GetCronSummary(String) | undocumented |
Kandra.Application.Scheduling.SchedulerBootstrapService<T0, T1>
Constructors
| Constructor | Description |
|---|
SchedulerBootstrapService(ISchedulerFactory, IServiceScopeFactory, SchedulerHistoryListener) | undocumented |
Kandra.Application.Scheduling.SchedulerHistoryListener
Constructors
| Constructor | Description |
|---|
SchedulerHistoryListener(IServiceScopeFactory) | undocumented |
Kandra.Application.Scheduling.SchedulerJobDataKeys
Fields
| Field | Description |
|---|
DataProcessorGroup | undocumented |
Kandra.Application.Services.Accounting.AccountNodeLocalizationExtensions
Mirrors Kandra.Localization.EnumLocalizationExtensions.GetCaption, adapted for AccountNode. Lives here (not in Kandra.Localization, home of GetCaption) because Kandra.Localization is Common/client-loadable plane and must never reference Kandra.Domain (Backend/server-only plane).
Kandra.Application.Services.Accounting.AccountTransactionsService
The orchestrator (Service 3) - fetches via IAccountTransactionsReader, resolves via ISubcontoResolutionService, then maps into the wire Kandra.Forms.Accounting DTOs. Not generic over TEntity (see IAccountTransactionsService's own doc comment for why one instance suffices for every document type).
Constructors
| Constructor | Description |
|---|
AccountTransactionsService(IAccountTransactionsReader, ISubcontoResolutionService, IChartOfAccountsService, IStringLocalizer, ICurrentTimeZone) | The orchestrator (Service 3) - fetches via IAccountTransactionsReader, resolves via ISubcontoResolutionService, then maps into the wire Kandra.Forms.Accounting DTOs. Not generic over TEntity (see IAccountTransactionsService's own doc comment for why one instance suffices for every document type). |
Kandra.Application.Services.Accounting.ChartOfAccountsService
The concrete chart is supplied via an injected IChartOfAccountsBuilder, not by subclassing and overriding a method - that avoids calling an overridable member from a base constructor. Building the chart is an ordinary, fully-constructed dependency handed in through DI, resolved before this constructor even runs.
Methods
| Method | Description |
|---|
ResolveDictionaryRoot(Type) | undocumented |
Kandra.Application.Services.Accounting.ChartOfAccountsTreeService
Walks Kandra.Domain.Accounting.IChartOfAccountsService.Chart (the code-defined, in-memory tree) into the wire ChartOfAccountsNodeDto shape. Scoped, not Singleton, purely because IStringLocalizer resolves per-request culture - the chart itself never changes within a process (see ChartOfAccountsService).
Constructors
| Constructor | Description |
|---|
ChartOfAccountsTreeService(IChartOfAccountsService, IStringLocalizer) | Walks Kandra.Domain.Accounting.IChartOfAccountsService.Chart (the code-defined, in-memory tree) into the wire ChartOfAccountsNodeDto shape. Scoped, not Singleton, purely because IStringLocalizer resolves per-request culture - the chart itself never changes within a process (see ChartOfAccountsService). |
Kandra.Application.Services.Accounting.PostingService
Does NOT open or commit a database transaction itself - transaction scope isn't its concern. It's called from within whatever transaction the calling use case already opened around persisting its own document. The caller must call SaveChangesAsync after PostAsync returns, or nothing gets persisted (PrepareMergedBatchAsync only stages entities into the DbContext's change tracker).
Constructors
| Constructor | Description |
|---|
PostingService(IChartOfAccountsService, IPostingRepository, IAccountBalanceUpdater) | Does NOT open or commit a database transaction itself - transaction scope isn't its concern. It's called from within whatever transaction the calling use case already opened around persisting its own document. The caller must call SaveChangesAsync after PostAsync returns, or nothing gets persisted (PrepareMergedBatchAsync only stages entities into the DbContext's change tracker). |
Methods
| Method | Description |
|---|
BuildCandidate(PostingTransaction) | Builds an unpersisted candidate row - content fields set, Id/BatchId/IsActive/ CreatedAtUtc intentionally left unassigned. The repository decides which candidates actually get activated and only then assigns those. |
Kandra.Application.Services.Accounting.SubcontoKindClassifier
Classifies an ISubconto-implementing CLR type into the wire-safe SubcontoKind enum (Kandra.Enums) - shared structural-inspection logic. ChartOfAccountsService.BuildInfo is the one caller (computed once per distinct chart-declared type, at chart-construction time), and both ChartOfAccountsTreeService (slot declarations) and SubcontoResolutionService (posted-value resolution) read the resulting SubcontoInfo.Kind off IChartOfAccountsService.GetSubcontoInfo rather than classifying independently.
Methods
| Method | Description |
|---|
TryUnwrapEnumSubconto(Type, Type) | True when subcontoType is a closed EnumSubconto<TEnum> - in which case enumType is the underlying enum (TEnum itself), the type whose simple Name is the correct caption/resx key (see ChartOfAccountsTreeService.CaptionKeyFor's own doc comment for why EnumSubconto<TEnum>'s own .Name, "EnumSubconto`1", is never usable as one). |
Kandra.Application.Services.Accounting.SubcontoResolutionService
Service 2 - the only place in this feature that dispatches per concrete ISubconto CLR type. Dictionary-kind ids go through the existing ILookupRepository.GetDictionaryAsync<T> (reflection-invoked, since the concrete/root type is only known at runtime here, from IChartOfAccountsService.GetSubcontoInfo); document-kind ids go through ILookupRepository.GetDocumentsAsync, filtered by DocumentTypeId - one query covers every document-typed subconto uniformly, since SubcontoSlot.TypeId for a document IS its DocumentTypeId. Also resolves a localized type caption per TypeId - the CLR type's own simple name is the resx key, since this is the one place that already knows the concrete type. Engine-level, not configuration-specific - depends only on ILookupRepository and IChartOfAccountsService, both engine abstractions/services driven entirely by whichever chart the consuming configuration registers, same posture as AccountTransactionsReader/Service.
Constructors
| Constructor | Description |
|---|
SubcontoResolutionService(ILookupRepository, IChartOfAccountsService) | Service 2 - the only place in this feature that dispatches per concrete ISubconto CLR type. Dictionary-kind ids go through the existing ILookupRepository.GetDictionaryAsync<T> (reflection-invoked, since the concrete/root type is only known at runtime here, from IChartOfAccountsService.GetSubcontoInfo); document-kind ids go through ILookupRepository.GetDocumentsAsync, filtered by DocumentTypeId - one query covers every document-typed subconto uniformly, since SubcontoSlot.TypeId for a document IS its DocumentTypeId. Also resolves a localized type caption per TypeId - the CLR type's own simple name is the resx key, since this is the one place that already knows the concrete type. Engine-level, not configuration-specific - depends only on ILookupRepository and IChartOfAccountsService, both engine abstractions/services driven entirely by whichever chart the consuming configuration registers, same posture as AccountTransactionsReader/Service. |
Kandra.Application.Services.Blobs.BlobService
Constructors
| Constructor | Description |
|---|
BlobService(IBlobRepository, IBlobContentStore, IOptions<BlobStorageOptions>) | undocumented |
Methods
| Method | Description |
|---|
StoreAsync(Stream, String, String, CancellationToken) | undocumented |
Kandra.Application.Services.Blobs.BlobService.AdminRow
Constructors
| Constructor | Description |
|---|
AdminRow(Guid, ReferenceState, String, String, Int64, Boolean, Guid, String, Int32, Nullable<Guid>, Nullable<Guid>, String, Nullable<Guid>, DateTime, Nullable<DateTime>, Nullable<DateTime>, Nullable<DateTime>, String) | undocumented |
Kandra.Application.Services.ConstantsManageService
Write/enumerate access over ConstantRecord — direct DbContext access (Constants' composite (Name,Date) identity doesn't fit IEntityRepository<T>'s single-Id-keyed shape), but stamps ChangesInfo fields by hand (mirroring what EntityRepository does for entities that do go through it) and checks the write-constant permission before every mutation.
Constructors
| Constructor | Description |
|---|
ConstantsManageService(DbContext, IAuthorizationChecker, IConstantsService, IConstantRegistry, IChangeEventRecorder) | Write/enumerate access over ConstantRecord — direct DbContext access (Constants' composite (Name,Date) identity doesn't fit IEntityRepository<T>'s single-Id-keyed shape), but stamps ChangesInfo fields by hand (mirroring what EntityRepository does for entities that do go through it) and checks the write-constant permission before every mutation. |
Kandra.Application.Services.ConstantsService
Plain read-only access over ConstantRecord — direct DbContext access, no repository/audit ceremony, matching the "much simpler" flat-table shape decided for Constants (not built on the register engine). Deliberately ungated (no permission check) — document behaviors resolve constants (e.g. the home currency) freely. Writing goes through IConstantsManageService instead.
Constructors
| Constructor | Description |
|---|
ConstantsService(DbContext) | Plain read-only access over ConstantRecord — direct DbContext access, no repository/audit ceremony, matching the "much simpler" flat-table shape decided for Constants (not built on the register engine). Deliberately ungated (no permission check) — document behaviors resolve constants (e.g. the home currency) freely. Writing goes through IConstantsManageService instead. |
Kandra.Application.Services.DataProcessors.RegisterWritingDataProcessorService<T0, T1>
Mirrors DataProcessorService<TInput,TResult,TBehavior> for the register-writing opt-in (IRegisterWritingDataProcessorBehavior, design §2.5) — the only difference is ISubmitScope flows into OnProcessAsync.
Constructors
| Constructor | Description |
|---|
RegisterWritingDataProcessorService(IRegisterWritingDataProcessorBehavior<T0, T1>, IAuthorizationChecker, IValidator<T0>, ISubmitScope) | Mirrors DataProcessorService<TInput,TResult,TBehavior> for the register-writing opt-in (IRegisterWritingDataProcessorBehavior, design §2.5) — the only difference is ISubmitScope flows into OnProcessAsync. |
Kandra.Application.Services.Dictionaries.DictionaryCrudServiceBase<T0, T1, T2>
Constructors
| Constructor | Description |
|---|
DictionaryCrudServiceBase(IMapper, IAuthorizationChecker, IUnitOfWork, IEntityRepository<T2>, IValidator<T0>, IValidator<T1>, IChangeEventRecorder, IChangeEventQueryService, IDictionaryBehavior<T2>, IDictionaryFiltering<T2>) | undocumented |
Kandra.Application.Services.Dictionaries.FlatDictionaryCrudService<T0, T1, T2>
Constructors
| Constructor | Description |
|---|
FlatDictionaryCrudService(IMapper, IAuthorizationChecker, IUnitOfWork, IEntityRepository<T2>, IValidator<T0>, IValidator<T1>, IChangeEventRecorder, IChangeEventQueryService, IFlatDictionaryBehavior<T2>, IDictionaryFiltering<T2>) | undocumented |
Kandra.Application.Services.Dictionaries.HierarchicalDictionaryCrudService<T0, T1, T2, T3, T4>
Constructors
| Constructor | Description |
|---|
HierarchicalDictionaryCrudService(IMapper, IAuthorizationChecker, IUnitOfWork, IEntityRepository<T2>, IValidator<T0>, IValidator<T1>, IChangeEventRecorder, IChangeEventQueryService, IHierarchicalDictionaryBehavior<T2>, IDictionaryFiltering<T2>) | undocumented |
Methods
| Method | Description |
|---|
BuildChildPath(String, Guid) | undocumented |
RewriteDescendantsAsync(HierarchicalDictionaryCrudService<T0, T1, T2, T3, T4>, String, Int32, CancellationToken) | undocumented |
ValidateAndPrepareMoveAsync(Guid, Nullable<Guid>, CancellationToken) | undocumented |
Kandra.Application.Services.Document.DocumentLineCounter
Counts a document's total tabular-section rows, across every collection named by its own RowNavigations - reflection-based, same "resolve a static-abstract interface member off a runtime type" escape hatch as TypeIdResolver, needed because DocumentCrudService<DTO,QUERY_DTO,ENTITY> only constrains ENTITY to IDocumentEntity (not IEntityWithRows<ENTITY> - that constraint is only ever applied per concrete entity type, at DI-registration time, in RegisterEntityWithRowsRepositories). A document whose concrete type doesn't implement IEntityWithRows at all (no tabular sections) simply counts as zero lines.
Kandra.Application.Services.Document.DocumentLinkService
See IDocumentLinkService's own doc comment. GetLinksAsync's display resolution mirrors SubcontoResolutionService.ResolveAsync's Document-kind branch exactly (group ids by the other side's type id, one ILookupRepository.GetDocumentsAsync call per distinct type id - no reflection needed, since v1 is Document-only and every Document is queryable uniformly through the shared TPH DocumentBase set) - reusing IDocumentTypeResolver.Resolve(typeId)?.Name for the type caption key, the same convention SubcontoResolutionService already established (info.Type.Name used directly as the resx key).
Constructors
| Constructor | Description |
|---|
DocumentLinkService(IDocumentLinkRepository, ILookupRepository, IDocumentTypeResolver) | See IDocumentLinkService's own doc comment. GetLinksAsync's display resolution mirrors SubcontoResolutionService.ResolveAsync's Document-kind branch exactly (group ids by the other side's type id, one ILookupRepository.GetDocumentsAsync call per distinct type id - no reflection needed, since v1 is Document-only and every Document is queryable uniformly through the shared TPH DocumentBase set) - reusing IDocumentTypeResolver.Resolve(typeId)?.Name for the type caption key, the same convention SubcontoResolutionService already established (info.Type.Name used directly as the resx key). |
Kandra.Application.Services.Document.DocumentReaderService
Engine-level, registration-free (one plain instance covers every Document type, same posture as AccountTransactionsService) - see IDocumentReaderService's own doc comment. Resolution is a 3-step flow: (1) documentId -> DocumentTypeId via ILookupRepository.GetDocumentTypeIdAsync (a cheap query against the shared TPH DocumentBase set, no reflection needed yet); (2) DocumentTypeId -> concrete Type via IDocumentTypeResolver; (3) reflectively resolve IEntityRepository<T> for that Type from DI and invoke its GetAsync(id, includeChildren: true, ct) - mirrors SubcontoResolutionService. InvokeGetDictionaryAsync's cached-MethodInfo/MakeGenericMethod pattern exactly.
Constructors
| Constructor | Description |
|---|
DocumentReaderService(ILookupRepository, IDocumentTypeResolver, IServiceProvider) | Engine-level, registration-free (one plain instance covers every Document type, same posture as AccountTransactionsService) - see IDocumentReaderService's own doc comment. Resolution is a 3-step flow: (1) documentId -> DocumentTypeId via ILookupRepository.GetDocumentTypeIdAsync (a cheap query against the shared TPH DocumentBase set, no reflection needed yet); (2) DocumentTypeId -> concrete Type via IDocumentTypeResolver; (3) reflectively resolve IEntityRepository<T> for that Type from DI and invoke its GetAsync(id, includeChildren: true, ct) - mirrors SubcontoResolutionService. InvokeGetDictionaryAsync's cached-MethodInfo/MakeGenericMethod pattern exactly. |
Kandra.Application.Services.EntityCrudService<T0, T1, T2, T3>
Properties
| Property | Description |
|---|
Scope | undocumented |
Methods
| Method | Description |
|---|
BuildPagedResult<T0, T1>(ValueTuple<Int32, IList<T0>>, Int32, Int32, Func<T0, T1>) | undocumented |
CheckCreationLimitAsync(CancellationToken) | No-op here; overridden by DocumentCrudService to enforce the license's monthly document-creation limit (docs/kandra-licensing-approach.md §6.2) - dictionaries have no such limit, so this stays a no-op for them. |
Kandra.Application.Services.Navigation.InterfaceCatalogService
Methods
| Method | Description |
|---|
EnsureVisible(InterfaceNodeVisibility) | Throws UnauthorizedAccessException when denied - for gating a whole request (the top-level Interface check in GetInterface), where a denial should fail the request, not silently produce an empty result. |
IsVisible(InterfaceNodeVisibility) | Non-throwing wrapper around EnsureVisible - used for per-node pruning, where a failed check should just drop that one nav node rather than fail the whole response. |
Kandra.Application.Services.Numbering.GaplessNumberingSystem
Plain direct-DbContext access, no repository/audit ceremony - same shape as ConstantsService/SimpleNumberingSystem. Collision detection is atomic-by-construction: the unique index on (Bucket, FormattedNumber) is the signal itself (an insert that violates it throws DbUpdateException), never a separate check-then-insert.
Constructors
| Constructor | Description |
|---|
GaplessNumberingSystem(DbContext) | Plain direct-DbContext access, no repository/audit ceremony - same shape as ConstantsService/SimpleNumberingSystem. Collision detection is atomic-by-construction: the unique index on (Bucket, FormattedNumber) is the signal itself (an insert that violates it throws DbUpdateException), never a separate check-then-insert. |
Kandra.Application.Services.Numbering.SimpleNumberingSystem
Plain direct-DbContext access, no repository/audit ceremony - same shape as ConstantsService. Atomic increments follow the same load-mutate-save-retry-on-conflict pattern already accepted in BalanceRegisterWriter.ApplyDeltaAsync (portable across all 3 providers, no raw SQL).
Constructors
| Constructor | Description |
|---|
SimpleNumberingSystem(DbContext) | Plain direct-DbContext access, no repository/audit ceremony - same shape as ConstantsService. Atomic increments follow the same load-mutate-save-retry-on-conflict pattern already accepted in BalanceRegisterWriter.ApplyDeltaAsync (portable across all 3 providers, no raw SQL). |
Kandra.Application.Services.Registers.RegisterTransactionsMapper
Shared mapping/formatting logic behind every generated {Document}RegisterTransactionsService (Kandra.Generators.RegisterTransactions.Application, one per Document type). Hand-written once here, public so generated code in a different assembly (KandraWms.Application) can call it, rather than re-emitting this identical logic per generated class — only the set of readers to call it with (one per [RegisterTransactions]-declared row type) differs per document.
Methods
| Method | Description |
|---|
ToValue(RawFieldValue, IReadOnlyList<RegisterFieldShape>, IStringLocalizer, TimeZoneInfo) | Reference-kind fields (Dictionary, or a DynamicReference field resolved to a Dictionary/Document candidate — see RawFieldValue's own doc comment; static Document/User Kind resolution still isn't built) carry their resolved CodeNameRef/CodeDateRef straight through; everything else is formatted server-side via the same PrintValueFormatter every report/print column already uses, so a register-transactions grid cell renders identically to its print/report counterpart. |
Kandra.Application.Services.Reports.ReportResultCache
Constructors
| Constructor | Description |
|---|
ReportResultCache(IMemoryCache, IOptions<ReportResultCacheOptions>) | undocumented |
Kandra.Application.Services.Reports.ReportResultCache.CacheKey
Constructors
| Constructor | Description |
|---|
CacheKey(Guid) | undocumented |
Kandra.Application.Services.Reports.ReportResultSizeEstimator
Kandra.Application.Validation.PagedQueryDtoValidator<T0>
Shared base for query-DTO validators that add no filter-specific rules of their own - every dictionary/document query DTO that's just an empty subclass of DictionaryQueryDto/DocumentQueryDto gets a real (not empty-and-pointless) validator via a one-line subclass of this, satisfying the "validators are mandatory" rule without inventing rules that don't exist yet.