feat: add campaign copying scheduling and residual handling

This commit is contained in:
2026-08-07 14:54:04 +02:00
parent 696f8f6385
commit c2efd6b7bd
35 changed files with 3359 additions and 39 deletions
@@ -468,6 +468,30 @@ class AttachmentBasePathConfig(StrictModel):
source: str | None = None
class ResidualFileMode(StrEnum):
NONE = "none"
REPORT = "report"
ATTACH = "attach"
class ResidualFileDispositionConfig(StrictModel):
mode: ResidualFileMode = ResidualFileMode.NONE
recipient: RecipientConfig | None = None
subject: str = "Unassigned files in campaign {{local:campaign_name}}"
text: str = (
"The campaign build found {{local:residual_file_count}} file(s) that "
"were not assigned to a recipient.\n\n{{local:residual_file_list}}"
)
@model_validator(mode="after")
def require_recipient_for_routing(self) -> "ResidualFileDispositionConfig":
if self.mode != ResidualFileMode.NONE and self.recipient is None:
raise ValueError(
"Residual-file report or attachment routing requires a recipient."
)
return self
class AttachmentConfig(StrictModel):
id: str | None = None
label: str | None = None
@@ -509,6 +533,9 @@ class AttachmentsConfig(StrictModel):
global_: list[AttachmentConfig] = Field(default_factory=list, alias="global")
missing_behavior: Behavior = Behavior.WARN
ambiguous_behavior: Behavior = Behavior.ASK
residual_files: ResidualFileDispositionConfig = Field(
default_factory=ResidualFileDispositionConfig
)
@model_validator(mode="after")
def normalize_send_without_attachments_behavior(self) -> "AttachmentsConfig":