What is Kandra
Kandra is an ERP/business-application engine for .NET (C# / EF Core / Blazor / Avalonia). It reimagines the domain abstractions that 1C:Enterprise and Odoo built their success on — Documents, Dictionaries, Registers, Reports — using modern C# idioms and AI-native tooling, rather than a bespoke scripting language or a Python ORM layered on a generic web framework.
If you've configured 1C or built modules on Odoo, the concepts here will feel familiar. If you haven't, think of Kandra as a framework where the shape of a business object (does it post transactions? does it hold reference data? is it a read-only report?) determines a fixed set of pieces you implement, and the framework generates everything else — REST controllers, client DI wiring, EF Core configuration, and (for several entity kinds) the Blazor UI itself.
The two things in this repo family
- Kandra (the platform) — the reusable engine, consumed as a set of libraries. This is what you reference from a new configuration project; you don't fork it.
- KandraWms ("Kandra WMS") — the first reference configuration built on the platform: a real warehouse-management domain plus a chart-of-accounts posting subsystem. It exists to prove the engine works end-to-end for a real domain, and it's the codebase this documentation site's practical guides constantly point at for working examples.
A configuration, in Kandra terms, is your own KandraWms-shaped project: a set of
Dictionaries/Documents/Registers/Reports that model your business domain, built by
referencing the Kandra platform libraries the same way KandraWms does. In practice you don't
hand-build that shape — you scaffold it with a dotnet new template that produces an empty,
buildable project referencing the platform purely via NuGet packages, with no platform source
checkout required. See How to start.
The entity type system — the core abstraction
Every business object you build is one of seven kinds, each with a fixed capability profile:
| Kind | Posts/submits? | Has UI/form? | Typical use |
|---|---|---|---|
| Document | Yes | Yes | Transactional records — waybills, invoices, transfers. Writes registers and postings only inside OnSubmitAsync. |
| Dictionary | No | Yes | Reference/master data — items, warehouses, counterparties. Optional hierarchy. |
| Report | No | Yes (generated) | Parameterized, read-only queries over registers/documents. |
| DataProcessor | No | Yes (transient) | A batch-operation form that isn't itself persisted. |
| Register | — | No | Analytical storage (balances, turnovers, info records) written only by document behaviors. |
| Constant | No | Admin UI only | A single scalar configuration value. |
| Enum | — | No | A plain C# enum, used like any other .NET enum but integrated into the generated UI/print layer. |
Every form-bearing kind (Document/Dictionary/Report/DataProcessor) is built from the same four pieces, split across two planes — server-only and client+server shared:
*Baseentity — the pure EF Core model. No UI or auth attributes.*Dto— the attribute-rich contract the generators read to emit controllers, client DI wiring, and generated UI.*Behavior— lifecycle hooks (OnNewAsync,OnBeforeSaveAsync,OnSubmitAsync, ...).*Validator— shared client+server validation, mandatory even if it just delegates to the base implementation.
You'll build all four, in that shape, for nearly everything you add. The Creating a dictionary and Creating a document guides walk through this concretely.
Why generators, not a scripting language
1C and Odoo both solve "don't repeat yourself across form/API/storage" with a dynamic runtime
metadata layer or a scripting language. Kandra solves the same problem with Roslyn source
generators: you write a strongly-typed C# *Dto with attributes, and the generator emits
real C# — a real controller, real DI registration, a real EF Core configuration — into a
Generated.Net/ folder you can read (but must never hand-edit). This means:
- Full IntelliSense and compile-time checking on generated code, because it is checked-in compilable C#, not a runtime-interpreted script.
- No separate "recompile the metadata layer" step — a normal
dotnet buildregenerates everything. - AI coding assistants (this documentation exists partly for that reason) can read generated output directly to understand what a Dto attribute actually produces, instead of having to simulate a scripting runtime.
Where to go next
- How to start — scaffold, build, and run your own configuration.
- Overall architecture — how the pieces above fit together as a running system (projects, DI, request pipeline).
- Then pick the entity kind you need: Dictionary, Register, Document, Report, Data processor, or Constant.