django-forms-workflows 0.19.0__tar.gz → 0.19.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/PKG-INFO +1 -1
  2. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/tasks.py +15 -4
  3. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/submission_detail.html +69 -0
  4. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/views.py +31 -12
  5. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/pyproject.toml +1 -1
  6. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/CHANGELOG.md +0 -0
  7. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/LICENSE +0 -0
  8. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/README.md +0 -0
  9. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/__init__.py +0 -0
  10. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/admin.py +0 -0
  11. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/apps.py +0 -0
  12. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/context_processors.py +0 -0
  13. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/data_sources/__init__.py +0 -0
  14. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/data_sources/base.py +0 -0
  15. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/data_sources/database_source.py +0 -0
  16. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/data_sources/ldap_source.py +0 -0
  17. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/data_sources/user_source.py +0 -0
  18. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/email_backends/__init__.py +0 -0
  19. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/email_backends/gmail_api.py +0 -0
  20. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/form_builder_urls.py +0 -0
  21. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/form_builder_views.py +0 -0
  22. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/forms.py +0 -0
  23. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/__init__.py +0 -0
  24. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/api_handler.py +0 -0
  25. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/base.py +0 -0
  26. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/database_handler.py +0 -0
  27. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/email_handler.py +0 -0
  28. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/executor.py +0 -0
  29. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/file_handler.py +0 -0
  30. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/handlers/ldap_handler.py +0 -0
  31. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/ldap_backend.py +0 -0
  32. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/create_default_templates.py +0 -0
  33. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/pull_forms.py +0 -0
  34. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/push_forms.py +0 -0
  35. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/seed_farm_demo.py +0 -0
  36. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/seed_pcn_form.py +0 -0
  37. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/seed_prefill_sources.py +0 -0
  38. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/sync_ldap_profiles.py +0 -0
  39. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/management/commands/test_db_connection.py +0 -0
  40. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0001_initial.py +0 -0
  41. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0002_prefillsource_alter_formfield_prefill_source_and_more.py +0 -0
  42. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0003_postsubmissionaction.py +0 -0
  43. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0004_formtemplate.py +0 -0
  44. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0005_advanced_conditional_logic.py +0 -0
  45. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0006_workflowdefinition_visual_workflow_data_and_more.py +0 -0
  46. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0007_add_userprofile_ldap_enhancements.py +0 -0
  47. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0008_add_file_workflow_models.py +0 -0
  48. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0009_add_formfield_readonly.py +0 -0
  49. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0010_add_prefillsource_template_fields.py +0 -0
  50. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0011_postsubmissionaction_email_body_template_and_more.py +0 -0
  51. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0012_add_approval_step_fields.py +0 -0
  52. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0013_add_database_query_key.py +0 -0
  53. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0014_add_form_category.py +0 -0
  54. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0015_add_workflowstage_staged_approvals.py +0 -0
  55. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0016_add_pdf_generation.py +0 -0
  56. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0017_add_notification_batching.py +0 -0
  57. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0018_add_form_category_parent.py +0 -0
  58. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0019_add_ldap_group_profile.py +0 -0
  59. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0020_add_bulk_export_field.py +0 -0
  60. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0021_alter_pendingnotification_scheduled_for_and_more.py +0 -0
  61. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0022_add_bulk_pdf_export.py +0 -0
  62. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0023_add_performance_indexes.py +0 -0
  63. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0024_add_allow_resubmit.py +0 -0
  64. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0025_workflowstage_approve_label.py +0 -0
  65. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0026_alter_formfield_width.py +0 -0
  66. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0027_formsubmission_form_data_gin.py +0 -0
  67. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0028_workflowstagegroupconfig.py +0 -0
  68. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0029_formfield_workflow_stage_fk_drop_groupconfig.py +0 -0
  69. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0030_formfield_phone_type.py +0 -0
  70. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0031_sub_workflows.py +0 -0
  71. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0032_approved_pending_detached_sub_workflow.py +0 -0
  72. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0033_add_reject_parent_to_subworkflowdefinition.py +0 -0
  73. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0034_add_notificationlog.py +0 -0
  74. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0035_migrate_prefill_source_strings.py +0 -0
  75. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0036_add_workflowstage_auto_created.py +0 -0
  76. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0037_migrate_flat_workflows_to_staged.py +0 -0
  77. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0038_remove_legacy_prefill_source_and_approval_step.py +0 -0
  78. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0039_update_help_text.py +0 -0
  79. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0040_migrate_deprecated_features.py +0 -0
  80. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/0041_remove_deprecated_fields.py +0 -0
  81. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/migrations/__init__.py +0 -0
  82. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/models.py +0 -0
  83. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/signals.py +0 -0
  84. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/sso_backends.py +0 -0
  85. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/sso_urls.py +0 -0
  86. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/sso_views.py +0 -0
  87. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/static/django_forms_workflows/js/form-builder.js +0 -0
  88. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/static/django_forms_workflows/js/form-enhancements.js +0 -0
  89. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/static/django_forms_workflows/js/workflow-builder.js +0 -0
  90. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/sync_api.py +0 -0
  91. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/sync_views.py +0 -0
  92. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/form_builder.html +0 -0
  93. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/formdef_change_form.html +0 -0
  94. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/formdefinition/change_list.html +0 -0
  95. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/sync_import.html +0 -0
  96. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/sync_pull.html +0 -0
  97. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/sync_push.html +0 -0
  98. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/admin/django_forms_workflows/workflow_builder.html +0 -0
  99. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/_category_node.html +0 -0
  100. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/approval_inbox.html +0 -0
  101. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/approve.html +0 -0
  102. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/base.html +0 -0
  103. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/completed_approvals.html +0 -0
  104. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/form_categories.html +0 -0
  105. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/form_list.html +0 -0
  106. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/form_submit.html +0 -0
  107. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/my_submissions.html +0 -0
  108. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/resubmit_confirm.html +0 -0
  109. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/sso/login.html +0 -0
  110. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/sub_workflow_detail.html +0 -0
  111. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/submission_bulk_pdf.html +0 -0
  112. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/submission_pdf.html +0 -0
  113. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/django_forms_workflows/withdraw_confirm.html +0 -0
  114. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/approval_notification.html +0 -0
  115. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/approval_reminder.html +0 -0
  116. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/approval_request.html +0 -0
  117. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/email_styles.html +0 -0
  118. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/escalation_notification.html +0 -0
  119. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/notification_digest.html +0 -0
  120. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/rejection_notification.html +0 -0
  121. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/emails/submission_notification.html +0 -0
  122. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templates/registration/login.html +0 -0
  123. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templatetags/__init__.py +0 -0
  124. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/templatetags/forms_workflows_tags.py +0 -0
  125. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/urls.py +0 -0
  126. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/utils.py +0 -0
  127. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/workflow_builder_views.py +0 -0
  128. {django_forms_workflows-0.19.0 → django_forms_workflows-0.19.2}/django_forms_workflows/workflow_engine.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-forms-workflows
3
- Version: 0.19.0
3
+ Version: 0.19.2
4
4
  Summary: Enterprise-grade, database-driven form builder with approval workflows and external data integration
5
5
  License: LGPL-3.0-only
6
6
  License-File: LICENSE
@@ -46,11 +46,22 @@ except Exception: # pragma: no cover
46
46
 
47
47
 
48
48
  def _base_url() -> str:
49
- return (
50
- getattr(settings, "FORMS_WORKFLOWS_BASE_URL", None)
51
- or getattr(settings, "SITE_BASE_URL", None)
52
- or ""
49
+ """Return the base URL (scheme + domain) for building absolute links in emails.
50
+
51
+ Checks, in order:
52
+ 1. ``settings.FORMS_WORKFLOWS_BASE_URL``
53
+ 2. ``settings.SITE_BASE_URL``
54
+ 3. First entry in ``settings.CSRF_TRUSTED_ORIGINS``
55
+ """
56
+ url = getattr(settings, "FORMS_WORKFLOWS_BASE_URL", None) or getattr(
57
+ settings, "SITE_BASE_URL", None
53
58
  )
59
+ if url:
60
+ return url
61
+ origins = getattr(settings, "CSRF_TRUSTED_ORIGINS", None)
62
+ if origins:
63
+ return origins[0]
64
+ return ""
54
65
 
55
66
 
56
67
  def _abs(url_path: str) -> str:
@@ -241,6 +241,75 @@
241
241
  </div>
242
242
  {% endfor %}
243
243
 
244
+ {% if sub_workflow_groups %}
245
+ {# ---- Sub-workflow tasks (detached child workflow stages) ---- #}
246
+ <h5 class="mb-3 mt-4"><i class="bi bi-diagram-2"></i> Sub-Workflow Approvals</h5>
247
+ {% for stage in sub_workflow_groups %}
248
+ <div class="card mb-3
249
+ {% if stage.rejected_count > 0 %}border-danger
250
+ {% elif stage.approved_count == stage.total_count and stage.total_count > 0 %}border-success
251
+ {% elif stage.approved_count > 0 %}border-primary
252
+ {% else %}border-secondary{% endif %}">
253
+ <div class="card-header d-flex justify-content-between align-items-center
254
+ {% if stage.rejected_count > 0 %}bg-danger text-white
255
+ {% elif stage.approved_count == stage.total_count and stage.total_count > 0 %}bg-success text-white
256
+ {% elif stage.approved_count > 0 %}bg-primary text-white
257
+ {% else %}bg-light text-muted{% endif %}">
258
+ <span>
259
+ <strong>{{ stage.name }}</strong>
260
+ {% if stage.approval_logic == "all" %}
261
+ <span class="badge bg-light text-dark ms-2" style="font-size:11px;">All must approve</span>
262
+ {% elif stage.approval_logic == "any" %}
263
+ <span class="badge bg-light text-dark ms-2" style="font-size:11px;">Any can approve</span>
264
+ {% elif stage.approval_logic == "sequence" %}
265
+ <span class="badge bg-light text-dark ms-2" style="font-size:11px;">Sequential</span>
266
+ {% endif %}
267
+ </span>
268
+ <span class="badge bg-light text-dark">{{ stage.approved_count }}/{{ stage.total_count }} approved</span>
269
+ </div>
270
+ <div class="card-body p-0">
271
+ <div class="table-responsive">
272
+ <table class="table table-sm mb-0">
273
+ <thead>
274
+ <tr>
275
+ <th>Assigned To</th>
276
+ <th>Status</th>
277
+ <th>Completed By</th>
278
+ <th>Completed At</th>
279
+ <th>Comments</th>
280
+ </tr>
281
+ </thead>
282
+ <tbody>
283
+ {% for task in stage.tasks %}
284
+ <tr>
285
+ <td>
286
+ {% if task.assigned_to %}
287
+ {{ task.assigned_to.get_full_name|default:task.assigned_to.username }}
288
+ {% elif task.assigned_group %}
289
+ {{ task.assigned_group.name }} <span class="text-muted">(Group)</span>
290
+ {% else %}-{% endif %}
291
+ </td>
292
+ <td>
293
+ {% if task.status == 'pending' %}<span class="badge bg-warning text-dark">Pending</span>
294
+ {% elif task.status == 'approved' %}<span class="badge bg-success">Approved</span>
295
+ {% elif task.status == 'rejected' %}<span class="badge bg-danger">Rejected</span>
296
+ {% elif task.status == 'expired' %}<span class="badge bg-secondary">Expired</span>
297
+ {% elif task.status == 'skipped' %}<span class="badge bg-secondary">Skipped</span>
298
+ {% endif %}
299
+ </td>
300
+ <td>{% if task.completed_by %}{{ task.completed_by.get_full_name|default:task.completed_by.username }}{% else %}-{% endif %}</td>
301
+ <td>{% if task.completed_at %}{{ task.completed_at|date:"Y-m-d H:i" }}{% else %}-{% endif %}</td>
302
+ <td>{{ task.comments|default:"-" }}</td>
303
+ </tr>
304
+ {% endfor %}
305
+ </tbody>
306
+ </table>
307
+ </div>
308
+ </div>
309
+ </div>
310
+ {% endfor %}
311
+ {% endif %}
312
+
244
313
  {% else %}
245
314
  {# ---- Legacy flat workflow ---- #}
246
315
  <div class="card mb-4">
@@ -544,23 +544,37 @@ def submission_detail(request, submission_id):
544
544
  "workflow_stage", "assigned_to", "assigned_group", "completed_by"
545
545
  ).order_by("stage_number", "-created_at")
546
546
 
547
- # Build stage groups if any tasks are linked to a workflow stage
547
+ # Build stage groups if any tasks are linked to a workflow stage.
548
+ # Separate parent-workflow tasks from sub-workflow tasks so they
549
+ # render in distinct sections.
548
550
  stage_groups = None
551
+ sub_workflow_groups = None
549
552
  if approval_tasks.filter(workflow_stage__isnull=False).exists():
550
- groups: dict = {}
553
+ # Determine which stage IDs belong to the parent workflow
554
+ workflow = getattr(submission.form_definition, "workflow", None)
555
+ parent_stage_ids = set()
556
+ if workflow:
557
+ parent_stage_ids = set(workflow.stages.values_list("id", flat=True))
558
+
559
+ parent_groups: dict = {}
560
+ sub_groups: dict = {}
551
561
  for task in approval_tasks:
552
- stage_key = task.stage_number or 0
553
- if stage_key not in groups:
562
+ if not task.workflow_stage_id:
563
+ continue
564
+ is_parent = task.workflow_stage_id in parent_stage_ids
565
+ target = parent_groups if is_parent else sub_groups
566
+ stage_key = task.workflow_stage_id
567
+ if stage_key not in target:
554
568
  stage_name = (
555
569
  task.workflow_stage.name
556
570
  if task.workflow_stage
557
- else f"Stage {stage_key}"
571
+ else f"Stage {task.stage_number or 0}"
558
572
  )
559
573
  approval_logic = (
560
574
  task.workflow_stage.approval_logic if task.workflow_stage else "all"
561
575
  )
562
- groups[stage_key] = {
563
- "number": stage_key,
576
+ target[stage_key] = {
577
+ "number": task.stage_number or 0,
564
578
  "name": stage_name,
565
579
  "approval_logic": approval_logic,
566
580
  "tasks": [],
@@ -568,13 +582,17 @@ def submission_detail(request, submission_id):
568
582
  "rejected_count": 0,
569
583
  "total_count": 0,
570
584
  }
571
- groups[stage_key]["tasks"].append(task)
572
- groups[stage_key]["total_count"] += 1
585
+ target[stage_key]["tasks"].append(task)
586
+ target[stage_key]["total_count"] += 1
573
587
  if task.status == "approved":
574
- groups[stage_key]["approved_count"] += 1
588
+ target[stage_key]["approved_count"] += 1
575
589
  elif task.status == "rejected":
576
- groups[stage_key]["rejected_count"] += 1
577
- stage_groups = sorted(groups.values(), key=lambda x: x["number"])
590
+ target[stage_key]["rejected_count"] += 1
591
+
592
+ if parent_groups:
593
+ stage_groups = sorted(parent_groups.values(), key=lambda x: x["number"])
594
+ if sub_groups:
595
+ sub_workflow_groups = sorted(sub_groups.values(), key=lambda x: x["number"])
578
596
 
579
597
  # Resolve fresh presigned URLs for any file-upload fields
580
598
  form_data = _resolve_form_data_urls(submission.form_data)
@@ -605,6 +623,7 @@ def submission_detail(request, submission_id):
605
623
  "submission": submission,
606
624
  "approval_tasks": approval_tasks,
607
625
  "stage_groups": stage_groups,
626
+ "sub_workflow_groups": sub_workflow_groups,
608
627
  "form_data": form_data,
609
628
  "form_data_ordered": form_data_ordered,
610
629
  "approval_field_names": approval_field_names,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-forms-workflows"
3
- version = "0.19.0"
3
+ version = "0.19.2"
4
4
  description = "Enterprise-grade, database-driven form builder with approval workflows and external data integration"
5
5
  license = "LGPL-3.0-only"
6
6
  readme = "README.md"