DevOp.DK.Plus is a typed .NET 10 integration layer for the DK Plus v1 and v2 APIs. It is designed for application code, background jobs, multitenant services, and command-line workflows.
Application
|
+-- DevOp.ERP.Abstractions
| IExternalErpClient
| IErpQuery<T>
| Provider-neutral DTOs
|
+-- DevOp.DK.Plus
IDkPlusClient
DK-specific resources
v1/v2 HTTP adapter
internal transport models
|
+-- DevOp.DK.Plus.Cli
devop-dk command
Applications normally inject one client rather than separate customer, product, invoice, and order services. Root resources are singular, and related operations are grouped by business area: Sales.Invoice/Order/Quote/Recurring/Person, Purchase.Vendor/Invoice, GeneralLedger.Account/Transaction/Journal/TaxGroup, and the child resources under Project (Phase, Task, WorkRequest, Transaction, Journal, WorkEntry). Reference data follows the same pattern through Currency, Payment.Mode/Term/Type, Customer.Group, and Product.Group/Category/Unit.
Use IExternalErpClient when the application should remain portable across ERP providers. It exposes customers, products, employees, projects, the sales documents, reference data, and read-only vendors (Purchase.Vendor, advertised by ErpCapability.Vendors):
public sealed class CustomerImport(IExternalErpClient erp)
{
public Task<IReadOnlyList<Customer>> ReadAsync(
DateTimeOffset modifiedSince,
CancellationToken cancellationToken) =>
erp.Customer.Query()
.ModifiedSince(modifiedSince)
.ToListAsync(cancellationToken);
}
Use IDkPlusClient when a workflow needs a DK Plus-specific capability such as generated documents, generic tables, token operations, or converting an order to an invoice.
Public APIs expose models owned by DevOp:
DevOp.ERP.Models contains the lean, provider-neutral models: Customer, Product, Employee, Project, Vendor, SalesPerson, and the sales documents.DevOp.DK.Plus.Models contains everything specific to DK Plus: a sealed DkPlus<Entity> subtype of each portable model (for example DkPlusCustomer : Customer), the request models, and DK-only types such as CustomerContact, MemoPage, and BankAccount.A member is portable only when other planned ERP providers clearly have it with the same meaning. A customer's number, name, national ID, email, phone, and address are portable; the VAT number, ÍSAT code, gender, payment defaults, contacts, receivers, and similar DK Plus data live on DkPlusCustomer. IExternalErpClient returns the portable type, and IDkPlusClient returns the subtype from every get, list, search, query, and paging member. This boundary prevents a remote schema change from automatically becoming a breaking change for every consumer.
If portable code exposes or logs the objects it receives, project them to your own DTOs first. JsonSerializer.Serialize<Customer>(value) writes only portable members, but serializers that use the runtime type (ASP.NET Core output formatters, minimal APIs, Serilog {@Customer}) also write the DkPlus* members, including contact personal data and accounting codes.
Each writable entity has a {Entity}CreateRequest (the full document) and a sparse {Entity}UpdateRequest whose identity is the path argument. Read models are never accepted as write input. See Models and writes.
Portable records with a natural label and identifier expose AsString(), for example Example product (P-1). Postal addresses use an ordered Address.Lines collection rather than provider-specific Address1, Address2, and Address3 properties; Address.AsString() and ToString() provide compact display text.
DTO properties use resource-backed DisplayAttribute metadata. English is the fallback language and Icelandic is included for is and is-IS. JSON contracts remain culture-independent. Invalid request models are rejected recursively before any HTTP request is sent.
See Localization and globalization and Request model validation.
The caller does not select an API version. Each resource uses the appropriate endpoint internally. For example, invoice operations and generic tables use v1, while sales-order and quote CRUD use v2. Generated order and quote PDF, HTML, and email operations use their v1 numeric document endpoints.
A future Regla or Payday adapter can implement IExternalErpClient through the same abstractions. Provider-specific capabilities remain on the provider client, and unsupported capabilities fail explicitly instead of silently returning empty data.
Next: Getting started