Add AQMM quality tags to existing service task checklist
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"""phase 3.1 aqmm quality tags on existing service tasks
|
||||
|
||||
Revision ID: 20260625_phase_3_1_aqmm_task_quality_tags
|
||||
Revises: 20260624_phase_3_aqmm_engagement_level_rework
|
||||
Create Date: 2026-06-25
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "20260625_phase_3_1_aqmm_task_quality_tags"
|
||||
down_revision = "20260624_phase_3_aqmm_engagement_level_rework"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
AQMM_TEMPLATE_COLUMNS = [
|
||||
sa.Column("is_aqmm_task", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_mandatory", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_evidence_required", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_manager_review_required", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_partner_review_required", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_review_partner_required", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_blocks_final_release", sa.Boolean(), nullable=False, server_default=sa.false()),
|
||||
sa.Column("aqmm_reference", sa.String(length=120), nullable=True),
|
||||
]
|
||||
|
||||
AQMM_INSTANCE_COLUMNS = AQMM_TEMPLATE_COLUMNS + [
|
||||
sa.Column("aqmm_status", sa.String(length=40), nullable=False, server_default="not_required"),
|
||||
sa.Column("aqmm_review_status", sa.String(length=40), nullable=False, server_default="not_required"),
|
||||
sa.Column("aqmm_completed_at_utc", sa.DateTime(timezone=True), nullable=True),
|
||||
]
|
||||
|
||||
|
||||
def _has_table(bind, table_name: str) -> bool:
|
||||
return sa.inspect(bind).has_table(table_name)
|
||||
|
||||
|
||||
def _has_column(bind, table_name: str, column_name: str) -> bool:
|
||||
if not _has_table(bind, table_name):
|
||||
return False
|
||||
return column_name in {c["name"] for c in sa.inspect(bind).get_columns(table_name)}
|
||||
|
||||
|
||||
def _add_column_once(bind, table: str, column: sa.Column) -> None:
|
||||
if not _has_column(bind, table, column.name):
|
||||
with op.batch_alter_table(table) as batch:
|
||||
batch.add_column(column)
|
||||
|
||||
|
||||
def _drop_column_once(bind, table: str, column_name: str) -> None:
|
||||
if _has_column(bind, table, column_name):
|
||||
with op.batch_alter_table(table) as batch:
|
||||
batch.drop_column(column_name)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
|
||||
for table in ["service_default_task_templates", "firm_service_task_templates"]:
|
||||
if _has_table(bind, table):
|
||||
for column in AQMM_TEMPLATE_COLUMNS:
|
||||
_add_column_once(bind, table, column.copy())
|
||||
|
||||
if _has_table(bind, "client_service_task_instances"):
|
||||
for column in AQMM_INSTANCE_COLUMNS:
|
||||
_add_column_once(bind, "client_service_task_instances", column.copy())
|
||||
|
||||
# Backfill existing generated tasks from their firm task templates.
|
||||
op.execute("""
|
||||
UPDATE client_service_task_instances csti
|
||||
SET is_aqmm_task = COALESCE(fst.is_aqmm_task, FALSE),
|
||||
aqmm_mandatory = COALESCE(fst.aqmm_mandatory, FALSE),
|
||||
aqmm_evidence_required = COALESCE(fst.aqmm_evidence_required, FALSE),
|
||||
aqmm_manager_review_required = COALESCE(fst.aqmm_manager_review_required, FALSE),
|
||||
aqmm_partner_review_required = COALESCE(fst.aqmm_partner_review_required, FALSE),
|
||||
aqmm_review_partner_required = COALESCE(fst.aqmm_review_partner_required, FALSE),
|
||||
aqmm_blocks_final_release = COALESCE(fst.aqmm_blocks_final_release, FALSE),
|
||||
aqmm_reference = fst.aqmm_reference,
|
||||
aqmm_status = CASE WHEN COALESCE(fst.is_aqmm_task, FALSE) THEN 'pending' ELSE 'not_required' END,
|
||||
aqmm_review_status = CASE
|
||||
WHEN COALESCE(fst.aqmm_manager_review_required, FALSE)
|
||||
OR COALESCE(fst.aqmm_partner_review_required, FALSE)
|
||||
OR COALESCE(fst.aqmm_review_partner_required, FALSE)
|
||||
THEN 'pending_review'
|
||||
ELSE 'not_required'
|
||||
END
|
||||
FROM firm_service_task_templates fst
|
||||
WHERE csti.firm_task_template_id = fst.id
|
||||
""")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
for table in ["client_service_task_instances"]:
|
||||
if _has_table(bind, table):
|
||||
for col in [
|
||||
"aqmm_completed_at_utc",
|
||||
"aqmm_review_status",
|
||||
"aqmm_status",
|
||||
"aqmm_reference",
|
||||
"aqmm_blocks_final_release",
|
||||
"aqmm_review_partner_required",
|
||||
"aqmm_partner_review_required",
|
||||
"aqmm_manager_review_required",
|
||||
"aqmm_evidence_required",
|
||||
"aqmm_mandatory",
|
||||
"is_aqmm_task",
|
||||
]:
|
||||
_drop_column_once(bind, table, col)
|
||||
|
||||
for table in ["firm_service_task_templates", "service_default_task_templates"]:
|
||||
if _has_table(bind, table):
|
||||
for col in [
|
||||
"aqmm_reference",
|
||||
"aqmm_blocks_final_release",
|
||||
"aqmm_review_partner_required",
|
||||
"aqmm_partner_review_required",
|
||||
"aqmm_manager_review_required",
|
||||
"aqmm_evidence_required",
|
||||
"aqmm_mandatory",
|
||||
"is_aqmm_task",
|
||||
]:
|
||||
_drop_column_once(bind, table, col)
|
||||
@@ -73,6 +73,14 @@ DEFAULT_TASK_COLUMNS = [
|
||||
"default_role_name",
|
||||
"is_mandatory",
|
||||
"requires_review",
|
||||
"is_aqmm_task",
|
||||
"aqmm_mandatory",
|
||||
"aqmm_evidence_required",
|
||||
"aqmm_manager_review_required",
|
||||
"aqmm_partner_review_required",
|
||||
"aqmm_review_partner_required",
|
||||
"aqmm_blocks_final_release",
|
||||
"aqmm_reference",
|
||||
"is_active",
|
||||
"description",
|
||||
]
|
||||
@@ -84,6 +92,14 @@ FIRM_TASK_COLUMNS = [
|
||||
"default_role_name",
|
||||
"is_mandatory",
|
||||
"requires_review",
|
||||
"is_aqmm_task",
|
||||
"aqmm_mandatory",
|
||||
"aqmm_evidence_required",
|
||||
"aqmm_manager_review_required",
|
||||
"aqmm_partner_review_required",
|
||||
"aqmm_review_partner_required",
|
||||
"aqmm_blocks_final_release",
|
||||
"aqmm_reference",
|
||||
"is_active",
|
||||
"description",
|
||||
]
|
||||
@@ -1010,6 +1026,14 @@ def import_system_default_tasks(db: Session, *, current_user, file_bytes: bytes,
|
||||
task.default_role_name = _clean(_cell(row, headers, "default_role_name")) or None
|
||||
task.is_mandatory = _bool(_cell(row, headers, "is_mandatory"), True)
|
||||
task.requires_review = _bool(_cell(row, headers, "requires_review"), False)
|
||||
task.is_aqmm_task = _bool(_cell(row, headers, "is_aqmm_task"), False)
|
||||
task.aqmm_mandatory = _bool(_cell(row, headers, "aqmm_mandatory"), False)
|
||||
task.aqmm_evidence_required = _bool(_cell(row, headers, "aqmm_evidence_required"), False)
|
||||
task.aqmm_manager_review_required = _bool(_cell(row, headers, "aqmm_manager_review_required"), False)
|
||||
task.aqmm_partner_review_required = _bool(_cell(row, headers, "aqmm_partner_review_required"), False)
|
||||
task.aqmm_review_partner_required = _bool(_cell(row, headers, "aqmm_review_partner_required"), False)
|
||||
task.aqmm_blocks_final_release = _bool(_cell(row, headers, "aqmm_blocks_final_release"), False)
|
||||
task.aqmm_reference = _clean(_cell(row, headers, "aqmm_reference")) or None
|
||||
task.is_active = _bool(_cell(row, headers, "is_active"), True)
|
||||
except Exception as exc:
|
||||
errors.append({"row": row_no, "message": str(exc)})
|
||||
@@ -1067,6 +1091,14 @@ def import_firm_task_templates(db: Session, *, current_user, tenant_id: int, fil
|
||||
task.default_role_name = _clean(_cell(row, headers, "default_role_name")) or None
|
||||
task.is_mandatory = _bool(_cell(row, headers, "is_mandatory"), True)
|
||||
task.requires_review = _bool(_cell(row, headers, "requires_review"), False)
|
||||
task.is_aqmm_task = _bool(_cell(row, headers, "is_aqmm_task"), False)
|
||||
task.aqmm_mandatory = _bool(_cell(row, headers, "aqmm_mandatory"), False)
|
||||
task.aqmm_evidence_required = _bool(_cell(row, headers, "aqmm_evidence_required"), False)
|
||||
task.aqmm_manager_review_required = _bool(_cell(row, headers, "aqmm_manager_review_required"), False)
|
||||
task.aqmm_partner_review_required = _bool(_cell(row, headers, "aqmm_partner_review_required"), False)
|
||||
task.aqmm_review_partner_required = _bool(_cell(row, headers, "aqmm_review_partner_required"), False)
|
||||
task.aqmm_blocks_final_release = _bool(_cell(row, headers, "aqmm_blocks_final_release"), False)
|
||||
task.aqmm_reference = _clean(_cell(row, headers, "aqmm_reference")) or None
|
||||
task.is_active = _bool(_cell(row, headers, "is_active"), True)
|
||||
task.updated_by_user_id = current_user.id
|
||||
except Exception as exc:
|
||||
|
||||
@@ -10,6 +10,7 @@ from app.core.security.session_auth import get_current_user
|
||||
from app.core.templating import templates
|
||||
from app.modules.services.models import ClientServiceSubscription, ClientServiceTaskInstance
|
||||
from app.modules.services.due_dates import apply_due_date_rule_to_subscription
|
||||
from app.modules.services.execution import aqmm_task_summary_for_subscription
|
||||
from app.modules.clients.models import Client
|
||||
from app.modules.services.client_services import (
|
||||
SUBSCRIPTION_STATUSES,
|
||||
@@ -398,6 +399,7 @@ def subscription_detail(request: Request, subscription_id: int):
|
||||
my_pending_declarations = [d for d in declarations if d.requested_user_id == user.id and d.status == "pending"]
|
||||
kyc_verification = get_latest_engagement_kyc_verification(db, subscription_id=row.id)
|
||||
engagement_letter = get_current_engagement_letter(db, subscription_id=row.id)
|
||||
aqmm_task_summary = aqmm_task_summary_for_subscription(db, subscription_id=row.id)
|
||||
|
||||
tasks = db.execute(
|
||||
select(ClientServiceTaskInstance)
|
||||
@@ -417,6 +419,7 @@ def subscription_detail(request: Request, subscription_id: int):
|
||||
my_pending_declarations=my_pending_declarations,
|
||||
kyc_verification=kyc_verification,
|
||||
engagement_letter=engagement_letter,
|
||||
aqmm_task_summary=aqmm_task_summary,
|
||||
can_manage=_can_manage_client_services(db, user),
|
||||
)
|
||||
finally:
|
||||
|
||||
@@ -6,6 +6,7 @@ from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.orm import Session, selectinload
|
||||
|
||||
from app.modules.clients.models import Client
|
||||
from app.modules.documents.models import EngagementDocument
|
||||
from app.modules.core.iam.models import User
|
||||
from app.modules.services.client_services import quality_required_for_engagement, QUALITY_APPROVED
|
||||
from app.modules.services.models import (
|
||||
@@ -222,6 +223,16 @@ def generate_tasks_for_subscription(db: Session, *, subscription: ClientServiceS
|
||||
internal_target_date=_default_internal_target_date(subscription, template),
|
||||
status="pending",
|
||||
priority="normal",
|
||||
is_aqmm_task=getattr(template, "is_aqmm_task", False),
|
||||
aqmm_mandatory=getattr(template, "aqmm_mandatory", False),
|
||||
aqmm_evidence_required=getattr(template, "aqmm_evidence_required", False),
|
||||
aqmm_manager_review_required=getattr(template, "aqmm_manager_review_required", False),
|
||||
aqmm_partner_review_required=getattr(template, "aqmm_partner_review_required", False),
|
||||
aqmm_review_partner_required=getattr(template, "aqmm_review_partner_required", False),
|
||||
aqmm_blocks_final_release=getattr(template, "aqmm_blocks_final_release", False),
|
||||
aqmm_reference=getattr(template, "aqmm_reference", None),
|
||||
aqmm_status="pending" if getattr(template, "is_aqmm_task", False) else "not_required",
|
||||
aqmm_review_status="pending_review" if (getattr(template, "aqmm_manager_review_required", False) or getattr(template, "aqmm_partner_review_required", False) or getattr(template, "aqmm_review_partner_required", False)) else "not_required",
|
||||
is_active=True,
|
||||
created_by_user_id=user_id,
|
||||
updated_by_user_id=user_id,
|
||||
@@ -231,6 +242,89 @@ def generate_tasks_for_subscription(db: Session, *, subscription: ClientServiceS
|
||||
return created
|
||||
|
||||
|
||||
|
||||
def _task_has_evidence(db: Session, task_id: int) -> bool:
|
||||
return bool(
|
||||
db.execute(
|
||||
select(EngagementDocument.id).where(
|
||||
EngagementDocument.task_instance_id == int(task_id),
|
||||
EngagementDocument.is_deleted.is_(False),
|
||||
)
|
||||
).first()
|
||||
)
|
||||
|
||||
|
||||
def _aqmm_task_issue(db: Session, task: ClientServiceTaskInstance) -> str | None:
|
||||
"""Return blocking reason for one AQMM-tagged task, or None if it passes.
|
||||
|
||||
This deliberately reuses the existing task status and task document upload
|
||||
workflow. Review flags are captured for reporting and future review-specific
|
||||
workflow; until dedicated manager/partner review statuses are added, the
|
||||
task must at least be completed and evidence uploaded wherever marked.
|
||||
"""
|
||||
if not getattr(task, "is_aqmm_task", False):
|
||||
return None
|
||||
if getattr(task, "aqmm_mandatory", False) and task.status != "completed":
|
||||
return "Task not completed"
|
||||
if getattr(task, "aqmm_evidence_required", False) and not _task_has_evidence(db, task.id):
|
||||
return "Evidence not uploaded"
|
||||
return None
|
||||
|
||||
|
||||
def list_aqmm_quality_tasks(db: Session, *, subscription_id: int) -> list[dict]:
|
||||
tasks = db.execute(
|
||||
select(ClientServiceTaskInstance)
|
||||
.where(
|
||||
ClientServiceTaskInstance.subscription_id == int(subscription_id),
|
||||
ClientServiceTaskInstance.is_active.is_(True),
|
||||
ClientServiceTaskInstance.is_aqmm_task.is_(True),
|
||||
)
|
||||
.order_by(ClientServiceTaskInstance.sequence_no.asc(), ClientServiceTaskInstance.id.asc())
|
||||
).scalars().all()
|
||||
rows: list[dict] = []
|
||||
for task in tasks:
|
||||
has_evidence = _task_has_evidence(db, task.id)
|
||||
issue = _aqmm_task_issue(db, task)
|
||||
rows.append({"task": task, "has_evidence": has_evidence, "issue": issue, "passes": issue is None})
|
||||
return rows
|
||||
|
||||
|
||||
def aqmm_task_summary_for_subscription(db: Session, *, subscription_id: int) -> dict:
|
||||
rows = list_aqmm_quality_tasks(db, subscription_id=subscription_id)
|
||||
total = len(rows)
|
||||
completed = sum(1 for r in rows if r["task"].status == "completed")
|
||||
mandatory = sum(1 for r in rows if getattr(r["task"], "aqmm_mandatory", False))
|
||||
mandatory_done = sum(1 for r in rows if getattr(r["task"], "aqmm_mandatory", False) and r["passes"])
|
||||
evidence_required = sum(1 for r in rows if getattr(r["task"], "aqmm_evidence_required", False))
|
||||
evidence_missing = sum(1 for r in rows if getattr(r["task"], "aqmm_evidence_required", False) and not r["has_evidence"])
|
||||
blockers = [r for r in rows if getattr(r["task"], "aqmm_blocks_final_release", False) and not r["passes"]]
|
||||
status = "not_required"
|
||||
if total:
|
||||
status = "completed" if mandatory_done == mandatory and evidence_missing == 0 else "in_progress"
|
||||
return {
|
||||
"rows": rows,
|
||||
"total": total,
|
||||
"completed": completed,
|
||||
"mandatory": mandatory,
|
||||
"mandatory_done": mandatory_done,
|
||||
"evidence_required": evidence_required,
|
||||
"evidence_missing": evidence_missing,
|
||||
"blockers": blockers,
|
||||
"status": status,
|
||||
}
|
||||
|
||||
|
||||
def assert_aqmm_quality_tasks_complete(db: Session, *, subscription_id: int) -> None:
|
||||
summary = aqmm_task_summary_for_subscription(db, subscription_id=subscription_id)
|
||||
if summary["blockers"]:
|
||||
first = summary["blockers"][0]
|
||||
task = first["task"]
|
||||
raise ValueError(f"AQMM final release blocked: {task.task_name} - {first['issue']}")
|
||||
if summary["mandatory"] and summary["mandatory_done"] < summary["mandatory"]:
|
||||
raise ValueError("AQMM mandatory quality checklist tasks are pending.")
|
||||
if summary["evidence_missing"]:
|
||||
raise ValueError("AQMM evidence upload is pending for one or more quality checklist tasks.")
|
||||
|
||||
def _decorate_task_for_tracker(task: ClientServiceTaskInstance, *, today: date) -> ClientServiceTaskInstance:
|
||||
target_date = getattr(task, "internal_target_date", None)
|
||||
subscription = getattr(task, "subscription", None)
|
||||
|
||||
@@ -111,6 +111,19 @@ class ServiceDefaultTaskTemplate(CommonBase):
|
||||
default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
requires_review: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
|
||||
# AQMM task tags. These flags allow the existing service checklist to become
|
||||
# the engagement quality checklist for assurance engagements, without creating
|
||||
# a separate duplicate AQMM checklist module.
|
||||
is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
|
||||
aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
catalogue = relationship("ServiceCatalogue", back_populates="default_task_templates")
|
||||
@@ -166,6 +179,17 @@ class FirmServiceTaskTemplate(CommonBase):
|
||||
default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
||||
is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
requires_review: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
|
||||
# AQMM task tags copied into generated ClientServiceTaskInstance rows.
|
||||
is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
|
||||
aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
|
||||
@@ -572,6 +596,21 @@ class ClientServiceTaskInstance(CommonBase):
|
||||
internal_target_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
|
||||
status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
|
||||
priority: Mapped[str] = mapped_column(String(20), nullable=False, default="normal")
|
||||
|
||||
# Copied from FirmServiceTaskTemplate at generation time. Existing task
|
||||
# completion/evidence upload flow is reused to calculate AQMM quality status.
|
||||
is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
|
||||
aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
||||
aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
aqmm_status: Mapped[str] = mapped_column(String(40), default="not_required", nullable=False, index=True)
|
||||
aqmm_review_status: Mapped[str] = mapped_column(String(40), default="not_required", nullable=False, index=True)
|
||||
aqmm_completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
remarks: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
started_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
@@ -47,6 +47,24 @@
|
||||
<textarea name="description" rows="4" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">{{ task.description or '' }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-2 rounded-2xl border border-indigo-200 bg-indigo-50 p-4">
|
||||
<div class="text-sm font-semibold text-indigo-950">AQMM / Quality Control Tagging</div>
|
||||
<p class="mt-1 text-xs text-indigo-800">Use these only for tasks that should become part of the assurance engagement quality checklist. Non-assurance engagements will continue using the normal task flow.</p>
|
||||
<div class="mt-3 grid gap-3 md:grid-cols-2 text-sm text-slate-700">
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="is_aqmm_task" {% if task.is_aqmm_task %}checked{% endif %}> Part of AQMM / quality checklist</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_mandatory" {% if task.aqmm_mandatory %}checked{% endif %}> Mandatory for AQMM completion</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_evidence_required" {% if task.aqmm_evidence_required %}checked{% endif %}> Evidence upload required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_manager_review_required" {% if task.aqmm_manager_review_required %}checked{% endif %}> Manager review required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_partner_review_required" {% if task.aqmm_partner_review_required %}checked{% endif %}> Partner review required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_review_partner_required" {% if task.aqmm_review_partner_required %}checked{% endif %}> Review partner review required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_blocks_final_release" {% if task.aqmm_blocks_final_release %}checked{% endif %}> Block final release until completed</label>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-indigo-800">AQMM Reference / Clause</label>
|
||||
<input type="text" name="aqmm_reference" value="{{ task.aqmm_reference or '' }}" class="w-full rounded-xl border border-indigo-200 px-3 py-2 text-sm" placeholder="e.g. Acceptance / Planning / Review">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-2 rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800">
|
||||
To deactivate this system default task, untick <strong>Active</strong> and save. Existing firm-copied tasks will not be automatically changed.
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="mt-1 text-xs text-slate-500">
|
||||
Role: {{ task.default_role_name or '-' }} ·
|
||||
Mandatory: {{ 'Yes' if task.is_mandatory else 'No' }} ·
|
||||
Review: {{ 'Yes' if task.requires_review else 'No' }}
|
||||
Review: {{ 'Yes' if task.requires_review else 'No' }} · AQMM: {{ 'Yes' if task.is_aqmm_task else 'No' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
@@ -46,6 +46,19 @@
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Sequence No</label><input type="number" min="1" name="sequence_no" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"></div>
|
||||
<div class="flex items-center gap-6 pt-7 text-sm text-slate-700"><label class="inline-flex items-center gap-2"><input type="checkbox" name="is_mandatory" checked> Mandatory</label><label class="inline-flex items-center gap-2"><input type="checkbox" name="requires_review"> Review</label><label class="inline-flex items-center gap-2"><input type="checkbox" name="is_active" checked> Active</label></div>
|
||||
<div class="md:col-span-2"><label class="mb-2 block text-sm font-medium text-slate-700">Description</label><textarea name="description" rows="4" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"></textarea></div>
|
||||
<div class="md:col-span-2 rounded-2xl border border-indigo-200 bg-indigo-50 p-4">
|
||||
<div class="text-sm font-semibold text-indigo-950">AQMM / Quality Control Tagging</div>
|
||||
<div class="mt-3 grid gap-2 md:grid-cols-4 text-xs text-slate-700">
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="is_aqmm_task"> Part of AQMM</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_mandatory"> Mandatory</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_evidence_required"> Evidence required</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_manager_review_required"> Manager review</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_partner_review_required"> Partner review</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_review_partner_required"> Review partner review</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_blocks_final_release"> Blocks final release</label>
|
||||
<input name="aqmm_reference" placeholder="AQMM reference / clause" class="rounded-lg border border-indigo-200 px-2 py-1 text-xs">
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:col-span-2 flex justify-end"><button class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-medium text-white hover:bg-brand-700">Add Default Task</button></div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -45,6 +45,41 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div class="mt-5 rounded-2xl border border-white bg-white p-4">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-slate-900">AQMM Quality Checklist Tasks</h4>
|
||||
<p class="mt-1 text-xs text-slate-500">These are your existing service tasks tagged as part of AQMM. No separate checklist is created.</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 text-xs md:grid-cols-4">
|
||||
<span class="rounded-xl bg-slate-100 px-3 py-2">Total: <strong>{{ aqmm_task_summary.total if aqmm_task_summary else 0 }}</strong></span>
|
||||
<span class="rounded-xl bg-emerald-100 px-3 py-2 text-emerald-800">Completed: <strong>{{ aqmm_task_summary.completed if aqmm_task_summary else 0 }}</strong></span>
|
||||
<span class="rounded-xl bg-amber-100 px-3 py-2 text-amber-800">Evidence Missing: <strong>{{ aqmm_task_summary.evidence_missing if aqmm_task_summary else 0 }}</strong></span>
|
||||
<span class="rounded-xl bg-purple-100 px-3 py-2 text-purple-800">Final Blockers: <strong>{{ aqmm_task_summary.blockers|length if aqmm_task_summary else 0 }}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 overflow-hidden rounded-xl border border-slate-200">
|
||||
<table class="min-w-full divide-y divide-slate-200 text-sm">
|
||||
<thead class="bg-slate-50"><tr><th class="px-3 py-2 text-left">Task</th><th class="px-3 py-2 text-left">AQMM Flags</th><th class="px-3 py-2 text-left">Evidence</th><th class="px-3 py-2 text-left">Status</th><th class="px-3 py-2 text-right">Action</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for item in aqmm_task_summary.rows if aqmm_task_summary %}
|
||||
{% set task = item.task %}
|
||||
<tr>
|
||||
<td class="px-3 py-2"><div class="font-medium text-slate-900">{{ task.sequence_no }}. {{ task.task_name }}</div>{% if task.aqmm_reference %}<div class="text-xs text-slate-500">{{ task.aqmm_reference }}</div>{% endif %}</td>
|
||||
<td class="px-3 py-2 text-xs"><div class="flex flex-wrap gap-1">{% if task.aqmm_mandatory %}<span class="rounded-full bg-rose-100 px-2 py-1 text-rose-800">Mandatory</span>{% endif %}{% if task.aqmm_evidence_required %}<span class="rounded-full bg-sky-100 px-2 py-1 text-sky-800">Evidence</span>{% endif %}{% if task.aqmm_manager_review_required %}<span class="rounded-full bg-amber-100 px-2 py-1 text-amber-800">Manager</span>{% endif %}{% if task.aqmm_partner_review_required %}<span class="rounded-full bg-indigo-100 px-2 py-1 text-indigo-800">Partner</span>{% endif %}{% if task.aqmm_review_partner_required %}<span class="rounded-full bg-purple-100 px-2 py-1 text-purple-800">Review Partner</span>{% endif %}{% if task.aqmm_blocks_final_release %}<span class="rounded-full bg-slate-900 px-2 py-1 text-white">Final Gate</span>{% endif %}</div></td>
|
||||
<td class="px-3 py-2">{{ 'Uploaded' if item.has_evidence else ('Required' if task.aqmm_evidence_required else '-') }}</td>
|
||||
<td class="px-3 py-2"><span class="rounded-full px-2 py-1 text-xs font-medium {{ 'bg-emerald-100 text-emerald-800' if item.passes else 'bg-amber-100 text-amber-800' }}">{{ 'OK' if item.passes else item.issue }}</span><div class="mt-1 text-xs text-slate-500">Task: {{ task.status|replace('_',' ')|title }}</div></td>
|
||||
<td class="px-3 py-2 text-right"><a href="/documents/tasks/{{ task.id }}" class="text-xs font-medium text-brand-700 hover:underline">Open Evidence</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="px-3 py-6 text-center text-sm text-slate-500">No AQMM-tagged task generated yet. Mark selected service tasks as AQMM and generate work tracker tasks.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if my_pending_declarations %}
|
||||
<div class="mt-5 rounded-2xl border border-white bg-white p-4">
|
||||
<h4 class="text-sm font-semibold text-slate-900">My Pending Declarations</h4>
|
||||
@@ -83,7 +118,7 @@
|
||||
{% for task in tasks or [] %}
|
||||
<tr>
|
||||
<td class="px-4 py-3 text-sm font-medium text-slate-900">{{ task.sequence_no }}</td>
|
||||
<td class="px-4 py-3 text-sm"><div class="font-medium text-slate-900">{{ task.task_name }}</div>{% if task.description %}<div class="text-xs text-slate-500">{{ task.description }}</div>{% endif %}</td>
|
||||
<td class="px-4 py-3 text-sm"><div class="font-medium text-slate-900">{{ task.task_name }}</div>{% if task.description %}<div class="text-xs text-slate-500">{{ task.description }}</div>{% endif %}{% if task.is_aqmm_task %}<div class="mt-1 flex flex-wrap gap-1 text-[11px]"><span class="rounded-full bg-indigo-100 px-2 py-1 text-indigo-800">AQMM</span>{% if task.aqmm_mandatory %}<span class="rounded-full bg-rose-100 px-2 py-1 text-rose-800">Mandatory</span>{% endif %}{% if task.aqmm_evidence_required %}<span class="rounded-full bg-sky-100 px-2 py-1 text-sky-800">Evidence</span>{% endif %}</div>{% endif %}</td>
|
||||
<td class="px-4 py-3 text-sm text-slate-700">{{ task.status.replace('_',' ').title() }}</td>
|
||||
<td class="px-4 py-3 text-right"><a href="/documents/tasks/{{ task.id }}" class="text-sm font-medium text-brand-700 hover:underline">Open Task Documents</a></td>
|
||||
</tr>
|
||||
|
||||
@@ -47,6 +47,24 @@
|
||||
<textarea name="description" rows="4" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">{{ task.description or '' }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-2 rounded-2xl border border-indigo-200 bg-indigo-50 p-4">
|
||||
<div class="text-sm font-semibold text-indigo-950">AQMM / Quality Control Tagging</div>
|
||||
<p class="mt-1 text-xs text-indigo-800">Use these only for tasks that should become part of the assurance engagement quality checklist. Non-assurance engagements will continue using the normal task flow.</p>
|
||||
<div class="mt-3 grid gap-3 md:grid-cols-2 text-sm text-slate-700">
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="is_aqmm_task" {% if task.is_aqmm_task %}checked{% endif %}> Part of AQMM / quality checklist</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_mandatory" {% if task.aqmm_mandatory %}checked{% endif %}> Mandatory for AQMM completion</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_evidence_required" {% if task.aqmm_evidence_required %}checked{% endif %}> Evidence upload required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_manager_review_required" {% if task.aqmm_manager_review_required %}checked{% endif %}> Manager review required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_partner_review_required" {% if task.aqmm_partner_review_required %}checked{% endif %}> Partner review required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_review_partner_required" {% if task.aqmm_review_partner_required %}checked{% endif %}> Review partner review required</label>
|
||||
<label class="inline-flex items-center gap-2"><input type="checkbox" name="aqmm_blocks_final_release" {% if task.aqmm_blocks_final_release %}checked{% endif %}> Block final release until completed</label>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-indigo-800">AQMM Reference / Clause</label>
|
||||
<input type="text" name="aqmm_reference" value="{{ task.aqmm_reference or '' }}" class="w-full rounded-xl border border-indigo-200 px-3 py-2 text-sm" placeholder="e.g. Acceptance / Planning / Review">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-2 rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800">
|
||||
To deactivate this task, untick <strong>Active</strong> and save. No hard delete is used.
|
||||
</div>
|
||||
|
||||
@@ -74,6 +74,20 @@
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="is_active" checked> Active</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-12 rounded-2xl border border-indigo-200 bg-indigo-50 p-4">
|
||||
<div class="text-sm font-semibold text-indigo-950">AQMM / Quality Control Tagging</div>
|
||||
<p class="mt-1 text-xs text-indigo-800">Tick only the service checklist tasks that should form part of assurance engagement quality evidence.</p>
|
||||
<div class="mt-3 grid gap-2 md:grid-cols-4 text-xs text-slate-700">
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="is_aqmm_task"> Part of AQMM</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_mandatory"> Mandatory</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_evidence_required"> Evidence required</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_manager_review_required"> Manager review</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_partner_review_required"> Partner review</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_review_partner_required"> Review partner review</label>
|
||||
<label class="inline-flex items-center gap-1"><input type="checkbox" name="aqmm_blocks_final_release"> Blocks final release</label>
|
||||
<input name="aqmm_reference" placeholder="AQMM reference / clause" class="rounded-lg border border-indigo-200 px-2 py-1 text-xs">
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-10">
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Description</label>
|
||||
<textarea name="description" rows="2" class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm" placeholder="Optional instructions for staff or reviewer"></textarea>
|
||||
@@ -115,6 +129,10 @@
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{% if task.is_mandatory %}<span class="rounded-full bg-slate-100 px-2 py-1">Mandatory</span>{% endif %}
|
||||
{% if task.requires_review %}<span class="rounded-full bg-amber-100 px-2 py-1 text-amber-800">Review</span>{% endif %}
|
||||
{% if task.is_aqmm_task %}<span class="rounded-full bg-indigo-100 px-2 py-1 text-indigo-800">AQMM</span>{% endif %}
|
||||
{% if task.aqmm_mandatory %}<span class="rounded-full bg-rose-100 px-2 py-1 text-rose-800">AQMM Mandatory</span>{% endif %}
|
||||
{% if task.aqmm_evidence_required %}<span class="rounded-full bg-sky-100 px-2 py-1 text-sky-800">Evidence</span>{% endif %}
|
||||
{% if task.aqmm_blocks_final_release %}<span class="rounded-full bg-purple-100 px-2 py-1 text-purple-800">Final Gate</span>{% endif %}
|
||||
<span class="rounded-full px-2 py-1 {{ 'bg-emerald-100 text-emerald-800' if task.is_active else 'bg-slate-100 text-slate-500' }}">{{ 'Active' if task.is_active else 'Inactive' }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -814,7 +814,7 @@ def task_templates_detail(request: Request, catalogue_id: int):
|
||||
|
||||
|
||||
@router.post('/templates/{catalogue_id}/tasks/new')
|
||||
def task_template_create_submit(request: Request, catalogue_id: int, task_name: str = Form(...), description: str = Form(''), default_role_name: str = Form(''), sequence_no: int | None = Form(None), is_mandatory: str | None = Form(None), requires_review: str | None = Form(None), is_active: str | None = Form(None), csrf_token: str = Form(...)):
|
||||
def task_template_create_submit(request: Request, catalogue_id: int, task_name: str = Form(...), description: str = Form(''), default_role_name: str = Form(''), sequence_no: int | None = Form(None), is_mandatory: str | None = Form(None), requires_review: str | None = Form(None), is_aqmm_task: str | None = Form(None), aqmm_mandatory: str | None = Form(None), aqmm_evidence_required: str | None = Form(None), aqmm_manager_review_required: str | None = Form(None), aqmm_partner_review_required: str | None = Form(None), aqmm_review_partner_required: str | None = Form(None), aqmm_blocks_final_release: str | None = Form(None), aqmm_reference: str = Form(''), is_active: str | None = Form(None), csrf_token: str = Form(...)):
|
||||
validate_csrf(request, csrf_token)
|
||||
db = CommonSessionLocal()
|
||||
try:
|
||||
@@ -839,6 +839,14 @@ def task_template_create_submit(request: Request, catalogue_id: int, task_name:
|
||||
sequence_no=sequence_no or next_task_sequence(db, tenant_id=tenant_id, catalogue_id=catalogue_id),
|
||||
is_mandatory=is_mandatory is not None,
|
||||
requires_review=requires_review is not None,
|
||||
is_aqmm_task=is_aqmm_task is not None,
|
||||
aqmm_mandatory=aqmm_mandatory is not None,
|
||||
aqmm_evidence_required=aqmm_evidence_required is not None,
|
||||
aqmm_manager_review_required=aqmm_manager_review_required is not None,
|
||||
aqmm_partner_review_required=aqmm_partner_review_required is not None,
|
||||
aqmm_review_partner_required=aqmm_review_partner_required is not None,
|
||||
aqmm_blocks_final_release=aqmm_blocks_final_release is not None,
|
||||
aqmm_reference=aqmm_reference.strip() or None,
|
||||
is_active=is_active is not None,
|
||||
created_by_user_id=user.id,
|
||||
updated_by_user_id=user.id,
|
||||
@@ -888,7 +896,7 @@ def default_templates_detail(request: Request, catalogue_id: int):
|
||||
|
||||
|
||||
@router.post('/catalogue/{catalogue_id}/defaults/new')
|
||||
def default_template_create_submit(request: Request, catalogue_id: int, task_name: str = Form(...), description: str = Form(''), default_role_name: str = Form(''), sequence_no: int | None = Form(None), is_mandatory: str | None = Form(None), requires_review: str | None = Form(None), is_active: str | None = Form(None), csrf_token: str = Form(...)):
|
||||
def default_template_create_submit(request: Request, catalogue_id: int, task_name: str = Form(...), description: str = Form(''), default_role_name: str = Form(''), sequence_no: int | None = Form(None), is_mandatory: str | None = Form(None), requires_review: str | None = Form(None), is_aqmm_task: str | None = Form(None), aqmm_mandatory: str | None = Form(None), aqmm_evidence_required: str | None = Form(None), aqmm_manager_review_required: str | None = Form(None), aqmm_partner_review_required: str | None = Form(None), aqmm_review_partner_required: str | None = Form(None), aqmm_blocks_final_release: str | None = Form(None), aqmm_reference: str = Form(''), is_active: str | None = Form(None), csrf_token: str = Form(...)):
|
||||
validate_csrf(request, csrf_token)
|
||||
db = CommonSessionLocal()
|
||||
try:
|
||||
@@ -901,7 +909,7 @@ def default_template_create_submit(request: Request, catalogue_id: int, task_nam
|
||||
catalogue = get_catalogue(db, catalogue_id)
|
||||
if not catalogue:
|
||||
return RedirectResponse(url='/services/defaults', status_code=303)
|
||||
row = ServiceDefaultTaskTemplate(service_catalogue_id=catalogue_id, task_name=task_name.strip(), description=description.strip() or None, default_role_name=default_role_name.strip() or None, sequence_no=sequence_no or next_default_task_sequence(db, catalogue_id=catalogue_id), is_mandatory=is_mandatory is not None, requires_review=requires_review is not None, is_active=is_active is not None)
|
||||
row = ServiceDefaultTaskTemplate(service_catalogue_id=catalogue_id, task_name=task_name.strip(), description=description.strip() or None, default_role_name=default_role_name.strip() or None, sequence_no=sequence_no or next_default_task_sequence(db, catalogue_id=catalogue_id), is_mandatory=is_mandatory is not None, requires_review=requires_review is not None, is_aqmm_task=is_aqmm_task is not None, aqmm_mandatory=aqmm_mandatory is not None, aqmm_evidence_required=aqmm_evidence_required is not None, aqmm_manager_review_required=aqmm_manager_review_required is not None, aqmm_partner_review_required=aqmm_partner_review_required is not None, aqmm_review_partner_required=aqmm_review_partner_required is not None, aqmm_blocks_final_release=aqmm_blocks_final_release is not None, aqmm_reference=aqmm_reference.strip() or None, is_active=is_active is not None)
|
||||
db.add(row); db.commit()
|
||||
return RedirectResponse(url=f'/services/catalogue/{catalogue_id}/defaults', status_code=303)
|
||||
finally:
|
||||
@@ -940,6 +948,14 @@ def copy_defaults_to_firm(request: Request, catalogue_id: int, csrf_token: str =
|
||||
default_role_name=d.default_role_name,
|
||||
is_mandatory=d.is_mandatory,
|
||||
requires_review=d.requires_review,
|
||||
is_aqmm_task=getattr(d, "is_aqmm_task", False),
|
||||
aqmm_mandatory=getattr(d, "aqmm_mandatory", False),
|
||||
aqmm_evidence_required=getattr(d, "aqmm_evidence_required", False),
|
||||
aqmm_manager_review_required=getattr(d, "aqmm_manager_review_required", False),
|
||||
aqmm_partner_review_required=getattr(d, "aqmm_partner_review_required", False),
|
||||
aqmm_review_partner_required=getattr(d, "aqmm_review_partner_required", False),
|
||||
aqmm_blocks_final_release=getattr(d, "aqmm_blocks_final_release", False),
|
||||
aqmm_reference=getattr(d, "aqmm_reference", None),
|
||||
is_active=d.is_active,
|
||||
created_by_user_id=user.id,
|
||||
updated_by_user_id=user.id,
|
||||
@@ -1136,6 +1152,14 @@ def firm_task_template_edit_submit(
|
||||
sequence_no: int = Form(1),
|
||||
is_mandatory: str | None = Form(None),
|
||||
requires_review: str | None = Form(None),
|
||||
is_aqmm_task: str | None = Form(None),
|
||||
aqmm_mandatory: str | None = Form(None),
|
||||
aqmm_evidence_required: str | None = Form(None),
|
||||
aqmm_manager_review_required: str | None = Form(None),
|
||||
aqmm_partner_review_required: str | None = Form(None),
|
||||
aqmm_review_partner_required: str | None = Form(None),
|
||||
aqmm_blocks_final_release: str | None = Form(None),
|
||||
aqmm_reference: str = Form(''),
|
||||
is_active: str | None = Form(None),
|
||||
csrf_token: str = Form(...),
|
||||
):
|
||||
@@ -1166,6 +1190,14 @@ def firm_task_template_edit_submit(
|
||||
task.sequence_no = sequence_no
|
||||
task.is_mandatory = is_mandatory is not None
|
||||
task.requires_review = requires_review is not None
|
||||
task.is_aqmm_task = is_aqmm_task is not None
|
||||
task.aqmm_mandatory = aqmm_mandatory is not None
|
||||
task.aqmm_evidence_required = aqmm_evidence_required is not None
|
||||
task.aqmm_manager_review_required = aqmm_manager_review_required is not None
|
||||
task.aqmm_partner_review_required = aqmm_partner_review_required is not None
|
||||
task.aqmm_review_partner_required = aqmm_review_partner_required is not None
|
||||
task.aqmm_blocks_final_release = aqmm_blocks_final_release is not None
|
||||
task.aqmm_reference = aqmm_reference.strip() or None
|
||||
task.is_active = is_active is not None
|
||||
task.updated_by_user_id = user.id
|
||||
|
||||
@@ -1227,6 +1259,14 @@ def default_task_template_edit_submit(
|
||||
sequence_no: int = Form(1),
|
||||
is_mandatory: str | None = Form(None),
|
||||
requires_review: str | None = Form(None),
|
||||
is_aqmm_task: str | None = Form(None),
|
||||
aqmm_mandatory: str | None = Form(None),
|
||||
aqmm_evidence_required: str | None = Form(None),
|
||||
aqmm_manager_review_required: str | None = Form(None),
|
||||
aqmm_partner_review_required: str | None = Form(None),
|
||||
aqmm_review_partner_required: str | None = Form(None),
|
||||
aqmm_blocks_final_release: str | None = Form(None),
|
||||
aqmm_reference: str = Form(''),
|
||||
is_active: str | None = Form(None),
|
||||
csrf_token: str = Form(...),
|
||||
):
|
||||
@@ -1259,6 +1299,14 @@ def default_task_template_edit_submit(
|
||||
task.sequence_no = sequence_no
|
||||
task.is_mandatory = is_mandatory is not None
|
||||
task.requires_review = requires_review is not None
|
||||
task.is_aqmm_task = is_aqmm_task is not None
|
||||
task.aqmm_mandatory = aqmm_mandatory is not None
|
||||
task.aqmm_evidence_required = aqmm_evidence_required is not None
|
||||
task.aqmm_manager_review_required = aqmm_manager_review_required is not None
|
||||
task.aqmm_partner_review_required = aqmm_partner_review_required is not None
|
||||
task.aqmm_review_partner_required = aqmm_review_partner_required is not None
|
||||
task.aqmm_blocks_final_release = aqmm_blocks_final_release is not None
|
||||
task.aqmm_reference = aqmm_reference.strip() or None
|
||||
task.is_active = is_active is not None
|
||||
|
||||
db.commit()
|
||||
|
||||
Reference in New Issue
Block a user