v2026.5.9¶
Released 2026-05-09. First public release.
This release covers a sixteen-phase build that took the plugin from "scaffold" to "shipped" in about 24 hours of focused work. Every phase shipped end-to-end with tests, browser verification, and updated docs.
Schema¶
- 8 migrations (
0001→0008) - 7 models:
ServiceProvider,Contract,Invoice,ContractAssignment,ContractAttachment,InvoiceAttachment,CostSnapshot - 4 ChoiceSet enums:
ContractTypeChoices,CoverageHoursChoices,ResponseTimeChoices,RestorationTimeChoices,BillingPeriodChoices
What's in this release¶
Core data model¶
ServiceProvider,Contract,Invoice,ContractAssignment— the v1 spine- Generic-FK
ContractAssignmentso one contract row can cover any Nautobot model with a UUID PK - 4 statuses (Active / Expired / Cancelled / Pending) registered at install via data migration
Real-world contract structure (Phase 7)¶
- Structured SLA fields on
Contract:contract_type,coverage_hours,response_time,restoration_time,notice_period_days,auto_renew,term_months - Per-assignment scope on
ContractAssignment:coverage_start,coverage_end,scope_notes,is_primary - Transitive coverage helper walking
(self, tenant, location, rack, device)ancestry CoverageGapJob+ "Coverage Gaps" home dashboard panel
Cost analytics (Phase 8)¶
Contract.billing_periodChoiceSet (monthly/quarterly/semiannual/annual/one_time)cost.pywith 7 helpers (per-contract math + fleet-wide aggregations)- "Cost Summary" + "Renewal Forecast" home dashboard panels (per-currency, no FX conversion)
CostReportJobwrites burn / renewal / top vendor / gap-count to JobLogEntry
Renewal Calendar (Phase 9 + 10)¶
- Forward-looking month-by-month grid at
/reports/renewal-calendar/ - Single-hue amber saturation scale, dark-mode-aware, print-friendly
- "NOW" badge + amber rails on the current month
- Hover tooltips with per-cell contract names
- Cross-link from the Renewal Forecast dashboard panel
Bulk CSV import (Phase 11)¶
- Standard Nautobot import flow at
/contracts/import/— fully auto-wired byNautobotUIViewSetRouter - Sample CSV + format quirks docs in
development/sample-data/
Action Required surface (Phase 12)¶
- Centralized
priority.action_priority()rubric — URGENT / WARNING / INFO RenewalCheckJobrefactored to use the same rubric (log lines tagged[URGENT]/[WARNING]/[INFO])- Dedicated page at
/reports/action-required/with three bucket cards - "Action Required" home dashboard panel showing top 5 priority items
Cost history time-series (Phase 13)¶
CostSnapshotmodel — write-once telemetry decoupled from Contract live statecost.take_snapshot()+cost.history()helpersCostHistoryJobfor weekly persistence/reports/cost-history/page with three inline-SVG line charts (no JS chart library)
REST API for snapshots (Phase 14)¶
- Read-only endpoint at
/api/plugins/contracts/cost-snapshots/ - DRF mixin composition (
ListModelMixin + RetrieveModelMixin + GenericViewSet) — write methods aren't routable, returns 405 - Filterable by
snapshot_date__gte,snapshot_date__lte,currency
Cost-anomaly detection (Phase 15)¶
cost.detect_anomalies(threshold, lookback_weeks)helperCostAnomalyJobwith configurable thresholds- New currencies report
pct_change=Noneso callers can render "NEW"
Notes (Phase 16)¶
- Auto-wired by Nautobot's
NautobotUIViewSetRouter— no plugin code needed - Markdown notes on every Contract / Invoice / ServiceProvider / Assignment / Attachment
Tests¶
73 integration tests + 9 anomaly tests = 82 tests, all passing. Coverage spans:
- Transitive coverage walking
- Severity rubric edge cases
- billing_period normalization
- Per-currency aggregation
- Cost-history snapshot idempotency
- Anomaly threshold logic
- Calendar window math
Breaking changes¶
None — first release.
Known issues¶
- Migration
0007_contract_billing_perioddefaults existing contracts tobilling_period='monthly'. If you're somehow upgrading from a development copy that pre-dates Phase 8, edit annual/quarterly contracts after migration to fix the burn-rate calculation. (Not applicable to fresh installs.)
Upgrade path¶
This is the first release; install fresh per Install.