Version 0.4.0 of the three packages fixes writes that DK Plus silently ignored and removes the 50,000-record paging wall. It is a breaking release; use this page as a checklist. Upgrading from 0.2 or earlier? Read Upgrading to 0.3 first. Going on to 0.5? Continue with Upgrading to 0.5 after this page.
DK Plus ignores contacts, item receivers, and memos inside the customer document: a create or update that carried them stored none. They are removed from the customer requests and managed through their own resources.
| Before | Now |
|---|---|
CustomerCreateRequest.Contacts and CustomerUpdateRequest.Contacts |
Removed. Use client.Customer.Contact (GetAllAsync, GetAsync, CreateAsync with CustomerContactCreateRequest, UpdateAsync with the sparse CustomerContactUpdateRequest, DeleteAsync) |
Receivers on CustomerCreateRequest and CustomerUpdateRequest |
Removed. Use client.Customer.Receiver (same members, CustomerReceiverCreateRequest, CustomerReceiverUpdateRequest). GetByNumberAsync and GetCustomersAsync look a receiver up across customers |
Memos on customer requests |
Removed. Customer memos have no write endpoint and are read-only (still returned on DkPlusCustomer.Memos) |
CustomerReceiver without an owner |
CustomerReceiver.Customer is the owning customer returned by the receiver endpoints |
An update request that sets no member throws ArgumentException before anything is sent. The contact id is the contact number. DK Plus reads lag writes by a few seconds, so a read right after a create or update may not show the change yet.
| Before | Now |
|---|---|
| Product requests nested the read models for barcodes, warehouse settings, units, and vendors | Dedicated request types: ProductBarcodeRequest, ProductWarehouseSettingsRequest (with ProductVariationRequest), ProductUnitConversionRequest, ProductVendorRequest. Stock quantities, purchase history, and nested Modified times are never sent |
Vendor and product Memos took the MemoPage read model |
MemoPageRequest |
Warehouse.UpdateProductAsync(..., ProductWarehouse) |
Takes ProductWarehouseSettingsRequest. It no longer sends QuantityInStock, QuantityOnBackOrders, QuantityPoOrders, Modified, or an unset Variations list. The ProductWarehouse and WarehouseProductVariation read models are removed |
GeneralLedger.Account.CreateAsync(GeneralLedgerAccount) |
GeneralLedgerAccountCreateRequest (the account Number is validated before sending; Modified is never sent) |
GeneralLedger.TaxGroup.CreateAsync(GeneralLedgerTaxGroup) |
GeneralLedgerTaxGroupCreateRequest |
A warehouse Variations list that was null was sent as [] |
Omitted when null |
Change stock quantities with inventory journals or counts, not through product or warehouse writes.
DK Plus ignores Lines in the head body of v2 documents. The client now writes lines through the line endpoints.
| Before | Now |
|---|---|
Order, quote, or recurring-sale CreateAsync with Lines was rejected by DK Plus (HTTP 400 "This action does not support creating lines in the process") |
The head is posted without lines, each line is posted to {uid}/Line in order, and the document is re-read so the result carries its lines. The whole request is validated before the first call |
| Failure while adding lines | The new head is deleted and the original exception is rethrown (an AggregateException if that delete also fails). If you supplied the document Number, the head is never deleted, because DK Plus may have matched an existing document; the exception says so |
| Re-read after the lines fail | Once every line is stored, a failed re-read no longer fails the create: a warning is logged and the head response is returned, whose lines may be missing |
UpdateAsync with Lines answered HTTP 200 but DK Plus ignored the lines |
The head is sent without lines, then each line in order: PUT {uid}/Line/{sequence} for a line with a Sequence, POST {uid}/Line for a line without one. Stored lines you do not list are kept. An update that sets only Lines sends no head update, and Lines = [] sends nothing |
| A failed line mid-update | Nothing is rolled back. The exception keeps status, body, and correlation ID, and its message names the failed line and states that the head and earlier lines were already applied |
Quote lines with QuantityDelivered, OrderedQuantity, DailyRate, or IndexRatio |
Rejected before sending, because v2 quote lines cannot store them |
Known DK Plus issue with quotes. DK Plus can create a v2 quote head with Id 0 and no Number. Its line endpoint then fails and the head cannot be deleted. Sales.Quote.CreateAsync with lines therefore stops after the head, sends no line and no delete, and throws InvalidOperationException naming the UId and stating that the lines were not added and the head may not be deletable. Creating a quote without lines is unaffected. Order and recurring-sale creates with lines work.
| Before | Now |
|---|---|
Sales.Quote.DeleteAsync used DELETE /api/v2/sales/quote/{uid}, which DK Plus answers with HTTP 405 |
Uses the route in the DK Plus Swagger document, DELETE /api/v2/sales/quote/api/v2/sales/quote/{uid}. This route has not yet been confirmed against a live company |
ObjectDate) and Modified| Before | Now |
|---|---|
Modified was filled from DK Plus ObjectDate |
Modified is the dkERP modification time and can now be null. ObjectDate is a separate read-only member labelled Stored Date (Icelandic: Vistað dags.) |
| No stored time on some models | ObjectDate on DkPlusCustomer, CustomerTransaction (new), DkPlusVendor, DkPlusProduct, the sales invoice, order, quote, and recurring-sale subtypes, v2 sales lines, work requests, and project journals |
Projecting with Include |
ObjectDate is projectable on customers, customer transactions, vendors, products, sales invoices, orders, quotes, recurring sales, and (through a raw include list) work-request pages; not on v2 lines or project journals |
| Modified-since, modified-after, and modified-before filters read as "modified" | For entities whose DK Plus list response has an ObjectDate, DK Plus pages and filters on ObjectDate; for the others it uses Modified. The client only forwards the window, so a record whose Modified is older than the window can still be returned |
Code that read Modified expecting the stored time should read ObjectDate.
DK Plus serves at most 50,000 records per query (page x count).
| Before | Now |
|---|---|
ToListAsync, AsAsyncEnumerable, and ToPageAsync failed at the limit |
They continue automatically: the query restarts at page 1 with a modified-after window just before the newest paging timestamp (never earlier than your ModifiedSince) and skips records it already returned. The dimension, project phase, task, and journal list methods do the same. Customers, products, vendors, sales invoices, customer transactions, v2 orders, quotes, recurring sales, and project journals page on ObjectDate; projects, dimensions, phases, and tasks page on Modified |
| A query that cannot continue safely | Throws InvalidOperationException asking for a narrower ModifiedSince/ModifiedBefore window, for example when there is no modification filter, records arrive out of order, or too many records share one millisecond |
Direct page methods (for example Purchase.Vendor.GetPageAsync, Customer.Transactions.GetPageAsync, Project.WorkRequest.GetPageAsync) accepted any page and count |
Reject page x count above 50,000 with ArgumentOutOfRangeException before sending. Narrow the query with a modified window or another filter and start again from page 1 |
| Continuation tokens from 0.3.x | Rejected with ArgumentException, as are tokens from another record type or a query with other filters. Start the query again without a token. Tokens are opaque and should not be logged |
Include projections sent only the identity plus your fields |
A projection on a paged query always adds the identity and the paging timestamp. Existing include lists therefore gain ObjectDate (for customer transactions the identity Id and ObjectDate replace Modified). Exclude of either throws ArgumentException before sending |
Work-request page with a raw Include list |
Always selects both Id and Number, so every returned record can be updated or deleted |
Lists without a modification filter (sales people, product units, Project.WorkRequest.GetForProjectAsync) throw InvalidOperationException before requesting a page past 50,000.
JournalType) read as their documented names instead of digits.AdditionalData and written back on update.Properties object.DevOp.ERP.Abstractions adds the ModelDisplayNames.ObjectDate label and the general-ledger labels used by the new create requests, in English and Icelandic. Nothing is removed from the abstractions package.| Before | Now |
|---|---|
customer create --contact-number --contact-name --contact-email --contact-mobile |
Removed. Use customer contact create C-100 --number 1 --name "Accounts payable" --email ... --mobile ... |
customer create --receiver-number --receiver-name --receiver-email --receiver-phone |
Removed. Use customer receiver create C-100 --number MAIN --name "Main delivery" ... |
Customer --file with Contacts or Receivers |
Rejected with exit code 2, naming customer contact create or customer receiver create. Memos is rejected as read-only |
| (new) | customer contact and customer receiver each have list, get, create, update, and delete. Create and update take direct options or a --file (CustomerContactCreateRequest / sparse CustomerContactUpdateRequest, and the receiver equivalents); delete requires --yes. A --file update that sets no member stops with exit code 2. list uses the new customer.contact and customer.receiver column profiles |
warehouse update-product MAIN ITEM-1 --file doc.json --yes |
warehouse update-product MAIN ITEM-1 --file doc.json. It is an ordinary update; --yes is now an unknown option. The file is a ProductWarehouseSettingsRequest |
Product, memo, and warehouse files with nested stock quantities (QuantityInStock, QuantityOnBackOrders, QuantityPoOrders, QuantityOnHand, LastPurchased*, variation Quantity) or Modified |
Rejected with exit code 2 naming the replacement (inventory journal or count). ObjectDate is rejected as read-only in product, customer, vendor, and work-request files |
Warehouse files with LocationInWarehouse, DefaultPOQuantity, SkipPOSuggestion, Code2, Description2 |
Rejected with exit code 2; use Location, DefaultPurchaseQuantity, SkipPurchaseOrderSuggestions, SecondaryCode, SecondaryDescription |
general-ledger account create and tax-group create --file read the read models |
Read GeneralLedgerAccountCreateRequest and GeneralLedgerTaxGroupCreateRequest. Modified is rejected as read-only, and the tax-group keys Dim1, Dim2, Dim3, Reconciliate, InTaxType are rejected naming Dimension1, Dimension2, Dimension3, Reconcile, InputTaxType |
Unknown keys inside vendor Approvers, Dimensions, Expenses, Delivery, BankAccount, PaymentBankAccount failed with exit code 2 |
Forwarded to DK Plus |
JSON output and tables showed Modified filled from the stored time |
JSON includes ObjectDate; the default tables of customer, product, vendor, sales invoice, order, quote, recurring sale, project journal, and work request show an ObjectDate column; the get tables add a Stored Date row. Customer transaction JSON includes ObjectDate and its page command pages on it |
-m/--modified-since, --modified-after, --modified-before on any list |
Same options. Help now says DK Plus compares ObjectDate for entities that have one, otherwise Modified |
Page commands accepted any page and -n/--count |
Fail locally with exit code 2, before any request, when page x count passes 50,000, with a hint to narrow the query and start again from page 1 |
dimension list, project phase list, project task list, project journals stopped at 50,000 |
Continue past 50,000 records automatically. project work-request list-for has no modification filter and stops with exit code 2 before passing 50,000, as does any list whose window cannot advance safely |
| Vendor transaction JSON showed numeric enum digits | Shows the documented names (all but JournalType) |
Ordinary create and update commands still need no --yes; destructive and safety-sensitive commands keep it. See the command reference for the document type of each command.