{"id":32652541,"url":"https://github.com/opensensor/django-forms-workflows","last_synced_at":"2026-04-19T04:01:20.541Z","repository":{"id":321719396,"uuid":"1086837626","full_name":"opensensor/django-forms-workflows","owner":"opensensor","description":"An enterprise-grade Django app: database-driven form builder with approval workflows and external data integration. It bridges the gap between simple form libraries (like Crispy Forms) and expensive SaaS solutions (like JotForm, Formstack).","archived":false,"fork":false,"pushed_at":"2026-04-10T20:59:19.000Z","size":2316,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-10T22:15:04.371Z","etag":null,"topics":["django-forms"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opensensor.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-31T01:27:40.000Z","updated_at":"2026-04-10T20:59:22.000Z","dependencies_parsed_at":"2026-03-08T08:04:04.307Z","dependency_job_id":null,"html_url":"https://github.com/opensensor/django-forms-workflows","commit_stats":null,"previous_names":["opensensor/django-forms-workflows"],"tags_count":280,"template":false,"template_full_name":null,"purl":"pkg:github/opensensor/django-forms-workflows","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensensor%2Fdjango-forms-workflows","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensensor%2Fdjango-forms-workflows/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensensor%2Fdjango-forms-workflows/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensensor%2Fdjango-forms-workflows/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opensensor","download_url":"https://codeload.github.com/opensensor/django-forms-workflows/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensensor%2Fdjango-forms-workflows/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31994010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django-forms"],"created_at":"2025-10-31T08:03:59.625Z","updated_at":"2026-04-19T04:01:20.533Z","avatar_url":"https://github.com/opensensor.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Django Forms Workflows\n\n**Enterprise-grade, database-driven form builder with multi-stage approval workflows, external data integration, and cross-instance sync**\n\n[![License: LGPL v3](https://img.shields.io/badge/License-LGPL_v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)\n[![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/downloads/)\n[![Django Version](https://img.shields.io/badge/django-5.2%2B-green)](https://www.djangoproject.com/)\n[![Version](https://img.shields.io/badge/version-0.49.0-orange)](https://github.com/opensensor/django-forms-workflows)\n\n## Overview\n\nDjango Forms Workflows bridges the gap between simple form libraries (like Crispy Forms) and expensive SaaS solutions (like JotForm, Formstack). It provides:\n\n- 📝 **Database-Driven Forms** — Define forms in the database, not code. 25+ field types including calculated/formula fields and spreadsheet uploads.\n- 🔄 **Multi-Stage Approval Workflows** — Sequential, parallel, or hybrid approval flows with configurable stages and conditional trigger logic.\n- ↩️ **Send Back for Correction** — Approvers can return a submission to any prior stage without terminating the workflow.\n- 🎯 **Dynamic Assignees** — Resolve individual approvers at submit time from form field values (email, username, full name, or LDAP lookup).\n- 🔀 **Sub-Workflows** — Spawn child workflows from a parent submission (e.g. one form creates N payment approvals).\n- 🔌 **External Data Integration** — Prefill fields from LDAP, databases, REST APIs, or the Django user model.\n- ⚡ **Post-Submission Actions** — Trigger emails, database writes, LDAP updates, or custom Python handlers on submit/approve/reject.\n- 🔄 **Cross-Instance Sync** — Push/pull form definitions between environments directly from the Django Admin.\n- 🔒 **Enterprise Security** — LDAP/AD \u0026 SSO authentication, RBAC with four permission tiers, complete audit trails.\n- 🌐 **REST API** — Opt-in Bearer-token API: list forms, fetch field schema, submit (or save draft), poll status. OpenAPI 3.0 schema + Swagger UI included.\n- 🖼️ **Embeddable Forms** — Embed forms on any website via a `\u003cscript\u003e` tag or `\u003ciframe\u003e`. Auto-resize, theme/accent color support, postMessage callbacks. WordPress plugin included.\n- 💳 **Pluggable Payments** — Collect payments during form submission with a pluggable provider system. Stripe ships built-in; add custom providers via `register_provider()`.\n- 📋 **Shared Option Lists** — Define reusable choice lists once, reference from any form field. Update the list and every field reflects the change.\n- 🔗 **Dependent Workflows** — Workflows that start only after all other workflows on the form complete, enabling convergence gates after parallel tracks.\n- 📁 **Managed File Uploads** — File uploads with approval, rejection, and version tracking per submission.\n- 🧮 **Formula Fields** — Calculated fields that compute values live from other field values using a template formula.\n- 🏠 **Self-Hosted** — No SaaS fees, full data control.\n\n## Key Features\n\n### 🎯 No-Code Form Creation\nBusiness users create and modify forms through Django Admin:\n- **25+ field types:** text, email, phone, select, radio, checkbox, checkboxes, multiselect, date, time, datetime, decimal, currency, number, URL, file, multi-file, textarea, hidden, section headers, **calculated/formula**, **spreadsheet upload (CSV/Excel)**, country picker, US state picker, **signature (draw or type)**\n- Field ordering with explicit `order` integer\n- Column-width layout per field: full, half, one-third, one-quarter\n- Validation rules (required, regex, min/max length, min/max value)\n- Conditional field visibility (`show_if_field` / `show_if_value`)\n- Custom help text, placeholders, and CSS classes\n- Read-only and pre-filled fields\n- Draft saving with auto-save support\n\n### 🔄 Multi-Stage Approval Workflows\nFlexible approval engine built on `WorkflowStage` records:\n- Each stage has its own approval groups and logic (`any` / `all` / `sequence`)\n- Stages execute in order; next stage unlocks when the current one completes\n- Multiple `WorkflowDefinition` records can run as parallel approval tracks on the same form\n- **Conditional stages** — each stage can carry `trigger_conditions` JSON; stages whose conditions don't match the submission data are automatically skipped\n- **Workflow-level trigger conditions** — entire parallel approval tracks are skipped when their workflow `trigger_conditions` don't match (e.g. only trigger a Finance track when `amount \u003e 10000`)\n- **Dynamic individual assignees** — set `assignee_form_field` + `assignee_lookup_type` (`email` / `username` / `full_name` / `ldap`) on a stage to resolve the approver from a form field value at runtime; falls back to group tasks when the value cannot be resolved\n- **Send Back for Correction** — approvers can return a submission to any prior stage that has `allow_send_back` enabled without terminating the workflow; full audit entry recorded\n- Stage-specific form fields (e.g. approver notes, signature date) appear only during that stage\n- Configurable approval deadline (`approval_deadline_days`) sets `due_date` on created tasks\n- Reminder emails (`send_reminder_after_days`) and optional auto-approval after expiry (`auto_approve_after_days`)\n- Immediate or batched notifications (`immediate` / `daily` / `weekly` / `monthly` / `form_field_date`)\n- Optional per-stage reassignment, editable submission data during approval, custom approve button labels, hidden approval history, and bulk export / bulk PDF export controls\n- Rejection handling with per-stage or global rejection semantics\n- Complete audit trail on every approval, rejection, send-back, and status change\n- First-class outbound webhooks per workflow with signed async delivery, retry/backoff, and delivery logs\n\nSee the [Workflow Guide](docs/WORKFLOWS.md), [Visual Workflow Builder](docs/VISUAL_WORKFLOW_BUILDER.md), [Dynamic Assignees](docs/DYNAMIC_ASSIGNEES.md), [Send Back for Correction](docs/SEND_BACK.md), [Notifications](docs/NOTIFICATIONS.md), and [Workflow Webhooks](docs/WEBHOOKS.md).\n\n### 🔀 Sub-Workflows\nSpawn child workflow instances from a parent submission:\n- `SubWorkflowDefinition` links a parent workflow to a child form definition\n- `count_field` controls how many sub-workflows to create (driven by a form field value)\n- `section_label` and `label_template` control how child instances are presented in the UI\n- `data_prefix` slices the parent's form data to populate each child\n- Triggered `on_submission` or `on_approval`\n- `detached` mode lets child instances complete independently of the parent\n- `reject_parent=True` lets a rejected child immediately reject the parent and cancel sibling child instances\n- Parent submission status reflects aggregate child completion when not detached\n- Sub-workflows support the same send-back mechanism via `handle_sub_workflow_send_back`\n\nSee [Sub-Workflows Guide](docs/SUB_WORKFLOWS.md) for a full walkthrough.\n\n### 🖼️ Embeddable Forms\nEmbed DFW forms on any external website:\n- Single `\u003cscript\u003e` tag creates a responsive iframe with auto-resize via `postMessage`\n- Configurable theme (`light`/`dark`), accent color, min height\n- Callbacks: `data-on-submit`, `data-on-load` for parent page integration\n- WordPress plugin included (`[dfw_form]` shortcode + Gutenberg block)\n- Admin embed code panel with copy-to-clipboard snippets\n- Inline success state — no redirects, iframe stays self-contained\n\nSee [Embedding Guide](docs/EMBEDDING.md).\n\n### 💳 Payment Collection\nCollect payments as part of the form submission flow:\n- Pluggable provider architecture: `PaymentProvider` ABC, provider registry, `PaymentResult` dataclass\n- Two flow types: **INLINE** (payment form on your site, e.g. Stripe Elements) and **REDIRECT** (external payment page)\n- Built-in **Stripe provider** using PaymentIntents with automatic payment methods\n- `PaymentRecord` model tracks full payment lifecycle (pending → completed/failed/refunded)\n- Configurable per form: provider, amount (fixed or from field), currency, description template\n- Custom providers self-register via `register_provider()` in `AppConfig.ready()`\n\nSee [Payment System Guide](docs/PAYMENTS.md).\n\n### 📋 Shared Option Lists\nDefine reusable choice lists shared across forms:\n- `SharedOptionList` model with name, slug, items (JSON array of strings or value/label objects)\n- `shared_option_list` FK on `FormField` overrides inline choices for select, radio, multiselect, checkboxes\n- Choice resolution priority: database prefill → shared option list → inline choices\n- Update a list once, every referencing field updates immediately\n- Form builder UI with shared list dropdown on choice-based fields\n\nSee [Shared Option Lists Guide](docs/SHARED_OPTION_LISTS.md).\n\n### 🔗 Dependent Workflows\nWorkflows that wait for other workflows to complete before starting:\n- `start_trigger = \"on_all_complete\"` on `WorkflowDefinition`\n- Enables convergence patterns: parallel tracks must all finish before a final review step\n- Peer-level relationship (not parent/child like sub-workflows)\n- Complements sub-workflows — both can coexist on the same form\n\nSee the [Dependent Workflows section](docs/WORKFLOWS.md#dependent-workflows) of the Workflows Guide.\n\n### 🧮 Calculated / Formula Fields\nAuto-compute field values from other field inputs:\n- `field_type = \"calculated\"` with a `formula` template string (e.g. `{first_name} {last_name}`)\n- Live client-side evaluation updates the read-only field as the user types\n- Authoritative server-side re-evaluation on submit prevents tampering\n- Available in both regular form submission and approval step forms\n\nSee [Calculated Fields Guide](docs/CALCULATED_FIELDS.md).\n\n### 📊 Spreadsheet Upload Fields\nAccept CSV or Excel files as structured form input:\n- `field_type = \"spreadsheet\"` with accept `.csv`, `.xls`, `.xlsx`\n- Parsed and stored as `{\"headers\": [...], \"rows\": [{...}, ...]}` in `form_data`\n- Available during initial submission and approval step forms\n\n### 🔌 Configurable Prefill Sources\nPopulate form fields automatically from reusable `PrefillSource` records:\n- **User model** — `user.email`, `user.first_name`, `user.username`, etc.\n- **LDAP / Active Directory** — any LDAP attribute (department, title, manager, custom)\n- **External databases** — schema/table/column lookup with template support for multi-column composition\n- **Custom database queries** — reference a named query via `database_query_key`\n- **System values** — `current_date`, `current_time`\n\n### ⚡ Post-Submission Actions\nAutomatically run side-effects after a submission event:\n\n| Trigger | Description |\n|---------|-------------|\n| `on_submit` | Runs immediately on form submission |\n| `on_approve` | Runs when the submission is approved |\n| `on_reject` | Runs when the submission is rejected |\n| `on_complete` | Runs when the entire workflow completes |\n\n**Action types:** `email`, `database`, `ldap`, `api`, `custom`\n\n**Features:**\n- Conditional execution with 10 operators (`equals`, `not_equals`, `greater_than`, `less_than`, `contains`, `not_contains`, `is_empty`, `is_not_empty`, `is_true`, `is_false`, plus date comparisons)\n- Automatic retries with configurable `max_retries`\n- Execution ordering for dependent actions\n- Idempotent locking (`is_locked`) to prevent double-execution\n- Full execution logging via `ActionExecutionLog`\n- Pluggable handler architecture — register custom handlers via `FORMS_WORKFLOWS_CALLBACKS` setting or `register_handler()` API; use short names in the admin instead of full Python paths\n\n### 🔄 Cross-Instance Form Sync\nMove form definitions between environments from the Django Admin:\n- **Pull from Remote** — connect to a configured remote instance and import selected forms\n- **Push to Remote** — select forms and push to any destination\n- **Import / Export JSON** — portable `.json` snapshots\n- **Conflict modes** — `update`, `skip`, or `new_slug`\n- **`FORMS_SYNC_REMOTES`** setting — pre-configure named instances (URL + token)\n- HTTP endpoints protected by Bearer token for CI/scripted use\n\n### 📁 Managed File Uploads\n- `FileUploadConfig` per form definition (allowed extensions, max size)\n- `ManagedFile` records with approval/rejection/supersede lifecycle\n- Version tracking with `is_current` flag\n\n### 🔒 Enterprise-Ready Security\n- LDAP/Active Directory authentication with auto-sync of profile attributes\n- SSO integration (SAML, OAuth) with attribute mapping to `UserProfile`\n- Role-based access — four permission tiers on `FormDefinition`:\n  - `submit_groups` — can see and submit the form\n  - `view_groups` — prerequisite gate for form access\n  - `reviewer_groups` — read-only view of all submissions and approval history\n  - `admin_groups` — full administrative view of all submissions\n- Group-based approval routing via `WorkflowStage.approval_groups`\n- Complete audit logging (`AuditLog` — who, what, when, IP address)\n- **Public / anonymous forms** — mark any form as `requires_login=False` and anonymous users can submit it; IP-based rate limiting prevents abuse\n- `UserProfile` auto-created on first login with LDAP/SSO sync\n\n## Quick Start\n\n### Installation\n\n```bash\npip install django-forms-workflows\n```\n\n### Setup\n\n1. Add to `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n    # ...\n    'crispy_forms',\n    'crispy_bootstrap5',\n    'django_forms_workflows',\n]\n\nCRISPY_ALLOWED_TEMPLATE_PACKS = \"bootstrap5\"\nCRISPY_TEMPLATE_PACK = \"bootstrap5\"\n```\n\n2. Include URLs and run migrations:\n\n```python\n# urls.py\nurlpatterns = [\n    path('forms/', include('django_forms_workflows.urls')),\n]\n```\n\n```bash\npython manage.py migrate django_forms_workflows\n```\n\n3. Create your first form in Django Admin!\n\n### Optional Settings\n\n```python\nFORMS_WORKFLOWS = {\n    # Branding — replaces \"Django Forms Workflows\" across all templates\n    \"SITE_NAME\": \"Acme Corp Workflows\",\n\n    # LDAP attribute sync on every login\n    \"LDAP_SYNC\": {\n        \"enabled\": True,\n        \"attributes\": {\n            \"department\": \"department\",\n            \"title\": \"title\",\n            \"employee_id\": \"extensionAttribute1\",\n        },\n    },\n}\n\n# Cross-instance form sync\nFORMS_SYNC_API_TOKEN = \"shared-secret\"\nFORMS_SYNC_REMOTES = {\n    \"production\": {\n        \"url\": \"https://prod.example.com/forms-sync/\",\n        \"token\": \"prod-token\",\n    },\n}\n\n# Custom callback handlers — register by short name instead of full Python paths\n# These names can be used in PostSubmissionAction.custom_handler_path\nFORMS_WORKFLOWS_CALLBACKS = {\n    \"id_photo_copy\": \"myapp.handlers.IDPhotoHandler\",\n    \"sync_to_erp\": \"myapp.handlers.ERPSyncHandler\",\n}\n\n# Context processor — injects site_name into every template\nTEMPLATES = [\n    {\n        \"OPTIONS\": {\n            \"context_processors\": [\n                # ... standard processors ...\n                \"django_forms_workflows.context_processors.forms_workflows\",\n            ]\n        }\n    }\n]\n```\n\n## Architecture\n\n```mermaid\ngraph TB\n    subgraph UI[\"User Interface\"]\n        FB[\"Form Builder\u003cbr/\u003e(Admin)\"]\n        FV[\"Form Viewer\u003cbr/\u003e(End User)\"]\n        AU[\"Approval UI\u003cbr/\u003e(Approvers)\"]\n    end\n\n    subgraph Core[\"Django Forms Workflows\"]\n        FD[\"FormDefinition\u003cbr/\u003e+ FormField\"]\n        WF[\"WorkflowDefinition\u003cbr/\u003e+ WorkflowStage\"]\n        PS[\"PrefillSource\"]\n        PA[\"PostSubmissionAction\u003cbr/\u003e+ Executor\"]\n        SYNC[\"Sync API\u003cbr/\u003e(Push/Pull)\"]\n    end\n\n    subgraph External[\"External Systems\"]\n        AD[\"LDAP / AD\"]\n        DB[\"External\u003cbr/\u003eDatabases\"]\n        API[\"REST APIs\"]\n        SSO[\"SSO\u003cbr/\u003e(SAML/OAuth)\"]\n    end\n\n    FB --\u003e FD\n    FV --\u003e FD\n    AU --\u003e WF\n    FD --\u003e PS\n    FD --\u003e PA\n    FD --\u003e SYNC\n    PS --\u003e AD\n    PS --\u003e DB\n    PA --\u003e DB\n    PA --\u003e AD\n    PA --\u003e API\n    SSO --\u003e Core\n```\n\n## Use Cases\n\n- **HR** — Onboarding, time-off requests, expense reports, status changes\n- **IT** — Access requests, equipment requests, change management\n- **Finance** — Purchase orders, invoice approvals, budget requests\n- **Education** — Student applications, course registrations, facility booking\n- **Healthcare** — Patient intake, referrals, insurance claims\n- **Government** — Permit applications, FOIA requests, citizen services\n\n## Comparison\n\n| Feature | Django Forms Workflows | Crispy Forms | FormStack | Django-Formtools |\n|---------|----------------------|--------------|-----------|------------------|\n| Database-driven forms | ✅ | ❌ | ✅ | ❌ |\n| No-code form creation | ✅ | ❌ | ✅ | ❌ |\n| Self-hosted | ✅ | ✅ | ❌ | ✅ |\n| Multi-stage approval workflows | ✅ | ❌ | ⚠️ | ❌ |\n| Sub-workflows | ✅ | ❌ | ❌ | ❌ |\n| Post-submission actions | ✅ | ❌ | ⚠️ | ❌ |\n| External data prefill | ✅ | ❌ | ⚠️ | ❌ |\n| REST API (Bearer token) | ✅ | ❌ | ✅ | ❌ |\n| Bulk export (Excel / CSV) | ✅ | ❌ | ✅ | ❌ |\n| Cross-instance sync | ✅ | ❌ | ❌ | ❌ |\n| LDAP/AD + SSO integration | ✅ | ❌ | ❌ | ❌ |\n| Managed file uploads | ✅ | ❌ | ✅ | ❌ |\n| Audit trail | ✅ | ❌ | ✅ | ❌ |\n| Open source | ✅ | ✅ | ❌ | ✅ |\n\n## Requirements\n\n- Python 3.10+\n- Django 5.2+\n- PostgreSQL, MySQL, or SQLite (PostgreSQL recommended for production)\n- Optional: Celery 5.0+ with Redis/Valkey for background task processing\n- Optional: `openpyxl` for Excel spreadsheet field support (`pip install django-forms-workflows[excel]`)\n- Optional: `django-auth-ldap` for LDAP/AD integration (`pip install django-forms-workflows[ldap]`)\n- Optional: WeasyPrint for PDF export (`pip install django-forms-workflows[pdf]`)\n\n## Testing\n\n```bash\ncd django-forms-workflows\npip install pytest pytest-django\npython -m pytest tests/ -v\n```\n\nThe test suite covers models, forms, workflow engine (including dynamic assignees, conditional stages, multi-workflow parallel tracks, sub-workflows), sync API, post-submission action executor, views, signals, conditions, and utilities — **298 tests**.\n\n## Contributing\n\nWe welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n## License\n\nGNU Lesser General Public License v3.0 (LGPLv3) — see [LICENSE](LICENSE) for details.\n\n## Support\n\n- 📖 [Documentation](https://django-forms-workflows.readthedocs.io/)\n- 💬 [Discussions](https://github.com/opensensor/django-forms-workflows/discussions)\n- 🐛 [Issue Tracker](https://github.com/opensensor/django-forms-workflows/issues)\n\n## Roadmap\n\nSee [docs/ROADMAP.md](docs/ROADMAP.md) for the full prioritized roadmap with rationale and implementation notes.\n\n### ✅ Delivered (through v0.48)\n- [x] Database-driven form definitions with 25+ field types\n- [x] Dynamic form rendering with Crispy Forms + Bootstrap 5\n- [x] Multi-stage approval workflows (any/all/sequence logic per stage)\n- [x] Conditional workflow \u0026 stage trigger logic\n- [x] Dynamic individual assignee resolution (email / username / full name / LDAP)\n- [x] Send Back for Correction (return to any prior stage without terminating)\n- [x] Sub-workflow support (spawn N child instances from a parent)\n- [x] Calculated / formula fields with live client-side evaluation\n- [x] Spreadsheet upload fields (CSV, XLS, XLSX)\n- [x] LDAP/AD integration with profile sync\n- [x] SSO attribute mapping (SAML/OAuth)\n- [x] Configurable prefill sources (user, LDAP, database, API, system values)\n- [x] Post-submission actions with conditional execution, ordering \u0026 retries\n- [x] Cross-instance form sync (push/pull/JSON import-export)\n- [x] Managed file uploads with approval lifecycle and S3/Spaces presigned URLs\n- [x] Conditional field visibility (client-side JS, no page reload)\n- [x] Form templates and cloning\n- [x] Nested category hierarchy with group-based access control\n- [x] WeasyPrint PDF export with multi-column layout\n- [x] Column-picker approval inbox with DataTables\n- [x] Complete audit logging (who, what, when, IP)\n- [x] Configurable site branding via `FORMS_WORKFLOWS['SITE_NAME']`\n- [x] Comprehensive test suite (298 tests)\n- [x] REST API — Bearer-token authenticated endpoints: list forms, fetch field schema, submit (JSON or multipart, including `?draft=1`), poll submission status; OpenAPI 3.0 schema + Swagger UI\n- [x] Bulk export — Excel and CSV export of submission data from the approval inbox (`allow_bulk_export` / `allow_bulk_pdf_export` per workflow)\n- [x] Four-tier RBAC — `submit_groups`, `view_groups`, `reviewer_groups` (read-only submission history), `admin_groups` (full submission view) with consistent enforcement across all list and detail views\n- [x] Auto-save with configurable interval; Save Draft bypasses required-field validation without storing browser internals (CSRF token, button names)\n- [x] Signature field type (drawn or typed) — v0.45.0\n- [x] Form versioning — ChangeHistory tracking, sync API snapshots, admin diff viewer action — v0.45.0\n- [x] Advanced reporting dashboard (submission analytics, approval times, bottleneck stages) — v0.46.0\n- [x] Settings-based callback handler registry (`FORMS_WORKFLOWS_CALLBACKS`) — register custom handlers by name instead of hardcoding Python paths in the database — v0.48.0\n- [x] First-class workflow webhooks with HMAC signing, async retry/backoff, admin configuration, delivery logs, cloning, and sync support\n\n### 🚧 Near-term (next 1–3 releases)\n\n### 📋 Planned (medium-term)\n\n## Credits\n\nBuilt with ❤️ by the Django community.\n\nSpecial thanks to:\n- Logan Nickerson for his time spent testing and helping with business requirements\n- [Django Crispy Forms](https://github.com/django-crispy-forms/django-crispy-forms)\n- [Celery](https://github.com/celery/celery)\n- [django-auth-ldap](https://github.com/django-auth-ldap/django-auth-ldap)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensensor%2Fdjango-forms-workflows","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopensensor%2Fdjango-forms-workflows","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensensor%2Fdjango-forms-workflows/lists"}