Перейти до основного вмісту

Kandra.Application

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

ConstructorDescription
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

MethodDescription
ResolveObjectName(Type)undocumented

Kandra.Application.ConfigureServices

Methods

MethodDescription
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.

Kandra.Application.Scheduling.ConfigureServices

Methods

MethodDescription
AddKandraScheduling<T0, T1>(IServiceCollection)undocumented

Kandra.Application.Scheduling.QuartzTriggerFactory

Kandra.Application.Scheduling.SchedulerAdminService<T0, T1>

Methods

MethodDescription
GetCronSummary(String)undocumented

Kandra.Application.Scheduling.SchedulerBootstrapService<T0, T1>

Constructors

ConstructorDescription
SchedulerBootstrapService(ISchedulerFactory, IServiceScopeFactory, SchedulerHistoryListener)undocumented

Kandra.Application.Scheduling.SchedulerHistoryListener

Constructors

ConstructorDescription
SchedulerHistoryListener(IServiceScopeFactory)undocumented

Kandra.Application.Scheduling.SchedulerJobDataKeys

Fields

FieldDescription
DataProcessorGroupundocumented

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

ConstructorDescription
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

MethodDescription
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

ConstructorDescription
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

ConstructorDescription
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

MethodDescription
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

MethodDescription
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

ConstructorDescription
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

ConstructorDescription
BlobService(IBlobRepository, IBlobContentStore, IOptions<BlobStorageOptions>)undocumented

Methods

MethodDescription
StoreAsync(Stream, String, String, CancellationToken)undocumented

Kandra.Application.Services.Blobs.BlobService.AdminRow

Constructors

ConstructorDescription
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

ConstructorDescription
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

ConstructorDescription
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

ConstructorDescription
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

ConstructorDescription
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

ConstructorDescription
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

ConstructorDescription
HierarchicalDictionaryCrudService(IMapper, IAuthorizationChecker, IUnitOfWork, IEntityRepository<T2>, IValidator<T0>, IValidator<T1>, IChangeEventRecorder, IChangeEventQueryService, IHierarchicalDictionaryBehavior<T2>, IDictionaryFiltering<T2>)undocumented

Methods

MethodDescription
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

ConstructorDescription
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

ConstructorDescription
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

PropertyDescription
Scopeundocumented

Methods

MethodDescription
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

MethodDescription
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

ConstructorDescription
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

ConstructorDescription
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

MethodDescription
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

ConstructorDescription
ReportResultCache(IMemoryCache, IOptions<ReportResultCacheOptions>)undocumented

Kandra.Application.Services.Reports.ReportResultCache.CacheKey

Constructors

ConstructorDescription
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.