Removed `IDocReceiverElementWriter` interface and its implementation, replacing it with repository-based operations (`IRepository<DocReceiverElement>`). Updated `CreateEnvelopeCommandHandler` to use repository methods for deleting and creating `DocReceiverElement` records. Cleaned up dependency injection by removing `IDocReceiverElementWriter` registration. Added new database triggers for `DocReceiverElement` in `appsettings.json` and `appsettings.Database.json`. Introduced `SENDER_SAVE_EDIT_MIGRATION_PLAN.md` to outline the migration of the sender save/edit workflow to Blazor Server. This includes splitting save and send actions, supporting draft persistence, and enabling draft editing from the dashboard. Removed unused namespaces and performed code cleanup. Updated testing strategies to ensure correctness and prevent regressions.
9.4 KiB
9.4 KiB
Sender Save/Edit Migration Plan (Legacy Form -> Blazor Server)
Objective
Migrate the legacy frmEnvelopeEditor save/edit workflow into current architecture so that:
- Sender can save incomplete envelope as draft (
EnvelopeSaved = 1002) fromEnvelopeSenderEditorPage.razor. - Sender can resume editing saved/created envelopes from
EnvelopeSenderPage.razorvia Bearbeiten and continue where they left off.
This plan introduces a distinct draft flow and a same-ID send-from-draft flow, aligned with legacy Form app semantics.
Scope
In scope
- Add separate Save and Send actions in sender editor.
- Persist draft data server-side with partial/incomplete payload support.
- Re-open existing draft from dashboard and restore editor state.
- Preserve existing delete/withdraw logic and status model.
- Add endpoints and application commands required for progressive sender workflow.
Out of scope (this ticket)
- Full redesign of editor UX.
- Multi-document support parity with
frmOrderFiles. - New DB schema changes (unless separately approved).
Current Gap Analysis
Current behavior (problem)
EnvelopeSenderEditorPage.razorhas only one save path (SaveAsync) that behaves like "send":- requires title + PDF + receivers + fields.
- calls
EnvelopeReceiverService.CreateAsync(...)->/api/EnvelopeReceivermonolithic create/send pipeline.
EnvelopeSenderPage.razorEditEnvelope()is TODO and does not navigate to editable persisted draft data.
Legacy behavior (target)
frmEnvelopeEditorhas both:- Save: persists envelope without full validation/sending.
- Send: validates required business data then sends invitations.
- Dashboard "Bearbeiten" opens selected unsent envelope and restores work state.
Architectural Decisions
-
Split command model by intent
- Draft persistence must not reuse monolithic
/api/EnvelopeReceivercreate+send endpoint. - Introduce dedicated sender workflow commands/endpoints for draft lifecycle.
- Draft persistence must not reuse monolithic
-
Progressive persistence
- Persist envelope metadata, document, receivers, and fields incrementally.
- Draft save accepts partial data and updates only provided segments.
-
Status ownership
- Draft operations set status to
EnvelopeSaved(1002). - Send operation transitions to queued/sent path (existing 1003+ behavior).
- Draft operations set status to
-
Editor route strategy
- Keep
/sender/editorfor new envelope. - Support edit mode via query:
/sender/editor?envelopeId={id}(or route variant in follow-up if preferred).
- Keep
-
No DB schema migration by default
- Use existing tables (
TBSIG_ENVELOPE,TBSIG_DOCUMENT,TBSIG_ENVELOPE_RECEIVER,TBSIG_DOC_RECEIVER_ELEMENT). - If a field is missing for a requirement, handle in application logic first and escalate only if unavoidable.
- Use existing tables (
Target Workflow
A) New envelope (draft first)
- User opens
/sender/editor. - User enters any subset of data.
- User clicks Save.
- System creates or updates draft envelope and sets status
EnvelopeSaved. - User gets confirmation and remains in editor (or optional return to dashboard).
B) New envelope (direct send)
- User fills required data.
- User clicks Send.
- System validates complete data and sends invitations.
- Status transitions to queued/sent flow.
C) Edit existing draft/created
- User selects row in
/senderwithEnvelopeCreatedorEnvelopeSaved. - User clicks Bearbeiten.
- App navigates to
/sender/editor?envelopeId={id}. - Editor loads persisted data (metadata, PDF, receivers, fields).
- User can save again (draft) or send.
Detailed Implementation Plan
Phase 1 - Contracts and Application Use Cases
1.1 Add sender draft commands (Application)
CreateEnvelopeCommand(create/update draft with partial data)ReadEnvelopeDraftQuery(load full editable projection by envelope id)CreateEnvelopeCommand(same request,Send=truefor final send)
Each command should enforce sender ownership (UserId) and return structured result DTOs.
1.2 Validation policy split
- Draft Save validation (minimal):
- title optional at first save? choose one policy and keep consistent.
- no requirement for receivers/fields/document.
- Send validation (strict):
- title required
- document required
- at least one receiver
- required signature field mapping per receiver (business rule)
1.3 History policy
- On draft save/update: optional lightweight history entry (
EnvelopeSaved) if legacy parity requires it. - On send: keep current history/send pipeline behavior.
Phase 2 - Server Endpoints
Implement in EnvelopeGenerator.Server only.
2.1 Draft endpoints
POST /api/Envelope- Create/update draft envelope (partial payload allowed), and send when
Send=true.
- Create/update draft envelope (partial payload allowed), and send when
GET /api/Envelope/{id}/draft- Read full editable aggregate for editor hydration.
2.2 Authorization + errors
- 400 validation issues
- 403 sender mismatch
- 404 envelope not found
- 409 invalid state transition (optional, if needed)
Phase 3 - Sender Editor UI (EnvelopeSenderEditorPage.razor)
3.1 Action buttons
- Keep current action as Send (rename from "Speichern" to "Senden").
- Add separate Save button next to Send.
3.2 Editor mode state
- Add
_editingEnvelopeIdfrom query paramenvelopeId. null=> create mode, value => edit mode.
3.3 Load existing draft
- On parameter set / first render in edit mode:
- call draft read endpoint.
- hydrate title, message, PDF bytes, receiver list, field list.
- map DB inches <-> PDF points correctly:
- DB inches -> UI points:
pt = inches * 72 - UI points -> DB inches:
inches = pt / 72
- DB inches -> UI points:
3.4 Save behavior (new)
- Save button calls
SaveDraftAsync():- no strict send validations.
- create draft if no id yet, otherwise update.
- force status
EnvelopeSaved. - keep user in editor and show non-blocking success feedback.
3.5 Send behavior (existing path replacement)
- Send button calls
SendAsync():- strict validation.
- if no draft id: create draft first then send, or direct send command.
- if edit mode: send current draft id.
3.6 Session cache coexistence
- Keep
esidmemory cache for transient UX resilience. - In edit mode, server data is source of truth; cache is fallback only.
Phase 4 - Dashboard Edit Resume (EnvelopeSenderPage.razor)
4.1 Edit button enablement
- Enable Bearbeiten for statuses:
EnvelopeCreatedEnvelopeSaved
- Keep disabled for sent/queued/completed/deleted/withdrawn/rejected.
4.2 Navigation
- Implement
EditEnvelope():Navigation.NavigateTo($"/sender/editor?envelopeId={_selectedEnvelope.Id}").
4.3 Optional UX
- Double-click on editable rows can open editor instead of preview (or keep current preview behavior per product decision).
Phase 5 - Data Mapping Rules
- Document
- Persist original PDF bytes (no visual placeholder burn-in).
- Receivers
- Draft can contain receivers with/without fields.
- Fields
- Store in DB inches with fixed signature size semantics.
- Ownership
- Sender can read/update/send only own envelopes.
Suggested DTO Shape (Draft)
Use one aggregate DTO for editor hydration/save/update:
EnvelopeDraftDtoEnvelopeIdStatusTitleMessageDocumentBase64(or document id + fetch endpoint)Receivers[]ReceiverId?NameEmailPhoneFields[] { XInches, YInches, Page }
Migration Strategy
Step A (safe vertical slice)
- Add read/edit route query handling and dashboard navigation first.
- Hydrate editor from existing envelope data where possible.
Step B (draft save)
- Implement draft create/update endpoints + app commands.
- Wire Save button.
Step C (send from draft)
- Move Send to explicit command/endpoint.
- Ensure existing email/history behavior remains intact.
Step D (hardening)
- Add tests + manual QA checklist.
Testing Plan
Automated (Application)
- Save draft with partial data -> status
EnvelopeSaved. - Edit draft updates existing records, no duplicate orphan data.
- Send draft with missing required data -> validation error.
- Send draft complete -> queued/sent flow success.
- Ownership checks -> 403 behavior.
Manual (UI)
- New editor -> save with only title.
- Reopen from dashboard -> state restored.
- Add PDF only -> save -> reopen -> PDF still present.
- Add receivers/fields incrementally -> save repeatedly.
- Final send -> status transitions and receiver notifications.
Risks and Mitigations
- Monolithic endpoint coupling risk
- Mitigation: draft/send dedicated endpoints.
- Data duplication on repeated save
- Mitigation: idempotent upsert logic for receivers/fields.
- Coordinate regression
- Mitigation: centralize inches/points conversion helpers.
- Trigger side effects/history duplication
- Mitigation: verify DB-trigger interactions per status events.
Definition of Done
- Editor has two distinct actions: Save and Send.
- Save allows incomplete payload and persists with
EnvelopeSavedstatus. - Dashboard Bearbeiten opens editor for
EnvelopeCreated/EnvelopeSavedand restores state. - Sender can continue from last saved point and eventually send.
- Server endpoints support save/edit/send draft workflow with same envelope id.
- No DB schema changes introduced without explicit approval.