Add high-level API behavior coverage and public model fixes - #334
Add high-level API behavior coverage and public model fixes#334aligalipcamli wants to merge 97 commits into
Conversation
Detailed production-fix guideSince this PR contains both broad high-level API coverage and production fixes, here is a more detailed guide to the production changes. The production changes were made only when a focused public API behavior test exposed a clear bug. Surprising but stable current behavior was generally left unchanged and documented as characterization coverage. Entity / LedgerEntity parent creation orderingProblem: Fix: Parent resolution and validation now happen before saving the new entity. The entity is inserted as a child only after the parent is known to be valid. Why it matters: This protects entity tree integrity and avoids failed public API calls leaving persisted side effects. Explicit CoA lookup in Entity account helpersProblem: Entity account helper paths using explicit CoA slug/UUID referenced the wrong related manager. Fix: The helper now uses the correct Chart of Accounts related manager. Why it matters: Public account creation helpers now work consistently with model, slug, and UUID CoA inputs. Fiscal-year closing digest wrapperProblem: Fix: The wrapper now passes the expected Why it matters: Fiscal-year closing helpers now correctly delegate to the date-based API. Closing metadata cache invalidationProblem: Closing-entry date metadata could be cached on an entity instance and remain stale after closing books. Fix: The closing-date cache is cleared when closing metadata is saved. Why it matters: Reusing the same entity instance after closing books now reflects current closing metadata. Explicit bank-account account guardProblem: Fix: The explicit account is now validated against the entity / selected CoA. Why it matters: This prevents cross-entity financial account bindings. Ledger queryset and locked-period fixesProblem: Several public ledger helpers had edge-case issues: Fix: The queryset predicates were corrected, and locked-period detection now checks the earliest posted journal entry where appropriate. Why it matters: Ledger state queries and locked-period guards are public accounting safety behavior. Import / Staged transactions / ReceiptsImportJob entity slug scopingProblem: Fix: Slug scoping now filters through the import job’s bank account entity. Why it matters: Import jobs are entity-scoped; slug lookup should behave like other public ImportJob URL helpersProblem: Some ImportJob URL helpers referenced stale route names. Fix: The helpers were updated to current route names. Why it matters: Public URL helpers should not raise StagedTransaction unmatch cleanupProblem: Fix: It now clears both match references. Why it matters: Reconciliation state should be clean after unmatching. Receipt delete with no closing dateProblem: Fix: The delete predicate now handles Why it matters: New entities often have no closing date; receipt undo/delete paths should still work. Receipt configure lookup variantsProblem: Several receipt configure paths had lookup bugs: vendor UUID checked the wrong variable, customer lookup did not consistently scope through the resolved entity, and unit slug/UUID values could be assigned before resolution. Fix: Vendor, customer, and unit inputs are now resolved through the intended entity-scoped paths before assignment. Why it matters: Receipt configuration is used by import and receipt workflows and must not mix raw identifiers or cross-entity lookups. Closing entriesClosingEntryTransaction entity scopingProblem: Fix: Model resolution and slug scoping were corrected. Why it matters: Closing-entry transaction querysets are public entity-scoped APIs. ClosingEntry delete URLProblem: Fix: The helper now points to the existing delete route. Why it matters: Model URL helpers should remain usable by views/templates/downstream code. Single-sided closing migrationProblem: Invalid single-sided closing migration data leaked a Fix: It now raises Why it matters: Public validation paths should raise domain-level validation errors, not implementation exceptions. InvoicesReview queryset filterProblem: Fix: It now filters by the review status. Why it matters: This is a direct public queryset contract. Lifecycle date persistenceProblem: Fix: The correct date fields are now assigned and persisted. Why it matters: Lifecycle dates are public document state. Descriptive title labelProblem: Fix: It now uses the invoice label. Why it matters: This is user-facing display/helper behavior. EstimatesExplicit draft dateProblem: Fix: Explicit draft dates are now preserved. Why it matters: Public configuration APIs should respect caller-supplied document dates. Cross-entity customer guardProblem: Fix: Cross-entity customers are rejected. Why it matters: Commercial documents must not bind parties across entity boundaries. Missing void action routeProblem: Estimate mark-as-void URL helper pointed to a route/view that did not exist. Fix: The missing view and URL route were added. Why it matters: Public URL helpers should resolve to real routes. Invoice / Estimate cross-entity bindingProblem: Fix: A cross-entity guard was added. Why it matters: Invoice-to-estimate binding must preserve entity isolation. Purchase orders / Bill bindingPO item aggregationProblem: Non-DB PO item aggregation summed the wrong field and could return Fix: It now sums Why it matters: Public aggregate helpers should produce consistent totals in DB-backed and in-memory paths. Lifecycle date and amount persistenceProblem: Several PO lifecycle methods did not persist public state correctly: draft date, void date, fulfilled date, and received amount. Fix: The relevant lifecycle methods now persist the intended fields. Why it matters: Purchase order lifecycle methods should update durable public state. Void transition query conflictProblem: PO void transition could crash due to a queryset Fix: The conflicting query optimization was removed. Why it matters: Public lifecycle actions should not fail due to internal queryset construction. Configure-time estimate bindingProblem: Fix: Entity assignment now happens before binding. Why it matters: Binding predicates rely on entity context. Bill / PO cross-entity guardProblem: Fix: Cross-entity PO binding is now rejected. Why it matters: Bills and purchase orders must remain within the same entity. List-backed fulfillment inputProblem: Fix: List-backed input is now handled safely. Why it matters: The public helper is more robust for prefetched item lists. PO bill queryset specificityProblem: Fix: It now scopes through this PO’s actual Why it matters: PO-to-bill relationship lookup should not overmatch through shared catalog items. Items / Item transactions / InventoryEstimate item eligibilityProblem: Fix: It now uses estimate eligibility semantics. Why it matters: Manager and queryset helpers now agree on estimate-eligible items. Orphan item transactionsProblem: Fix: Invoice-owned item transactions are now excluded. Why it matters: Document-owned rows should not be classified as orphaned. Ordered status helperProblem: Fix: It now compares against Why it matters: Public status helpers and CSS helpers now reflect the actual item status. Inventory account CoA resolutionProblem: Fix: The selected/default CoA is resolved before validating the explicit account. Why it matters: Entity inventory factory helpers now work with default CoA behavior and explicit inventory accounts. Customer / VendorVendor picture upload pathProblem: Fix: It now uses vendor numbering. Why it matters: Vendor upload paths should be based on vendor identity. Customer/Vendor validate_for_entity invalid inputsProblem: Invalid inputs leaked implementation exceptions: Fix: Both now raise model-specific validation errors. Why it matters: Public validation helpers should expose domain errors. Customer validation wordingProblem: Customer validation used stale “Vendor” wording. Fix: The wording was corrected. Why it matters: Validation output should match the model being validated. ContactInfoMixin validationProblem: Customer/Vendor Fix: Both now call Why it matters: Concrete party models now apply mixin-level validation such as address consistency. Notes on non-fix characterizationSome surprising behavior was intentionally left unchanged and covered by characterization tests instead. Examples:
These tests document current public behavior so future refactors can change it intentionally rather than accidentally. |
|
I opened a follow-up / stacked PR for the next layer of this work: This PR builds on top of the high-level API behavior/contracts branch here. The intention is not to mix the two review scopes, but to make the next direction visible early. In short, the follow-up branch explores Swapper-backed custom model support for non-core/domain-facing Django Ledger models, while keeping the accounting core fixed. It assumes the model behavior coverage and public API fixes from this PR as its safety foundation. Important boundary: the follow-up work is still scoped as new-project / before-initial-migrate support. It does not claim that existing installations can freely switch from built-in tables to custom model tables without project-specific data migrations. I’m sharing it here mostly for visibility and architectural context. This PR should still be reviewed on its own merits as the high-level API behavior/test foundation. |
Add High-Level Public API Behavior Coverage for Django Ledger Models
Summary
This PR adds a broad high-level API behavior test layer under:
The goal is to document and protect Django Ledger’s public model API behavior: model methods, managers, querysets, numbering helpers, lifecycle actions, URL/display helpers, cross-model bindings, import/staging workflows, and entity-scoped data integrity.
This is not intended to replace the existing test suite. It adds contract-style coverage around public APIs that downstream applications may call directly.
The test campaign also surfaced several public API bugs. Those production fixes are included with focused regression coverage.
Review note
The commit history reflects the chronological development of the campaign. It shows the work progressing model-by-model, with failures either characterized or fixed as they appeared.
For review, it may be easier to use the model-family sections below rather than reading the commits as the primary organization.
Some package/build metadata and generated asset files may also appear in the branch diff from the broader branch history. The main review focus is the API behavior test campaign and related public API fixes described below.
What changed
This PR adds or expands high-level API coverage for these model families:
The README for
django_ledger/tests/api/was also updated to explain the purpose, scope, and design goals of this test layer.Test design
The tests follow these principles:
The tests avoid pinning brittle internals such as exact SQL annotation implementation, private dictionary shapes, full generated-number strings, and database error message text.
Production fixes included
Entity / Ledger
LedgerModelQuerySet.unposted()to return unposted ledgers.LedgerModelQuerySet.current()behavior when the entity has no closing date.Import / StagedTransaction / Receipt
StagedTransactionModel.unmatch()so matched transaction state is fully cleared.ReceiptModel.can_delete()whenlast_closing_dateisNone.ReceiptModel.configure(...)lookup variants for vendor, customer, and entity unit inputs.Closing entries
ClosingEntryTransactionModel.objects.for_entity(...)lazy-loader usage.ClosingEntryModel.get_delete_url()route resolution.ClosingEntryValidationErrorinstead of leakingKeyError.Invoice
InvoiceModelQuerySet.in_review()to filter review invoices instead of draft invoices.mark_as_approved(date_approved=...)persistence.mark_as_draft(draft_date=...)persistence.generate_descriptive_title()to identify invoices as invoices, not bills.Estimate
EstimateModel.configure(date_draft=...)to preserve explicit draft dates.EstimateModel.configure(...)to reject customers from another entity.InvoiceModel.can_bind_estimate(...).Purchase orders / Bill binding
po_total_amount.mark_as_draft(...).date_voidpersistence.po_amount_receivedpersistence.BillModel.can_bind_po(...).po_itemshandling in fulfillment.get_po_bill_queryset()overmatching through shared item models.Items / item transactions / inventory
ItemTransactionModelQuerySet.is_orphan()to exclude invoice-owned rows.ItemTransactionModel.is_ordered()to compare againstSTATUS_ORDERED.EntityModel.create_item_inventory(..., inventory_account=..., coa_model=None)default CoA resolution.Customer / Vendor
vendor_picture_upload_to(...)to use vendor numbering.CustomerModel.validate_for_entity(...).VendorModel.validate_for_entity(...).clean()methods so inherited contact validation runs.Characterization coverage
Some behaviors were intentionally documented rather than changed.
Examples include:
commit=Falsemay consume entity-state sequences.commit=Falsemay mutate in-memory state without persisting the main row.overdue()filters are date-based and not status-scoped.commited.active()and excluded byvisible().These tests are not intended to force behavior changes. They document current public behavior so future refactors can be intentional.
Verification
Targeted tests were run for each batch.
Whenever production code changed, the full high-level API suite was run.
Recent verification milestones:
Final isolated infrastructure and compatibility smoke tests also passed:
Why this is useful
This coverage should help protect downstream users and future maintainers before larger changes involving:
AI assistance disclosure
This test campaign was developed with AI assistance and human review. AI helped draft test structures and iterate on failures; the contributor inspected the source, ran the tests, reviewed behavior, and made the final decisions about which contracts and fixes to keep.