django-forms-workflows 0.42.2__tar.gz → 0.44.0__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.
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/CHANGELOG.md +31 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/PKG-INFO +1 -1
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/admin.py +49 -47
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/diff_views.py +6 -2
- django_forms_workflows-0.44.0/django_forms_workflows/migrations/0074_add_notification_rule.py +136 -0
- django_forms_workflows-0.44.0/django_forms_workflows/migrations/0075_migrate_to_notification_rules.py +98 -0
- django_forms_workflows-0.44.0/django_forms_workflows/migrations/0076_update_notification_event_choices.py +55 -0
- django_forms_workflows-0.44.0/django_forms_workflows/migrations/0077_drop_legacy_notification_models.py +27 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/models.py +113 -173
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/css/forms.css +1 -1
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/sync_api.py +30 -13
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/tasks.py +227 -339
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/workflow_engine.py +48 -163
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/pyproject.toml +1 -1
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/LICENSE +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/README.md +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/__init__.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/api_urls.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/api_views.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/apps.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/conditions.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/context_processors.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/data_sources/__init__.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/data_sources/base.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/data_sources/database_source.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/data_sources/ldap_source.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/data_sources/user_source.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/email_backends/__init__.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/email_backends/gmail_api.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/form_builder_urls.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/form_builder_views.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/forms.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/__init__.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/api_handler.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/base.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/database_handler.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/email_handler.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/executor.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/file_handler.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/handlers/ldap_handler.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/history.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/ldap_backend.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/create_default_templates.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/pull_forms.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/push_forms.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/seed_farm_demo.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/seed_pcn_form.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/seed_prefill_sources.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/sync_ldap_profiles.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/sync_ldap_users.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/management/commands/test_db_connection.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0001_initial.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0002_prefillsource_alter_formfield_prefill_source_and_more.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0003_postsubmissionaction.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0004_formtemplate.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0005_advanced_conditional_logic.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0006_workflowdefinition_visual_workflow_data_and_more.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0007_add_userprofile_ldap_enhancements.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0008_add_file_workflow_models.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0009_add_formfield_readonly.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0010_add_prefillsource_template_fields.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0011_postsubmissionaction_email_body_template_and_more.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0012_add_approval_step_fields.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0013_add_database_query_key.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0014_add_form_category.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0015_add_workflowstage_staged_approvals.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0016_add_pdf_generation.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0017_add_notification_batching.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0018_add_form_category_parent.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0019_add_ldap_group_profile.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0020_add_bulk_export_field.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0021_alter_pendingnotification_scheduled_for_and_more.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0022_add_bulk_pdf_export.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0023_add_performance_indexes.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0024_add_allow_resubmit.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0025_workflowstage_approve_label.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0026_alter_formfield_width.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0027_formsubmission_form_data_gin.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0028_workflowstagegroupconfig.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0029_formfield_workflow_stage_fk_drop_groupconfig.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0030_formfield_phone_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0031_sub_workflows.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0032_approved_pending_detached_sub_workflow.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0033_add_reject_parent_to_subworkflowdefinition.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0034_add_notificationlog.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0035_migrate_prefill_source_strings.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0036_add_workflowstage_auto_created.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0037_migrate_flat_workflows_to_staged.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0038_remove_legacy_prefill_source_and_approval_step.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0039_update_help_text.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0040_migrate_deprecated_features.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0041_remove_deprecated_fields.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0042_add_sub_workflow_section_label.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0043_fix_form_data_decimal_encoding.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0044_remove_subworkflowinstance_label.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0045_stageapprovalgroup_workflow_name_label.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0046_workflow_definition_one_to_many.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0047_add_hide_approval_history.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0048_workflowdefinition_trigger_conditions_and_more.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0049_formfield_multifile_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0050_workflowdefinition_collapse_parallel_stages.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0051_formdefinition_is_listed.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0052_dynamic_assignee_and_form_field_notifications.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0053_stageformfieldnotification_static_emails.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0054_rename_approved_pending_to_pending_approval.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0055_add_send_back.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0056_calculated_formula_spreadsheet.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0057_add_currency_field_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0058_rename_assignee_email_field_add_lookup_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0059_add_multiselect_list_field_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0060_alter_workflowstage_assignee_lookup_type_helptext.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0061_add_country_us_state_field_types.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0062_add_batch_import_to_formdefinition.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0063_formdefinition_api_enabled_apitoken.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0064_add_reviewer_groups.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0065_workflownotification.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0066_add_notify_submitter_to_workflownotification.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0067_migrate_legacy_notify_flags.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0068_drop_legacy_notify_fields.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0069_add_signature_field_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0070_workflowstage_allow_edit_form_data.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0071_alter_pendingnotification_notification_type.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0072_add_change_history.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/0073_add_stage_notify_assignee_on_final_decision.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/migrations/__init__.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/signals.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/sso_backends.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/sso_urls.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/sso_views.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/api/swagger-ui-bundle.js +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/api/swagger-ui.css +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/js/form-builder.js +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/js/form-enhancements.js +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/js/signature-pad.js +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/static/django_forms_workflows/js/workflow-builder.js +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/sync_views.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/_sync_diff.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/diff_forms.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/form_builder.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/formdef_change_form.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/formdefinition/change_list.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/sync_import.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/sync_pull.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/sync_push.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/admin/django_forms_workflows/workflow_builder.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/_category_node.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/api/docs.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/approval_inbox.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/approve.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/base.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/batch_import_result.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/completed_approvals.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/discard_draft_confirm.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/form_categories.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/form_list.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/form_submit.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/my_submissions.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/reassign_task.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/resubmit_confirm.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/sso/login.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/sub_workflow_detail.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/submission_bulk_pdf.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/submission_detail.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/submission_pdf.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/django_forms_workflows/withdraw_confirm.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/approval_notification.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/approval_reminder.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/approval_request.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/email_styles.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/escalation_notification.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/notification_digest.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/rejection_notification.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/emails/submission_notification.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templates/registration/login.html +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templatetags/__init__.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/templatetags/forms_workflows_tags.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/urls.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/utils.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/views.py +0 -0
- {django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/workflow_builder_views.py +0 -0
|
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.44.0] - 2026-03-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Standalone `NotificationRuleAdmin`** — Full admin interface for NotificationRule with list display, filters, search, and fieldsets. Supports browsing notification rules across all workflows.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Unified batching in `send_notification_rules`** — Cadence-aware dispatch now lives inside the unified task. Removed legacy `_queue_workflow_level_notifications` and `_queue_approval_request_notifications` functions. All batching (immediate, daily, weekly, monthly, form_field_date) flows through a single code path.
|
|
17
|
+
- **Simplified `_notify_task_request`** — No longer has its own batching logic; delegates entirely to `_dispatch_notification_rules` + built-in `send_approval_request`.
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
- **Legacy notification models** — Dropped `WorkflowNotification`, `StageFormFieldNotification`, and `WorkflowStage.notify_assignee_on_final_decision`. All configuration now uses `NotificationRule` exclusively. Legacy task stubs retained for in-flight Celery message compatibility.
|
|
21
|
+
- **Legacy notification functions** — Removed `send_workflow_definition_notifications`, `send_stage_form_field_notifications`, `send_submission_form_field_notifications` implementations (stubs remain as no-ops for Celery compat).
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **Signature field border visibility** — Changed signature pad canvas border from `#dee2e6` (nearly invisible light gray) to `#adb5bd` (clearly visible medium gray) so users can see the signature area boundary.
|
|
25
|
+
|
|
26
|
+
## [0.43.0] - 2026-03-31
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- **Unified `NotificationRule` model** — Replaces `WorkflowNotification`, `StageFormFieldNotification`, and `WorkflowStage.notify_assignee_on_final_decision` with a single, generic model. All six recipient sources (submitter, email field, static emails, stage assignees, stage groups, arbitrary groups) are available on every event type, letting admins configure any combination they need.
|
|
30
|
+
- **New event types** — `stage_decision` (fires when an individual stage completes), `workflow_approved`, `workflow_denied`, `form_withdrawn` replace the legacy `approval_notification`, `rejection_notification`, `withdrawal_notification` names.
|
|
31
|
+
- **Group notifications** — `notify_stage_groups` includes all users in a stage's approval groups; `notify_groups` M2M allows notifying arbitrary Django groups independent of stage assignment.
|
|
32
|
+
- **Stage-scoped vs. workflow-scoped rules** — Setting the optional `stage` FK scopes `notify_stage_assignees` and `notify_stage_groups` to a specific stage. Leaving it null includes all stages.
|
|
33
|
+
- **Unified `send_notification_rules` Celery task** — Single dispatch entry point for all `NotificationRule` records, with full conditions evaluation, recipient resolution, and template rendering.
|
|
34
|
+
- **Data migration `0075`** — Automatically migrates all existing `WorkflowNotification`, `StageFormFieldNotification`, and `notify_assignee_on_final_decision` records into `NotificationRule`.
|
|
35
|
+
- **Comprehensive notification documentation** — `docs/NOTIFICATIONS.md` with architecture overview, model reference, 10 configuration scenarios, troubleshooting guide, and migration notes.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- **Legacy models retained** — `WorkflowNotification` and `StageFormFieldNotification` remain for backward compatibility. The workflow engine dispatches both legacy and unified paths in parallel. Legacy models will be removed in a future release.
|
|
39
|
+
- **Updated `PendingNotification` and `NotificationLog` event types** — Both models now include the new event names alongside legacy names for historical records.
|
|
40
|
+
|
|
10
41
|
## [0.42.2] - 2026-03-31
|
|
11
42
|
|
|
12
43
|
### Fixed
|
{django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/admin.py
RENAMED
|
@@ -34,15 +34,14 @@ from .models import (
|
|
|
34
34
|
LDAPGroupProfile,
|
|
35
35
|
ManagedFile,
|
|
36
36
|
NotificationLog,
|
|
37
|
+
NotificationRule,
|
|
37
38
|
PostSubmissionAction,
|
|
38
39
|
PrefillSource,
|
|
39
40
|
StageApprovalGroup,
|
|
40
|
-
StageFormFieldNotification,
|
|
41
41
|
SubWorkflowDefinition,
|
|
42
42
|
SubWorkflowInstance,
|
|
43
43
|
UserProfile,
|
|
44
44
|
WorkflowDefinition,
|
|
45
|
-
WorkflowNotification,
|
|
46
45
|
WorkflowStage,
|
|
47
46
|
)
|
|
48
47
|
|
|
@@ -443,59 +442,49 @@ class StageApprovalGroupInline(nested_admin.NestedTabularInline):
|
|
|
443
442
|
autocomplete_fields = ("group",)
|
|
444
443
|
|
|
445
444
|
|
|
446
|
-
class StageFormFieldNotificationInline(nested_admin.NestedTabularInline):
|
|
447
|
-
"""Inline for configuring stage-level notifications (static and/or form-field driven)."""
|
|
448
|
-
|
|
449
|
-
model = StageFormFieldNotification
|
|
450
|
-
extra = 0
|
|
451
|
-
fields = (
|
|
452
|
-
"notification_type",
|
|
453
|
-
"email_field",
|
|
454
|
-
"static_emails",
|
|
455
|
-
"subject_template",
|
|
456
|
-
"conditions",
|
|
457
|
-
)
|
|
458
|
-
|
|
459
|
-
|
|
460
445
|
class WorkflowStageInline(nested_admin.NestedStackedInline):
|
|
461
446
|
"""Inline for defining ordered stages on a WorkflowDefinition."""
|
|
462
447
|
|
|
463
448
|
model = WorkflowStage
|
|
464
449
|
extra = 0
|
|
465
450
|
ordering = ("order",)
|
|
466
|
-
inlines = [StageApprovalGroupInline
|
|
451
|
+
inlines = [StageApprovalGroupInline]
|
|
467
452
|
fields = (
|
|
468
453
|
("order", "name"),
|
|
469
454
|
"approval_logic",
|
|
470
455
|
"approve_label",
|
|
471
456
|
"requires_manager_approval",
|
|
472
457
|
("assignee_form_field", "assignee_lookup_type"),
|
|
473
|
-
|
|
458
|
+
"validate_assignee_group",
|
|
474
459
|
("allow_reassign", "allow_send_back"),
|
|
475
460
|
"allow_edit_form_data",
|
|
476
461
|
"trigger_conditions",
|
|
477
462
|
)
|
|
478
463
|
|
|
479
464
|
|
|
480
|
-
class
|
|
481
|
-
"""
|
|
465
|
+
class NotificationRuleInline(nested_admin.NestedStackedInline):
|
|
466
|
+
"""Unified notification rule inline.
|
|
482
467
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
Use one row per distinct recipient group / event combination.
|
|
468
|
+
Replaces WorkflowNotificationInline and StageFormFieldNotificationInline
|
|
469
|
+
with a single, generic inline that supports all event types and all
|
|
470
|
+
recipient sources.
|
|
488
471
|
"""
|
|
489
472
|
|
|
490
|
-
model =
|
|
473
|
+
model = NotificationRule
|
|
491
474
|
extra = 0
|
|
492
475
|
fieldsets = (
|
|
493
476
|
(
|
|
494
477
|
None,
|
|
495
478
|
{
|
|
496
479
|
"fields": (
|
|
497
|
-
("
|
|
480
|
+
("event", "stage"),
|
|
481
|
+
(
|
|
482
|
+
"notify_submitter",
|
|
483
|
+
"notify_stage_assignees",
|
|
484
|
+
"notify_stage_groups",
|
|
485
|
+
),
|
|
498
486
|
("email_field", "static_emails"),
|
|
487
|
+
"notify_groups",
|
|
499
488
|
"subject_template",
|
|
500
489
|
)
|
|
501
490
|
},
|
|
@@ -521,7 +510,7 @@ class WorkflowDefinitionInline(nested_admin.NestedStackedInline):
|
|
|
521
510
|
|
|
522
511
|
model = WorkflowDefinition
|
|
523
512
|
extra = 0
|
|
524
|
-
inlines = [WorkflowStageInline,
|
|
513
|
+
inlines = [WorkflowStageInline, NotificationRuleInline]
|
|
525
514
|
fields = [
|
|
526
515
|
"name_label",
|
|
527
516
|
"requires_approval",
|
|
@@ -1398,7 +1387,6 @@ class WorkflowStageAdmin(nested_admin.NestedModelAdmin):
|
|
|
1398
1387
|
ordering = ("workflow", "order")
|
|
1399
1388
|
inlines = [
|
|
1400
1389
|
StageApprovalGroupInline,
|
|
1401
|
-
StageFormFieldNotificationInline,
|
|
1402
1390
|
ChangeHistoryInline,
|
|
1403
1391
|
]
|
|
1404
1392
|
fieldsets = (
|
|
@@ -1429,7 +1417,6 @@ class WorkflowStageAdmin(nested_admin.NestedModelAdmin):
|
|
|
1429
1417
|
"assignee_form_field",
|
|
1430
1418
|
"assignee_lookup_type",
|
|
1431
1419
|
"validate_assignee_group",
|
|
1432
|
-
"notify_assignee_on_final_decision",
|
|
1433
1420
|
"allow_reassign",
|
|
1434
1421
|
"allow_send_back",
|
|
1435
1422
|
),
|
|
@@ -1466,7 +1453,11 @@ class WorkflowStageAdmin(nested_admin.NestedModelAdmin):
|
|
|
1466
1453
|
|
|
1467
1454
|
@admin.register(WorkflowDefinition)
|
|
1468
1455
|
class WorkflowDefinitionAdmin(nested_admin.NestedModelAdmin):
|
|
1469
|
-
inlines = [
|
|
1456
|
+
inlines = [
|
|
1457
|
+
WorkflowStageInline,
|
|
1458
|
+
NotificationRuleInline,
|
|
1459
|
+
ChangeHistoryInline,
|
|
1460
|
+
]
|
|
1470
1461
|
list_display = (
|
|
1471
1462
|
"form_definition",
|
|
1472
1463
|
"requires_approval",
|
|
@@ -1549,23 +1540,32 @@ class WorkflowDefinitionAdmin(nested_admin.NestedModelAdmin):
|
|
|
1549
1540
|
)
|
|
1550
1541
|
|
|
1551
1542
|
|
|
1552
|
-
@admin.register(
|
|
1553
|
-
class
|
|
1554
|
-
"""Standalone admin for
|
|
1543
|
+
@admin.register(NotificationRule)
|
|
1544
|
+
class NotificationRuleAdmin(admin.ModelAdmin):
|
|
1545
|
+
"""Standalone admin for NotificationRule — searchable across all workflows."""
|
|
1555
1546
|
|
|
1556
1547
|
list_display = (
|
|
1557
1548
|
"workflow_form",
|
|
1558
|
-
"
|
|
1549
|
+
"event",
|
|
1550
|
+
"stage_name",
|
|
1559
1551
|
"notify_submitter",
|
|
1552
|
+
"notify_stage_assignees",
|
|
1553
|
+
"notify_stage_groups",
|
|
1560
1554
|
"email_field",
|
|
1561
1555
|
"static_emails_truncated",
|
|
1562
1556
|
"has_conditions",
|
|
1563
|
-
"subject_template_truncated",
|
|
1564
1557
|
)
|
|
1565
|
-
list_filter = (
|
|
1566
|
-
|
|
1558
|
+
list_filter = (
|
|
1559
|
+
"event",
|
|
1560
|
+
"notify_submitter",
|
|
1561
|
+
"notify_stage_assignees",
|
|
1562
|
+
"notify_stage_groups",
|
|
1563
|
+
"workflow__form_definition",
|
|
1564
|
+
)
|
|
1565
|
+
list_select_related = ("workflow__form_definition", "stage")
|
|
1567
1566
|
search_fields = (
|
|
1568
1567
|
"workflow__form_definition__name",
|
|
1568
|
+
"stage__name",
|
|
1569
1569
|
"email_field",
|
|
1570
1570
|
"static_emails",
|
|
1571
1571
|
)
|
|
@@ -1576,8 +1576,14 @@ class WorkflowNotificationAdmin(admin.ModelAdmin):
|
|
|
1576
1576
|
{
|
|
1577
1577
|
"fields": (
|
|
1578
1578
|
"workflow",
|
|
1579
|
-
("
|
|
1579
|
+
("event", "stage"),
|
|
1580
|
+
(
|
|
1581
|
+
"notify_submitter",
|
|
1582
|
+
"notify_stage_assignees",
|
|
1583
|
+
"notify_stage_groups",
|
|
1584
|
+
),
|
|
1580
1585
|
("email_field", "static_emails"),
|
|
1586
|
+
"notify_groups",
|
|
1581
1587
|
"subject_template",
|
|
1582
1588
|
)
|
|
1583
1589
|
},
|
|
@@ -1601,6 +1607,10 @@ class WorkflowNotificationAdmin(admin.ModelAdmin):
|
|
|
1601
1607
|
def workflow_form(self, obj):
|
|
1602
1608
|
return obj.workflow.form_definition.name if obj.workflow_id else "—"
|
|
1603
1609
|
|
|
1610
|
+
@admin.display(description="Stage", ordering="stage__name")
|
|
1611
|
+
def stage_name(self, obj):
|
|
1612
|
+
return obj.stage.name if obj.stage_id else "— (all stages)"
|
|
1613
|
+
|
|
1604
1614
|
@admin.display(description="Static emails")
|
|
1605
1615
|
def static_emails_truncated(self, obj):
|
|
1606
1616
|
if not obj.static_emails:
|
|
@@ -1611,14 +1621,6 @@ class WorkflowNotificationAdmin(admin.ModelAdmin):
|
|
|
1611
1621
|
def has_conditions(self, obj):
|
|
1612
1622
|
return bool(obj.conditions)
|
|
1613
1623
|
|
|
1614
|
-
@admin.display(description="Subject template")
|
|
1615
|
-
def subject_template_truncated(self, obj):
|
|
1616
|
-
if not obj.subject_template:
|
|
1617
|
-
return "—"
|
|
1618
|
-
return obj.subject_template[:50] + (
|
|
1619
|
-
"…" if len(obj.subject_template) > 50 else ""
|
|
1620
|
-
)
|
|
1621
|
-
|
|
1622
1624
|
|
|
1623
1625
|
@admin.register(PostSubmissionAction)
|
|
1624
1626
|
class PostSubmissionActionAdmin(admin.ModelAdmin):
|
{django_forms_workflows-0.42.2 → django_forms_workflows-0.44.0}/django_forms_workflows/diff_views.py
RENAMED
|
@@ -92,8 +92,12 @@ def _build_summary(forms_data):
|
|
|
92
92
|
diffs.append(
|
|
93
93
|
f"Stage '{key[1]}' {sk}: {bs.get(sk)!r} → {os_.get(sk)!r}"
|
|
94
94
|
)
|
|
95
|
-
b_notifs = bs.get(
|
|
96
|
-
|
|
95
|
+
b_notifs = bs.get(
|
|
96
|
+
"notification_rules", bs.get("form_field_notifications", [])
|
|
97
|
+
)
|
|
98
|
+
o_notifs = os_.get(
|
|
99
|
+
"notification_rules", os_.get("form_field_notifications", [])
|
|
100
|
+
)
|
|
97
101
|
if len(b_notifs) != len(o_notifs):
|
|
98
102
|
diffs.append(
|
|
99
103
|
f"Stage '{key[1]}' notifications: {len(b_notifs)} → {len(o_notifs)}"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Generated by Django 6.0.3 on 2026-03-31 16:14
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("auth", "0012_alter_user_first_name_max_length"),
|
|
11
|
+
("django_forms_workflows", "0073_add_stage_notify_assignee_on_final_decision"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name="NotificationRule",
|
|
17
|
+
fields=[
|
|
18
|
+
(
|
|
19
|
+
"id",
|
|
20
|
+
models.BigAutoField(
|
|
21
|
+
auto_created=True,
|
|
22
|
+
primary_key=True,
|
|
23
|
+
serialize=False,
|
|
24
|
+
verbose_name="ID",
|
|
25
|
+
),
|
|
26
|
+
),
|
|
27
|
+
(
|
|
28
|
+
"event",
|
|
29
|
+
models.CharField(
|
|
30
|
+
choices=[
|
|
31
|
+
("submission_received", "Submission Received"),
|
|
32
|
+
("approval_request", "Approval Request (stage activated)"),
|
|
33
|
+
(
|
|
34
|
+
"stage_decision",
|
|
35
|
+
"Stage Decision (individual stage completed)",
|
|
36
|
+
),
|
|
37
|
+
("workflow_approved", "Workflow Approved (final decision)"),
|
|
38
|
+
("workflow_denied", "Workflow Denied (final decision)"),
|
|
39
|
+
("form_withdrawn", "Form Withdrawn"),
|
|
40
|
+
],
|
|
41
|
+
help_text="The workflow event that triggers this notification.",
|
|
42
|
+
max_length=30,
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
(
|
|
46
|
+
"conditions",
|
|
47
|
+
models.JSONField(
|
|
48
|
+
blank=True,
|
|
49
|
+
help_text="Optional conditions evaluated against form_data. Uses the same format as stage trigger_conditions. When set, the notification only fires if conditions are met.",
|
|
50
|
+
null=True,
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
(
|
|
54
|
+
"subject_template",
|
|
55
|
+
models.CharField(
|
|
56
|
+
blank=True,
|
|
57
|
+
default="",
|
|
58
|
+
help_text="Custom email subject line. Supports {form_name} and {submission_id} placeholders. Leave blank for the default.",
|
|
59
|
+
max_length=500,
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
(
|
|
63
|
+
"notify_submitter",
|
|
64
|
+
models.BooleanField(
|
|
65
|
+
default=False,
|
|
66
|
+
help_text="Include the person who submitted the form.",
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
(
|
|
70
|
+
"email_field",
|
|
71
|
+
models.CharField(
|
|
72
|
+
blank=True,
|
|
73
|
+
default="",
|
|
74
|
+
help_text="Form field slug whose submitted value is an email address. Resolved from form_data at send time (varies per submission).",
|
|
75
|
+
max_length=200,
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
(
|
|
79
|
+
"static_emails",
|
|
80
|
+
models.CharField(
|
|
81
|
+
blank=True,
|
|
82
|
+
default="",
|
|
83
|
+
help_text="Comma-separated fixed email addresses.",
|
|
84
|
+
max_length=1000,
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
(
|
|
88
|
+
"notify_stage_assignees",
|
|
89
|
+
models.BooleanField(
|
|
90
|
+
default=False,
|
|
91
|
+
help_text="Include dynamically-assigned approvers. When a stage is specified, only that stage's assignee is included. When no stage is specified, assignees from all stages are included.",
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
(
|
|
95
|
+
"notify_stage_groups",
|
|
96
|
+
models.BooleanField(
|
|
97
|
+
default=False,
|
|
98
|
+
help_text="Include all users in the stage's approval groups. When a stage is specified, only that stage's groups are included. When no stage is specified, groups from all stages are included.",
|
|
99
|
+
),
|
|
100
|
+
),
|
|
101
|
+
(
|
|
102
|
+
"notify_groups",
|
|
103
|
+
models.ManyToManyField(
|
|
104
|
+
blank=True,
|
|
105
|
+
help_text="Additional groups to notify, independent of stage assignment. All users in these groups with a non-empty email will receive the notification.",
|
|
106
|
+
related_name="notification_rules",
|
|
107
|
+
to="auth.group",
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
(
|
|
111
|
+
"stage",
|
|
112
|
+
models.ForeignKey(
|
|
113
|
+
blank=True,
|
|
114
|
+
help_text="Optional. When set, scopes this rule to a specific stage. Recipient sources like 'Notify stage assignees' and 'Notify stage groups' will reference only this stage. When blank, they reference all stages in the workflow.",
|
|
115
|
+
null=True,
|
|
116
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
117
|
+
related_name="notification_rules",
|
|
118
|
+
to="django_forms_workflows.workflowstage",
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
(
|
|
122
|
+
"workflow",
|
|
123
|
+
models.ForeignKey(
|
|
124
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
125
|
+
related_name="notification_rules",
|
|
126
|
+
to="django_forms_workflows.workflowdefinition",
|
|
127
|
+
),
|
|
128
|
+
),
|
|
129
|
+
],
|
|
130
|
+
options={
|
|
131
|
+
"verbose_name": "Notification Rule",
|
|
132
|
+
"verbose_name_plural": "Notification Rules",
|
|
133
|
+
"ordering": ["workflow", "event", "stage"],
|
|
134
|
+
},
|
|
135
|
+
),
|
|
136
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data migration: populate NotificationRule from the three legacy sources.
|
|
3
|
+
|
|
4
|
+
1. WorkflowNotification → NotificationRule (stage=null)
|
|
5
|
+
2. StageFormFieldNotification → NotificationRule (stage=notif.stage)
|
|
6
|
+
3. WorkflowStage.notify_assignee_on_final_decision=True
|
|
7
|
+
→ two NotificationRule records (workflow_approved + workflow_denied)
|
|
8
|
+
with notify_stage_assignees=True, scoped to that stage.
|
|
9
|
+
|
|
10
|
+
Event name mapping (old → new):
|
|
11
|
+
approval_notification → workflow_approved
|
|
12
|
+
rejection_notification → workflow_denied
|
|
13
|
+
withdrawal_notification → form_withdrawn
|
|
14
|
+
submission_received → submission_received (unchanged)
|
|
15
|
+
approval_request → approval_request (unchanged)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from django.db import migrations
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
EVENT_MAP = {
|
|
22
|
+
"approval_notification": "workflow_approved",
|
|
23
|
+
"rejection_notification": "workflow_denied",
|
|
24
|
+
"withdrawal_notification": "form_withdrawn",
|
|
25
|
+
"submission_received": "submission_received",
|
|
26
|
+
"approval_request": "approval_request",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def forwards(apps, schema_editor):
|
|
31
|
+
NotificationRule = apps.get_model("django_forms_workflows", "NotificationRule")
|
|
32
|
+
WorkflowNotification = apps.get_model(
|
|
33
|
+
"django_forms_workflows", "WorkflowNotification"
|
|
34
|
+
)
|
|
35
|
+
StageFormFieldNotification = apps.get_model(
|
|
36
|
+
"django_forms_workflows", "StageFormFieldNotification"
|
|
37
|
+
)
|
|
38
|
+
WorkflowStage = apps.get_model("django_forms_workflows", "WorkflowStage")
|
|
39
|
+
|
|
40
|
+
# 1. WorkflowNotification → NotificationRule (workflow-scoped)
|
|
41
|
+
for wn in WorkflowNotification.objects.all():
|
|
42
|
+
NotificationRule.objects.create(
|
|
43
|
+
workflow_id=wn.workflow_id,
|
|
44
|
+
stage=None,
|
|
45
|
+
event=EVENT_MAP.get(wn.notification_type, wn.notification_type),
|
|
46
|
+
conditions=wn.conditions,
|
|
47
|
+
subject_template=wn.subject_template or "",
|
|
48
|
+
notify_submitter=wn.notify_submitter,
|
|
49
|
+
email_field=wn.email_field or "",
|
|
50
|
+
static_emails=wn.static_emails or "",
|
|
51
|
+
notify_stage_assignees=False,
|
|
52
|
+
notify_stage_groups=False,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# 2. StageFormFieldNotification → NotificationRule (stage-scoped)
|
|
56
|
+
for sfn in StageFormFieldNotification.objects.select_related("stage").all():
|
|
57
|
+
NotificationRule.objects.create(
|
|
58
|
+
workflow_id=sfn.stage.workflow_id,
|
|
59
|
+
stage_id=sfn.stage_id,
|
|
60
|
+
event=EVENT_MAP.get(sfn.notification_type, sfn.notification_type),
|
|
61
|
+
conditions=sfn.conditions,
|
|
62
|
+
subject_template=sfn.subject_template or "",
|
|
63
|
+
notify_submitter=False,
|
|
64
|
+
email_field=sfn.email_field or "",
|
|
65
|
+
static_emails=sfn.static_emails or "",
|
|
66
|
+
notify_stage_assignees=False,
|
|
67
|
+
notify_stage_groups=False,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 3. WorkflowStage.notify_assignee_on_final_decision → two rules
|
|
71
|
+
for stage in WorkflowStage.objects.filter(
|
|
72
|
+
notify_assignee_on_final_decision=True
|
|
73
|
+
):
|
|
74
|
+
for event in ("workflow_approved", "workflow_denied"):
|
|
75
|
+
NotificationRule.objects.create(
|
|
76
|
+
workflow_id=stage.workflow_id,
|
|
77
|
+
stage_id=stage.id,
|
|
78
|
+
event=event,
|
|
79
|
+
conditions=None,
|
|
80
|
+
subject_template="",
|
|
81
|
+
notify_submitter=False,
|
|
82
|
+
email_field="",
|
|
83
|
+
static_emails="",
|
|
84
|
+
notify_stage_assignees=True,
|
|
85
|
+
notify_stage_groups=False,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Migration(migrations.Migration):
|
|
90
|
+
|
|
91
|
+
dependencies = [
|
|
92
|
+
("django_forms_workflows", "0074_add_notification_rule"),
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
operations = [
|
|
96
|
+
migrations.RunPython(forwards, migrations.RunPython.noop),
|
|
97
|
+
]
|
|
98
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Generated by Django 6.0.3 on 2026-03-31 16:29
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("django_forms_workflows", "0075_migrate_to_notification_rules"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name="notificationlog",
|
|
15
|
+
name="notification_type",
|
|
16
|
+
field=models.CharField(
|
|
17
|
+
choices=[
|
|
18
|
+
("submission_received", "Submission Received"),
|
|
19
|
+
("approval_request", "Approval Request"),
|
|
20
|
+
("stage_decision", "Stage Decision"),
|
|
21
|
+
("workflow_approved", "Workflow Approved"),
|
|
22
|
+
("workflow_denied", "Workflow Denied"),
|
|
23
|
+
("form_withdrawn", "Form Withdrawn"),
|
|
24
|
+
("approval_reminder", "Approval Reminder"),
|
|
25
|
+
("escalation", "Escalation"),
|
|
26
|
+
("batched", "Batched Digest"),
|
|
27
|
+
("submission_created", "Submission Received (legacy)"),
|
|
28
|
+
("approval_notification", "Approved (legacy)"),
|
|
29
|
+
("rejection_notification", "Rejected (legacy)"),
|
|
30
|
+
("other", "Other"),
|
|
31
|
+
],
|
|
32
|
+
db_index=True,
|
|
33
|
+
default="other",
|
|
34
|
+
max_length=50,
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
migrations.AlterField(
|
|
38
|
+
model_name="pendingnotification",
|
|
39
|
+
name="notification_type",
|
|
40
|
+
field=models.CharField(
|
|
41
|
+
choices=[
|
|
42
|
+
("submission_received", "Submission Received"),
|
|
43
|
+
("approval_request", "Approval Request"),
|
|
44
|
+
("stage_decision", "Stage Decision"),
|
|
45
|
+
("workflow_approved", "Workflow Approved"),
|
|
46
|
+
("workflow_denied", "Workflow Denied"),
|
|
47
|
+
("form_withdrawn", "Form Withdrawn"),
|
|
48
|
+
("approval_notification", "Approval Notification (legacy)"),
|
|
49
|
+
("rejection_notification", "Rejection Notification (legacy)"),
|
|
50
|
+
("withdrawal_notification", "Withdrawal Notification (legacy)"),
|
|
51
|
+
],
|
|
52
|
+
max_length=30,
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Generated by Django 6.0.3 on 2026-03-31 16:50
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("django_forms_workflows", "0076_update_notification_event_choices"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(
|
|
14
|
+
model_name="workflownotification",
|
|
15
|
+
name="workflow",
|
|
16
|
+
),
|
|
17
|
+
migrations.RemoveField(
|
|
18
|
+
model_name="workflowstage",
|
|
19
|
+
name="notify_assignee_on_final_decision",
|
|
20
|
+
),
|
|
21
|
+
migrations.DeleteModel(
|
|
22
|
+
name="StageFormFieldNotification",
|
|
23
|
+
),
|
|
24
|
+
migrations.DeleteModel(
|
|
25
|
+
name="WorkflowNotification",
|
|
26
|
+
),
|
|
27
|
+
]
|