Skip to content
lvdsystemsPublic

About

A Product Lifecycle Management (PLM) web app for a small aerospace/defense manufacturer's

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

lvdplm

A Product Lifecycle Management (PLM) web app for a small aerospace/defense manufacturer's EN 9100 quality management system: parts, BOMs, documents, production batches, serial number genealogy, and RMAs. Django + DRF backend, React (TypeScript, Vite, Mantine) frontend.

Licensed under Apache 2.0. Contributions welcome — see CONTRIBUTING.md.

Architecture

Django REST Framework backend, one app per domain area, mirroring PLAN.md's architecture; React (TypeScript, Vite, Mantine) SPA frontend talking to it over session-authenticated JSON — see API.md for the full REST reference.

A few choices that shape the rest of the codebase:

  • Attach-anywhere documents and comments — Document and Comment use a GenericForeignKey rather than one FK column per attachable node type, so a document can hang off a Part, PartRevision, Project, or Serial Number without a schema change each time a new node type needs it.
  • Per-part/batch/serial custom fields — firmware revision and similar fields the company iterates on live in a JSONField (custom_field_definitions on Part, dynamic_fields on ProductionBatch/SerialNumber), not fixed columns.
  • Audit trail — every core model carries django-simple-history (full field-level history, who/when/what), plus explicit append-only event tables (SerialEvent, BatchPhaseChange) for the genealogy timelines EN 9100 traceability actually gets queried against.
  • State machines are admin-configurable — ProductionBatch, RMA, SerialNumber, and Document each drive a state machine with a hardcoded default transition graph, but RMA and ProductionBatch consult the WorkflowTransition table first, so an Admin can tighten or loosen allowed transitions from the Administration page without a code change.
  • Numbering is race-safe, not just unique — batch and RMA numbers increment via a SequenceCounter row updated inside select_for_update(), self-healing against the table's actual max so legacy-imported records that bypass the counter can't cause a collision on the next suggestion.

Data model

The core domain objects and how they relate (admin/config/audit models — WorkflowTransition, SiteSettings, ImportRun, Subscription — and the generic Document/Comment attach points omitted for clarity; see API.md for those):

erDiagram
    PROJECT ||--o{ PART : "top-level parts (optional)"
    PART ||--o{ PART_REVISION : has
    PART_REVISION ||--o{ BOM_ITEM : children
    PART ||--o{ BOM_ITEM : "used as child"
    VENDOR ||--o{ PART : supplies
    PROJECT ||--o{ PRODUCTION_BATCH : batches
    PART_REVISION ||--o{ PRODUCTION_BATCH : "frozen anchor"
    PRODUCTION_BATCH ||--o{ SERIAL_NUMBER : generates
    PART_REVISION ||--o{ SERIAL_NUMBER : "current revision"
    SERIAL_NUMBER ||--o{ RMA_ITEM : "tracked unit (optional)"
    RMA ||--o{ RMA_ITEM : items
    CUSTOMER ||--o{ RMA : reports
    CUSTOMER ||--o{ CUSTOMER_DIVISION : divisions
Loading

Part is either internal (owned, lives under a Project) or purchased (sourced from a Vendor) — never both, enforced in Part.clean(). A PartRevision becomes immutable once released; that's the pointer a ProductionBatch freezes onto at production start, which transitively pins the BOM tree and every attached document. SerialNumber starts pointing at its batch's frozen revision but can be upgraded per-unit independently (logged as a SerialEvent). An RMAItem links either a tracked SerialNumber or a free-text reference for a unit outside the system (legacy stock, third-party serials) — never both.

Dev environment (WSL Debian)

One-time machine setup (installs git, Docker, Python, Node — needs your sudo password, run it yourself in an interactive shell):

wsl -d Debian
bash /mnt/d/D/lvdplm/scripts/setup-wsl-debian.sh
# log out/in, or `newgrp docker`, so the docker group membership applies

Then, from a WSL Debian shell in this directory (/mnt/d/D/lvdplm):

cp backend/.env.example backend/.env
docker compose up --build

First-time database setup, in another terminal while docker compose up is running:

docker compose exec backend python manage.py migrate
docker compose exec backend python manage.py seed_groups
docker compose exec backend python manage.py seed_document_types
docker compose exec backend python manage.py createsuperuser

Running tests / lint locally

docker compose exec backend python manage.py test
docker compose exec backend ruff check .
docker compose exec frontend npm run lint
docker compose exec frontend npm run build

Production deployment

See DEPLOYMENT.md — docker-compose.prod.yml (MySQL + gunicorn + nginx), environment variable reference, TLS setup, backups, and a from-scratch Proxmox LXC (Debian) walkthrough.

About

A Product Lifecycle Management (PLM) web app for a small aerospace/defense manufacturer's

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages