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

Kandra.Application.Abstractions

Kandra.Application.Abstractions.Behaviors.IDataProcessorBehavior<T0, T1>

A data processor's behavior contract — deliberately simpler than entity CRUD (IDictionaryBehavior/IDocumentBehavior): a processor has no persistence of its own, so there is no Load/Save/Delete, only two hooks: OnNewAsync (produce/default a fresh input, mirrors the "new" endpoint every entity has) and OnProcessAsync (do the work, return a result).

Kandra.Application.Abstractions.Behaviors.IDocumentBehavior<T0>

Methods

MethodDescription
OnNewAsync(T0, Nullable<DateTime>, Nullable<Guid>, CancellationToken)date is the caller-requested business date, if any (e.g. a data processor creating a document for a specific historical date). Null means "no preference" — implementations typically default to DateTime.UtcNow in that case. sourceDocumentId is the id of the document the user is creating this one "from" (the View page's "Create {Type}" button, driven by [SourceFor] on the source Dto) — null for an ordinary, unseeded new document. A behavior that wants to prefill from it should resolve it via IDocumentReaderService and pattern-match to the concrete type it expects, no-op gracefully otherwise (the caller only guarantees a document id exists, never its concrete type).
OnPrintAsync(T0, ExportFormat, String, ILookupRepository, IPrintRendererResolver, IStringLocalizer, TimeZoneInfo, CancellationToken)undocumented
OnSubmitAsync(T0, Boolean, ISubmitScope, CancellationToken)registers is the only way to obtain a register writer (B03) — writer interfaces are not resolvable via plain DI.
OnUnsubmitAsync(T0, ISubmitScope, CancellationToken)Reverses the register writes made by OnSubmitAsync (unposts, doesn't delete the document itself). registers follows the same B03 write-scope rule.

Kandra.Application.Abstractions.Behaviors.IHandlerBehavior<T0, T1>

A handler's actual backend computation — deliberately not tied to any entity kind (see docs/kandra-document-handlers-design.md). Registered as a keyed DI service, keyed by the handler's own name (declared via [KandraHandler(Name = "...")] on its marker type), because TIn/TOut alone aren't guaranteed unique across handlers (two unrelated handlers may happen to share the same request/response shape) — only the name is.

Kandra.Application.Abstractions.Behaviors.IRegisterWritingDataProcessorBehavior<T0, T1>

Opt-in for a data processor that writes registers directly (design kandra-architecture.md §2.5) — administrative tasks such as period close or recompute. Mirrors IDataProcessorBehavior, except OnProcessAsync is given an ISubmitScope. The choice of this interface instead of IDataProcessorBehavior is the capability — no attribute needed.

Kandra.Application.Abstractions.Behaviors.IReportBehavior<T0, T1>

Methods

MethodDescription
Export(T1, ExportFormat, String, IPrintRendererResolver, IStringLocalizer, TimeZoneInfo)undocumented
GetDataAsync(T0, ILookupRepository, CancellationToken)undocumented
OnNewAsync(T0, CancellationToken)Produces/defaults a fresh filters DTO, mirroring OnNewAsync - lets a report prefill sensible defaults (e.g. "today", "non-empty only") server-side instead of every client hard-coding them.

Kandra.Application.Abstractions.CallerUser

Constructors

ConstructorDescription
CallerUser(Guid, String, IReadOnlyList<Claim>)undocumented

Kandra.Application.Abstractions.ClaimsHelper

Methods

MethodDescription
MakeClaim(String, ApplicationAction)For objects with no single Type whose Name already matches the claim's object name — e.g. a DataProcessor/RegisterWritingDataProcessor behavior, whose own ObjectName convention (input Dto's simple name minus the trailing "Dto") never equals the behavior class's own Type.Name.

Kandra.Application.Abstractions.Constants.ConstantRegistryEntry

One compile-time-known constant, as discovered by the domain's constants registry generator. DefaultInstance is a real, already-constructed instance carrying the compiled default — nothing needs to reflectively construct one to read it.

Constructors

ConstructorDescription
ConstantRegistryEntry(String, Guid, Type, Type, IConstant)One compile-time-known constant, as discovered by the domain's constants registry generator. DefaultInstance is a real, already-constructed instance carrying the compiled default — nothing needs to reflectively construct one to read it.

Kandra.Application.Abstractions.Constants.ConstantSnapshot

One constant's effective value as of a given date — either a real DB override, or the compiled default when nothing has ever been set. Id is the backing ConstantRecord.Id when IsOverridden is true (null otherwise) - lets a caller edit that exact row by id rather than by re-deriving its effective instant. HasFutureOverride is true when at least one row exists dated after the query's as-of instant - e.g. a value already scheduled to replace the current/default one, which would otherwise be invisible from this snapshot alone (its own ValueJson/SetAt only ever reflect what's effective right now).

Constructors

ConstructorDescription
ConstantSnapshot(String, String, Boolean, Nullable<DateTime>, Nullable<Guid>, Boolean)One constant's effective value as of a given date — either a real DB override, or the compiled default when nothing has ever been set. Id is the backing ConstantRecord.Id when IsOverridden is true (null otherwise) - lets a caller edit that exact row by id rather than by re-deriving its effective instant. HasFutureOverride is true when at least one row exists dated after the query's as-of instant - e.g. a value already scheduled to replace the current/default one, which would otherwise be invisible from this snapshot alone (its own ValueJson/SetAt only ever reflect what's effective right now).

Kandra.Application.Abstractions.Constants.Constant<T0>

Base class for a typed, code-first constant descriptor (e.g. HomeCurrencyConstant : Constant<string>("UAH"), IConstant). Derived classes must be a public, parameterless-constructible sealed class carrying the compiled default via their own primary constructor's base-call argument — see docs/kandra-architecture.md §2.7 and the plan this implements.

Constructors

ConstructorDescription
Constant(T0)Base class for a typed, code-first constant descriptor (e.g. HomeCurrencyConstant : Constant<string>("UAH"), IConstant). Derived classes must be a public, parameterless-constructible sealed class carrying the compiled default via their own primary constructor's base-call argument — see docs/kandra-architecture.md §2.7 and the plan this implements.

Kandra.Application.Abstractions.Constants.IConstant

Non-generic marker for IConstant — anchors the where C : IConstant, new() constraint on IConstantsService.GetAsync<C> without needing the value type spelled out at the call site.

Kandra.Application.Abstractions.Constants.IConstantRegistry

Lists every [KandraConstant]-attributed Constant known to the current domain config. Implemented by generated code in the domain-config layer (e.g. KandraWms.Application) and registered into DI there — the engine layer only ever sees this interface, never the concrete constant classes themselves.

Kandra.Application.Abstractions.Constants.IConstant<T0>

Deliberately NOT : IConstant — a static abstract interface member (Name) can't be left unimplemented by an intermediate abstract class the way an instance member can (the compiler requires some concrete provider once the interface is implemented, and abstract classes can't declare abstract statics). So Constant only carries Value via this interface; each concrete leaf class implements IConstant directly for Name — see the leaf class example this generates from.

Kandra.Application.Abstractions.Files.IFileData

Kandra.Application.Abstractions.ICallerContext

Kandra.Application.Abstractions.ICallerContextOverride

Kandra.Application.Abstractions.ICurrentTimeZone

Kandra.Application.Abstractions.Navigation.EntityRouteConventions

Resolves the client route, permission check, and nav caption key for a [Kandra*Form]-attributed Dto type purely by reflection over its own attributes - the runtime counterpart of the route/policy convention baked into the four Kandra.Generators.Ui.Blazor.*UiEmitter classes (UiCodeGenHelpers.RouteStem/ .PolicyStem, plus each emitter's own kind-segment/hierarchical handling) and cross-checked against Kandra.Generators.Di.Application.EntityClaimsEmitter's claim-seeding convention. That generator project is Roslyn-analyzer-only and can't be referenced at runtime, so this is a deliberate second copy of the same algorithm - keep the two in sync if the route/claim convention ever changes. Reports are the one kind deliberately excluded from Visibility: a Report's real permission object name is a hand-picked string on its IReportBehavior<,>.ObjectName (e.g. RestsReportDto -> ObjectName "Rest", not "RestsReport") with no attribute-derivable convention, and EntityClaimsEmitter itself never generates report claims - so there's nothing correct to check here. An Entity node bound to a Report Dto is always visible once its containing Interface is.

Methods

MethodDescription
PolicyStem(String)Mirrors UiCodeGenHelpers.PolicyStem and EntityClaimsEmitter.ObjectNameFromDto: strip a trailing "Dto", keep PascalCase - the same string a generated claim's ObjectName carries.
RouteStem(String)Mirrors UiCodeGenHelpers.RouteStem: strip a trailing "Dto", camelCase the rest.

Kandra.Application.Abstractions.Navigation.InterfaceDefinition

One named, server-defined navigation tree ("Interface", 1C-style command interface). A configuration registers at least one via Kandra.Application.ConfigureServices.AddKandraInterface - the first one registered is the default. Visibility gates the whole interface (null = visible to every authenticated user); individual nodes are additionally pruned by their own visibility when the tree is served.

Constructors

ConstructorDescription
InterfaceDefinition(String, InterfaceNodeText, InterfaceNodeVisibility, IReadOnlyList<InterfaceNodeDefinition>)One named, server-defined navigation tree ("Interface", 1C-style command interface). A configuration registers at least one via Kandra.Application.ConfigureServices.AddKandraInterface - the first one registered is the default. Visibility gates the whole interface (null = visible to every authenticated user); individual nodes are additionally pruned by their own visibility when the tree is served.

Kandra.Application.Abstractions.Navigation.InterfaceNodeDefinition

One node of an InterfaceDefinition tree. Closed set of shapes via Kind plus the static factories below - construct through those, never the private constructor directly.

Properties

PropertyDescription
CaptionAlways set for Folder/Special/Link. Null for Entity unless explicitly overridden - a null Entity caption means "resolve it from the bound Dto's own [NavigationName]".
ChildrenFolder only; empty for every other kind.
DtoTypeEntity only.
RouteLink only.
SpecialKindSpecial only.
VisibilitySpecial/Link only - Entity nodes always auto-derive their visibility from the bound Dto (see EntityRouteConventions), and Folder nodes have none of their own (visibility follows from their visible children).

Methods

MethodDescription
Link(String, InterfaceNodeText, String, InterfaceNodeVisibility)A hardcoded, non-Dto-bound route that isn't one of the engine's own predefined SpecialInterfaceNodeKind pages - e.g. a configuration's own dev/admin tool page.

Kandra.Application.Abstractions.Navigation.InterfaceNodeText

A localizable label for an Interface/InterfaceNode. Same Value/IsLiteral shape as Kandra.Attributes.Naming.VisibleNameAttribute - resolved the same way, IsLiteral ? Value : localizer[Value] - but as a plain constructible value instead of an attribute, since Interface trees are built as instances at DI-registration time, not declared on a class.

Constructors

ConstructorDescription
InterfaceNodeText(String, Boolean)A localizable label for an Interface/InterfaceNode. Same Value/IsLiteral shape as Kandra.Attributes.Naming.VisibleNameAttribute - resolved the same way, IsLiteral ? Value : localizer[Value] - but as a plain constructible value instead of an attribute, since Interface trees are built as instances at DI-registration time, not declared on a class.

Kandra.Application.Abstractions.Navigation.InterfaceNodeVisibility

Gates one Interface/InterfaceNode against the same claim-based permission model every generated CRUD service already enforces (IAuthorizationChecker.Authorize(IAuthorizationPoint) - see Kandra.Application.Authorization.AuthorizationPoint). There is no ASP.NET Core named-policy mechanism registered server-side (only the client's own AuthorizationCore, used purely for AuthorizeView UI-gating, has named policies) - this is the real, enforced-server-side equivalent.

Methods

MethodDescription
AdminOnlySame posture as the old NavMenu's admin-only links (Users/Roles/Selector Demo) - those used a client-only "RequireRole(admin)" policy with no claim behind it, so the real server-side equivalent is a plain admin check, not a claim.

Kandra.Application.Abstractions.Navigation.SpecialInterfaceNodeKind

Predefined, non-Dto-bound page kinds the engine itself knows how to route to. Deliberately closed and engine-owned - a configuration-specific hardcoded route (a dev/admin page that isn't an engine page) belongs in Link instead, not as a new member here (adding one would leak a configuration-specific concept into the engine, which the platform's naming/placement rule forbids).

Kandra.Application.Abstractions.Scheduling.DataProcessorExecutionOutcome

Constructors

ConstructorDescription
DataProcessorExecutionOutcome(Boolean, String, String)undocumented

Kandra.Application.Abstractions.Scheduling.DataProcessorRegistryEntry

Constructors

ConstructorDescription
DataProcessorRegistryEntry(String, Type, Type, Type, Func<IServiceProvider, CancellationToken, Task<DataProcessorExecutionOutcome>>)undocumented

Kandra.Application.Abstractions.Scheduling.IDataProcessorRegistry

Kandra.Application.Abstractions.Services.AccountTransactionsReadResult

In-process-only shape - never serialized to the wire (see Kandra.Forms.Accounting for the wire DTOs a caller eventually builds from this).

Constructors

ConstructorDescription
AccountTransactionsReadResult(IReadOnlyList<PostingTransactionRecord>, IReadOnlyDictionary<Guid, IReadOnlyList<Guid>>)In-process-only shape - never serialized to the wire (see Kandra.Forms.Accounting for the wire DTOs a caller eventually builds from this).

Kandra.Application.Abstractions.Services.Blobs.EntitySyncResult

Constructors

ConstructorDescription
EntitySyncResult(IReadOnlyList<Guid>, IReadOnlyList<Guid>, IReadOnlyList<Guid>, IReadOnlyList<Guid>)undocumented

Kandra.Application.Abstractions.Services.Blobs.IBlobService

Business logic for the blob storage subsystem (docs/blob-storage-architecture.md §6.2), built on top of IBlobRepository (Kandra.Persistence.Abstractions). Never calls SaveChanges/commits a transaction itself, so it composes safely inside a caller's own unit of work - a document/dictionary behavior's OnSaveAsync/OnDeleteAsync calls this in the middle of the entity's own save/delete transaction; a standalone BlobsController action opens/commits its own.

Methods

MethodDescription
DetachAllForEntityAsync(Guid, Guid, CancellationToken)undocumented
OpenReadAsync(Guid, CancellationToken)undocumented
RestoreAsync(Guid, CancellationToken)undocumented
SearchReferencesAsync(BlobReferenceSearchQueryDto, CancellationToken)undocumented
SyncEntityReferencesAsync(Guid, Guid, IReadOnlyCollection<Guid>, CancellationToken, String)undocumented
TombstoneAsync(Guid, Nullable<TimeSpan>, String, CancellationToken)undocumented
UploadAsync(Stream, String, String, Nullable<Guid>, Nullable<TimeSpan>, CancellationToken)undocumented
UploadForEntityAsync(Stream, String, String, Guid, Guid, CancellationToken, String)undocumented

Kandra.Application.Abstractions.Services.DocumentLinkSyncItem

Constructors

ConstructorDescription
DocumentLinkSyncItem(Guid, Guid, String, Nullable<Guid>, Nullable<Guid>)undocumented

Kandra.Application.Abstractions.Services.IAccountTransactionsReader

Fetches raw posted ledger transactions for a document, and prepares the set of subconto (TypeId -> distinct EntityIds) that still needs display resolution - the input Kandra.Persistence.Abstractions.Accounting.ISubcontoResolutionService needs. Deliberately configuration-agnostic (no KandraWms-specific knowledge) - it only reads raw (TypeId, EntityId) pairs off PostingTransactionRecord.SourceSlot1-5/DestinationSlot1-5, it never interprets what concrete CLR type a TypeId corresponds to.

Kandra.Application.Abstractions.Services.IAccountTransactionsService

One instance covers every Document type - unlike Kandra.Forms.Registers' IDocumentRegisterTransactionsService<TEntity>, there is exactly one universal posted-transaction shape (Kandra.Domain.Accounting.PostingTransactionRecord) for every account on every document, so there is no per-Document specialization to generate. Registered once (Kandra.Application.ConfigureServices), not per-ENTITY.

Kandra.Application.Abstractions.Services.IChangeEventQueryService

Methods

MethodDescription
GetHistoryAsync(Guid, ChangeHistoryQueryDto, CancellationToken)undocumented
GetPayloadJsonAsync(Guid, Guid, CancellationToken)undocumented
QueryAsync(ChangeEventQueryDto, CancellationToken)undocumented
ResolveUserNamesAsync(IEnumerable<Guid>, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.IChangeEventRecorder

Write side of the change log (gap C07). Adds a ChangeEvent row to the current DbContext without saving - it flushes atomically with the entity/constant write inside the caller's own SaveChanges (IUnitOfWork.SaveAsync for the generic CRUD path, DbContext.SaveChangesAsync for ConstantsManageService).

Methods

MethodDescription
RecordRawAsync(ChangeEventScope, Guid, Guid, ChangeAction, String, Guid, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.IChartOfAccountsTreeService

Builds the wire tree DTO from Kandra.Domain.Accounting.IChartOfAccountsService.Chart - the Application-layer orchestrator a hand-written controller delegates to, same placement/role as ILookupService (LookupController) and IChangeEventQueryService (ChangeEventsController). One instance covers every configuration's chart (engine-level, configuration-agnostic, like IAccountTransactionsService).

Kandra.Application.Abstractions.Services.IClientFeatureFilter

Optional hook, not registered by the platform by default: when a configuration registers an implementation, it decides which Microsoft.FeatureManagement feature names are allowed to be stamped into the JWT as claims (e.g. to keep server-only features out of the client-visible set). When nothing is registered, every server-enabled feature is included.

Kandra.Application.Abstractions.Services.IConstantsManageService

Write/enumerate access to constants. Every method here checks the write-constant permission (unlike the ungated IConstantsService read side). Upserting at an already-used date is how a past-dated row gets corrected — there is no separate "edit history" operation.

Methods

MethodDescription
DeleteAsync(Guid, CancellationToken)Soft-deletes one historical row by its own Id - the admin API's path, since round-tripping a row's exact effective instant through a URL is needlessly fragile once Constants carry real instant (not just day) precision.
DeleteAsync(String, DateTime, CancellationToken)Soft-deletes one historical row, matched by its exact effective instant (not day-truncated - pass the same DateTime a row was set with).
GetAllAsync(Nullable<DateTime>, CancellationToken)Every known constant's effective value as of a date (defaults to now) — including ones that have never been overridden in the database, via their compiled default.
GetHistoryAsync(String, CancellationToken)All dated rows for one constant name, newest first.
GetNames(CancellationToken)Every constant name known to the current domain config (from its generated registry).
SetAsync(String, String, Nullable<DateTime>, CancellationToken)Stores value verbatim as the row's ValueJson — no JSON encoding is applied, so the caller is responsible for passing an already-correctly-shaped string.
SetAsync<T0>(String, T0, Nullable<DateTime>, CancellationToken)Wraps value as {"Value": ...}, the shape GetAsync/GetAsync expect. When T is itself String, C# overload resolution prefers the non-generic SetAsync for a call written as SetAsync(name, someString) — pass the type argument explicitly (SetAsync<string>(name, someString)) to actually reach this overload.
SetRawAsync(String, JsonElement, Nullable<DateTime>, CancellationToken)Same wrapping as SetAsync, but for a value whose CLR type isn't known at compile time (e.g. an admin API endpoint working off IConstantRegistry metadata and a raw JSON request body). This is the "add a new value" path - matched/upserted by exact effective instant, same as every other SetAsync overload. To edit an existing row's value without touching its effective instant, use UpdateValueAsync instead.
UpdateValueAsync(Guid, JsonElement, CancellationToken)Replaces one existing row's value in place, identified purely by its own Id - deliberately does not touch Date at all, and never falls back to matching by effective instant (re-deriving and comparing a DateTime across a network round trip is unreliable - offset/precision differences, DST boundaries - so it's not used as an identity key for an edit). Throws NotFoundException if id doesn't match a live row.

Kandra.Application.Abstractions.Services.IConstantsService

Read-only access to constants' current (or as-of-date) value. Ungated — no permission check — so document behaviors can resolve a constant (e.g. the home currency) without ceremony. Writing goes through IConstantsManageService instead.

Methods

MethodDescription
GetAsync<T0>(Nullable<DateTime>, CancellationToken)Typed access via a code-first Constant descriptor (e.g. GetAsync<HomeCurrencyConstant>()). Never returns null — falls back to the descriptor's compiled default when nothing has been overridden in the database.

Kandra.Application.Abstractions.Services.IDictionaryCrudService<T0, T1>

Dictionary-specific CRUD contract. Adds NewAsync - flat dictionaries construct their one ENTITY type with no extra parameters, unlike IHierarchicalDictionaryCrudService's NewAsync(parentId, isFolder, ...) or IDocumentCrudService's date-aware overload.

Methods

MethodDescription
LookupAsync(T1, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.IDocumentCrudService<T0, T1>

Document-specific CRUD contract. Adds the date-aware NewAsync - only documents have a Date concept, so this doesn't belong on the shared IEntityCrudService base (dictionaries get their own date-less overload via IDictionaryCrudService).

Methods

MethodDescription
GetAccountTransactionsAsync(Guid, CancellationToken)Every posted ledger transaction for this document (chart-of-accounts/posting subsystem), resolved for display. Gated by the same View permission as the document's own read endpoints — not a separate permission. Empty result for a document type that never posts, same empty-state convention as GetRegisterTransactionsAsync.
GetLinksAsync(Guid, CancellationToken)Every Document Link touching this document, both directions (links from it, links to it) - see IDocumentLinkService.GetLinksAsync. Gated by the same View permission as the document's own read endpoints — not a separate permission. Unconditional for every Document, unlike GetRegisterTransactionsAsync/GetAccountTransactionsAsync - there's no [SomeAttribute]-gated opt-in, every document can be linked.
GetRegisterTransactionsAsync(Guid, CancellationToken)Every register this document type is declared to touch (via [RegisterTransactions] on its Behavior), resolved for this document instance. Gated by the same View permission as the document's own read endpoints — not a separate permission.
LookupAsync(T1, CancellationToken)undocumented
NewAsync(Nullable<DateTime>, Nullable<Guid>, CancellationToken)Builds a fresh DTO for a not-yet-persisted document. date lets a caller that knows the intended business date (e.g. a data processor creating a document for a specific historical date) pass it through to the entity's OnNew hook, which can use it to stamp Date/derive Code instead of always defaulting to "now". Controllers pass null (today/now is the hook's default). sourceDocumentId is the "create from source document" id (View page's "Create {Type}" button, see [SourceFor]) - also passed straight through to the entity's OnNew hook, null for an ordinary unseeded new document.
PrintAsync(Guid, DocumentExportRequest, CancellationToken)Loads the document by id and fires OnPrintAsync with request's Format (defaults to Pdf when null)/LayoutTag. Null means the document has no print form (default no-op behavior hook) - a missing/deleted id throws NotFoundException same as every other by-id document operation.
SubmitAsync(Guid, CancellationToken)Flips IsActive to true and posts to registers (fires OnSubmitAsync). Silent no-op if the document is already active.
UnsubmitAsync(Guid, CancellationToken)Flips IsActive to false and unposts from registers (fires OnUnsubmitAsync). Silent no-op if the document is already inactive.

Kandra.Application.Abstractions.Services.IDocumentLinkService

Document Links: a directed, tagged reference from one document to another - either the "created from source" relationship (Invoice -> Waybill) or an ad-hoc link a user/data processor establishes (Payment -> Invoice). Written by a document's own OnSaveAsync, deliberately NOT tied to Submit/Unsubmit/Delete. Engine-level, registered once (Kandra.Application.ConfigureServices), not per-Document-type - same registration-free posture as IAccountTransactionsService/IDocumentReaderService.

Methods

MethodDescription
GetLinksAsync(Guid, CancellationToken)Both directions of Document Links touching entityId, resolved for display. A row whose other-side document no longer resolves (deleted, or its type is no longer registered) is dropped silently, never thrown - per the locked "never cascade-delete, fail to resolve gracefully" decision.
SyncLinksAsync(Guid, Guid, IReadOnlyCollection<DocumentLinkSyncItem>, CancellationToken)Diffs links (the document's own current desired state) against persisted rows for fromEntityId, matched by identity (ToEntityId, ToEntityTypeId, FromLineId, ToLineId, Tag) - inserts rows present in links but not yet persisted, removes persisted rows no longer present in links, leaves matching rows untouched (there is no meaningful "update" beyond identity). Call from OnSaveAsync; does not call SaveChangesAsync itself - the caller's own save pipeline flushes it, same convention as register writers/IPostingService.

Kandra.Application.Abstractions.Services.IDocumentReaderService

Generic "open any document by id" service - resolves documentId's concrete Document type at runtime (via IDocumentTypeResolver), then loads it fully typed with all tabular-section lines (IEntityRepository<T>.GetAsync(id, includeChildren: true, ...)). Callers who already know the type they expect (e.g. WaybillBehavior.OnNewAsync expecting an Invoice source document) pattern-match the result and no-op gracefully when it's some other/missing type - the caller only guarantees a document id exists, never its concrete type. Engine-level, registered once (Kandra.Application.ConfigureServices), not per-Document-type - same "registration-free, stays generic on a bare Guid" posture as IAccountTransactionsService.

Methods

MethodDescription
GetAsync(Guid, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.IDocumentRegisterTransactionsService<T0>

Combines every register a Document type is declared to touch (via [RegisterTransactions] on its Behavior class) into one DTO for a given document instance. One implementation is generated per Document type by Kandra.Generators.RegisterTransactions.Application, uniformly — a Document with zero [RegisterTransactions] attributes still gets a (trivial, empty-result) implementation, so DocumentCrudService<,,> can always constructor-inject this without a nullable/optional dance.

Kandra.Application.Abstractions.Services.IEntityCrudService<T0, T1>

Methods

MethodDescription
GetChangeStateAsync(Guid, CancellationToken)undocumented
GetHistoryAsync(Guid, ChangeHistoryQueryDto, CancellationToken)undocumented
GetPastStateAsync(Guid, Guid, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.IGaplessNumberingSystem

Number issuance for the Gapless family (docs/numbering-subsystem-spec.md 3.2): no gaps, no pooling - callers must defer generation to OnSave/OnSubmit once the document's final business state (esp. date, if used in the bucket) is authoritative. Formatting is always the caller's responsibility.

Methods

MethodDescription
GetNextAsync(String, Func<UInt64, String>, Nullable<Guid>, String, CancellationToken)Atomically increments the bucket's raw counter, formats the result, and durably records the formatted string as issued. If the formatted string already exists in the bucket's issued set (e.g. a manual entry pre-claimed it), the raw counter is incremented again and reformatted, repeating until a free string is found or a bounded retry limit is hit (then throws with bucket/last-raw-value/requestor context).
RegisterManualAsync(String, String, Nullable<Guid>, String, CancellationToken)Registers a number entered manually (outside the auto-generation path) as issued, so future GetNextAsync calls never produce a colliding formatted string. Throws if the number is already issued.

Kandra.Application.Abstractions.Services.IHierarchicalDictionaryCrudService<T0, T1>

Hierarchical-dictionary CRUD contract. Does not extend IDictionaryCrudService - its date-less NewAsync(CancellationToken) has no room for the parentId/isFolder a hierarchical dictionary needs to pick which concrete (folder vs leaf) entity type to construct.

Methods

MethodDescription
LookupAsync(T1, CancellationToken)undocumented
MoveToAsync(Guid, Nullable<Guid>, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.IInterfaceCatalogService

Resolves the registered Kandra.Application.Abstractions.Navigation.InterfaceDefinitions (see its own doc comment) into wire Dtos for the current caller - the business logic behind Kandra.Api.Lib.Controllers.V1.InterfacesController, split out so it can be unit tested without standing up ASP.NET Core (no ControllerBase/HttpContext involved, just IAuthorizationChecker).

Methods

MethodDescription
GetInterface(String)The full, visibility-pruned tree for one Interface by name. Throws Kandra.Domain.Exceptions.NotFoundException if no Interface is registered under name, or UnauthorizedAccessException if the caller can't use it - both handled by HttpGlobalExceptionFilter the same way every other domain exception already is, so the controller needs no branching of its own. Individual node-level visibility failures inside a found, permitted Interface never throw - they just prune that node, since one hidden link shouldn't fail the whole request.
GetVisibleInterfacesEvery registered Interface the current caller may see, in registration order (first = default). Throws InvalidOperationException if no Interface has been registered at all - a configuration always needs at least one default.

Kandra.Application.Abstractions.Services.IPostingService

Methods

MethodDescription
AsOf(AccountCode)Convenience proxy for IChartOfAccountsService.AsOf(...) - lets a document behavior build a PostingTransaction's Source/Destination without needing its own IChartOfAccountsService dependency alongside IPostingService.
PostAsync(Guid, IReadOnlyList<PostingTransaction>, DateTime, String, CancellationToken)Posts a batch of linked source->destination transactions for a document. Upsert semantics: if the document already has posted transactions, they're merged in place with the new list - added, updated, or removed line by line - rather than reversed and reinserted. The caller never needs to know whether this is the first post or a re-post. Never calls SaveChangesAsync itself - the caller must call SaveChangesAsync on the same DbContext after PostAsync returns, or nothing gets persisted.
UnpostAsync(Guid, CancellationToken)Reverses a document's entire posting batch - deprecates every active transaction row and reverts their balance effect. Safe no-op if the document was never posted. Never calls SaveChangesAsync - same convention as PostAsync.

Kandra.Application.Abstractions.Services.IReportResultCache

Kandra.Application.Abstractions.Services.IReportService<T0, T1>

Methods

MethodDescription
ExportAsync(ReportExportRequest<T0>, CancellationToken)undocumented

Kandra.Application.Abstractions.Services.ISchedulerAdminService

Methods

MethodDescription
GetAllAsync(ScheduledJobQueryDto, CancellationToken)Paged, same shape as GetRunsAsync/every other admin list endpoint - GetJobLookupAsync is the lightweight, unpaged alternative for a filter dropdown that just needs Id/Name.

Kandra.Application.Abstractions.Services.ISimpleNumberingSystem

Number issuance for the Simple family (docs/numbering-subsystem-spec.md 3.1): gaps tolerated, number visible immediately in OnNew, pooling supported. Formatting is always the caller's responsibility - a bucket is an opaque string, the service only guarantees atomic, monotonically increasing raw values.

Methods

MethodDescription
GetNextAsync(String, Func<UInt64, String>, Nullable<Guid>, String, CancellationToken)Atomically increments the bucket's raw counter (creating it, starting from 1, on first use) and applies formatter to the result. Never returns null; throws on a real storage failure.
GetNextFromPoolAsync(String, String, Func<UInt64, String>, Nullable<Guid>, String, CancellationToken)Draws the smallest not-yet-drawn raw value previously leased into poolName (FIFO) and applies formatter to it. Returns null when the pool is exhausted - an expected, non-exceptional outcome, not an error.
LeaseToPoolAsync(String, String, Int32, Nullable<Guid>, String, CancellationToken)Atomically reserves count raw values from the bucket's counter into poolName for later sequential draw. Stores raw values only - the formatter is applied at draw time.

Kandra.Application.Abstractions.Services.ISubmitScope

The only way to obtain a register writer or the posting service (design: kandra-architecture.md §3.6, kandra-register-engine-design.md §3.6/§8.1). Available inside engine-managed write scopes — document posting (OnSubmitAsync/OnUnsubmitAsync/OnDeleteAsync) and opt-in register-writing data processors — never resolvable from the DI container directly, since writer interfaces and IPostingService are registered as keyed services only ISubmitScope resolves against.

Methods

MethodDescription
GetPostingServiceThe chart-of-accounts posting service. Gated the same way as register writers - only reachable inside a submit scope, never via plain constructor injection.
GetReader<T0>Convenience accessor for register readers through the same handle as GetWriter. Readers are ordinary, ungated scoped services (unlike writers) - this doesn't add a new gating mechanism, it just lets posting-hook code reach them without a separate constructor injection.

Kandra.Application.Abstractions.Services.IUserClaimsBuilder<T0, T1>