Compare commits
136 Commits
master
...
feat/final
| Author | SHA1 | Date | |
|---|---|---|---|
| b996102a86 | |||
| 4b1d316b46 | |||
| 51234cda2a | |||
| e719dded48 | |||
| 6195d99fab | |||
| 50a372a224 | |||
| 35dd2e8e07 | |||
| fac5419589 | |||
| d442ad0ce0 | |||
| cbe2acc37d | |||
| 40cc467b47 | |||
| d1513dab5e | |||
| dee424e7db | |||
| e57e9e1834 | |||
| 5b30465126 | |||
| 2dfa1de7e1 | |||
| e68965543e | |||
| 958bcdfc42 | |||
| d88ed324be | |||
| 380b141738 | |||
| f3a15216a8 | |||
| 8a48bf6b51 | |||
| 0038eeed76 | |||
| 4ed118ed2b | |||
|
|
350aa259c8 | ||
|
|
737de2202e | ||
|
|
ff60cd7ef8 | ||
|
|
e1aa7fe650 | ||
|
|
9c0b1e3fa8 | ||
| 6b34b55c4f | |||
| 4a5608249e | |||
|
|
965838513f | ||
|
|
9c4766518e | ||
|
|
292b6b2ccf | ||
|
|
6f9b5d4b13 | ||
|
|
8459706c45 | ||
|
|
972c63388e | ||
|
|
cde9ed06a1 | ||
|
|
d94f885e92 | ||
|
|
028785a8c9 | ||
|
|
e6285f13f7 | ||
| 35b7b1a080 | |||
| 67d0980c63 | |||
| c6a99b56a2 | |||
| 31f5d1f340 | |||
| a2df5d7691 | |||
| f8c586dd31 | |||
| 8aea3c8301 | |||
| 495adb8c31 | |||
| 86c0a65540 | |||
| d2e8f1fc5e | |||
| f8369e350f | |||
| b8a2ad97ef | |||
| ffffc2d470 | |||
| 36b03da084 | |||
| defa53fa26 | |||
| 6134b58a4c | |||
| 5299016b43 | |||
| 55c20e83d8 | |||
| f1a140faa7 | |||
| b20d25e5b9 | |||
| e21eb2c0d6 | |||
| d237b4ab95 | |||
| dff99b163d | |||
| 70fbf31b14 | |||
| 339e0e81cd | |||
| a386ad72bb | |||
| 02c5f286ec | |||
| 5fa358ca79 | |||
| 7233d2ce98 | |||
| 5784cc7a97 | |||
| f31f680f91 | |||
| e7c2d46ef0 | |||
| 85d70c1db4 | |||
| fb340fb08a | |||
| 7d3959ae51 | |||
| 9d4890b10d | |||
| b64f4d71f5 | |||
| b7d146ddb5 | |||
| 6a5c9a3489 | |||
|
|
15f6ee7be0 | ||
|
|
1051c5356f | ||
|
|
7b13350fbf | ||
|
|
2bada327d8 | ||
|
|
a47729ebca | ||
|
|
fcfed963b7 | ||
|
|
b2ace61cd4 | ||
|
|
77cdc83a4e | ||
|
|
a611df4914 | ||
|
|
1341f69ab1 | ||
|
|
0480513288 | ||
|
|
9dbd1b16b5 | ||
|
|
c649c93921 | ||
|
|
1843119b1b | ||
|
|
bc4371cb99 | ||
|
|
05130d6163 | ||
| 567b9c9565 | |||
| b798181f91 | |||
| 7e8fc25ec9 | |||
| db76162697 | |||
| 2b4573ea73 | |||
| 4a043ed247 | |||
| a62a035ec6 | |||
| 1713a65014 | |||
| 695d7c83e0 | |||
| 428f71863d | |||
| cce2f8f90e | |||
|
|
86c9fdfcd7 | ||
|
|
89ec887510 | ||
|
|
7d5b988842 | ||
|
|
3c456562cc | ||
|
|
4d6b01030c | ||
|
|
75e7e9925b | ||
|
|
0a175b9e9d | ||
|
|
f611e74de1 | ||
|
|
08ca116628 | ||
|
|
4997f7d75c | ||
| b5cd42b6fa | |||
| 187f4a42fc | |||
| 23d4b2f31e | |||
| 8e71e5b4bb | |||
| b693615561 | |||
| 36dc9266bc | |||
| 9aabe270b4 | |||
| b303b7be06 | |||
| 02937360ea | |||
| c2735b92e0 | |||
| 568f43186c | |||
| 3bc5439b5a | |||
| 22b494a262 | |||
| 209785dda5 | |||
| 44ea893f05 | |||
| b4aa7984aa | |||
| 7c5a505ad1 | |||
| bd6d57e1e8 | |||
| 8403ce2c6a |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -365,8 +365,3 @@ FodyWeavers.xsd
|
|||||||
/EnvelopeGenerator.GeneratorAPI/ClientApp/envelope-generator-ui/.vscode
|
/EnvelopeGenerator.GeneratorAPI/ClientApp/envelope-generator-ui/.vscode
|
||||||
/EnvelopeGenerator.Tests.Application/Services/BugFixTests.cs
|
/EnvelopeGenerator.Tests.Application/Services/BugFixTests.cs
|
||||||
/EnvelopeGenerator.Tests.Application/annotations.json
|
/EnvelopeGenerator.Tests.Application/annotations.json
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/TekH - SoftHSM Test.md
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/tekh_softHSM_test.md
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/publish-output
|
|
||||||
/EnvelopeGenerator.Server/EnvelopeGenerator.Server/tekh_softHSM_test.md
|
|
||||||
/ignore/EnvelopeGenerator.Form
|
|
||||||
|
|||||||
316
AGENTS.md
316
AGENTS.md
@@ -1,316 +0,0 @@
|
|||||||
# EnvelopeGenerator - Agent Guide
|
|
||||||
|
|
||||||
## Must Read First
|
|
||||||
- **`COPILOT_CONTEXT.md`** - Architecture, coordinate systems, migration status
|
|
||||||
- **`FORM_APPLICATION_CONTEXT.md`** - Legacy VB.NET features to migrate
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Documentation Catalog
|
|
||||||
|
|
||||||
This section explains every markdown file in the repo: what it contains, why it exists, and when you need it. Read this before deciding which file to open.
|
|
||||||
|
|
||||||
### Core Architecture Docs (Active — Always Relevant)
|
|
||||||
|
|
||||||
#### `AGENTS.md` ← you are here
|
|
||||||
**What:** Authoritative agent/copilot onboarding guide. Covers project structure, route table, render mode rules, coordinate system, API gaps, and common mistakes.
|
|
||||||
**Why it exists:** Single source of truth for agents so they don't re-derive architecture from code on every session.
|
|
||||||
**When you need it:** Always. Read first on every session.
|
|
||||||
**Maintenance:** Update whenever project structure, routes, render modes, or naming conventions change.
|
|
||||||
|
|
||||||
#### `COPILOT_CONTEXT.md`
|
|
||||||
**What:** Deep-dive architecture reference. Covers the full Blazor Auto hybrid setup, authentication flows (JWT + cookies, per-envelope receiver tokens), service registration patterns, coordinate system proof, and an explicit "prefer Server/Server.Client over old WebUI/ReceiverUI" rule.
|
|
||||||
**Why it exists:** Prevents agents from re-deriving or misinterpreting the hybrid SSR+WASM architecture. Also documents coordinate system evidence to prevent regressions (inches vs. points vs. pixels).
|
|
||||||
**When you need it:**
|
|
||||||
- Implementing any new page or service
|
|
||||||
- Debugging auth/cookie issues
|
|
||||||
- Any coordinate conversion question
|
|
||||||
- Unsure which project a new file should go in
|
|
||||||
**Maintenance:** Update when render mode assignments change, new auth schemes are added, or the coordinate system is touched.
|
|
||||||
|
|
||||||
#### `FORM_APPLICATION_CONTEXT.md`
|
|
||||||
**What:** Comprehensive reverse-engineering of the legacy VB.NET WinForms app (`EnvelopeGenerator.Form`). Documents every form (frmMain, frmEnvelopeEditor, frmFieldEditor, etc.), their UI layout, toolbar actions, data models, coordinate system, and a feature-by-feature migration mapping table to `EnvelopeGenerator.Server`.
|
|
||||||
**Why it exists:** The WinForms app is the functional specification for the sender-side web UI. It documents the full intended workflow that is being migrated to Blazor — especially the envelope creation, signature field placement, and dashboard features that are not yet implemented.
|
|
||||||
**When you need it:**
|
|
||||||
- Implementing any sender-side feature (`/sender`, `/sender/editor`, `/sender/envelope/{id}`)
|
|
||||||
- Understanding what a specific toolbar button or form should do
|
|
||||||
- Designing API endpoints for sender workflow
|
|
||||||
- Checking coordinate system behavior (INCHES proof is here)
|
|
||||||
**Maintenance:** Update the mapping table as features are implemented (change `❌ Not implemented` → `✅ Exists`). Do not rename `ReceiverUI` back — it was the old project name; current target is `EnvelopeGenerator.Server`.
|
|
||||||
|
|
||||||
#### `RECEIVER_PDF_VIEWER_CONTEXT.md`
|
|
||||||
**What:** Detailed technical reference for the receiver-side PDF viewer (`EnvelopeReceiverPage.razor`). Documents the PDF.js integration, signature button rendering, JS interop API (`window.pdfViewer`), signature capture flow, HiDPI/zoom/thumbnail config, and the full component lifecycle.
|
|
||||||
**Why it exists:** The receiver PDF viewer is the most complex component in the codebase. This doc prevents agents from misunderstanding the JS↔Blazor boundary, re-implementing already-solved problems (HiDPI, zoom, signature caching), or breaking the signing flow.
|
|
||||||
**When you need it:**
|
|
||||||
- Working on `EnvelopeReceiverPage.razor` or `pdf-viewer.js`
|
|
||||||
- Adding new signature field rendering logic
|
|
||||||
- Debugging signature rendering, zoom, or thumbnail issues
|
|
||||||
- Implementing sender-side PDF.js overlay (signature field placement) — the receiver side is the reference implementation
|
|
||||||
**Maintenance:** Update when `pdf-viewer.js` API changes, new JS interop functions are added, or the signing flow is modified.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Deployment Docs
|
|
||||||
|
|
||||||
#### `EnvelopeGenerator.Server/EnvelopeGenerator.Server/README.md`
|
|
||||||
**What:** IIS publish and deployment guide for `EnvelopeGenerator.Server`. Covers why self-contained publish is required (WASM assembly version strictness), the exact `dotnet publish` command, IIS Application Pool settings (`No Managed Code`), directory structure after publish, and common deployment errors.
|
|
||||||
**Why it exists:** Blazor Auto (Server + WASM hybrid) has non-obvious deployment requirements that differ from a standard ASP.NET Core app. Forgetting self-contained publish or using the wrong app pool will break WASM loading in production.
|
|
||||||
**When you need it:**
|
|
||||||
- Deploying to IIS for the first time
|
|
||||||
- Troubleshooting 500 errors on production after deploy
|
|
||||||
- Setting up a new deployment environment
|
|
||||||
**Maintenance:** Update if the publish target, IIS config, or runtime version changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Active Architecture (Post-Migration)
|
|
||||||
|
|
||||||
**Frontend:** Blazor Auto (Server+WASM hybrid)
|
|
||||||
- **EnvelopeGenerator.Server** (Server): `@rendermode InteractiveServer` - PDF viewers requiring DevExpress backend
|
|
||||||
- **EnvelopeGenerator.Server.Client** (WASM): `@rendermode InteractiveWebAssembly` - Login, dashboards, business logic
|
|
||||||
|
|
||||||
**Backend + API Host:** `EnvelopeGenerator.Server` (ASP.NET Core 8.0, merged Blazor + Web API host)
|
|
||||||
|
|
||||||
**Proxy:** YARP in `EnvelopeGenerator.Server` is used for external integrations (for example AuthHub forwarding), not for routing app APIs to `EnvelopeGenerator.API`.
|
|
||||||
|
|
||||||
### Deprecated Projects - DO NOT USE
|
|
||||||
- `EnvelopeGenerator.ReceiverUI` - Pure WASM (migrated to Server)
|
|
||||||
- `EnvelopeGenerator.Web` - Razor Pages (replaced by Server)
|
|
||||||
- **VB.NET projects** (`Form`, `Service`, `BBTests`) - Legacy, read-only for reference
|
|
||||||
|
|
||||||
## Development Commands
|
|
||||||
|
|
||||||
### Run Active Host
|
|
||||||
```powershell
|
|
||||||
# Terminal - Blazor + Web API host
|
|
||||||
cd EnvelopeGenerator.Server\EnvelopeGenerator.Server
|
|
||||||
dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
**Critical:** `EnvelopeGenerator.Server` is the active runtime host for both UI and API controllers.
|
|
||||||
|
|
||||||
### Build
|
|
||||||
```powershell
|
|
||||||
dotnet build EnvelopeGenerator.sln
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Boundaries
|
|
||||||
|
|
||||||
```
|
|
||||||
EnvelopeGenerator.Domain/ # Entities (Envelope, Receiver, Document, etc.)
|
|
||||||
EnvelopeGenerator.Application/ # MediatR CQRS (Commands, Queries, Handlers)
|
|
||||||
EnvelopeGenerator.Infrastructure/ # EF Core, SQL executors, repositories
|
|
||||||
EnvelopeGenerator.Server/
|
|
||||||
EnvelopeGenerator.Server/ # Server-side Blazor + API controllers
|
|
||||||
├─ Components/Pages/ # @rendermode InteractiveServer
|
|
||||||
├─ Controllers/ # Active Web API endpoints
|
|
||||||
EnvelopeGenerator.Server.Client/ # Client-side WASM components
|
|
||||||
├─ Pages/ # @rendermode InteractiveWebAssembly
|
|
||||||
├─ Services/ # HTTP API clients
|
|
||||||
├─ Models/ # DTOs
|
|
||||||
```
|
|
||||||
|
|
||||||
## Route Structure (Critical)
|
|
||||||
|
|
||||||
| Route | File Location | Render Mode | Purpose |
|
|
||||||
|-------|--------------|-------------|---------|
|
|
||||||
| `/` | `Server.Client/Pages/Index.razor` | WASM | Landing page |
|
|
||||||
| `/sender/login` | `Server.Client/Pages/LoginSenderPage.razor` | WASM | Sender auth |
|
|
||||||
| `/sender` | `Server/Components/Pages/EnvelopeSenderPage.razor` | **Server** | Sender dashboard |
|
|
||||||
| `/sender/editor` | `Server/Components/Pages/EnvelopeSenderEditorPage.razor` | **Server** | Envelope editor + PDF viewer |
|
|
||||||
| `/envelope/login/{key}` | `Server.Client/Pages/LoginReceiverPage.razor` | WASM | Receiver auth |
|
|
||||||
| `/envelope/{key}` | `Server/Components/Pages/EnvelopeReceiverPage.razor` | **Server** | PDF viewer + signing |
|
|
||||||
|
|
||||||
**Rule:** PDF viewers MUST use `@rendermode InteractiveServer` (DevExpress backend requirement). Everything else uses WASM.
|
|
||||||
|
|
||||||
## Coordinate System (CRITICAL)
|
|
||||||
|
|
||||||
**Database stores INCHES** (GdPicture14 native). Origin: top-left, Y-axis down.
|
|
||||||
|
|
||||||
### Conversions
|
|
||||||
```csharp
|
|
||||||
// Database (INCHES) → PDF Points
|
|
||||||
float points = inches * 72;
|
|
||||||
|
|
||||||
// Database (INCHES) → DevExpress DX
|
|
||||||
float dx = inches * 100;
|
|
||||||
|
|
||||||
// PDF.js Pixels → Database (INCHES)
|
|
||||||
float inches = (pixelX / canvasWidth) * pageWidthInches;
|
|
||||||
```
|
|
||||||
|
|
||||||
**A4 Page:** 8.27" wide × 11.69" tall = 595pt × 842pt
|
|
||||||
|
|
||||||
**Signature Field Size:** 1.77" × 1.96" (FIXED, do not change)
|
|
||||||
|
|
||||||
**Evidence:** See `COPILOT_CONTEXT.md` lines 158-185, `EnvelopeGenerator.Form/frmFieldEditor.vb`
|
|
||||||
|
|
||||||
## API Architecture Quirks
|
|
||||||
|
|
||||||
### Monolithic Endpoint (Avoid for UI)
|
|
||||||
`POST /api/EnvelopeReceiver` - Creates envelope+document+receivers+fields atomically.
|
|
||||||
- **Use case:** External API consumers
|
|
||||||
- **Not suitable for:** Step-by-step UI workflow (no draft support, no partial updates)
|
|
||||||
|
|
||||||
### Sender Workflow Endpoint
|
|
||||||
```
|
|
||||||
POST /api/Envelope # Create/update/save/send envelope workflow payload
|
|
||||||
DELETE /api/Envelope/{id} # Delete with reason
|
|
||||||
POST /api/Envelope/{id}/document # Upload PDF
|
|
||||||
POST /api/Envelope/{id}/receivers # Add receiver
|
|
||||||
POST /api/Envelope/{id}/signature-fields # Place signature field
|
|
||||||
```
|
|
||||||
|
|
||||||
See `FORM_APPLICATION_CONTEXT.md` for detailed workflow requirements.
|
|
||||||
|
|
||||||
## Status Color Coding
|
|
||||||
|
|
||||||
Form app uses DevExpress `CustomDrawCell`. EnvelopeGenerator.Server needs CSS:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.envelope-row.status-partly-signed { background-color: #81C784; } /* GREEN_300 */
|
|
||||||
.envelope-row.status-queued,
|
|
||||||
.envelope-row.status-sent { background-color: #FFB74D; } /* ORANGE_300 */
|
|
||||||
.envelope-row.status-completed { background-color: #81C784; }
|
|
||||||
.envelope-row.status-deleted,
|
|
||||||
.envelope-row.status-rejected { background-color: #E57373; } /* RED_300 */
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### YARP Proxy (`EnvelopeGenerator.Server/yarp.json`)
|
|
||||||
Routes `/api/*`, `/swagger/*`, `/openapi/*`, `/scalar/*` → `https://localhost:8088`
|
|
||||||
|
|
||||||
### PDF.js Settings (`EnvelopeGenerator.Server/wwwroot/appsettings.json`)
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"PdfViewerOptions": {
|
|
||||||
"ThumbnailBaseScale": 0.75,
|
|
||||||
"ThumbnailEnableHiDPI": true,
|
|
||||||
"MainCanvasEnableHiDPI": true,
|
|
||||||
"ZoomStepPercentage": 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Server Host Config (`EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json`)
|
|
||||||
- `ConnectionStrings:Default` - SQL Server DB
|
|
||||||
- `AllowedOrigins` - CORS (includes `http://localhost:5131`, `http://localhost:7192`)
|
|
||||||
- `Cache:SignatureCacheExpiration` - Signature persistence timeout
|
|
||||||
- `PSPDFKitLicenseKey` - **DEPRECATED** (use PDF.js instead)
|
|
||||||
|
|
||||||
## Migration Status
|
|
||||||
|
|
||||||
### Complete ✅
|
|
||||||
- Receiver login/authentication
|
|
||||||
- PDF viewing with PDF.js (HiDPI, zoom, thumbnails)
|
|
||||||
- Signature capture (draw/type/image)
|
|
||||||
- Signature caching (Redis/SQL)
|
|
||||||
- Sender login
|
|
||||||
|
|
||||||
### Missing (High Priority) ❌
|
|
||||||
- Sender dashboard (`/sender`) - Empty stub
|
|
||||||
- Envelope editor (`/sender/envelope/{id}`)
|
|
||||||
- Signature field placement tool (PDF.js + draggable overlays)
|
|
||||||
- Granular API endpoints (draft, receivers, fields)
|
|
||||||
- Master-detail grids for receivers/history
|
|
||||||
|
|
||||||
## Common Mistakes (DO NOT REPEAT)
|
|
||||||
|
|
||||||
| Mistake | Why Wrong |
|
|
||||||
|---------|-----------|
|
|
||||||
| Using iText7 in receiver pages | GPL license issue. Use PDF.js overlays. |
|
|
||||||
| Using PSPDFKit | Removed from architecture. Use PDF.js + DevExpress. |
|
|
||||||
| `@rendermode InteractiveWebAssembly` on PDF viewers | DevExpress DxPdfViewer requires server-side rendering. |
|
|
||||||
| Hardcoded quality in PDF.js | Use `appsettings.json` `PdfViewerOptions`. |
|
|
||||||
| Coordinates in points/pixels for DB | Database uses INCHES. Convert before save. |
|
|
||||||
| `BottomMarginBand` for signatures | Repeats on every page. Use `DetailBand`. |
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
**No automated tests exist yet.**
|
|
||||||
|
|
||||||
Manual testing workflow:
|
|
||||||
1. Start EnvelopeGenerator.Server (`dotnet run` in `EnvelopeGenerator.Server\EnvelopeGenerator.Server`)
|
|
||||||
2. Navigate to `https://localhost:5131` (or check console output for port)
|
|
||||||
3. Test sender login at `/sender/login`
|
|
||||||
4. Test receiver flow at `/envelope/login/{envelopeKey}`
|
|
||||||
|
|
||||||
## Database
|
|
||||||
|
|
||||||
**SQL Server** (DD_ECM)
|
|
||||||
- Connection string in `EnvelopeGenerator.Server/EnvelopeGenerator.Server/appsettings.json`
|
|
||||||
- EF Core migrations NOT used (manual SQL scripts)
|
|
||||||
- Stored procedures: `PRSIG_*` prefix
|
|
||||||
|
|
||||||
**Key Tables:**
|
|
||||||
- `TBSIG_ENVELOPE` - Envelope metadata
|
|
||||||
- `TBSIG_ENVELOPE_RECEIVER` - Receiver assignments
|
|
||||||
- `TBSIG_DOC_RECEIVER_ELEMENT` - Signature fields (X, Y in INCHES)
|
|
||||||
- `TBSIG_RECEIVER` - Receiver registry
|
|
||||||
- `TBSIG_DOCUMENT` - PDF binary data
|
|
||||||
- `TBSIG_ENVELOPE_HISTORY` - Audit trail
|
|
||||||
|
|
||||||
## DevExpress
|
|
||||||
|
|
||||||
**License:** Commercial (v25.2.3)
|
|
||||||
**Components Used:**
|
|
||||||
- `DxGrid` - Master-detail grids
|
|
||||||
- `DxPdfViewer` - Server-side PDF rendering
|
|
||||||
- `DxPopup` - Modal dialogs
|
|
||||||
- `DxToolbar` - Action bars
|
|
||||||
- `DxFormLayout` - Forms
|
|
||||||
|
|
||||||
**Theme:** Blazing Berry (default)
|
|
||||||
|
|
||||||
## JavaScript Interop
|
|
||||||
|
|
||||||
**PDF Viewer:** `wwwroot/js/pdf-viewer.js`
|
|
||||||
```javascript
|
|
||||||
window.pdfViewer = {
|
|
||||||
initialize(canvasId, pdfDataUrl, dotNetRef),
|
|
||||||
renderPage(num),
|
|
||||||
renderSignatureButtons(signatures, pageNum, dotNetRef),
|
|
||||||
applySignature(signatureId, dataUrl, fullName, position, place),
|
|
||||||
zoomIn(), zoomOut(), dispose()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Signature Pad:** `wwwroot/js/receiver-signature.js`
|
|
||||||
```javascript
|
|
||||||
window.receiverSignature = {
|
|
||||||
initializeDrawPad(canvasId, dotNetRef),
|
|
||||||
getSignatureDataUrl(canvasId),
|
|
||||||
clearPad(canvasId)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Envelope Support
|
|
||||||
|
|
||||||
Receivers can login to **multiple envelopes simultaneously** via per-envelope cookies:
|
|
||||||
```
|
|
||||||
AuthTokenSignFLOWReceiver.{envelopeKey}
|
|
||||||
```
|
|
||||||
|
|
||||||
Each envelope maintains independent authentication state.
|
|
||||||
|
|
||||||
## External Dependencies
|
|
||||||
|
|
||||||
**CDN:**
|
|
||||||
- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
|
||||||
|
|
||||||
**NuGet (Server.Client):**
|
|
||||||
- `DevExpress.Blazor.*` 25.2.3
|
|
||||||
- `SkiaSharp.*` 3.119.1 (WASM rendering)
|
|
||||||
|
|
||||||
**External Services:**
|
|
||||||
- LDAP/AD authentication (optional)
|
|
||||||
- GTX Messaging (SMS 2FA)
|
|
||||||
- Email dispatcher (signFlow)
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
None required. All config in `appsettings.json`.
|
|
||||||
|
|
||||||
**Local dev ports:**
|
|
||||||
- App host (UI + API): `https://localhost:5131` or `http://localhost:7192`
|
|
||||||
@@ -1,351 +0,0 @@
|
|||||||
# EnvelopeGenerator — Current Workspace Context
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
Digital document signing system for senders and receivers.
|
|
||||||
|
|
||||||
- Senders authenticate, view envelope lists, and manage envelope workflows.
|
|
||||||
- Receivers authenticate per envelope, open PDFs, create signatures, and apply them in the viewer.
|
|
||||||
- The active UI stack is `Blazor Auto` with server-side and WebAssembly render modes.
|
|
||||||
- Primary UI/PDF libraries are `DevExpress` and `PDF.js`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Active Application Structure
|
|
||||||
|
|
||||||
### Main Host
|
|
||||||
**Primary active application:** `EnvelopeGenerator.Server`
|
|
||||||
|
|
||||||
`EnvelopeGenerator.Server` is the current runtime host and contains:
|
|
||||||
- Blazor server host
|
|
||||||
- WebAssembly host integration
|
|
||||||
- API controllers
|
|
||||||
- authentication/authorization setup
|
|
||||||
- Swagger/Scalar setup
|
|
||||||
- YARP reverse proxy configuration
|
|
||||||
- DevExpress server-side services
|
|
||||||
- SQL Server distributed cache setup
|
|
||||||
|
|
||||||
### Client Project
|
|
||||||
**Client UI project:** `EnvelopeGenerator.Server.Client`
|
|
||||||
|
|
||||||
This project contains:
|
|
||||||
- WebAssembly-rendered pages
|
|
||||||
- client-side services
|
|
||||||
- client models and options
|
|
||||||
- sender and receiver login flows
|
|
||||||
|
|
||||||
### Other Projects
|
|
||||||
- `EnvelopeGenerator.Application` — MediatR/CQRS handlers and business logic
|
|
||||||
- `EnvelopeGenerator.Domain` — domain models, constants, shared abstractions
|
|
||||||
- `EnvelopeGenerator.Infrastructure` — EF Core and infrastructure services
|
|
||||||
- `EnvelopeGenerator.PdfEditor` — PDF-related backend utilities
|
|
||||||
- `EnvelopeGenerator.API` — deprecated project (no longer part of the active solution/runtime path)
|
|
||||||
|
|
||||||
### Legacy / Do Not Touch
|
|
||||||
- `EnvelopeGenerator.Service`
|
|
||||||
- `EnvelopeGenerator.Form`
|
|
||||||
- `EnvelopeGenerator.BBTests`
|
|
||||||
- `EnvelopeGenerator.CommonServices`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current Hosting Model
|
|
||||||
|
|
||||||
`EnvelopeGenerator.Server/Program.cs` currently configures:
|
|
||||||
- `AddRazorComponents()` with both interactive server and interactive WebAssembly components
|
|
||||||
- `AddControllers()` and `MapControllers()`
|
|
||||||
- JWT authentication for sender and receiver flows
|
|
||||||
- cookie authentication
|
|
||||||
- authorization policies using `AuthScheme.Sender`, `AuthScheme.Receiver`, `AuthPolicy.Sender`, `AuthPolicy.Receiver`
|
|
||||||
- `AddReverseProxy()` with `yarp.json`
|
|
||||||
- Swagger / OpenAPI / Scalar
|
|
||||||
- distributed SQL Server cache
|
|
||||||
- DevExpress Blazor and DevExpress PDF Viewer server-side services
|
|
||||||
- request localization middleware
|
|
||||||
|
|
||||||
This means the active app is a **merged UI + API host**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reverse Proxy
|
|
||||||
|
|
||||||
**Config file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
|
|
||||||
|
|
||||||
Current YARP usage is focused on **AuthHub forwarding**, not a general `/api/* -> EnvelopeGenerator.API` proxy.
|
|
||||||
|
|
||||||
Configured routes forward:
|
|
||||||
- `POST /api/auth` -> AuthHub `/api/auth/sign-flow`
|
|
||||||
- `POST /api/Auth/envelope-receiver/{key}` -> AuthHub `/api/auth/envelope-receiver/{key}?cookie=true`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Active Routes and Files
|
|
||||||
|
|
||||||
### WebAssembly Pages (`EnvelopeGenerator.Server.Client`)
|
|
||||||
| Route | File | Render Mode | Purpose |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `/` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/IndexPage.razor` | WebAssembly | Landing page |
|
|
||||||
| `/sender/login` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginSenderPage.razor` | WebAssembly | Sender login |
|
|
||||||
| `/sender` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor` | WebAssembly (`prerender: false`) | Sender dashboard |
|
|
||||||
| `/envelope/login/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginReceiverPage.razor` | WebAssembly | Receiver login |
|
|
||||||
|
|
||||||
### Server Pages (`EnvelopeGenerator.Server`)
|
|
||||||
| Route | File | Render Mode | Purpose |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `/envelope/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` | InteractiveServer | Main receiver PDF viewer and signing page |
|
|
||||||
| `/envelope/DxPdfViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | InteractiveServer | DevExpress PDF Viewer test page |
|
|
||||||
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | InteractiveServer | DevExpress report-based PDF rendering |
|
|
||||||
| `/envelope/Embed` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_embed.razor` | InteractiveServer | Embedded browser PDF view test page |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Current API Location
|
|
||||||
|
|
||||||
The active application exposes controllers from:
|
|
||||||
`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers`
|
|
||||||
|
|
||||||
Current controller set includes:
|
|
||||||
- `AnnotationController`
|
|
||||||
- `AuthController`
|
|
||||||
- `CacheController`
|
|
||||||
- `ConfigController`
|
|
||||||
- `DocumentController`
|
|
||||||
- `EmailTemplateController`
|
|
||||||
- `EnvelopeController`
|
|
||||||
- `EnvelopeReceiverController`
|
|
||||||
- `EnvelopeTypeController`
|
|
||||||
- `HistoryController`
|
|
||||||
- `LocalizationController`
|
|
||||||
- `ReadOnlyController`
|
|
||||||
- `ReceiverController`
|
|
||||||
- `SignatureController`
|
|
||||||
- `TfaRegistrationController`
|
|
||||||
|
|
||||||
Do not assume API behavior lives in `EnvelopeGenerator.API`; the active merged host contains controller endpoints directly.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Authentication Model
|
|
||||||
|
|
||||||
### Sender
|
|
||||||
Client login page uses `EnvelopeGenerator.Server.Client/Services/AuthService.cs`.
|
|
||||||
|
|
||||||
Key sender endpoints:
|
|
||||||
- `POST /api/auth?cookie=true` — login
|
|
||||||
- `GET /api/auth/check` — current sender access check
|
|
||||||
- `POST /api/auth/logout` — logout
|
|
||||||
|
|
||||||
### Receiver
|
|
||||||
Receiver authentication is **per envelope**.
|
|
||||||
|
|
||||||
Key receiver endpoints used by client services:
|
|
||||||
- `POST /api/Auth/envelope-receiver/{envelopeKey}` — submit access code
|
|
||||||
- `GET /api/auth/check/envelope/{envelopeKey}` — check access
|
|
||||||
- `POST /api/auth/logout/envelope/{envelopeKey}` — logout receiver for one envelope
|
|
||||||
|
|
||||||
Receiver cookie resolution in server auth uses an envelope-specific cookie name derived from:
|
|
||||||
- `AuthTokenSignFLOWReceiver.{envelopeKey}` pattern
|
|
||||||
|
|
||||||
### Receiver Server-Side Authorization
|
|
||||||
`EnvelopeReceiverPage.razor` does **not** rely on its own API access-check call for page authorization.
|
|
||||||
|
|
||||||
It uses:
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
- tries the current `HttpContext.User`
|
|
||||||
- if needed, reads the per-envelope receiver cookie directly
|
|
||||||
- validates the JWT with the receiver auth scheme
|
|
||||||
- verifies the token subject matches the route envelope key
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Receiver Page Data Loading
|
|
||||||
|
|
||||||
Main server-side page data service:
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
|
|
||||||
|
|
||||||
This service loads directly via MediatR and distributed cache:
|
|
||||||
- document bytes
|
|
||||||
- receiver envelope data
|
|
||||||
- signature placeholders
|
|
||||||
- cached signature data
|
|
||||||
|
|
||||||
For signature placeholders, the service:
|
|
||||||
- reads document receiver elements
|
|
||||||
- filters them for the authenticated receiver
|
|
||||||
- converts coordinates to `UnitOfLength.Point` before UI use
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Receiver PDF Viewer
|
|
||||||
|
|
||||||
**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
|
|
||||||
|
|
||||||
Current receiver viewer characteristics:
|
|
||||||
- route: `/envelope/{EnvelopeKey}`
|
|
||||||
- render mode: `InteractiveServer`
|
|
||||||
- PDF rendering: `PDF.js`
|
|
||||||
- toolbar: page navigation, zoom, thumbnail toggle, signature navigation, signature reset
|
|
||||||
- signature popup: `DxPopup`
|
|
||||||
- thumbnail sidebar: resizable and stored in `localStorage`
|
|
||||||
|
|
||||||
### JS Assets
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
|
|
||||||
|
|
||||||
### CSS
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
|
|
||||||
|
|
||||||
### PDF.js CDN
|
|
||||||
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
|
|
||||||
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Signature Workflow
|
|
||||||
|
|
||||||
Receiver signatures are handled as a **viewer overlay workflow**.
|
|
||||||
|
|
||||||
### Current behavior
|
|
||||||
1. Server-side authorization validates receiver access.
|
|
||||||
2. The page loads document bytes, receiver data, signature placeholders, and cached signature state.
|
|
||||||
3. If no cached signature exists, the signature popup opens automatically.
|
|
||||||
4. Receiver creates signature using one of three tabs:
|
|
||||||
- draw
|
|
||||||
- text
|
|
||||||
- image
|
|
||||||
5. Required metadata:
|
|
||||||
- full name
|
|
||||||
- place
|
|
||||||
6. Optional metadata:
|
|
||||||
- position
|
|
||||||
7. Clicking a signature placeholder applies the signature as a client-side overlay in the PDF viewer.
|
|
||||||
|
|
||||||
### Important note
|
|
||||||
Although `itext` is referenced by the server project, the current receiver page signing flow is **not PDF stamping-based**. The active receiver UI uses client-side overlay behavior in the viewer.
|
|
||||||
|
|
||||||
### Signature DTO
|
|
||||||
`EnvelopeGenerator.Server.Client/Models/SignatureCaptureDto.cs`
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public sealed record SignatureCaptureDto {
|
|
||||||
public required string DataUrl { get; init; }
|
|
||||||
public required string FullName { get; init; }
|
|
||||||
public string Position { get; init; } = "";
|
|
||||||
public required string Place { get; init; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Signature Cache
|
|
||||||
|
|
||||||
### Active cache model
|
|
||||||
The current receiver page cache flow is handled directly in the server project through:
|
|
||||||
- `EnvelopeReceiverPageDataService`
|
|
||||||
- `IDistributedCache`
|
|
||||||
- SQL Server distributed cache configuration from `Program.cs`
|
|
||||||
|
|
||||||
### Cache key format
|
|
||||||
Current server-side key prefix:
|
|
||||||
- `envelope-generator.receiver-ui.signature:{receiverSignature}`
|
|
||||||
|
|
||||||
This is different from an envelope-key-only cache convention.
|
|
||||||
|
|
||||||
### Config
|
|
||||||
`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Options/CacheOptions.cs`
|
|
||||||
- section name: `Cache`
|
|
||||||
- option: `SignatureCacheExpiration`
|
|
||||||
|
|
||||||
### Related controller
|
|
||||||
A cache API controller also exists in:
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/CacheController.cs`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sender Dashboard
|
|
||||||
|
|
||||||
**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor`
|
|
||||||
|
|
||||||
Current behavior:
|
|
||||||
- checks sender access through `AuthService.CheckSenderAccessAsync()`
|
|
||||||
- redirects to `/sender/login` when unauthorized
|
|
||||||
- loads envelope list through client `EnvelopeService`
|
|
||||||
- separates envelopes into active/completed tabs
|
|
||||||
- uses `DevExpress DxGrid`
|
|
||||||
|
|
||||||
The sender page is active, but create/edit/delete actions are still marked with TODO behavior in the UI page.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Localization
|
|
||||||
|
|
||||||
Current server host localization setup in `Program.cs`:
|
|
||||||
- supported cultures: `de-DE`, `en-US`
|
|
||||||
- request localization middleware is enabled
|
|
||||||
- `QueryStringRequestCultureProvider` is added
|
|
||||||
- cookie-based localization services are registered via `AddCookieBasedLocalizer()`
|
|
||||||
|
|
||||||
Do not assume the old ReceiverUI-only `localStorage` culture approach is the current source of truth for the active host.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Coordinate System
|
|
||||||
|
|
||||||
### Source data
|
|
||||||
Database signature coordinates are still based on:
|
|
||||||
- **unit:** inches
|
|
||||||
- **origin:** top-left
|
|
||||||
- **axes:** X right, Y down
|
|
||||||
|
|
||||||
### Relevant conversions
|
|
||||||
- inches -> PDF points: `x_pt = x_inches * 72`
|
|
||||||
- inches -> DevExpress DX units: `x_dx = x_inches * 100`
|
|
||||||
|
|
||||||
### Current receiver page behavior
|
|
||||||
The server page data service converts signature placeholders to **points** before sending them into the viewer workflow.
|
|
||||||
|
|
||||||
### Unit systems to keep in mind
|
|
||||||
| System | Unit | Origin | Y-axis |
|
|
||||||
|---|---|---|---|
|
|
||||||
| Database | Inches | Top-left | Down |
|
|
||||||
| PDF.js display | Pixels | Top-left | Down |
|
|
||||||
| PDF points | Points | Depends on PDF model | Depends on consumer |
|
|
||||||
| DevExpress DX | 1/100 inch style coordinates | Top-left-oriented usage in this app | Down-oriented usage |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Services and Files
|
|
||||||
|
|
||||||
### Client services
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs`
|
|
||||||
|
|
||||||
### Server services
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
|
|
||||||
|
|
||||||
### Server config and host files
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs`
|
|
||||||
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Working Rules for This Workspace
|
|
||||||
|
|
||||||
- Treat `EnvelopeGenerator.Server` as the active main application host.
|
|
||||||
- Treat `EnvelopeGenerator.Server.Client` as the active client UI project.
|
|
||||||
- Prefer current `Server` / `Server.Client` paths over old `WebUI` / `ReceiverUI` references.
|
|
||||||
- Do not use `EnvelopeGenerator.Web` or `EnvelopeGenerator.ReceiverUI` as the primary implementation target unless explicitly asked.
|
|
||||||
- Do not modify the legacy VB.NET projects unless explicitly requested.
|
|
||||||
- For receiver PDF/signature work, prefer the current `PDF.js`-based flow in `EnvelopeReceiverPage.razor`.
|
|
||||||
- For DevExpress PDF viewer issues, remember server-side services are registered in `EnvelopeGenerator.Server`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated:** 2026-06-29
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<RootNamespace>EnvelopeGenerator.Application.VB</RootNamespace>
|
||||||
|
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DevExpress.Data" Version="21.2.4" />
|
||||||
|
<PackageReference Include="DevExpress.Reporting.Core" Version="21.2.4" />
|
||||||
|
<PackageReference Include="DevExpress.Win" Version="21.2.0" />
|
||||||
|
<PackageReference Include="DevExpress.Xpo" Version="21.2.4" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Reports\rptEnvelopeHistory.vb">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
730
EnvelopeGenerator.Application.VB/Reports/rptEnvelopeHistory.Designer.vb
generated
Normal file
730
EnvelopeGenerator.Application.VB/Reports/rptEnvelopeHistory.Designer.vb
generated
Normal file
@@ -0,0 +1,730 @@
|
|||||||
|
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
|
||||||
|
Partial Public Class rptEnvelopeHistory
|
||||||
|
Inherits DevExpress.XtraReports.UI.XtraReport
|
||||||
|
|
||||||
|
'XtraReport overrides dispose to clean up the component list.
|
||||||
|
<System.Diagnostics.DebuggerNonUserCode()> _
|
||||||
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
|
If disposing AndAlso components IsNot Nothing Then
|
||||||
|
components.Dispose()
|
||||||
|
End If
|
||||||
|
MyBase.Dispose(disposing)
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
'Required by the Designer
|
||||||
|
Private components As System.ComponentModel.IContainer
|
||||||
|
|
||||||
|
'NOTE: The following procedure is required by the Designer
|
||||||
|
'It can be modified using the Designer.
|
||||||
|
'Do not modify it using the code editor.
|
||||||
|
<System.Diagnostics.DebuggerStepThrough()> _
|
||||||
|
Private Sub InitializeComponent()
|
||||||
|
Me.components = New System.ComponentModel.Container()
|
||||||
|
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(rptEnvelopeHistory))
|
||||||
|
Me.TopMargin = New DevExpress.XtraReports.UI.TopMarginBand()
|
||||||
|
Me.BottomMargin = New DevExpress.XtraReports.UI.BottomMarginBand()
|
||||||
|
Me.XrLabel1 = New DevExpress.XtraReports.UI.XRLabel()
|
||||||
|
Me.pageInfo1 = New DevExpress.XtraReports.UI.XRPageInfo()
|
||||||
|
Me.pageInfo2 = New DevExpress.XtraReports.UI.XRPageInfo()
|
||||||
|
Me.ReportHeader = New DevExpress.XtraReports.UI.ReportHeaderBand()
|
||||||
|
Me.label1 = New DevExpress.XtraReports.UI.XRLabel()
|
||||||
|
Me.XrTable3 = New DevExpress.XtraReports.UI.XRTable()
|
||||||
|
Me.XrTableRow8 = New DevExpress.XtraReports.UI.XRTableRow()
|
||||||
|
Me.XrTableCell13 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell15 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableRow9 = New DevExpress.XtraReports.UI.XRTableRow()
|
||||||
|
Me.XrTableCell14 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell17 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell16 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell18 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableRow10 = New DevExpress.XtraReports.UI.XRTableRow()
|
||||||
|
Me.XrTableCell19 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell20 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell21 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell22 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableRow11 = New DevExpress.XtraReports.UI.XRTableRow()
|
||||||
|
Me.XrTableCell23 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell24 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell25 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.XrTableCell26 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.GroupHeader1 = New DevExpress.XtraReports.UI.GroupHeaderBand()
|
||||||
|
Me.table1 = New DevExpress.XtraReports.UI.XRTable()
|
||||||
|
Me.tableRow1 = New DevExpress.XtraReports.UI.XRTableRow()
|
||||||
|
Me.tableCell1 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.tableCell2 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.tableCell3 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.Detail = New DevExpress.XtraReports.UI.DetailBand()
|
||||||
|
Me.table2 = New DevExpress.XtraReports.UI.XRTable()
|
||||||
|
Me.tableRow2 = New DevExpress.XtraReports.UI.XRTableRow()
|
||||||
|
Me.tableCell4 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.tableCell5 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.tableCell6 = New DevExpress.XtraReports.UI.XRTableCell()
|
||||||
|
Me.Title = New DevExpress.XtraReports.UI.XRControlStyle()
|
||||||
|
Me.DetailCaption1 = New DevExpress.XtraReports.UI.XRControlStyle()
|
||||||
|
Me.DetailData1 = New DevExpress.XtraReports.UI.XRControlStyle()
|
||||||
|
Me.DetailData3_Odd = New DevExpress.XtraReports.UI.XRControlStyle()
|
||||||
|
Me.PageInfo = New DevExpress.XtraReports.UI.XRControlStyle()
|
||||||
|
Me.GalleryDropDown1 = New DevExpress.XtraBars.Ribbon.GalleryDropDown(Me.components)
|
||||||
|
Me.ObjectDataSource1 = New DevExpress.DataAccess.ObjectBinding.ObjectDataSource(Me.components)
|
||||||
|
CType(Me.XrTable3, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.table1, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.table2, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.GalleryDropDown1, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me.ObjectDataSource1, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
CType(Me, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||||
|
'
|
||||||
|
'TopMargin
|
||||||
|
'
|
||||||
|
Me.TopMargin.Dpi = 254.0!
|
||||||
|
Me.TopMargin.HeightF = 190.6042!
|
||||||
|
Me.TopMargin.Name = "TopMargin"
|
||||||
|
'
|
||||||
|
'BottomMargin
|
||||||
|
'
|
||||||
|
Me.BottomMargin.Controls.AddRange(New DevExpress.XtraReports.UI.XRControl() {Me.XrLabel1, Me.pageInfo1, Me.pageInfo2})
|
||||||
|
Me.BottomMargin.Dpi = 254.0!
|
||||||
|
Me.BottomMargin.Name = "BottomMargin"
|
||||||
|
'
|
||||||
|
'XrLabel1
|
||||||
|
'
|
||||||
|
Me.XrLabel1.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.XrLabel1.Borders = DevExpress.XtraPrinting.BorderSide.Top
|
||||||
|
Me.XrLabel1.Dpi = 254.0!
|
||||||
|
Me.XrLabel1.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold)
|
||||||
|
Me.XrLabel1.LocationFloat = New DevExpress.Utils.PointFloat(896.5417!, 0!)
|
||||||
|
Me.XrLabel1.Name = "XrLabel1"
|
||||||
|
Me.XrLabel1.Padding = New DevExpress.XtraPrinting.PaddingInfo(0, 0, 0, 0, 254.0!)
|
||||||
|
Me.XrLabel1.SizeF = New System.Drawing.SizeF(645.5836!, 58.0!)
|
||||||
|
Me.XrLabel1.StyleName = "Title"
|
||||||
|
Me.XrLabel1.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrLabel1.StylePriority.UseBorders = False
|
||||||
|
Me.XrLabel1.StylePriority.UseFont = False
|
||||||
|
Me.XrLabel1.StylePriority.UsePadding = False
|
||||||
|
Me.XrLabel1.Text = "Erstellt mit SignFlow"
|
||||||
|
'
|
||||||
|
'pageInfo1
|
||||||
|
'
|
||||||
|
Me.pageInfo1.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.pageInfo1.Borders = DevExpress.XtraPrinting.BorderSide.Top
|
||||||
|
Me.pageInfo1.Dpi = 254.0!
|
||||||
|
Me.pageInfo1.LocationFloat = New DevExpress.Utils.PointFloat(0!, 0!)
|
||||||
|
Me.pageInfo1.Name = "pageInfo1"
|
||||||
|
Me.pageInfo1.PageInfo = DevExpress.XtraPrinting.PageInfo.DateTime
|
||||||
|
Me.pageInfo1.SizeF = New System.Drawing.SizeF(896.5417!, 58.0!)
|
||||||
|
Me.pageInfo1.StyleName = "PageInfo"
|
||||||
|
Me.pageInfo1.StylePriority.UseBorderColor = False
|
||||||
|
Me.pageInfo1.StylePriority.UseBorders = False
|
||||||
|
Me.pageInfo1.TextFormatString = "{0:dddd, d. MMMM yyyy HH:mm}"
|
||||||
|
'
|
||||||
|
'pageInfo2
|
||||||
|
'
|
||||||
|
Me.pageInfo2.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.pageInfo2.Borders = DevExpress.XtraPrinting.BorderSide.Top
|
||||||
|
Me.pageInfo2.Dpi = 254.0!
|
||||||
|
Me.pageInfo2.LocationFloat = New DevExpress.Utils.PointFloat(1542.125!, 0!)
|
||||||
|
Me.pageInfo2.Name = "pageInfo2"
|
||||||
|
Me.pageInfo2.SizeF = New System.Drawing.SizeF(357.875!, 58.0!)
|
||||||
|
Me.pageInfo2.StyleName = "PageInfo"
|
||||||
|
Me.pageInfo2.StylePriority.UseBorderColor = False
|
||||||
|
Me.pageInfo2.StylePriority.UseBorders = False
|
||||||
|
Me.pageInfo2.TextAlignment = DevExpress.XtraPrinting.TextAlignment.TopRight
|
||||||
|
Me.pageInfo2.TextFormatString = "Seite {0} von {1}"
|
||||||
|
'
|
||||||
|
'ReportHeader
|
||||||
|
'
|
||||||
|
Me.ReportHeader.Controls.AddRange(New DevExpress.XtraReports.UI.XRControl() {Me.label1, Me.XrTable3})
|
||||||
|
Me.ReportHeader.Dpi = 254.0!
|
||||||
|
Me.ReportHeader.HeightF = 406.0843!
|
||||||
|
Me.ReportHeader.Name = "ReportHeader"
|
||||||
|
'
|
||||||
|
'label1
|
||||||
|
'
|
||||||
|
Me.label1.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))
|
||||||
|
Me.label1.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.label1.Borders = DevExpress.XtraPrinting.BorderSide.Bottom
|
||||||
|
Me.label1.BorderWidth = 2.0!
|
||||||
|
Me.label1.Dpi = 254.0!
|
||||||
|
Me.label1.Font = New System.Drawing.Font("Segoe UI", 14.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.label1.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.label1.LocationFloat = New DevExpress.Utils.PointFloat(0.0002422333!, 0!)
|
||||||
|
Me.label1.Name = "label1"
|
||||||
|
Me.label1.Padding = New DevExpress.XtraPrinting.PaddingInfo(10, 10, 5, 5, 254.0!)
|
||||||
|
Me.label1.SizeF = New System.Drawing.SizeF(1900.0!, 77.32857!)
|
||||||
|
Me.label1.StyleName = "Title"
|
||||||
|
Me.label1.StylePriority.UseBackColor = False
|
||||||
|
Me.label1.StylePriority.UseBorderColor = False
|
||||||
|
Me.label1.StylePriority.UseBorders = False
|
||||||
|
Me.label1.StylePriority.UseBorderWidth = False
|
||||||
|
Me.label1.StylePriority.UseFont = False
|
||||||
|
Me.label1.StylePriority.UseForeColor = False
|
||||||
|
Me.label1.StylePriority.UsePadding = False
|
||||||
|
Me.label1.Text = "Signierungszertifikat"
|
||||||
|
'
|
||||||
|
'XrTable3
|
||||||
|
'
|
||||||
|
Me.XrTable3.Dpi = 254.0!
|
||||||
|
Me.XrTable3.LocationFloat = New DevExpress.Utils.PointFloat(0.0002422333!, 96.60422!)
|
||||||
|
Me.XrTable3.Name = "XrTable3"
|
||||||
|
Me.XrTable3.OddStyleName = "DetailData3_Odd"
|
||||||
|
Me.XrTable3.Rows.AddRange(New DevExpress.XtraReports.UI.XRTableRow() {Me.XrTableRow8, Me.XrTableRow9, Me.XrTableRow10, Me.XrTableRow11})
|
||||||
|
Me.XrTable3.SizeF = New System.Drawing.SizeF(1900.0!, 284.4801!)
|
||||||
|
'
|
||||||
|
'XrTableRow8
|
||||||
|
'
|
||||||
|
Me.XrTableRow8.Cells.AddRange(New DevExpress.XtraReports.UI.XRTableCell() {Me.XrTableCell13, Me.XrTableCell15})
|
||||||
|
Me.XrTableRow8.Dpi = 254.0!
|
||||||
|
Me.XrTableRow8.Name = "XrTableRow8"
|
||||||
|
Me.XrTableRow8.Weight = 1.0R
|
||||||
|
'
|
||||||
|
'XrTableCell13
|
||||||
|
'
|
||||||
|
Me.XrTableCell13.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))
|
||||||
|
Me.XrTableCell13.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.XrTableCell13.Borders = DevExpress.XtraPrinting.BorderSide.Bottom
|
||||||
|
Me.XrTableCell13.BorderWidth = 1.0!
|
||||||
|
Me.XrTableCell13.Dpi = 254.0!
|
||||||
|
Me.XrTableCell13.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell13.Name = "XrTableCell13"
|
||||||
|
Me.XrTableCell13.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell13.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell13.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell13.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell13.StylePriority.UseBorderWidth = False
|
||||||
|
Me.XrTableCell13.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell13.Text = "Ersteller"
|
||||||
|
Me.XrTableCell13.Weight = 0.38139956730411484R
|
||||||
|
'
|
||||||
|
'XrTableCell15
|
||||||
|
'
|
||||||
|
Me.XrTableCell15.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))
|
||||||
|
Me.XrTableCell15.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.XrTableCell15.Borders = DevExpress.XtraPrinting.BorderSide.Bottom
|
||||||
|
Me.XrTableCell15.BorderWidth = 1.0!
|
||||||
|
Me.XrTableCell15.Dpi = 254.0!
|
||||||
|
Me.XrTableCell15.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell15.Name = "XrTableCell15"
|
||||||
|
Me.XrTableCell15.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell15.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell15.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell15.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell15.StylePriority.UseBorderWidth = False
|
||||||
|
Me.XrTableCell15.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell15.Text = "Umschlag"
|
||||||
|
Me.XrTableCell15.Weight = 0.49859081604996847R
|
||||||
|
'
|
||||||
|
'XrTableRow9
|
||||||
|
'
|
||||||
|
Me.XrTableRow9.Cells.AddRange(New DevExpress.XtraReports.UI.XRTableCell() {Me.XrTableCell14, Me.XrTableCell17, Me.XrTableCell16, Me.XrTableCell18})
|
||||||
|
Me.XrTableRow9.Dpi = 254.0!
|
||||||
|
Me.XrTableRow9.Name = "XrTableRow9"
|
||||||
|
Me.XrTableRow9.Weight = 1.0R
|
||||||
|
'
|
||||||
|
'XrTableCell14
|
||||||
|
'
|
||||||
|
Me.XrTableCell14.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell14.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell14.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell14.Dpi = 254.0!
|
||||||
|
Me.XrTableCell14.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell14.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell14.Multiline = True
|
||||||
|
Me.XrTableCell14.Name = "XrTableCell14"
|
||||||
|
Me.XrTableCell14.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell14.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell14.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell14.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell14.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell14.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell14.Text = "Name"
|
||||||
|
Me.XrTableCell14.Weight = 0.11578820509129036R
|
||||||
|
'
|
||||||
|
'XrTableCell17
|
||||||
|
'
|
||||||
|
Me.XrTableCell17.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell17.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell17.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell17.Dpi = 254.0!
|
||||||
|
Me.XrTableCell17.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Envelope].[User].[FullName]")})
|
||||||
|
Me.XrTableCell17.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell17.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell17.Multiline = True
|
||||||
|
Me.XrTableCell17.Name = "XrTableCell17"
|
||||||
|
Me.XrTableCell17.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell17.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell17.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell17.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell17.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell17.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell17.Text = "XrTableCell17"
|
||||||
|
Me.XrTableCell17.Weight = 0.2656113622128245R
|
||||||
|
'
|
||||||
|
'XrTableCell16
|
||||||
|
'
|
||||||
|
Me.XrTableCell16.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell16.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell16.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell16.Dpi = 254.0!
|
||||||
|
Me.XrTableCell16.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell16.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell16.Multiline = True
|
||||||
|
Me.XrTableCell16.Name = "XrTableCell16"
|
||||||
|
Me.XrTableCell16.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell16.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell16.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell16.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell16.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell16.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell16.Text = "Titel"
|
||||||
|
Me.XrTableCell16.Weight = 0.11578821158083684R
|
||||||
|
'
|
||||||
|
'XrTableCell18
|
||||||
|
'
|
||||||
|
Me.XrTableCell18.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell18.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell18.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell18.Dpi = 254.0!
|
||||||
|
Me.XrTableCell18.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Envelope].[Title]")})
|
||||||
|
Me.XrTableCell18.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell18.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell18.Multiline = True
|
||||||
|
Me.XrTableCell18.Name = "XrTableCell18"
|
||||||
|
Me.XrTableCell18.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell18.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell18.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell18.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell18.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell18.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell18.Text = "XrTableCell18"
|
||||||
|
Me.XrTableCell18.Weight = 0.38280260446913167R
|
||||||
|
'
|
||||||
|
'XrTableRow10
|
||||||
|
'
|
||||||
|
Me.XrTableRow10.Cells.AddRange(New DevExpress.XtraReports.UI.XRTableCell() {Me.XrTableCell19, Me.XrTableCell20, Me.XrTableCell21, Me.XrTableCell22})
|
||||||
|
Me.XrTableRow10.Dpi = 254.0!
|
||||||
|
Me.XrTableRow10.Name = "XrTableRow10"
|
||||||
|
Me.XrTableRow10.Weight = 1.0R
|
||||||
|
'
|
||||||
|
'XrTableCell19
|
||||||
|
'
|
||||||
|
Me.XrTableCell19.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell19.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell19.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell19.Dpi = 254.0!
|
||||||
|
Me.XrTableCell19.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell19.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell19.Multiline = True
|
||||||
|
Me.XrTableCell19.Name = "XrTableCell19"
|
||||||
|
Me.XrTableCell19.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell19.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell19.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell19.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell19.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell19.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell19.Text = "EmailAddress"
|
||||||
|
Me.XrTableCell19.Weight = 0.11578820509129036R
|
||||||
|
'
|
||||||
|
'XrTableCell20
|
||||||
|
'
|
||||||
|
Me.XrTableCell20.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell20.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell20.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell20.Dpi = 254.0!
|
||||||
|
Me.XrTableCell20.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Envelope].[User].[EmailAddress]")})
|
||||||
|
Me.XrTableCell20.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell20.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell20.Multiline = True
|
||||||
|
Me.XrTableCell20.Name = "XrTableCell20"
|
||||||
|
Me.XrTableCell20.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell20.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell20.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell20.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell20.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell20.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell20.Text = "XrTableCell20"
|
||||||
|
Me.XrTableCell20.Weight = 0.2656113622128245R
|
||||||
|
'
|
||||||
|
'XrTableCell21
|
||||||
|
'
|
||||||
|
Me.XrTableCell21.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell21.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell21.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell21.Dpi = 254.0!
|
||||||
|
Me.XrTableCell21.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell21.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell21.Multiline = True
|
||||||
|
Me.XrTableCell21.Name = "XrTableCell21"
|
||||||
|
Me.XrTableCell21.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell21.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell21.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell21.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell21.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell21.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell21.Text = "Umschlag-ID"
|
||||||
|
Me.XrTableCell21.Weight = 0.11578821158083684R
|
||||||
|
'
|
||||||
|
'XrTableCell22
|
||||||
|
'
|
||||||
|
Me.XrTableCell22.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell22.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell22.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell22.Dpi = 254.0!
|
||||||
|
Me.XrTableCell22.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Envelope].[Uuid]")})
|
||||||
|
Me.XrTableCell22.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell22.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell22.Multiline = True
|
||||||
|
Me.XrTableCell22.Name = "XrTableCell22"
|
||||||
|
Me.XrTableCell22.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell22.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell22.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell22.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell22.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell22.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell22.Text = "XrTableCell22"
|
||||||
|
Me.XrTableCell22.Weight = 0.38280260446913167R
|
||||||
|
'
|
||||||
|
'XrTableRow11
|
||||||
|
'
|
||||||
|
Me.XrTableRow11.Cells.AddRange(New DevExpress.XtraReports.UI.XRTableCell() {Me.XrTableCell23, Me.XrTableCell24, Me.XrTableCell25, Me.XrTableCell26})
|
||||||
|
Me.XrTableRow11.Dpi = 254.0!
|
||||||
|
Me.XrTableRow11.Name = "XrTableRow11"
|
||||||
|
Me.XrTableRow11.Weight = 1.0R
|
||||||
|
'
|
||||||
|
'XrTableCell23
|
||||||
|
'
|
||||||
|
Me.XrTableCell23.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell23.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell23.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell23.Dpi = 254.0!
|
||||||
|
Me.XrTableCell23.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell23.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell23.Multiline = True
|
||||||
|
Me.XrTableCell23.Name = "XrTableCell23"
|
||||||
|
Me.XrTableCell23.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell23.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell23.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell23.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell23.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell23.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell23.Weight = 0.11578820509129036R
|
||||||
|
'
|
||||||
|
'XrTableCell24
|
||||||
|
'
|
||||||
|
Me.XrTableCell24.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell24.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell24.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell24.Dpi = 254.0!
|
||||||
|
Me.XrTableCell24.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell24.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell24.Multiline = True
|
||||||
|
Me.XrTableCell24.Name = "XrTableCell24"
|
||||||
|
Me.XrTableCell24.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell24.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell24.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell24.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell24.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell24.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell24.Weight = 0.2656113622128245R
|
||||||
|
'
|
||||||
|
'XrTableCell25
|
||||||
|
'
|
||||||
|
Me.XrTableCell25.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell25.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell25.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell25.Dpi = 254.0!
|
||||||
|
Me.XrTableCell25.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell25.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell25.Multiline = True
|
||||||
|
Me.XrTableCell25.Name = "XrTableCell25"
|
||||||
|
Me.XrTableCell25.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell25.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell25.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell25.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell25.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell25.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell25.Text = "Zertifizierung"
|
||||||
|
Me.XrTableCell25.Weight = 0.11578821158083684R
|
||||||
|
'
|
||||||
|
'XrTableCell26
|
||||||
|
'
|
||||||
|
Me.XrTableCell26.BackColor = System.Drawing.Color.White
|
||||||
|
Me.XrTableCell26.BorderColor = System.Drawing.Color.Empty
|
||||||
|
Me.XrTableCell26.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.XrTableCell26.Dpi = 254.0!
|
||||||
|
Me.XrTableCell26.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[Envelope].[CertificationType]")})
|
||||||
|
Me.XrTableCell26.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
|
||||||
|
Me.XrTableCell26.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.XrTableCell26.Multiline = True
|
||||||
|
Me.XrTableCell26.Name = "XrTableCell26"
|
||||||
|
Me.XrTableCell26.StyleName = "DetailCaption1"
|
||||||
|
Me.XrTableCell26.StylePriority.UseBackColor = False
|
||||||
|
Me.XrTableCell26.StylePriority.UseBorderColor = False
|
||||||
|
Me.XrTableCell26.StylePriority.UseBorders = False
|
||||||
|
Me.XrTableCell26.StylePriority.UseFont = False
|
||||||
|
Me.XrTableCell26.StylePriority.UseForeColor = False
|
||||||
|
Me.XrTableCell26.Text = "XrTableCell26"
|
||||||
|
Me.XrTableCell26.Weight = 0.38280260446913161R
|
||||||
|
'
|
||||||
|
'GroupHeader1
|
||||||
|
'
|
||||||
|
Me.GroupHeader1.Controls.AddRange(New DevExpress.XtraReports.UI.XRControl() {Me.table1})
|
||||||
|
Me.GroupHeader1.Dpi = 254.0!
|
||||||
|
Me.GroupHeader1.GroupUnion = DevExpress.XtraReports.UI.GroupUnion.WithFirstDetail
|
||||||
|
Me.GroupHeader1.HeightF = 71.12!
|
||||||
|
Me.GroupHeader1.Name = "GroupHeader1"
|
||||||
|
'
|
||||||
|
'table1
|
||||||
|
'
|
||||||
|
Me.table1.Dpi = 254.0!
|
||||||
|
Me.table1.LocationFloat = New DevExpress.Utils.PointFloat(0!, 0!)
|
||||||
|
Me.table1.Name = "table1"
|
||||||
|
Me.table1.Rows.AddRange(New DevExpress.XtraReports.UI.XRTableRow() {Me.tableRow1})
|
||||||
|
Me.table1.SizeF = New System.Drawing.SizeF(1900.0!, 71.12!)
|
||||||
|
'
|
||||||
|
'tableRow1
|
||||||
|
'
|
||||||
|
Me.tableRow1.Cells.AddRange(New DevExpress.XtraReports.UI.XRTableCell() {Me.tableCell1, Me.tableCell2, Me.tableCell3})
|
||||||
|
Me.tableRow1.Dpi = 254.0!
|
||||||
|
Me.tableRow1.Name = "tableRow1"
|
||||||
|
Me.tableRow1.Weight = 1.0R
|
||||||
|
'
|
||||||
|
'tableCell1
|
||||||
|
'
|
||||||
|
Me.tableCell1.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))
|
||||||
|
Me.tableCell1.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.tableCell1.Borders = DevExpress.XtraPrinting.BorderSide.Bottom
|
||||||
|
Me.tableCell1.BorderWidth = 1.0!
|
||||||
|
Me.tableCell1.Dpi = 254.0!
|
||||||
|
Me.tableCell1.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.tableCell1.Name = "tableCell1"
|
||||||
|
Me.tableCell1.StyleName = "DetailCaption1"
|
||||||
|
Me.tableCell1.StylePriority.UseBackColor = False
|
||||||
|
Me.tableCell1.StylePriority.UseBorderColor = False
|
||||||
|
Me.tableCell1.StylePriority.UseBorders = False
|
||||||
|
Me.tableCell1.StylePriority.UseBorderWidth = False
|
||||||
|
Me.tableCell1.StylePriority.UseForeColor = False
|
||||||
|
Me.tableCell1.Text = "Ereignis"
|
||||||
|
Me.tableCell1.Weight = 0.51726482588176181R
|
||||||
|
'
|
||||||
|
'tableCell2
|
||||||
|
'
|
||||||
|
Me.tableCell2.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))
|
||||||
|
Me.tableCell2.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.tableCell2.Borders = DevExpress.XtraPrinting.BorderSide.Bottom
|
||||||
|
Me.tableCell2.BorderWidth = 1.0!
|
||||||
|
Me.tableCell2.Dpi = 254.0!
|
||||||
|
Me.tableCell2.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.tableCell2.Name = "tableCell2"
|
||||||
|
Me.tableCell2.StyleName = "DetailCaption1"
|
||||||
|
Me.tableCell2.StylePriority.UseBackColor = False
|
||||||
|
Me.tableCell2.StylePriority.UseBorderColor = False
|
||||||
|
Me.tableCell2.StylePriority.UseBorders = False
|
||||||
|
Me.tableCell2.StylePriority.UseBorderWidth = False
|
||||||
|
Me.tableCell2.StylePriority.UseForeColor = False
|
||||||
|
Me.tableCell2.Text = "Benutzer"
|
||||||
|
Me.tableCell2.Weight = 0.45140669898958585R
|
||||||
|
'
|
||||||
|
'tableCell3
|
||||||
|
'
|
||||||
|
Me.tableCell3.BackColor = System.Drawing.Color.FromArgb(CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer), CType(CType(224, Byte), Integer))
|
||||||
|
Me.tableCell3.BorderColor = System.Drawing.Color.FromArgb(CType(CType(165, Byte), Integer), CType(CType(36, Byte), Integer), CType(CType(49, Byte), Integer))
|
||||||
|
Me.tableCell3.Borders = DevExpress.XtraPrinting.BorderSide.Bottom
|
||||||
|
Me.tableCell3.BorderWidth = 1.0!
|
||||||
|
Me.tableCell3.Dpi = 254.0!
|
||||||
|
Me.tableCell3.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.tableCell3.Name = "tableCell3"
|
||||||
|
Me.tableCell3.StyleName = "DetailCaption1"
|
||||||
|
Me.tableCell3.StylePriority.UseBackColor = False
|
||||||
|
Me.tableCell3.StylePriority.UseBorderColor = False
|
||||||
|
Me.tableCell3.StylePriority.UseBorders = False
|
||||||
|
Me.tableCell3.StylePriority.UseBorderWidth = False
|
||||||
|
Me.tableCell3.StylePriority.UseForeColor = False
|
||||||
|
Me.tableCell3.Text = "Zeitstempel"
|
||||||
|
Me.tableCell3.Weight = 0.22479583469004233R
|
||||||
|
'
|
||||||
|
'Detail
|
||||||
|
'
|
||||||
|
Me.Detail.Controls.AddRange(New DevExpress.XtraReports.UI.XRControl() {Me.table2})
|
||||||
|
Me.Detail.Dpi = 254.0!
|
||||||
|
Me.Detail.HeightF = 86.174!
|
||||||
|
Me.Detail.HierarchyPrintOptions.Indent = 50.8!
|
||||||
|
Me.Detail.Name = "Detail"
|
||||||
|
'
|
||||||
|
'table2
|
||||||
|
'
|
||||||
|
Me.table2.Dpi = 254.0!
|
||||||
|
Me.table2.LocationFloat = New DevExpress.Utils.PointFloat(0!, 0!)
|
||||||
|
Me.table2.Name = "table2"
|
||||||
|
Me.table2.Rows.AddRange(New DevExpress.XtraReports.UI.XRTableRow() {Me.tableRow2})
|
||||||
|
Me.table2.SizeF = New System.Drawing.SizeF(1900.0!, 63.42!)
|
||||||
|
'
|
||||||
|
'tableRow2
|
||||||
|
'
|
||||||
|
Me.tableRow2.Cells.AddRange(New DevExpress.XtraReports.UI.XRTableCell() {Me.tableCell4, Me.tableCell5, Me.tableCell6})
|
||||||
|
Me.tableRow2.Dpi = 254.0!
|
||||||
|
Me.tableRow2.Name = "tableRow2"
|
||||||
|
Me.tableRow2.Weight = 11.683999633789062R
|
||||||
|
'
|
||||||
|
'tableCell4
|
||||||
|
'
|
||||||
|
Me.tableCell4.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.tableCell4.Dpi = 254.0!
|
||||||
|
Me.tableCell4.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[ItemStatusTranslated]")})
|
||||||
|
Me.tableCell4.Name = "tableCell4"
|
||||||
|
Me.tableCell4.StyleName = "DetailData1"
|
||||||
|
Me.tableCell4.StylePriority.UseBorders = False
|
||||||
|
Me.tableCell4.Weight = 0.51726482142156094R
|
||||||
|
'
|
||||||
|
'tableCell5
|
||||||
|
'
|
||||||
|
Me.tableCell5.Dpi = 254.0!
|
||||||
|
Me.tableCell5.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[ItemUserReference]")})
|
||||||
|
Me.tableCell5.Name = "tableCell5"
|
||||||
|
Me.tableCell5.StyleName = "DetailData1"
|
||||||
|
Me.tableCell5.Weight = 0.45140669932916544R
|
||||||
|
'
|
||||||
|
'tableCell6
|
||||||
|
'
|
||||||
|
Me.tableCell6.Dpi = 254.0!
|
||||||
|
Me.tableCell6.ExpressionBindings.AddRange(New DevExpress.XtraReports.UI.ExpressionBinding() {New DevExpress.XtraReports.UI.ExpressionBinding("BeforePrint", "Text", "[ItemDate]")})
|
||||||
|
Me.tableCell6.Name = "tableCell6"
|
||||||
|
Me.tableCell6.StyleName = "DetailData1"
|
||||||
|
Me.tableCell6.StylePriority.UseTextAlignment = False
|
||||||
|
Me.tableCell6.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleRight
|
||||||
|
Me.tableCell6.Weight = 0.22479581593269077R
|
||||||
|
'
|
||||||
|
'Title
|
||||||
|
'
|
||||||
|
Me.Title.BackColor = System.Drawing.Color.Transparent
|
||||||
|
Me.Title.BorderColor = System.Drawing.Color.Black
|
||||||
|
Me.Title.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.Title.BorderWidth = 1.0!
|
||||||
|
Me.Title.Font = New System.Drawing.Font("Arial", 14.25!)
|
||||||
|
Me.Title.ForeColor = System.Drawing.Color.FromArgb(CType(CType(75, Byte), Integer), CType(CType(75, Byte), Integer), CType(CType(75, Byte), Integer))
|
||||||
|
Me.Title.Name = "Title"
|
||||||
|
Me.Title.Padding = New DevExpress.XtraPrinting.PaddingInfo(15, 15, 0, 0, 254.0!)
|
||||||
|
'
|
||||||
|
'DetailCaption1
|
||||||
|
'
|
||||||
|
Me.DetailCaption1.BackColor = System.Drawing.Color.FromArgb(CType(CType(75, Byte), Integer), CType(CType(75, Byte), Integer), CType(CType(75, Byte), Integer))
|
||||||
|
Me.DetailCaption1.BorderColor = System.Drawing.Color.White
|
||||||
|
Me.DetailCaption1.Borders = DevExpress.XtraPrinting.BorderSide.Left
|
||||||
|
Me.DetailCaption1.BorderWidth = 2.0!
|
||||||
|
Me.DetailCaption1.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold)
|
||||||
|
Me.DetailCaption1.ForeColor = System.Drawing.Color.White
|
||||||
|
Me.DetailCaption1.Name = "DetailCaption1"
|
||||||
|
Me.DetailCaption1.Padding = New DevExpress.XtraPrinting.PaddingInfo(15, 15, 0, 0, 254.0!)
|
||||||
|
Me.DetailCaption1.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleLeft
|
||||||
|
'
|
||||||
|
'DetailData1
|
||||||
|
'
|
||||||
|
Me.DetailData1.BorderColor = System.Drawing.Color.Transparent
|
||||||
|
Me.DetailData1.Borders = DevExpress.XtraPrinting.BorderSide.Left
|
||||||
|
Me.DetailData1.BorderWidth = 2.0!
|
||||||
|
Me.DetailData1.Font = New System.Drawing.Font("Arial", 8.25!)
|
||||||
|
Me.DetailData1.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.DetailData1.Name = "DetailData1"
|
||||||
|
Me.DetailData1.Padding = New DevExpress.XtraPrinting.PaddingInfo(15, 15, 0, 0, 254.0!)
|
||||||
|
Me.DetailData1.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleLeft
|
||||||
|
'
|
||||||
|
'DetailData3_Odd
|
||||||
|
'
|
||||||
|
Me.DetailData3_Odd.BackColor = System.Drawing.Color.FromArgb(CType(CType(231, Byte), Integer), CType(CType(231, Byte), Integer), CType(CType(231, Byte), Integer))
|
||||||
|
Me.DetailData3_Odd.BorderColor = System.Drawing.Color.Transparent
|
||||||
|
Me.DetailData3_Odd.Borders = DevExpress.XtraPrinting.BorderSide.None
|
||||||
|
Me.DetailData3_Odd.BorderWidth = 1.0!
|
||||||
|
Me.DetailData3_Odd.Font = New System.Drawing.Font("Arial", 8.25!)
|
||||||
|
Me.DetailData3_Odd.ForeColor = System.Drawing.Color.Black
|
||||||
|
Me.DetailData3_Odd.Name = "DetailData3_Odd"
|
||||||
|
Me.DetailData3_Odd.Padding = New DevExpress.XtraPrinting.PaddingInfo(15, 15, 0, 0, 254.0!)
|
||||||
|
Me.DetailData3_Odd.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleLeft
|
||||||
|
'
|
||||||
|
'PageInfo
|
||||||
|
'
|
||||||
|
Me.PageInfo.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold)
|
||||||
|
Me.PageInfo.ForeColor = System.Drawing.Color.FromArgb(CType(CType(75, Byte), Integer), CType(CType(75, Byte), Integer), CType(CType(75, Byte), Integer))
|
||||||
|
Me.PageInfo.Name = "PageInfo"
|
||||||
|
Me.PageInfo.Padding = New DevExpress.XtraPrinting.PaddingInfo(15, 15, 0, 0, 254.0!)
|
||||||
|
'
|
||||||
|
'GalleryDropDown1
|
||||||
|
'
|
||||||
|
Me.GalleryDropDown1.Manager = Nothing
|
||||||
|
Me.GalleryDropDown1.Name = "GalleryDropDown1"
|
||||||
|
'
|
||||||
|
'ObjectDataSource1
|
||||||
|
'
|
||||||
|
Me.ObjectDataSource1.DataMember = "Items"
|
||||||
|
'Me.ObjectDataSource1.DataSource = GetType(EnvelopeGenerator.CommonServices.ReportSource)
|
||||||
|
Me.ObjectDataSource1.Name = "ObjectDataSource1"
|
||||||
|
'
|
||||||
|
'rptEnvelopeHistory
|
||||||
|
'
|
||||||
|
Me.Bands.AddRange(New DevExpress.XtraReports.UI.Band() {Me.TopMargin, Me.BottomMargin, Me.ReportHeader, Me.GroupHeader1, Me.Detail})
|
||||||
|
Me.ComponentStorage.AddRange(New System.ComponentModel.IComponent() {Me.ObjectDataSource1})
|
||||||
|
Me.DataSource = Me.ObjectDataSource1
|
||||||
|
Me.Dpi = 254.0!
|
||||||
|
Me.Font = New System.Drawing.Font("Arial", 9.75!)
|
||||||
|
Me.Margins = New System.Drawing.Printing.Margins(100, 100, 191, 100)
|
||||||
|
Me.PageHeight = 2970
|
||||||
|
Me.PageWidth = 2100
|
||||||
|
Me.PaperKind = System.Drawing.Printing.PaperKind.A4
|
||||||
|
Me.ReportUnit = DevExpress.XtraReports.UI.ReportUnit.TenthsOfAMillimeter
|
||||||
|
Me.SnapGridSize = 25.0!
|
||||||
|
Me.StyleSheet.AddRange(New DevExpress.XtraReports.UI.XRControlStyle() {Me.Title, Me.DetailCaption1, Me.DetailData1, Me.DetailData3_Odd, Me.PageInfo})
|
||||||
|
Me.Version = "21.2"
|
||||||
|
Me.Watermark.ImageSource = New DevExpress.XtraPrinting.Drawing.ImageSource("img", resources.GetString("rptEnvelopeHistory.Watermark.ImageSource"))
|
||||||
|
Me.Watermark.ImageTransparency = 220
|
||||||
|
CType(Me.XrTable3, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.table1, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.table2, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.GalleryDropDown1, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me.ObjectDataSource1, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
CType(Me, System.ComponentModel.ISupportInitialize).EndInit()
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
|
||||||
|
Friend WithEvents TopMargin As DevExpress.XtraReports.UI.TopMarginBand
|
||||||
|
Friend WithEvents BottomMargin As DevExpress.XtraReports.UI.BottomMarginBand
|
||||||
|
Friend WithEvents pageInfo1 As DevExpress.XtraReports.UI.XRPageInfo
|
||||||
|
Friend WithEvents pageInfo2 As DevExpress.XtraReports.UI.XRPageInfo
|
||||||
|
Friend WithEvents ReportHeader As DevExpress.XtraReports.UI.ReportHeaderBand
|
||||||
|
Friend WithEvents label1 As DevExpress.XtraReports.UI.XRLabel
|
||||||
|
Friend WithEvents GroupHeader1 As DevExpress.XtraReports.UI.GroupHeaderBand
|
||||||
|
Friend WithEvents table1 As DevExpress.XtraReports.UI.XRTable
|
||||||
|
Friend WithEvents tableRow1 As DevExpress.XtraReports.UI.XRTableRow
|
||||||
|
Friend WithEvents tableCell1 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents tableCell2 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents tableCell3 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents Detail As DevExpress.XtraReports.UI.DetailBand
|
||||||
|
Friend WithEvents table2 As DevExpress.XtraReports.UI.XRTable
|
||||||
|
Friend WithEvents tableRow2 As DevExpress.XtraReports.UI.XRTableRow
|
||||||
|
Friend WithEvents tableCell4 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents tableCell5 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents tableCell6 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents ObjectDataSource1 As DevExpress.DataAccess.ObjectBinding.ObjectDataSource
|
||||||
|
Friend WithEvents Title As DevExpress.XtraReports.UI.XRControlStyle
|
||||||
|
Friend WithEvents DetailCaption1 As DevExpress.XtraReports.UI.XRControlStyle
|
||||||
|
Friend WithEvents DetailData1 As DevExpress.XtraReports.UI.XRControlStyle
|
||||||
|
Friend WithEvents DetailData3_Odd As DevExpress.XtraReports.UI.XRControlStyle
|
||||||
|
Friend WithEvents PageInfo As DevExpress.XtraReports.UI.XRControlStyle
|
||||||
|
Friend WithEvents XrLabel1 As DevExpress.XtraReports.UI.XRLabel
|
||||||
|
Friend WithEvents XrTable3 As DevExpress.XtraReports.UI.XRTable
|
||||||
|
Friend WithEvents XrTableRow8 As DevExpress.XtraReports.UI.XRTableRow
|
||||||
|
Friend WithEvents XrTableCell13 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell15 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableRow9 As DevExpress.XtraReports.UI.XRTableRow
|
||||||
|
Friend WithEvents XrTableCell14 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell17 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell16 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell18 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableRow10 As DevExpress.XtraReports.UI.XRTableRow
|
||||||
|
Friend WithEvents XrTableCell19 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell20 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell21 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell22 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableRow11 As DevExpress.XtraReports.UI.XRTableRow
|
||||||
|
Friend WithEvents XrTableCell23 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell24 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell25 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents XrTableCell26 As DevExpress.XtraReports.UI.XRTableCell
|
||||||
|
Friend WithEvents GalleryDropDown1 As DevExpress.XtraBars.Ribbon.GalleryDropDown
|
||||||
|
End Class
|
||||||
129
EnvelopeGenerator.Application.VB/Reports/rptEnvelopeHistory.resx
Normal file
129
EnvelopeGenerator.Application.VB/Reports/rptEnvelopeHistory.resx
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
|||||||
|
Public Class rptEnvelopeHistory
|
||||||
|
|
||||||
|
End Class
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCommand"></typeparam>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class CreateCommandHandler<TCommand, TEntity> : IRequestHandler<TCommand, TEntity>
|
|
||||||
where TCommand : class, IRequest<TEntity>
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
protected readonly IRepository<TEntity> Repository;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public CreateCommandHandler(IRepository<TEntity> repository)
|
|
||||||
{
|
|
||||||
Repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task<TEntity> Handle(TCommand request, CancellationToken cancel) => Repository.CreateAsync(request, cancel);
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using MediatR;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TUpdateDto"></typeparam>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public abstract record UpdateCommand<TUpdateDto, TEntity> : IRequest where TUpdateDto : class where TEntity : class
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public TUpdateDto Update { get; init; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public abstract Expression<Func<TEntity, bool>> BuildQueryExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCommand"></typeparam>
|
|
||||||
/// <typeparam name="TUpdateDto"></typeparam>
|
|
||||||
/// <typeparam name="TEntity"></typeparam>
|
|
||||||
public class UpdateCommandHandler<TCommand, TUpdateDto, TEntity> : IRequestHandler<TCommand>
|
|
||||||
where TUpdateDto : class
|
|
||||||
where TEntity : class
|
|
||||||
where TCommand : UpdateCommand<TUpdateDto, TEntity>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
protected readonly IRepository<TEntity> Repository;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public UpdateCommandHandler(IRepository<TEntity> repository)
|
|
||||||
{
|
|
||||||
Repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task Handle(TCommand request, CancellationToken cancel)
|
|
||||||
=> Repository.UpdateAsync(request.Update, request.BuildQueryExpression(), cancel);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Configurations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class GdPictureParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string License { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using EnvelopeGenerator.Application.Common.Interfaces.Model;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Configurations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class PDFBurnerParams : ITextStyle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int ConcurrencyLimit { get; set; } = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> IgnoredLabels { get; set; } = new List<string>
|
||||||
|
{
|
||||||
|
"Date", "Datum", "ZIP", "PLZ", "Place", "Ort", "Position", "Stellung"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
public double TopMargin { get; set; } = 0.1;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public double YOffset { get; set; } = -0.3;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string FontName { get; set; } = "Arial";
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int FontSize { get; set; } = 8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
|
||||||
|
public FontStyle FontStyle { get; set; } = FontStyle.Italic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, int> IndexOfAnnot { get; init; } = new()
|
||||||
|
{
|
||||||
|
{ string.Empty, 0 },
|
||||||
|
{ "seal", 0 },
|
||||||
|
{ "position", 1 },
|
||||||
|
{ "city", 2 },
|
||||||
|
{ "date", 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int DefaultIndexOfAnnot { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int GetAnnotationIndex(string name) => IndexOfAnnot.TryGetValue(name, out var value) ? value : DefaultIndexOfAnnot;
|
||||||
|
}
|
||||||
@@ -8,77 +8,42 @@ public record AnnotationCreateDto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long Id { get; set; }
|
public int ElementId { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Not required for DevExpress")]
|
public string Name { get; init; } = null!;
|
||||||
public int ElementId { get; init; } = -1;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Not required for DevExpress")]
|
public string Value { get; init; } = null!;
|
||||||
public string Name { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Not required for DevExpress")]
|
public string Type { get; init; } = null!;
|
||||||
public string Value { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public string Type { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Horizontal position of the signature field on the page.
|
|
||||||
/// <br/><br/>
|
|
||||||
/// <b>Unit:</b> INCHES (GdPicture14 native), origin at the <b>top-left</b> corner of the page, X increases to the right.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to DevExpress:</b> Multiply by 100 (DX uses 1/100 inch).
|
|
||||||
/// Convert: <c>xDX = xInches * 100.0</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to PDF Points:</b> Multiply by 72 (PSPDFKit, iText7 use 1/72 inch).
|
|
||||||
/// Convert: <c>xPt = xInches * 72.0</c>
|
|
||||||
/// </summary>
|
|
||||||
public double? X { get; init; }
|
public double? X { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Vertical position of the signature field on the page.
|
///
|
||||||
/// <br/><br/>
|
|
||||||
/// <b>Unit:</b> INCHES (GdPicture14 native), origin at the <b>top-left</b> corner of the page, Y increases downward.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to DevExpress:</b> Multiply by 100 (DX uses 1/100 inch).
|
|
||||||
/// Convert: <c>yDX = yInches * 100.0</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to PDF Points (top-left origin):</b> Multiply by 72.
|
|
||||||
/// Convert: <c>yPt = yInches * 72.0</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Conversion to PDF Points (bottom-left origin - iText7):</b> Y-flip required.
|
|
||||||
/// Convert: <c>yPt = (pageHeightInches - yInches - elemHeightInches) * 72.0</c>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Y { get; init; }
|
public double? Y { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public double? Width { get; init; }
|
public double? Width { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Not required for DevExpress")]
|
|
||||||
public double? Height { get; init; }
|
public double? Height { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Added to eliminate the need for SignatureDto in DevExpress
|
|
||||||
/// </summary>
|
|
||||||
public int? Page { get; init; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Transfer Object representing configuration settings.
|
/// Data Transfer Object representing configuration settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class ConfigDto
|
public class ConfigDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Transfer Object representing a document within an envelope, including optional binary data and form elements.
|
/// Data Transfer Object representing a document within an envelope, including optional binary data and form elements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class DocumentDto
|
public class DocumentDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,5 +31,5 @@ public class DocumentDto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
/// Gets or sets the collection of elements associated with the document for receiver interactions, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<DocReceiverElementDto>? Elements { get; set; }
|
public IEnumerable<SignatureDto>? Elements { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Transfer Object representing the status of a document for a specific receiver.
|
/// Data Transfer Object representing the status of a document for a specific receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class DocumentStatusDto
|
public class DocumentStatusDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,42 +1,31 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
/// <summary>
|
namespace EnvelopeGenerator.Application.Common.Dto
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record EmailTemplateDto
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Id { get; init; }
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public record EmailTemplateDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int Id{ get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; } = null!;
|
public required string Name { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime AddedWhen { get; set; }
|
public required string Body { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Body { get; set; }
|
public required string Subject { get; set; }
|
||||||
|
};
|
||||||
/// <summary>
|
}
|
||||||
/// Der Betreff der E-Mail-Vorlage. Kann null sein.
|
|
||||||
/// </summary>
|
|
||||||
public string? Subject { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Sprachcode der E-Mail-Vorlage.
|
|
||||||
/// </summary>
|
|
||||||
public string LangCode { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
};
|
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
||||||
using DigitalData.UserManager.Application.DTOs.User;
|
using DigitalData.UserManager.Application.DTOs.User;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.History;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record EnvelopeDto : IEnvelope
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public record EnvelopeDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -74,13 +74,6 @@ public record EnvelopeDto : IEnvelope
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int? EnvelopeTypeId { get; set; }
|
public int? EnvelopeTypeId { get; set; }
|
||||||
|
|
||||||
// TODO: use ReadAndConfirm property name
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use EnvelopeExtensions.IsReadAndConfirm extension metot instead.")]
|
|
||||||
public bool ReadOnly => this.IsReadAndConfirm();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -89,7 +82,7 @@ public record EnvelopeDto : IEnvelope
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseAccessCode { get; set; } = true;
|
public bool? UseAccessCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -125,14 +118,4 @@ public record EnvelopeDto : IEnvelope
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<DocumentDto>? Documents { get; set; }
|
public IEnumerable<DocumentDto>? Documents { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<EnvelopeReceiverDto>? EnvelopeReceivers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Envelope history entries tracking actions like DocumentSigned, EnvelopeOpened, etc.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<HistoryDto>? Histories { get; set; }
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
using DigitalData.EmailProfilerDispatcher.Abstraction.Attributes;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public record EnvelopeReceiverDto
|
public record EnvelopeReceiverDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -60,11 +61,6 @@ public record EnvelopeReceiverDto
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? PrivateMessage { get; init; }
|
public string? PrivateMessage { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? AccessCode { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -79,13 +75,4 @@ public record EnvelopeReceiverDto
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasPhoneNumber { get; init; }
|
public bool HasPhoneNumber { get; init; }
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Indicates whether this receiver has signed the envelope.
|
|
||||||
/// Checks if there is a DocumentSigned history entry for this receiver in the envelope's history.
|
|
||||||
/// </summary>
|
|
||||||
public bool Signed => Envelope?.Histories?.Any(h =>
|
|
||||||
h.Receiver?.Id == ReceiverId &&
|
|
||||||
h.Status == EnvelopeStatus.DocumentSigned
|
|
||||||
) ?? false;
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public record EnvelopeReceiverSecretDto : EnvelopeReceiverDto
|
public record EnvelopeReceiverSecretDto : EnvelopeReceiverDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
||||||
@@ -7,6 +8,7 @@ namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="DateValid"></param>
|
/// <param name="DateValid"></param>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public record EnvelopeReceiverReadOnlyCreateDto(
|
public record EnvelopeReceiverReadOnlyCreateDto(
|
||||||
DateTime DateValid)
|
DateTime DateValid)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
||||||
|
|
||||||
@@ -6,6 +8,7 @@ namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
|||||||
/// Represents a read-only Data Transfer Object (DTO) for an envelope receiver.
|
/// Represents a read-only Data Transfer Object (DTO) for an envelope receiver.
|
||||||
/// Contains information about the receiver, associated envelope, and audit details.
|
/// Contains information about the receiver, associated envelope, and audit details.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class EnvelopeReceiverReadOnlyDto
|
public class EnvelopeReceiverReadOnlyDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Transfer Object for updating a read-only envelope receiver.
|
/// Data Transfer Object for updating a read-only envelope receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class EnvelopeReceiverReadOnlyUpdateDto
|
public class EnvelopeReceiverReadOnlyUpdateDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public record EnvelopeReportDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int EnvelopeId { get; set; }
|
||||||
|
|
||||||
|
// --- HEAD ---
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string HeadUuid { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string EnvelopeTitle { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string HeadMessage { get; set; } = null!;
|
||||||
|
|
||||||
|
// --- POSITIONS ---
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int ItemStatus { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? ItemDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string ItemUserReference { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Transfer Object representing a type of envelope with its configuration settings.
|
/// Data Transfer Object representing a type of envelope with its configuration settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class EnvelopeTypeDto
|
public class EnvelopeTypeDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ using EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the AutoMapper profile configuration for mapping between
|
/// Represents the AutoMapper profile configuration for mapping between
|
||||||
@@ -25,41 +23,38 @@ public class MappingProfile : Profile
|
|||||||
{
|
{
|
||||||
// Entity to DTO mappings
|
// Entity to DTO mappings
|
||||||
CreateMap<Config, ConfigDto>();
|
CreateMap<Config, ConfigDto>();
|
||||||
CreateMap<DocReceiverElement, DocReceiverElementDto>();
|
CreateMap<Signature, SignatureDto>();
|
||||||
CreateMap<DocumentStatus, DocumentStatusDto>();
|
CreateMap<DocumentStatus, DocumentStatusDto>();
|
||||||
CreateMap<EmailTemplate, EmailTemplateDto>();
|
CreateMap<EmailTemplate, EmailTemplateDto>();
|
||||||
CreateMap<Envelope, EnvelopeDto>()
|
CreateMap<Envelope, EnvelopeDto>();
|
||||||
.ForMember(dest => dest.EnvelopeTypeTitle,
|
|
||||||
opt => opt.MapFrom(src => src.Type != null ? src.Type.Title : null));
|
|
||||||
CreateMap<Document, DocumentDto>();
|
CreateMap<Document, DocumentDto>();
|
||||||
CreateMap<History, HistoryDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
CreateMap<Domain.Entities.History, HistoryDto>();
|
||||||
CreateMap<History, HistoryCreateDto>().ForMember(dest => dest.ActionDate, opt => opt.MapFrom(src => src.ChangedWhen));
|
CreateMap<Domain.Entities.History, HistoryCreateDto>();
|
||||||
CreateMap<EnvelopeReceiver, EnvelopeReceiverDto>();
|
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverDto>();
|
||||||
CreateMap<EnvelopeReceiver, EnvelopeReceiverSecretDto>();
|
CreateMap<Domain.Entities.EnvelopeReceiver, EnvelopeReceiverSecretDto>();
|
||||||
CreateMap<EnvelopeType, EnvelopeTypeDto>();
|
CreateMap<EnvelopeType, EnvelopeTypeDto>();
|
||||||
CreateMap<Receiver, ReceiverDto>();
|
CreateMap<Domain.Entities.Receiver, ReceiverDto>();
|
||||||
CreateMap<EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
|
CreateMap<Domain.Entities.EnvelopeReceiverReadOnly, EnvelopeReceiverReadOnlyDto>();
|
||||||
CreateMap<ElementAnnotation, AnnotationDto>();
|
CreateMap<ElementAnnotation, AnnotationDto>();
|
||||||
|
CreateMap<ThirdPartyModule, ThirdPartyModuleDto>();
|
||||||
|
CreateMap<EnvelopeReport, EnvelopeReportDto>();
|
||||||
|
|
||||||
// DTO to Entity mappings
|
// DTO to Entity mappings
|
||||||
CreateMap<ConfigDto, Config>();
|
CreateMap<ConfigDto, Config>();
|
||||||
CreateMap<DocReceiverElementDto, DocReceiverElement>();
|
CreateMap<SignatureDto, Signature>();
|
||||||
CreateMap<Signature, DocReceiverElement>()
|
|
||||||
.ForMember(dest => dest.Ink, opt => opt.MapFrom(src => src.DataUrl.MapDataUrlToRequiredBytes()))
|
|
||||||
.MapChangedWhen();
|
|
||||||
CreateMap<DocumentStatusDto, DocumentStatus>();
|
CreateMap<DocumentStatusDto, DocumentStatus>();
|
||||||
CreateMap<EmailTemplateDto, EmailTemplate>();
|
CreateMap<EmailTemplateDto, EmailTemplate>();
|
||||||
CreateMap<EnvelopeDto, Envelope>();
|
CreateMap<EnvelopeDto, Envelope>();
|
||||||
CreateMap<DocumentDto, Document>();
|
CreateMap<DocumentDto, Document>();
|
||||||
CreateMap<HistoryDto, History>().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate));
|
CreateMap<HistoryDto, Domain.Entities.History>();
|
||||||
CreateMap<HistoryCreateDto, History>().ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(src => src.ActionDate));
|
CreateMap<HistoryCreateDto, Domain.Entities.History>();
|
||||||
CreateMap<EnvelopeReceiverDto, EnvelopeReceiver>();
|
CreateMap<EnvelopeReceiverDto, Domain.Entities.EnvelopeReceiver>();
|
||||||
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
CreateMap<EnvelopeTypeDto, EnvelopeType>();
|
||||||
CreateMap<ReceiverDto, Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
|
CreateMap<ReceiverDto, Domain.Entities.Receiver>().ForMember(rcv => rcv.EnvelopeReceivers, rcvReadDto => rcvReadDto.Ignore());
|
||||||
CreateMap<EnvelopeReceiverReadOnlyCreateDto, EnvelopeReceiverReadOnly>();
|
CreateMap<EnvelopeReceiverReadOnlyCreateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
|
||||||
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, EnvelopeReceiverReadOnly>();
|
CreateMap<EnvelopeReceiverReadOnlyUpdateDto, Domain.Entities.EnvelopeReceiverReadOnly>();
|
||||||
CreateMap<AnnotationCreateDto, ElementAnnotation>()
|
CreateMap<AnnotationCreateDto, ElementAnnotation>()
|
||||||
.MapAddedWhen();
|
.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.UtcNow));
|
||||||
|
|
||||||
// Messaging mappings
|
// Messaging mappings
|
||||||
// for GTX messaging
|
// for GTX messaging
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class GtxMessagingResponse : Dictionary<string, object?> { }
|
public class GtxMessagingResponse : Dictionary<string, object?> { }
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.Messaging;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public record SmsResponse
|
public record SmsResponse
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using EnvelopeGenerator.Application.Exceptions;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class Annotation
|
||||||
|
{
|
||||||
|
private string? _id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int EnvelopeId { get; private set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiverId { get; private set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int Index { get; private set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string EgName { get; private set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public bool HasStructuredID { get; private set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLabel
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(EgName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var parts = EgName.Split('_');
|
||||||
|
return parts.Length > 1 && parts[1] == "label";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? Id
|
||||||
|
{
|
||||||
|
get => _id;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_id = value;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
throw new BurnAnnotationException("The identifier of annotation is null or empty.");
|
||||||
|
|
||||||
|
var parts = value.Split('#');
|
||||||
|
|
||||||
|
if (parts.Length != 4)
|
||||||
|
return;
|
||||||
|
// throw new BurnAnnotationException($"The identifier of annotation has more or less than 4 sub-part. Id: {_id}");
|
||||||
|
|
||||||
|
if (!int.TryParse(parts[0], out int envelopeId))
|
||||||
|
throw new BurnAnnotationException($"The envelope ID of annotation is not integer. Id: {_id}");
|
||||||
|
EnvelopeId = envelopeId;
|
||||||
|
|
||||||
|
if (!int.TryParse(parts[1], out int receiverId))
|
||||||
|
throw new BurnAnnotationException($"The receiver ID of annotation is not integer. Id: {_id}");
|
||||||
|
ReceiverId = receiverId;
|
||||||
|
|
||||||
|
if (!int.TryParse(parts[2], out int index))
|
||||||
|
throw new BurnAnnotationException($"The index of annotation is not integer. Id: {_id}");
|
||||||
|
Index = index;
|
||||||
|
|
||||||
|
EgName = parts[3];
|
||||||
|
HasStructuredID = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public List<double>? Bbox { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSignature { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? ImageAttachmentId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public Lines? Lines { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int PageIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? StrokeColor { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Binary"></param>
|
||||||
|
/// <param name="ContentType"></param>
|
||||||
|
public record Attachment(string Binary, string ContentType);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Name"></param>
|
||||||
|
/// <param name="Value"></param>
|
||||||
|
public record FormFieldValue(string Name, string? Value = null);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Lines"></param>
|
||||||
|
/// <param name="StrokeColor"></param>
|
||||||
|
public record Ink(Lines Lines, string? StrokeColor = null);
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class InstantData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public List<Annotation>? Annotations { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<List<Annotation>>? AnnotationsByReceiver
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Annotations?
|
||||||
|
.Where(a => a.HasStructuredID)
|
||||||
|
.GroupBy(a => a.ReceiverId)
|
||||||
|
.Select(g => g.ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<List<Annotation>>? UnstructuredAnnotations
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Annotations?
|
||||||
|
.Where(a => !a.HasStructuredID)
|
||||||
|
.GroupBy(a => a.ReceiverId)
|
||||||
|
.Select(g => g.ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, Attachment>? Attachments { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public List<FormFieldValue>? FormFieldValues { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Points"></param>
|
||||||
|
public record Lines(List<List<List<float>>> Points);
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using System.Dynamic;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents PSPDFKit annotation data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Instant">Instant annotation data.</param>
|
|
||||||
/// <param name="Structured">Structured annotation data.</param>
|
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto.Receiver;
|
namespace EnvelopeGenerator.Application.Common.Dto.Receiver;
|
||||||
@@ -6,6 +7,7 @@ namespace EnvelopeGenerator.Application.Common.Dto.Receiver;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public class ReceiverDto
|
public class ReceiverDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a captured signature with metadata created by the receiver in the signature popup.
|
|
||||||
/// This model holds the signature image (as base64 data URL) along with signer information
|
|
||||||
/// used for rendering applied signatures on the PDF canvas.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <b>Used in:</b> EnvelopeViewer.razor signature popup workflow
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Creation:</b> User draws/types/uploads signature and fills required fields
|
|
||||||
/// </remarks>
|
|
||||||
public sealed record Signature
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// TBDD_DOCUMENT_RECEIVER_ELEMENT.ID - identifies the specific signature field on the PDF page.
|
|
||||||
/// </summary>
|
|
||||||
public required int Id { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Base64-encoded data URL of the signature image.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Format:</b> <c>data:image/png;base64,iVBORw0KG...</c>
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Source:</b> Canvas.toDataURL() from signature pad (draw/text/image tabs)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Usage:</b> Set as <c>img.src</c> in applied signature overlay
|
|
||||||
/// </summary>
|
|
||||||
public required string DataUrl { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Full name of the signer (first and last name).
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Required:</b> Yes (validated in popup)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Example:</b> "Max Mustermann"
|
|
||||||
/// </summary>
|
|
||||||
public required string FullName { get; init; }
|
|
||||||
|
|
||||||
private readonly string? _position = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Job title or position of the signer.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Required:</b> No (optional field)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Example:</b> "Geschäftsführer" or empty string
|
|
||||||
/// </summary>
|
|
||||||
public string? Position
|
|
||||||
{
|
|
||||||
get => _position;
|
|
||||||
init => _position = string.IsNullOrWhiteSpace(value) ? null : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Location/place where the signature was created.
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Required:</b> Yes (validated in popup)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Display:</b> Shown with current date in German format (dd.MM.yyyy)
|
|
||||||
/// <br/>
|
|
||||||
/// <b>Example:</b> "Berlin" ? rendered as "Berlin, 26.01.2025"
|
|
||||||
/// </summary>
|
|
||||||
public required string Place { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Interfaces;
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
/// Data Transfer Object representing a positioned element assigned to a document receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DocReceiverElementDto : IDocReceiverElement
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public class SignatureDto : ISignature
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the unique identifier of the element.
|
/// Gets or sets the unique identifier of the element.
|
||||||
@@ -92,34 +93,4 @@ public class DocReceiverElementDto : IDocReceiverElement
|
|||||||
/// Gets or sets the left position of the element (in layout terms).
|
/// Gets or sets the left position of the element (in layout terms).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Left => X;
|
public double Left => X;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<AnnotationDto>? Annotations { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public SenderAppType SenderAppType { get; set; } = SenderAppType.LegacyFormApp;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? FullName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? Position { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? Place { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public byte[]? Ink { get; set; }
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public record ThirdPartyModuleDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public bool Active { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; init; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? Description { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string License { get; init; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string Version { get; init; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? AddedWho { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? AddedWhen { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string? ChangedWho { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? ChangedWhen { get; init; }
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
namespace EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a user's UI preferences stored in the distributed cache (dbo.TBDD_CACHE).
|
|
||||||
/// </summary>
|
|
||||||
public sealed record UserPreferencesDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The active DevExpress theme name.
|
|
||||||
/// Allowed values: "blazing-berry", "fluent", "purple"
|
|
||||||
/// </summary>
|
|
||||||
public string ThemeName { get; set; } = "blazing-berry";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether dark mode is active.
|
|
||||||
/// Default: false (light mode).
|
|
||||||
/// </summary>
|
|
||||||
public bool IsDarkMode { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Persisted DxGrid layout states, keyed by a stable grid identifier.
|
|
||||||
/// Example keys: "sender.active-envelopes", "sender.completed-envelopes"
|
|
||||||
/// Values are opaque JSON strings produced by DxGrid.SaveLayoutAsync().
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, string> GridLayouts { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The authenticated user's display name ("{GivenName} {Surname}").
|
|
||||||
/// Populated by the server on GET responses from JWT claims.
|
|
||||||
/// NOT persisted in cache — injected at read time by UserPreferencesController.
|
|
||||||
/// Ignored on PUT requests.
|
|
||||||
/// </summary>
|
|
||||||
public string? DisplayName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static UserPreferencesDto Default { get; } = new UserPreferencesDto();
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces.Auditing;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for applying auditing timestamps during AutoMapper mappings.
|
|
||||||
/// </summary>
|
|
||||||
public static class AutoMapperAuditingExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Maps <see cref="IHasAddedWhen.AddedWhen"/> to the current UTC time.
|
|
||||||
/// </summary>
|
|
||||||
public static IMappingExpression<TSource, TDestination> MapAddedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
|
||||||
where TDestination : IHasAddedWhen
|
|
||||||
=> expression.ForMember(dest => dest.AddedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps <see cref="IHasChangedWhen.ChangedWhen"/> to the current UTC time.
|
|
||||||
/// </summary>
|
|
||||||
public static IMappingExpression<TSource, TDestination> MapChangedWhen<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
|
|
||||||
where TDestination : IHasChangedWhen
|
|
||||||
=> expression.ForMember(dest => dest.ChangedWhen, opt => opt.MapFrom(_ => DateTime.Now));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a base64 data URL string to a byte array.
|
|
||||||
/// Handles data URLs in the format: "data:image/png;base64,iVBORw0KG..."
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataUrl">The base64 data URL string from Canvas.toDataURL()</param>
|
|
||||||
/// <returns>The decoded byte array, or null if the input is null or empty</returns>
|
|
||||||
public static byte[]? MapDataUrlToRequiredBytes(this string dataUrl)
|
|
||||||
{
|
|
||||||
// Remove data URL prefix (e.g., "data:image/png;base64,")
|
|
||||||
var base64Index = dataUrl.IndexOf(',', StringComparison.Ordinal);
|
|
||||||
if (base64Index == -1)
|
|
||||||
throw new ArgumentException("Invalid data URL format. Unable to extract base64 data.", nameof(dataUrl));
|
|
||||||
|
|
||||||
var base64Data = dataUrl[(base64Index + 1)..];
|
|
||||||
return Convert.FromBase64String(base64Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
using EnvelopeGenerator.Application.Common.Dto.PSPDFKitInstant;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using GdPicture14;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using System.Drawing;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Interfaces.Model;
|
||||||
|
using EnvelopeGenerator.Application.Common.Configurations;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class GdPictureExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="x"></param>
|
||||||
|
/// <param name="y"></param>
|
||||||
|
/// <param name="width"></param>
|
||||||
|
/// <param name="height"></param>
|
||||||
|
/// <param name="page"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="textStyle"></param>
|
||||||
|
public static void AddFormFieldValue(this AnnotationManager manager, double x, double y, double width, double height, int page, string value, ITextStyle textStyle)
|
||||||
|
{
|
||||||
|
manager.SelectPage(page);
|
||||||
|
|
||||||
|
// Add the text annotation
|
||||||
|
var ant = manager.AddTextAnnot((float)x, (float)y, (float)width, (float)height, value);
|
||||||
|
|
||||||
|
// Set the font properties
|
||||||
|
ant.FontName = textStyle.FontName;
|
||||||
|
ant.FontSize = textStyle.FontSize;
|
||||||
|
ant.FontStyle = textStyle.FontStyle;
|
||||||
|
|
||||||
|
manager.SaveAnnotationsToPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="pAnnotation"></param>
|
||||||
|
/// <param name="formFieldValue"></param>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
public static void AddFormFieldValue(this AnnotationManager manager, Annotation pAnnotation, FormFieldValue formFieldValue, PDFBurnerParams options)
|
||||||
|
{
|
||||||
|
var ffIndex = options.GetAnnotationIndex(pAnnotation.EgName);
|
||||||
|
|
||||||
|
// Convert pixels to Inches
|
||||||
|
var oBounds = pAnnotation.Bbox?.Select(points => points.ToInches()).ToList();
|
||||||
|
|
||||||
|
if (oBounds is null || oBounds.Count < 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double oX = oBounds[0];
|
||||||
|
double oY = oBounds[1] + options.YOffset * ffIndex + options.TopMargin;
|
||||||
|
double oWidth = oBounds[2];
|
||||||
|
double oHeight = oBounds[3];
|
||||||
|
|
||||||
|
manager.SelectPage(pAnnotation.PageIndex + 1);
|
||||||
|
|
||||||
|
// Add the text annotation
|
||||||
|
var ant = manager.AddTextAnnot((float)oX, (float)oY, (float)oWidth, (float)oHeight, formFieldValue.Value);
|
||||||
|
|
||||||
|
// Set the font properties
|
||||||
|
ant.FontName = options.FontName;
|
||||||
|
ant.FontSize = options.FontSize;
|
||||||
|
ant.FontStyle = options.FontStyle;
|
||||||
|
|
||||||
|
manager.SaveAnnotationsToPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="x"></param>
|
||||||
|
/// <param name="y"></param>
|
||||||
|
/// <param name="width"></param>
|
||||||
|
/// <param name="height"></param>
|
||||||
|
/// <param name="page"></param>
|
||||||
|
/// <param name="base64"></param>
|
||||||
|
public static void AddImageAnnotation(this AnnotationManager manager, double x, double y, double width, double height, int page, string base64)
|
||||||
|
{
|
||||||
|
manager.SelectPage(page);
|
||||||
|
manager.AddEmbeddedImageAnnotFromBase64(base64, (float)x, (float)y, (float)width, (float)height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="pAnnotation"></param>
|
||||||
|
/// <param name="pAttachments"></param>
|
||||||
|
public static void AddImageAnnotation(this AnnotationManager manager, Annotation pAnnotation, Dictionary<string, Attachment> pAttachments)
|
||||||
|
{
|
||||||
|
var oAttachment = pAttachments
|
||||||
|
.Where(a => a.Key == pAnnotation.ImageAttachmentId)
|
||||||
|
.SingleOrDefault();
|
||||||
|
|
||||||
|
if (oAttachment.Value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Convert pixels to Inches
|
||||||
|
var oBounds = pAnnotation.Bbox?.Select(post => post.ToInches()).ToList();
|
||||||
|
|
||||||
|
if (oBounds is null || oBounds.Count < 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var oX = oBounds[0];
|
||||||
|
var oY = oBounds[1];
|
||||||
|
var oWidth = oBounds[2];
|
||||||
|
var oHeight = oBounds[3];
|
||||||
|
|
||||||
|
manager.SelectPage(pAnnotation.PageIndex + 1);
|
||||||
|
manager.AddEmbeddedImageAnnotFromBase64(oAttachment.Value.Binary, (float)oX, (float)oY, (float)oWidth, (float)oHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="page"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
public static void AddInkAnnotation(this AnnotationManager manager, int page, string value)
|
||||||
|
{
|
||||||
|
var ink = JsonConvert.DeserializeObject<Ink>(value);
|
||||||
|
|
||||||
|
var oSegments = ink?.Lines.Points;
|
||||||
|
var oColor = ColorTranslator.FromHtml(ink?.StrokeColor ?? "#000000");
|
||||||
|
manager.SelectPage(page);
|
||||||
|
|
||||||
|
if (oSegments is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var oSegment in oSegments)
|
||||||
|
{
|
||||||
|
var oPoints = oSegment
|
||||||
|
.Select(points => points.ToPointF())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
manager.AddFreeHandAnnot(oColor, oPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="manager"></param>
|
||||||
|
/// <param name="pAnnotation"></param>
|
||||||
|
public static void AddInkAnnotation(this AnnotationManager manager, Annotation pAnnotation)
|
||||||
|
{
|
||||||
|
var oSegments = pAnnotation.Lines?.Points;
|
||||||
|
var oColor = ColorTranslator.FromHtml(pAnnotation.StrokeColor ?? "#000000");
|
||||||
|
manager.SelectPage(pAnnotation.PageIndex + 1);
|
||||||
|
|
||||||
|
if (oSegments is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var oSegment in oSegments)
|
||||||
|
{
|
||||||
|
var oPoints = oSegment
|
||||||
|
.Select(points => points.ToPointF())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
manager.AddFreeHandAnnot(oColor, oPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,19 +3,8 @@ using System.Text;
|
|||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions
|
namespace EnvelopeGenerator.Application.Common.Extensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class LoggerExtensions
|
public static class LoggerExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
/// <param name="envelopeReceiverId"></param>
|
|
||||||
/// <param name="exception"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
|
public static void LogEnvelopeError(this ILogger logger, string envelopeReceiverId, Exception? exception = null, string? message = null, params object?[] args)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());
|
var sb = new StringBuilder().AppendLine(envelopeReceiverId.DecodeEnvelopeReceiverId().ToTitle());
|
||||||
@@ -29,15 +18,6 @@ namespace EnvelopeGenerator.Application.Common.Extensions
|
|||||||
logger.Log(LogLevel.Error, exception, sb.AppendLine(exception.Message).ToString(), args);
|
logger.Log(LogLevel.Error, exception, sb.AppendLine(exception.Message).ToString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger"></param>
|
|
||||||
/// <param name="uuid"></param>
|
|
||||||
/// <param name="signature"></param>
|
|
||||||
/// <param name="exception"></param>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args)
|
public static void LogEnvelopeError(this ILogger logger, string? uuid, string? signature = null, Exception? exception = null, string? message = null, params object?[] args)
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder($"Envelope Uuid: {uuid}");
|
var sb = new StringBuilder($"Envelope Uuid: {uuid}");
|
||||||
@@ -54,11 +34,6 @@ namespace EnvelopeGenerator.Application.Common.Extensions
|
|||||||
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
|
logger.Log(LogLevel.Error, exception, sb.ToString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiverTuple"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)
|
public static string ToTitle(this (string? UUID, string? Signature) envelopeReceiverTuple)
|
||||||
{
|
{
|
||||||
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
|
return $"UUID is {envelopeReceiverTuple.UUID} and signature is {envelopeReceiverTuple.Signature}";
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class MathematExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static PointF ToPointF(this List<float> points)
|
||||||
|
{
|
||||||
|
var pointsInch = points.Select(ToInches).ToList();
|
||||||
|
return new PointF(pointsInch[0], pointsInch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static double ToInches(this double value)
|
||||||
|
{
|
||||||
|
return value / 72.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static float ToInches(this float value)
|
||||||
|
{
|
||||||
|
return value / 72f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ namespace EnvelopeGenerator.Application.Common.Extensions;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension methods for tasks
|
/// Extension methods for tasks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static class TaskExtensions
|
public static class TaskExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -18,7 +17,6 @@ public static class TaskExtensions
|
|||||||
/// <param name="factory">Exception provider</param>
|
/// <param name="factory">Exception provider</param>
|
||||||
/// <returns>The awaited result if not <c>null</c>.</returns>
|
/// <returns>The awaited result if not <c>null</c>.</returns>
|
||||||
/// <exception>Thrown if the result is <c>null</c>.</exception>
|
/// <exception>Thrown if the result is <c>null</c>.</exception>
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static async Task<T> ThrowIfNull<T, TException>(this Task<T?> task, Func<TException> factory) where TException : Exception
|
public static async Task<T> ThrowIfNull<T, TException>(this Task<T?> task, Func<TException> factory) where TException : Exception
|
||||||
{
|
{
|
||||||
var result = await task;
|
var result = await task;
|
||||||
@@ -35,7 +33,6 @@ public static class TaskExtensions
|
|||||||
/// <param name="factory">Exception provider</param>
|
/// <param name="factory">Exception provider</param>
|
||||||
/// <returns>The awaited collection if it is not <c>null</c> or empty.</returns>
|
/// <returns>The awaited collection if it is not <c>null</c> or empty.</returns>
|
||||||
/// <exception cref="NotFoundException">Thrown if the result is <c>null</c> or empty.</exception>
|
/// <exception cref="NotFoundException">Thrown if the result is <c>null</c> or empty.</exception>
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static async Task<IEnumerable<T>> ThrowIfEmpty<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory) where TException : Exception
|
public static async Task<IEnumerable<T>> ThrowIfEmpty<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory) where TException : Exception
|
||||||
{
|
{
|
||||||
var result = await task;
|
var result = await task;
|
||||||
@@ -50,33 +47,11 @@ public static class TaskExtensions
|
|||||||
/// <param name="task"></param>
|
/// <param name="task"></param>
|
||||||
/// <param name="act"></param>
|
/// <param name="act"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static async Task<I> Then<T, I>(this Task<T> task, Func<T, I> act)
|
public static async Task<I> Then<T, I>(this Task<T> task, Func<T, I> act)
|
||||||
{
|
{
|
||||||
var res = await task;
|
var res = await task;
|
||||||
return act(res);
|
return act(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static Task<T?> FirstOrDefaultAsync<T>(this Task<IEnumerable<T>> task) => task.Then(t => t.FirstOrDefault());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TException"></typeparam>
|
|
||||||
/// <param name="task"></param>
|
|
||||||
/// <param name="factory"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Task<T> FirstAsync<T, TException>(this Task<IEnumerable<T>> task, Func<TException> factory)
|
|
||||||
where TException : Exception
|
|
||||||
=> task.Then(t => t.FirstOrDefault() ?? throw factory());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -93,13 +68,11 @@ public static class Exceptions
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static BadRequestException BadRequest() => new();
|
public static BadRequestException BadRequest() => new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[Obsolete("Implement Mediator behaviors in the Osolete .NET project.")]
|
|
||||||
public static ForbiddenException Forbidden() => new();
|
public static ForbiddenException Forbidden() => new();
|
||||||
}
|
}
|
||||||
@@ -2,42 +2,16 @@
|
|||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Extensions;
|
namespace EnvelopeGenerator.Application.Common.Extensions
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class XSSExtensions
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static class XSSExtensions
|
||||||
///
|
{
|
||||||
/// </summary>
|
public static string? TryEncode(this string? value, UrlEncoder encoder) => value is null ? value : encoder.Encode(value);
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="encoder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TryEncode(this string? value, UrlEncoder encoder) => value is null ? value : encoder.Encode(value);
|
|
||||||
|
|
||||||
/// <summary>
|
public static string? TryEncode(this LocalizedString? value, UrlEncoder encoder) => value is null ? null : encoder.Encode(value);
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="encoder"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TryEncode(this LocalizedString? value, UrlEncoder encoder) => value is null ? null : encoder.Encode(value);
|
|
||||||
|
|
||||||
/// <summary>
|
public static string? TrySanitize(this string? html, HtmlSanitizer sanitizer) => html is null ? html : sanitizer.Sanitize(html);
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="html"></param>
|
|
||||||
/// <param name="sanitizer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TrySanitize(this string? html, HtmlSanitizer sanitizer) => html is null ? html : sanitizer.Sanitize(html);
|
|
||||||
|
|
||||||
/// <summary>
|
public static string? TrySanitize(this LocalizedString? html, HtmlSanitizer sanitizer) => html is null ? null : sanitizer.Sanitize(html);
|
||||||
///
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="html"></param>
|
|
||||||
/// <param name="sanitizer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string? TrySanitize(this LocalizedString? html, HtmlSanitizer sanitizer) => html is null ? null : sanitizer.Sanitize(html);
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Interfaces.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public interface ITextStyle
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string FontName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int FontSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
|
||||||
|
public FontStyle FontStyle { get; set; }
|
||||||
|
}
|
||||||
@@ -6,6 +6,6 @@ namespace EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Use IRepository")]
|
[Obsolete("Use IRepository")]
|
||||||
public interface IDocumentReceiverElementRepository : ICRUDRepository<DocReceiverElement, int>
|
public interface IDocumentReceiverElementRepository : ICRUDRepository<Signature, int>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,6 @@ namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Use MediatR")]
|
[Obsolete("Use MediatR")]
|
||||||
public interface IDocumentReceiverElementService : IBasicCRUDService<DocReceiverElementDto, DocReceiverElement, int>
|
public interface IDocumentReceiverElementService : IBasicCRUDService<SignatureDto, Signature, int>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Manages per-user UI preferences (theme, dark mode, grid layouts).
|
|
||||||
/// Preferences are stored in the distributed cache (dbo.TBDD_CACHE) as JSON.
|
|
||||||
/// Cache key schema: "signflow.user-preferences:{username}"
|
|
||||||
/// </summary>
|
|
||||||
public interface IUserPreferencesService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves preferences for the given user.
|
|
||||||
/// Always returns a valid object — never throws, never returns null.
|
|
||||||
/// Returns defaults if no entry exists in cache.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
Task<UserPreferencesDto> GetOrCreateAsync(string id, CancellationToken ct = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Persists the given preferences for the user in the distributed cache.
|
|
||||||
/// Failures are non-fatal and silently ignored.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="preferences">The preferences to persist.</param>
|
|
||||||
/// <param name="ct">Cancellation token.</param>
|
|
||||||
Task SaveAsync(string id, UserPreferencesDto preferences, CancellationToken ct = default);
|
|
||||||
}
|
|
||||||
@@ -1,37 +1,88 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Notifications.RemoveSignature;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using System.Dynamic;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notification raised when a document is signed by a receiver.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
/// <param name="Instant"></param>
|
||||||
public record DocSignedNotification : INotification, ISendMailNotification
|
/// <param name="Structured"></param>
|
||||||
|
public record PsPdfKitAnnotation(ExpandoObject Instant, IEnumerable<AnnotationCreateDto> Structured);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Original"></param>
|
||||||
|
public record DocSignedNotification(EnvelopeReceiverDto Original) : EnvelopeReceiverDto(Original), INotification, ISendMailNotification
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The envelope receiver information.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required EnvelopeReceiverDto EnvelopeReceiver { get; init; }
|
public PsPdfKitAnnotation PsPdfKitAnnotation { get; init; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The PSPDFKit annotation data.
|
///
|
||||||
/// </summary>
|
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the email template type.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
|
public EmailTemplateType TemplateType => EmailTemplateType.DocumentSigned;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the email address of the receiver.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string EmailAddress => EnvelopeReceiver.Receiver?.EmailAddress
|
public string EmailAddress => Receiver?.EmailAddress
|
||||||
?? throw new InvalidOperationException($"Receiver is null." +
|
?? throw new InvalidOperationException($"Receiver is null." +
|
||||||
$"DocSignedNotification:\n{this.ToJson(Format.Json.ForDiagnostics)}");
|
$"DocSignedNotification:\n{this.ToJson(Format.Json.ForDiagnostics)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class DocSignedNotificationExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an <see cref="EnvelopeReceiverDto"/> to a <see cref="DocSignedNotification"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto">The DTO to convert.</param>
|
||||||
|
/// <param name="psPdfKitAnnotation"></param>
|
||||||
|
/// <returns>A new <see cref="DocSignedNotification"/> instance.</returns>
|
||||||
|
public static DocSignedNotification ToDocSignedNotification(this EnvelopeReceiverDto dto, PsPdfKitAnnotation psPdfKitAnnotation)
|
||||||
|
=> new(dto) { PsPdfKitAnnotation = psPdfKitAnnotation };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dtoTask"></param>
|
||||||
|
/// <param name="psPdfKitAnnotation"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<DocSignedNotification?> ToDocSignedNotification(this Task<EnvelopeReceiverDto?> dtoTask, PsPdfKitAnnotation psPdfKitAnnotation)
|
||||||
|
=> await dtoTask is EnvelopeReceiverDto dto ? new(dto) { PsPdfKitAnnotation = psPdfKitAnnotation } : null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="publisher"></param>
|
||||||
|
/// <param name="notification"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task PublishSafely(this IPublisher publisher, DocSignedNotification notification, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await publisher.Publish(notification, cancel);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
await publisher.Publish(new RemoveSignatureNotification()
|
||||||
|
{
|
||||||
|
EnvelopeId = notification.EnvelopeId,
|
||||||
|
ReceiverId = notification.ReceiverId
|
||||||
|
}, cancel);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||||
@@ -8,7 +7,6 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,9 +29,6 @@ public class AnnotationHandler : INotificationHandler<DocSignedNotification>
|
|||||||
/// <param name="notification"></param>
|
/// <param name="notification"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
public Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
||||||
{
|
=> _repo.CreateAsync(notification.PsPdfKitAnnotation.Structured, cancel);
|
||||||
if (notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot)
|
|
||||||
await _repo.CreateAsync(annot.Structured, cancel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using EnvelopeGenerator.Application.DocStatus.Commands;
|
using EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -9,11 +8,8 @@ namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
|
||||||
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
||||||
{
|
{
|
||||||
private const string BlankAnnotationJson = "{}";
|
|
||||||
|
|
||||||
private readonly ISender _sender;
|
private readonly ISender _sender;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,13 +27,13 @@ public class DocStatusHandler : INotificationHandler<DocSignedNotification>
|
|||||||
/// <param name="notification"></param>
|
/// <param name="notification"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
||||||
public Task Handle(DocSignedNotification notification, CancellationToken cancel) => _sender.Send(new CreateDocStatusCommand()
|
|
||||||
{
|
{
|
||||||
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
await _sender.Send(new SaveDocStatusCommand()
|
||||||
ReceiverId = notification.EnvelopeReceiver.ReceiverId,
|
{
|
||||||
Value = notification.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
Envelope = new() { Id = notification.EnvelopeId },
|
||||||
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
Receiver = new() { Id = notification.ReceiverId},
|
||||||
: BlankAnnotationJson
|
Value = JsonSerializer.Serialize(notification.PsPdfKitAnnotation.Instant, Format.Json.ForAnnotations)
|
||||||
}, cancel);
|
}, cancel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,13 +29,13 @@ public class HistoryHandler : INotificationHandler<DocSignedNotification>
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
public async Task Handle(DocSignedNotification notification, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
if (notification.EnvelopeReceiver.Receiver is null)
|
if (notification.Receiver is null)
|
||||||
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {notification.ToJson(Format.Json.ForDiagnostics)}");
|
throw new InvalidOperationException($"Receiver information is missing in the notification. DocSignedNotification:\n {notification.ToJson(Format.Json.ForDiagnostics)}");
|
||||||
|
|
||||||
await _sender.Send(new CreateHistoryCommand()
|
await _sender.Send(new CreateHistoryCommand()
|
||||||
{
|
{
|
||||||
EnvelopeId = notification.EnvelopeReceiver.EnvelopeId,
|
EnvelopeId = notification.EnvelopeId,
|
||||||
UserReference = notification.EnvelopeReceiver.Receiver.EmailAddress,
|
UserReference = notification.Receiver.EmailAddress,
|
||||||
Status = EnvelopeStatus.DocumentSigned,
|
Status = EnvelopeStatus.DocumentSigned,
|
||||||
}, cancel);
|
}, cancel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
using EnvelopeGenerator.Application.Common.Configurations;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
namespace EnvelopeGenerator.Application.Common.Notifications.DocSigned.Handlers;
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
|
|||||||
protected override void ConfigureEmailOut(DocSignedNotification notification, EmailOut emailOut)
|
protected override void ConfigureEmailOut(DocSignedNotification notification, EmailOut emailOut)
|
||||||
{
|
{
|
||||||
emailOut.ReferenceString = notification.EmailAddress;
|
emailOut.ReferenceString = notification.EmailAddress;
|
||||||
emailOut.ReferenceId = notification.EnvelopeReceiver.ReceiverId;
|
emailOut.ReferenceId = notification.ReceiverId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -42,29 +41,10 @@ public class SendSignedMailHandler : SendMailHandler<DocSignedNotification>
|
|||||||
{
|
{
|
||||||
var placeHolders = new Dictionary<string, string>()
|
var placeHolders = new Dictionary<string, string>()
|
||||||
{
|
{
|
||||||
{ "[NAME_RECEIVER]", notification.EnvelopeReceiver.Name ?? string.Empty },
|
{ "[NAME_RECEIVER]", notification.Name ?? string.Empty },
|
||||||
{ "[DOCUMENT_TITLE]", notification.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
|
{ "[DOCUMENT_TITLE]", notification.Envelope?.Title ?? string.Empty },
|
||||||
};
|
};
|
||||||
|
|
||||||
if (notification.EnvelopeReceiver.Envelope.IsReadAndConfirm())
|
|
||||||
{
|
|
||||||
placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
|
|
||||||
placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
|
|
||||||
placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
|
|
||||||
placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
|
|
||||||
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
|
|
||||||
placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
|
|
||||||
placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
|
|
||||||
placeHolders["[FINAL_STATUS]"] = "Signatur";
|
|
||||||
placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
|
|
||||||
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
|
|
||||||
placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
|
|
||||||
}
|
|
||||||
|
|
||||||
return placeHolders;
|
return placeHolders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.EnvelopeDeleted;
|
|
||||||
|
|
||||||
public record EnvelopeDeletedNotification : INotification, ISendMailNotification
|
|
||||||
{
|
|
||||||
public required Envelope Envelope { get; init; }
|
|
||||||
|
|
||||||
public required EnvelopeReceiver EnvelopeReceiver { get; init; }
|
|
||||||
|
|
||||||
public required string Reason { get; init; }
|
|
||||||
|
|
||||||
public required string SenderReference { get; init; }
|
|
||||||
|
|
||||||
public EmailTemplateType TemplateType => EmailTemplateType.DocumentDeleted;
|
|
||||||
|
|
||||||
public string EmailAddress => EnvelopeReceiver.Receiver?.EmailAddress
|
|
||||||
?? throw new InvalidOperationException("Receiver email is missing for EnvelopeDeletedNotification.");
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Notifications.EnvelopeDeleted.Handlers;
|
|
||||||
|
|
||||||
public class SendDeletedMailHandler : SendMailHandler<EnvelopeDeletedNotification>
|
|
||||||
{
|
|
||||||
public SendDeletedMailHandler(
|
|
||||||
IRepository<EmailTemplate> tempRepo,
|
|
||||||
IRepository<EmailOut> emailOutRepo,
|
|
||||||
IOptions<MailParams> mailParamsOptions,
|
|
||||||
IOptions<DispatcherParams> dispatcherParamsOptions)
|
|
||||||
: base(tempRepo, emailOutRepo, mailParamsOptions, dispatcherParamsOptions)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ConfigureEmailOut(EnvelopeDeletedNotification notification, EmailOut emailOut)
|
|
||||||
{
|
|
||||||
emailOut.ReferenceString = notification.EmailAddress;
|
|
||||||
emailOut.ReferenceId = notification.EnvelopeReceiver.ReceiverId;
|
|
||||||
emailOut.WfId = (int)EnvelopeStatus.MessageDeletionSent;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Dictionary<string, string> CreatePlaceHolders(EnvelopeDeletedNotification notification)
|
|
||||||
{
|
|
||||||
var senderName = notification.Envelope.User?.Email;
|
|
||||||
if (string.IsNullOrWhiteSpace(senderName))
|
|
||||||
senderName = notification.SenderReference;
|
|
||||||
|
|
||||||
return new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["[NAME_RECEIVER]"] = notification.EnvelopeReceiver.Name ?? string.Empty,
|
|
||||||
["[NAME_SENDER]"] = senderName ?? string.Empty,
|
|
||||||
["[DOCUMENT_TITLE]"] = notification.Envelope.Title ?? string.Empty,
|
|
||||||
["[REASON]"] = notification.Reason
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Common.Notifications.RemoveSignature.Handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class RemoveDocResult : INotificationHandler<RemoveSignatureNotification>
|
||||||
|
{
|
||||||
|
private readonly IRepository<Envelope> _repo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repository"></param>
|
||||||
|
public RemoveDocResult(IRepository<Envelope> repository)
|
||||||
|
{
|
||||||
|
_repo = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="notification"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task Handle(RemoveSignatureNotification notification, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
if(notification.EnvelopeId is null && notification.EnvelopeUuid is null)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
return _repo.UpdateAsync(
|
||||||
|
envelope => envelope.DocResult = null,
|
||||||
|
query => {
|
||||||
|
if (notification.EnvelopeId is int envelopeId)
|
||||||
|
query = query.Where(envelope => envelope.Id == envelopeId);
|
||||||
|
|
||||||
|
if (notification.EnvelopeUuid is string uuid)
|
||||||
|
query = query.Where(envelope => envelope.Uuid == uuid);
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}, cancel);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,7 +113,7 @@ public abstract class SendMailHandler<TNotification> : INotificationHandler<TNot
|
|||||||
EmailAddress = notification.EmailAddress,
|
EmailAddress = notification.EmailAddress,
|
||||||
EmailBody = temp.Body,
|
EmailBody = temp.Body,
|
||||||
EmailSubj = temp.Subject,
|
EmailSubj = temp.Subject,
|
||||||
AddedWhen = DateTime.Now,
|
AddedWhen = DateTime.UtcNow,
|
||||||
AddedWho = DispatcherParams.AddedWho,
|
AddedWho = DispatcherParams.AddedWho,
|
||||||
SendingProfile = DispatcherParams.SendingProfile,
|
SendingProfile = DispatcherParams.SendingProfile,
|
||||||
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
namespace EnvelopeGenerator.Application.Common.Query;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Common.Query;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stellt eine Abfrage dar, um die Details eines Empfängers zu lesen.
|
/// Stellt eine Abfrage dar, um die Details eines Empfängers zu lesen.
|
||||||
@@ -31,6 +29,5 @@ public record ReceiverQueryBase
|
|||||||
/// <see cref="Id"/>, <see cref="EmailAddress"/>, or <see cref="Signature"/> is not null.
|
/// <see cref="Id"/>, <see cref="EmailAddress"/>, or <see cref="Signature"/> is not null.
|
||||||
/// <para>Usage example: The query can be executed only if at least one criterion is specified.</para>
|
/// <para>Usage example: The query can be executed only if at least one criterion is specified.</para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[NotMapped]
|
public bool HasAnyCriteria => Id is not null || EmailAddress is not null || Signature is not null;
|
||||||
public virtual bool HasAnyCriteria => Id is not null || EmailAddress is not null || Signature is not null;
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ public class EnvelopeReceiverAddReadSQL : ISQL<Envelope>
|
|||||||
/// ENV_UID, EMAIL_ADRESS, SALUTATION, PHONE,
|
/// ENV_UID, EMAIL_ADRESS, SALUTATION, PHONE,
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Raw => @"
|
public string Raw => @"
|
||||||
DECLARE @OUT_RECEIVER_ID bigint
|
DECLARE @OUT_RECEIVER_ID int
|
||||||
|
|
||||||
EXEC [dbo].[PRSIG_API_CREATE_RECEIVER]
|
EXEC [dbo].[PRSIG_API_CREATE_RECEIVER]
|
||||||
{0},
|
{0},
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.Configs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultReadConfigQuery : IRequest<ConfigDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Guid MemoryCacheKey = Guid.NewGuid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class DefaultReadConfigQueryExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<ConfigDto> ReadDefaultConfigAsync(this ISender sender, CancellationToken cancel = default)
|
||||||
|
=> sender.Send(new DefaultReadConfigQuery(), cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultReadConfigQueryHandler : IRequestHandler<DefaultReadConfigQuery, ConfigDto>
|
||||||
|
{
|
||||||
|
private readonly IRepository<Config> _repo;
|
||||||
|
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
private readonly IMemoryCache _cache;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repo"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
/// <param name="cache"></param>
|
||||||
|
public DefaultReadConfigQueryHandler(IRepository<Config> repo, IMapper mapper, IMemoryCache cache)
|
||||||
|
{
|
||||||
|
_repo = repo;
|
||||||
|
_mapper = mapper;
|
||||||
|
_cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public async Task<ConfigDto> Handle(DefaultReadConfigQuery request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var config = await _cache.GetOrCreateAsync(DefaultReadConfigQuery.MemoryCacheKey, async entry =>
|
||||||
|
{
|
||||||
|
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
|
||||||
|
var configs = await _repo.GetAllAsync(cancel);
|
||||||
|
var defaultConfig = configs.FirstOrDefault();
|
||||||
|
var defaultConfigDto = _mapper.Map<ConfigDto>(defaultConfig);
|
||||||
|
return defaultConfigDto;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(config is null)
|
||||||
|
{
|
||||||
|
_cache.Remove(DefaultReadConfigQuery.MemoryCacheKey);
|
||||||
|
throw new NotFoundException("No configuration record is found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,16 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
using EnvelopeGenerator.Application.Common.Configurations;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
||||||
using EnvelopeGenerator.Application.Services;
|
using EnvelopeGenerator.Application.Services;
|
||||||
|
using EnvelopeGenerator.Application.ThirdPartyModules.Queries;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using QRCoder;
|
using QRCoder;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using MediatR;
|
using GdPicture14;
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
using EnvelopeGenerator.Application.Pdf.Behaviors;
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application;
|
namespace EnvelopeGenerator.Application;
|
||||||
|
|
||||||
@@ -50,36 +52,56 @@ public static class DependencyInjection
|
|||||||
services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
|
services.Configure<AuthenticatorParams>(config.GetSection(nameof(AuthenticatorParams)));
|
||||||
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
|
services.Configure<TotpSmsParams>(config.GetSection(nameof(TotpSmsParams)));
|
||||||
|
|
||||||
|
#region PDF Burner
|
||||||
|
services.Configure<PDFBurnerParams>(config.GetSection(nameof(PDFBurnerParams)));
|
||||||
|
services.AddOptions<GdPictureParams>()
|
||||||
|
.Configure((GdPictureParams opt, IServiceProvider provider) =>
|
||||||
|
{
|
||||||
|
opt.License = config["GdPictureLicenseKey"]
|
||||||
|
?? provider.GetRequiredService<IMediator>().ReadThirdPartyModuleLicenseAsync("GDPICTURE").GetAwaiter().GetResult()
|
||||||
|
?? throw new InvalidOperationException($"License record not found for key: {"GDPICTURE"}");
|
||||||
|
});
|
||||||
|
services.AddSingleton(provider =>
|
||||||
|
{
|
||||||
|
var license = provider.GetRequiredService<IOptions<GdPictureParams>>().Value.License;
|
||||||
|
var licenseManager = new LicenseManager();
|
||||||
|
licenseManager.RegisterKEY(license);
|
||||||
|
return licenseManager;
|
||||||
|
});
|
||||||
|
services.AddTransient(provider =>
|
||||||
|
{
|
||||||
|
// Ensure LicenseManager is resolved so that its constructor is called
|
||||||
|
_ = provider.GetRequiredService<LicenseManager>();
|
||||||
|
return new AnnotationManager();
|
||||||
|
});
|
||||||
|
#endregion PDF Burner
|
||||||
|
|
||||||
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
|
services.AddHttpClientService<GtxMessagingParams>(config.GetSection(nameof(GtxMessagingParams)));
|
||||||
services.TryAddScoped<ISmsSender, GTXSmsSender>(); // Changed: Singleton → Scoped
|
services.TryAddSingleton<ISmsSender, GTXSmsSender>();
|
||||||
services.TryAddScoped<IEnvelopeSmsHandler, EnvelopeSmsHandler>(); // Changed: Singleton → Scoped
|
services.TryAddSingleton<IEnvelopeSmsHandler, EnvelopeSmsHandler>();
|
||||||
services.TryAddSingleton<IAuthenticator, Authenticator>();
|
services.TryAddSingleton<IAuthenticator, Authenticator>();
|
||||||
services.TryAddSingleton<QRCodeGenerator>();
|
services.TryAddSingleton<QRCodeGenerator>();
|
||||||
|
|
||||||
services.AddMediatR(cfg =>
|
services.AddMediatR(cfg =>
|
||||||
{
|
{
|
||||||
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
// Register SignCommand pipeline behaviors in execution order
|
|
||||||
// 0. EnvelopeReceiverResolutionBehavior - Resolves EnvelopeReceiver from query parameters (executes FIRST)
|
|
||||||
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, EnvelopeReceiverResolutionBehavior>();
|
|
||||||
|
|
||||||
// 1. AnnotationBehavior - Saves annotations (executes second)
|
|
||||||
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, SaveSignatureBehavior>();
|
|
||||||
|
|
||||||
// 2. AnnotationBehavior - Saves annotations for legacy web flow
|
cfg.AddBehavior<CreateHistoryBehavior>();
|
||||||
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, AnnotationBehavior>();
|
cfg.AddBehavior<SavePdfBehavior>();
|
||||||
|
#if WINDOWS
|
||||||
// 3. DocStatusBehavior - Creates document status
|
cfg.AddBehavior<SendEmailBehavior>();
|
||||||
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, DocStatusBehavior>();
|
cfg.AddBehavior<WritePdfBehavior>();
|
||||||
|
cfg.AddBehavior<PdfMergeBehavior>();
|
||||||
// 4. HistoryBehavior - Records history
|
cfg.AddBehavior<AddReportBehavior>();
|
||||||
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, HistoryBehavior>();
|
#endif
|
||||||
|
|
||||||
// 5. SendSignedMailBehavior - Sends notification email (executes LAST)
|
|
||||||
cfg.AddBehavior<IPipelineBehavior<SigningCommand, Unit>, SendSignedMailBehavior>();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add memory cache
|
||||||
|
services.AddMemoryCache();
|
||||||
|
|
||||||
|
// Register mail services
|
||||||
|
services.AddScoped<IEnvelopeMailService, EnvelopeMailService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pipeline behavior that saves annotations.
|
|
||||||
/// Executes first in the signing process.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("The PSPDFKit library is deprecated.")]
|
|
||||||
public class AnnotationBehavior : IPipelineBehavior<SigningCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly IRepository<ElementAnnotation> _repo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="AnnotationBehavior"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public AnnotationBehavior(IRepository<ElementAnnotation> repository)
|
|
||||||
{
|
|
||||||
_repo = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if(request.ReceiverAppType != ReceiverAppType.LegacyWeb)
|
|
||||||
if(request.PsPdfKitAnnotation is null)
|
|
||||||
return await next(cancel);
|
|
||||||
else
|
|
||||||
throw new BadRequestException("PsPdfKit Annotation are only supported for the legacy web receiver type.");
|
|
||||||
|
|
||||||
if (request.PsPdfKitAnnotation is PsPdfKitAnnotation annot)
|
|
||||||
await _repo.CreateAsync(annot.Structured, cancel);
|
|
||||||
else
|
|
||||||
throw new BadRequestException("Annotation data is missing or invalid.");
|
|
||||||
|
|
||||||
return await next(cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.DocStatus.Commands;
|
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pipeline behavior that creates document status.
|
|
||||||
/// Executes second in the signing process.
|
|
||||||
/// </summary>
|
|
||||||
public class DocStatusBehavior : IPipelineBehavior<SigningCommand, Unit>
|
|
||||||
{
|
|
||||||
private const string BlankAnnotationJson = "{}";
|
|
||||||
|
|
||||||
private readonly ISender _sender;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
public DocStatusBehavior(ISender sender)
|
|
||||||
{
|
|
||||||
_sender = sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
|
||||||
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
await _sender.Send(new CreateDocStatusCommand()
|
|
||||||
{
|
|
||||||
EnvelopeId = request.EnvelopeReceiver.EnvelopeId,
|
|
||||||
ReceiverId = request.EnvelopeReceiver.ReceiverId,
|
|
||||||
Value = request.PsPdfKitAnnotation is PsPdfKitAnnotation annot
|
|
||||||
? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
|
|
||||||
: BlankAnnotationJson
|
|
||||||
}, cancellationToken);
|
|
||||||
|
|
||||||
return await next(cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pipeline behavior that resolves and validates EnvelopeReceiver.
|
|
||||||
/// Executes FIRST in the signing process - before all other behaviors.
|
|
||||||
/// If EnvelopeReceiver is not provided, it queries the database using EnvelopeReceiverQueryBase parameters.
|
|
||||||
/// </summary>
|
|
||||||
public class EnvelopeReceiverResolutionBehavior : IPipelineBehavior<SigningCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly IRepository<EnvelopeReceiver> _erRepo;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="erRepo"></param>
|
|
||||||
/// <param name="mapper"></param>
|
|
||||||
public EnvelopeReceiverResolutionBehavior(IRepository<EnvelopeReceiver> erRepo, IMapper mapper)
|
|
||||||
{
|
|
||||||
_erRepo = erRepo;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// If EnvelopeReceiver is not provided, query it from database
|
|
||||||
if (request.EnvelopeReceiver is null)
|
|
||||||
{
|
|
||||||
var er = await _erRepo.Query
|
|
||||||
.Where(request, notnull: true)
|
|
||||||
.Include(x => x.Envelope)
|
|
||||||
.Include(x => x.Receiver)
|
|
||||||
.SingleOrDefaultAsync(cancellationToken)
|
|
||||||
?? throw new NotFoundException("EnvelopeReceiver not found");
|
|
||||||
|
|
||||||
request.SetEnvelopeReceiver(_mapper.Map<EnvelopeReceiverDto>(er));
|
|
||||||
}
|
|
||||||
|
|
||||||
return await next(cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Histories.Commands;
|
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pipeline behavior that records history.
|
|
||||||
/// Executes third in the signing process.
|
|
||||||
/// </summary>
|
|
||||||
public class HistoryBehavior : IPipelineBehavior<SigningCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly ISender _sender;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
public HistoryBehavior(ISender sender)
|
|
||||||
{
|
|
||||||
_sender = sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (request.EnvelopeReceiver.Receiver is null)
|
|
||||||
throw new InvalidOperationException($"Receiver information is missing in the notification. SignCommand:\n {request.ToJson(Format.Json.ForDiagnostics)}");
|
|
||||||
|
|
||||||
await _sender.Send(new CreateHistoryCommand()
|
|
||||||
{
|
|
||||||
EnvelopeId = request.EnvelopeReceiver.EnvelopeId,
|
|
||||||
UserReference = request.EnvelopeReceiver.Receiver.EmailAddress,
|
|
||||||
Status = EnvelopeStatus.DocumentSigned,
|
|
||||||
}, cancellationToken);
|
|
||||||
|
|
||||||
return await next(cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pipeline behavior that creates document status.
|
|
||||||
/// Executes second in the signing process.
|
|
||||||
/// </summary>
|
|
||||||
public class SaveSignatureBehavior : IPipelineBehavior<SigningCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly ISender _sender;
|
|
||||||
|
|
||||||
private readonly IRepository<DocReceiverElement> _elementRepo;
|
|
||||||
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="elementRepo"></param>
|
|
||||||
/// <param name="mapper"></param>
|
|
||||||
public SaveSignatureBehavior(ISender sender, IRepository<DocReceiverElement> elementRepo, IMapper mapper)
|
|
||||||
{
|
|
||||||
_sender = sender;
|
|
||||||
_elementRepo = elementRepo;
|
|
||||||
_elementRepo = elementRepo;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if (request.ReceiverAppType == ReceiverAppType.LegacyWeb)
|
|
||||||
return await next(cancel);
|
|
||||||
else if(request.Signatures is not IEnumerable<Signature> signatures)
|
|
||||||
throw new BadRequestException($"Signatures are required for saving signature behavior.");
|
|
||||||
|
|
||||||
var elements = await _elementRepo
|
|
||||||
.Where(e => e.Document.EnvelopeId == request.EnvelopeReceiver.EnvelopeId)
|
|
||||||
.Where(e => e.ReceiverId == request.EnvelopeReceiver.ReceiverId)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
foreach (var element in elements)
|
|
||||||
{
|
|
||||||
var signatures = request.Signatures.Where(s => s.Id == element.Id).ToList();
|
|
||||||
if(signatures.Count == 0)
|
|
||||||
throw new BadRequestException("No signature found for element with id {element.Id}.");
|
|
||||||
else if(signatures.Count > 1)
|
|
||||||
throw new BadRequestException("Multiple signatures found for element with id {element.Id}.");
|
|
||||||
|
|
||||||
await _elementRepo.UpdateAsync(signatures.First(), e => e.Id == element.Id, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await next(cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|
||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using EnvelopeGenerator.Domain.Interfaces;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Behaviors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pipeline behavior that sends signed mail notification.
|
|
||||||
/// Executes LAST in the signing process - only if all previous behaviors succeed.
|
|
||||||
/// </summary>
|
|
||||||
public class SendSignedMailBehavior : IPipelineBehavior<SigningCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly IRepository<EmailTemplate> _tempRepo;
|
|
||||||
private readonly IRepository<EmailOut> _emailOutRepo;
|
|
||||||
private readonly MailParams _mailParams;
|
|
||||||
private readonly DispatcherParams _dispatcherParams;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tempRepo"></param>
|
|
||||||
/// <param name="emailOutRepo"></param>
|
|
||||||
/// <param name="mailParamsOptions"></param>
|
|
||||||
/// <param name="dispatcherParamsOptions"></param>
|
|
||||||
public SendSignedMailBehavior(
|
|
||||||
IRepository<EmailTemplate> tempRepo,
|
|
||||||
IRepository<EmailOut> emailOutRepo,
|
|
||||||
IOptions<MailParams> mailParamsOptions,
|
|
||||||
IOptions<DispatcherParams> dispatcherParamsOptions)
|
|
||||||
{
|
|
||||||
_tempRepo = tempRepo;
|
|
||||||
_emailOutRepo = emailOutRepo;
|
|
||||||
_mailParams = mailParamsOptions.Value;
|
|
||||||
_dispatcherParams = dispatcherParamsOptions.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="next"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<Unit> Handle(SigningCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var placeHolders = CreatePlaceHolders(request);
|
|
||||||
|
|
||||||
var temp = await _tempRepo
|
|
||||||
.Where(x => x.Name == EmailTemplateType.DocumentSigned.ToString())
|
|
||||||
.SingleOrDefaultAsync(cancellationToken)
|
|
||||||
?? throw new InvalidOperationException($"Email template not found. SignCommand:\n {request.ToJson(Format.Json.ForDiagnostics)}");
|
|
||||||
|
|
||||||
temp.Subject = ReplacePlaceHolders(temp.Subject, placeHolders, _mailParams.Placeholders);
|
|
||||||
temp.Body = ReplacePlaceHolders(temp.Body, placeHolders, _mailParams.Placeholders);
|
|
||||||
|
|
||||||
var emailOut = new EmailOut
|
|
||||||
{
|
|
||||||
EmailAddress = request.EnvelopeReceiver.Receiver!.EmailAddress,
|
|
||||||
EmailBody = temp.Body,
|
|
||||||
EmailSubj = temp.Subject,
|
|
||||||
AddedWhen = DateTime.Now,
|
|
||||||
AddedWho = _dispatcherParams.AddedWho,
|
|
||||||
SendingProfile = _dispatcherParams.SendingProfile,
|
|
||||||
ReminderTypeId = _dispatcherParams.ReminderTypeId,
|
|
||||||
EmailAttmt1 = _dispatcherParams.EmailAttmt1,
|
|
||||||
WfId = (int)EnvelopeStatus.MessageConfirmationSent,
|
|
||||||
ReferenceString = request.EnvelopeReceiver.Receiver!.EmailAddress,
|
|
||||||
ReferenceId = request.EnvelopeReceiver.ReceiverId
|
|
||||||
};
|
|
||||||
|
|
||||||
await _emailOutRepo.CreateAsync(emailOut, cancellationToken);
|
|
||||||
|
|
||||||
return await next(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, string> CreatePlaceHolders(SigningCommand request)
|
|
||||||
{
|
|
||||||
var placeHolders = new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{ "[NAME_RECEIVER]", request.EnvelopeReceiver.Name ?? string.Empty },
|
|
||||||
{ "[DOCUMENT_TITLE]", request.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.EnvelopeReceiver.Envelope.IsReadAndConfirm())
|
|
||||||
{
|
|
||||||
placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
|
|
||||||
placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
|
|
||||||
placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
|
|
||||||
placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
|
|
||||||
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
|
|
||||||
placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
|
|
||||||
placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
|
|
||||||
placeHolders["[FINAL_STATUS]"] = "Signatur";
|
|
||||||
placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
|
|
||||||
placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
|
|
||||||
placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
|
|
||||||
}
|
|
||||||
|
|
||||||
return placeHolders;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ReplacePlaceHolders(string text, params Dictionary<string, string>[] placeHoldersList)
|
|
||||||
{
|
|
||||||
foreach (var placeHolders in placeHoldersList)
|
|
||||||
foreach (var ph in placeHolders)
|
|
||||||
text = text.Replace(ph.Key, ph.Value);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Query;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Command to sign a document by a receiver.
|
|
||||||
/// </summary>
|
|
||||||
public record SigningCommand : EnvelopeReceiverQueryBase, IRequest
|
|
||||||
{
|
|
||||||
private EnvelopeReceiverDto? _envelopeReceiver;
|
|
||||||
|
|
||||||
internal void SetEnvelopeReceiver(EnvelopeReceiverDto envelopeReceiver)
|
|
||||||
{
|
|
||||||
_envelopeReceiver = envelopeReceiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The envelope receiver information.
|
|
||||||
/// </summary>
|
|
||||||
public EnvelopeReceiverDto EnvelopeReceiver
|
|
||||||
{
|
|
||||||
get => _envelopeReceiver!;
|
|
||||||
init => _envelopeReceiver = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The PSPDFKit annotation data.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("This notification is deprecated. Use Signature.Commands.SignCommand instead.")]
|
|
||||||
public PsPdfKitAnnotation? PsPdfKitAnnotation { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<Signature>? Signatures { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public ReceiverAppType ReceiverAppType { get; init; } = ReceiverAppType.ReceiverUI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the sign command. All work is done by pipeline behaviors.
|
|
||||||
/// This handler is intentionally empty - behaviors handle all the processing.
|
|
||||||
/// </summary>
|
|
||||||
public class SignCommandHandler : IRequestHandler<SigningCommand>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes the signing command. Pipeline behaviors handle all processing.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Task Handle(SigningCommand request, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public enum ReceiverAppType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
ReceiverUI = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
LegacyWeb = 1,
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using EnvelopeGenerator.Application.Common.Query;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocReceiverElements.Queries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record ReadDocReceiverElementQuery : EnvelopeReceiverQueryBase, IRequest<IEnumerable<DocReceiverElementDto>>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ReadDocReceiverElementQueryHandler : IRequestHandler<ReadDocReceiverElementQuery, IEnumerable<DocReceiverElementDto>>
|
|
||||||
{
|
|
||||||
private readonly IRepository<DocReceiverElement> _repository;
|
|
||||||
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
/// <param name="mapper"></param>
|
|
||||||
public ReadDocReceiverElementQueryHandler(IRepository<DocReceiverElement> repository, IMapper mapper)
|
|
||||||
{
|
|
||||||
_repository = repository;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
|
||||||
public async Task<IEnumerable<DocReceiverElementDto>> Handle(ReadDocReceiverElementQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var q = _repository.Query;
|
|
||||||
|
|
||||||
if(request.Envelope.Id is int envelopeId)
|
|
||||||
q = q.Where(e => e.Document.EnvelopeId == envelopeId);
|
|
||||||
|
|
||||||
if (request.Envelope.Uuid is string envelopeUuid)
|
|
||||||
q = q.Where(e => e.Document.Envelope.Uuid == envelopeUuid);
|
|
||||||
|
|
||||||
if (request.Receiver.Id is int receiverId)
|
|
||||||
q = q.Where(e => e.ReceiverId == receiverId);
|
|
||||||
|
|
||||||
if (request.Receiver.Signature is string signature)
|
|
||||||
q = q.Where(e => e.Receiver.Signature == signature);
|
|
||||||
|
|
||||||
var elements = await q.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<DocReceiverElementDto>>(elements);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +1,12 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
using EnvelopeGenerator.Application.Common.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public record CreateDocStatusCommand : IRequest<DocumentStatus>
|
public record CreateDocStatusCommand : ModifyDocStatusCommandBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int EnvelopeId { get; set; }
|
public DateTime AddedWhen => StatusChangedWhen;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the display value associated with the status.
|
|
||||||
/// </summary>
|
|
||||||
public string? Value { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class CreateDocStatusCommandHandler : CreateCommandHandler<CreateDocStatusCommand, DocumentStatus>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public CreateDocStatusCommandHandler(IRepository<DocumentStatus> repository) : base(repository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using EnvelopeGenerator.Application.Common.Query;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public record ModifyDocStatusCommandBase : EnvelopeReceiverQueryBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int? EnvelopeId => Envelope.Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int? ReceiverId => Receiver.Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public override ReceiverQueryBase Receiver { get => base.Receiver; set => base.Receiver = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current status code.
|
||||||
|
/// </summary>
|
||||||
|
public DocumentStatus Status => Value is null ? DocumentStatus.Created : DocumentStatus.Signed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the display value associated with the status.
|
||||||
|
/// </summary>
|
||||||
|
public string? Value { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets timestamp when this record was added.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime StatusChangedWhen { get; } = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps the current command to a new instance of the specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDest"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public TDest To<TDest>() where TDest : ModifyDocStatusCommandBase, new()
|
||||||
|
=> new()
|
||||||
|
{
|
||||||
|
Key = Key,
|
||||||
|
Envelope = Envelope,
|
||||||
|
Receiver = Receiver,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using AutoMapper;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a command to save the status of a document, either by creating a new status or updating an existing one based on the provided envelope and receiver identifiers.
|
||||||
|
/// It returns the identifier of the saved document status.
|
||||||
|
/// </summary>
|
||||||
|
public record SaveDocStatusCommand : ModifyDocStatusCommandBase, IRequest<DocumentStatusDto?>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class SaveDocStatusCommandHandler : IRequestHandler<SaveDocStatusCommand, DocumentStatusDto?>
|
||||||
|
{
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
private readonly IRepository<DocumentStatus> _repo;
|
||||||
|
|
||||||
|
private readonly IRepository<Envelope> _envRepo;
|
||||||
|
|
||||||
|
private readonly IRepository<Receiver> _rcvRepo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
/// <param name="repo"></param>
|
||||||
|
/// <param name="rcvRepo"></param>
|
||||||
|
/// <param name="envRepo"></param>
|
||||||
|
public SaveDocStatusCommandHandler(IMapper mapper, IRepository<DocumentStatus> repo, IRepository<Receiver> rcvRepo, IRepository<Envelope> envRepo)
|
||||||
|
{
|
||||||
|
_mapper = mapper;
|
||||||
|
_repo = repo;
|
||||||
|
_rcvRepo = rcvRepo;
|
||||||
|
_envRepo = envRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<DocumentStatusDto?> Handle(SaveDocStatusCommand request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
// ceck if exists
|
||||||
|
bool isExists = await _repo.ReadOnly().Where(request).AnyAsync(cancel);
|
||||||
|
|
||||||
|
var env = await _envRepo.ReadOnly().Where(request.Envelope).FirstAsync(cancel);
|
||||||
|
var rcv = await _rcvRepo.ReadOnly().Where(request.Receiver).FirstAsync(cancel);
|
||||||
|
|
||||||
|
request.Envelope.Id = env.Id;
|
||||||
|
request.Receiver.Id = rcv.Id;
|
||||||
|
|
||||||
|
if (isExists)
|
||||||
|
{
|
||||||
|
var uReq = request.To<UpdateDocStatusCommand>();
|
||||||
|
await _repo.UpdateAsync(uReq, q => q.Where(request), cancel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var cReq = request.To<CreateDocStatusCommand>();
|
||||||
|
await _repo.CreateAsync(cReq, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
var docStatus = await _repo.ReadOnly().Where(request).SingleOrDefaultAsync(cancel);
|
||||||
|
|
||||||
|
return _mapper.Map<DocumentStatusDto>(docStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +1,14 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Commands;
|
using EnvelopeGenerator.Domain;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
namespace EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Value"></param>
|
public record UpdateDocStatusCommand : ModifyDocStatusCommandBase
|
||||||
public record DocStatusUpdateDto(string? Value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record UpdateDocStatusCommand : UpdateCommand<DocStatusUpdateDto, DocumentStatus>
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets timestamp when this record was added. Returns the StatusChangedWhen value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int EnvelopeId { get; set; }
|
public DateTime? ChangedWhen => StatusChangedWhen;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public int ReceiverId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the display value associated with the status.
|
|
||||||
/// </summary>
|
|
||||||
public string? Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override Expression<Func<DocumentStatus, bool>> BuildQueryExpression()
|
|
||||||
{
|
|
||||||
return ds => ds.EnvelopeId == EnvelopeId && ds.ReceiverId == ReceiverId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.DocStatus.Commands;
|
using EnvelopeGenerator.Application.DocStatus.Commands;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
|
||||||
@@ -17,17 +16,10 @@ public class MappingProfile : Profile
|
|||||||
{
|
{
|
||||||
CreateMap<CreateDocStatusCommand, DocumentStatus>()
|
CreateMap<CreateDocStatusCommand, DocumentStatus>()
|
||||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
|
||||||
.ForMember(dest => dest.Status, opt => opt.MapFrom(
|
|
||||||
src => src.Value == null
|
|
||||||
? Domain.Constants.DocumentStatus.Created
|
|
||||||
: Domain.Constants.DocumentStatus.Signed))
|
|
||||||
.MapAddedWhen();
|
|
||||||
|
|
||||||
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
CreateMap<UpdateDocStatusCommand, DocumentStatus>()
|
||||||
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
.ForMember(dest => dest.Envelope, opt => opt.Ignore())
|
||||||
.ForMember(dest => dest.Receiver, opt => opt.Ignore())
|
.ForMember(dest => dest.Receiver, opt => opt.Ignore());
|
||||||
.ForMember(dest => dest.StatusChangedWhen, opt => opt.MapFrom(src => DateTime.Now))
|
|
||||||
.MapChangedWhen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ using EnvelopeGenerator.Domain.Entities;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Documents.Queries;
|
namespace EnvelopeGenerator.Application.Documents.Queries;
|
||||||
|
|
||||||
@@ -13,15 +12,14 @@ namespace EnvelopeGenerator.Application.Documents.Queries;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Id">The unique identifier of the document. Optional.</param>
|
/// <param name="Id">The unique identifier of the document. Optional.</param>
|
||||||
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
|
/// <param name="EnvelopeId">The identifier of the envelope associated with the document. Optional.</param>
|
||||||
/// <param name="UserId">The unique identifier of the sending user. Optional.</param>
|
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null) : IRequest<DocumentDto?>
|
||||||
public record ReadDocumentQuery(int? Id = null, int? EnvelopeId = null, int? UserId = null) : IRequest<DocumentDto>
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
|
/// Handles queries for reading <see cref="Document"/> data based on either the document ID or the envelope ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto>
|
public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, DocumentDto?>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TempRepo for accessing <see cref="Document"/> entities.
|
/// TempRepo for accessing <see cref="Document"/> entities.
|
||||||
@@ -52,25 +50,20 @@ public class ReadDocumentQueryHandler : IRequestHandler<ReadDocumentQuery, Docum
|
|||||||
/// <exception cref="InvalidOperationException">
|
/// <exception cref="InvalidOperationException">
|
||||||
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
|
/// Thrown when neither <see cref="ReadDocumentQuery.Id"/> nor <see cref="ReadDocumentQuery.EnvelopeId"/> is provided.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public async Task<DocumentDto> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
public async Task<DocumentDto?> Handle(ReadDocumentQuery query, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var docQuery = _repo.Query.Include(doc => doc.Elements).ThenInclude(e => e.Annotations).AsNoTracking();
|
|
||||||
|
|
||||||
if (query.UserId is int userId)
|
|
||||||
docQuery = docQuery.Where(doc => doc.Envelope.UserId == userId);
|
|
||||||
|
|
||||||
if (query.Id is not null)
|
if (query.Id is not null)
|
||||||
{
|
{
|
||||||
var doc = await docQuery.Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
var doc = await _repo.ReadOnly().Where(d => d.Id == query.Id).FirstOrDefaultAsync(cancel);
|
||||||
|
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
else if (query.EnvelopeId is not null)
|
else if (query.EnvelopeId is not null)
|
||||||
{
|
{
|
||||||
var doc = await docQuery.Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
var doc = await _repo.ReadOnly().Where(d => d.EnvelopeId == query.EnvelopeId).FirstOrDefaultAsync(cancel);
|
||||||
return _mapper.Map<DocumentDto>(doc);
|
return _mapper.Map<DocumentDto>(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotFoundException();
|
throw new InvalidOperationException(
|
||||||
|
$"Invalid {nameof(ReadDocumentQuery)}: either {nameof(query.Id)} or {nameof(query.EnvelopeId)} must be provided.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using EnvelopeGenerator.Domain;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ein Befehl zum Zurücksetzen einer E-Mail-Vorlage auf die Standardwerte.
|
||||||
|
/// Erbt von <see cref="EmailTemplateQuery"/> und ermöglicht die Angabe einer optionalen ID und eines Typs der E-Mail-Vorlage.<br/><br/>
|
||||||
|
/// Beispiele:<br/>
|
||||||
|
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.<br/>
|
||||||
|
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.<br/>
|
||||||
|
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.<br/>
|
||||||
|
/// 3 - DocumentCompleted: Benachrichtigung über den Abschluss eines Dokuments.<br/>
|
||||||
|
/// 4 - DocumentAccessCodeReceived: Benachrichtigung über den Erhalt eines Zugangscodes.<br/>
|
||||||
|
/// 5 - DocumentShared: Benachrichtigung über das Teilen eines Dokuments.<br/>
|
||||||
|
/// 6 - TotpSecret: Benachrichtigung über ein TOTP-Geheimnis.<br/>
|
||||||
|
/// 7 - DocumentRejected_ADM (Für den Absender): Mail an den Absender, wenn das Dokument abgelehnt wird.<br/>
|
||||||
|
/// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.<br/>
|
||||||
|
/// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.<br/>
|
||||||
|
/// </summary>
|
||||||
|
public record ResetEmailTemplateCommand : EmailTemplateQuery, IRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="orginal"></param>
|
||||||
|
public ResetEmailTemplateCommand(EmailTemplateQuery? orginal = null) : base(orginal ?? new())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Id">Die optionale ID der E-Mail-Vorlage, die zurückgesetzt werden soll.</param>
|
||||||
|
/// <param name="Type">Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional).</param>
|
||||||
|
public ResetEmailTemplateCommand(int? Id = null, EmailTemplateType? Type = null) : base(Id, Type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,38 +1,11 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EmailTemplates.Commands;
|
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Reset;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ein Befehl zum Zurücksetzen einer E-Mail-Vorlage auf die Standardwerte.
|
|
||||||
/// Erbt von <see cref="IEmailTemplateQuery"/> und ermöglicht die Angabe einer optionalen ID und eines Typs der E-Mail-Vorlage.<br/><br/>
|
|
||||||
/// </summary>
|
|
||||||
public record ResetEmailTemplateCommand : IEmailTemplateQuery, IRequest
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Die eindeutige Kennung der E-Mail-Vorlage (optional).
|
|
||||||
/// </summary>
|
|
||||||
public int? Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional). Beispiele:<br/>
|
|
||||||
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.<br/>
|
|
||||||
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.<br/>
|
|
||||||
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.<br/>
|
|
||||||
/// 3 - DocumentCompleted: Benachrichtigung über den Abschluss eines Dokuments.<br/>
|
|
||||||
/// 4 - DocumentAccessCodeReceived: Benachrichtigung über den Erhalt eines Zugangscodes.<br/>
|
|
||||||
/// 5 - DocumentShared: Benachrichtigung über das Teilen eines Dokuments.<br/>
|
|
||||||
/// 6 - TotpSecret: Benachrichtigung über ein TOTP-Geheimnis.<br/>
|
|
||||||
/// 7 - DocumentRejected_ADM (für den Absender): Mail an den Absender, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 8 - DocumentRejected_REC (für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 9 - DocumentRejected_REC_2 (für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
|
|
||||||
/// </summary>
|
|
||||||
public EmailTemplateType? Type { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -68,7 +41,7 @@ public class ResetEmailTemplateCommandHandler : IRequestHandler<ResetEmailTempla
|
|||||||
foreach (var temp in temps)
|
foreach (var temp in temps)
|
||||||
{
|
{
|
||||||
var def = Defaults.Where(t => t.Name == temp.Name).FirstOrDefault();
|
var def = Defaults.Where(t => t.Name == temp.Name).FirstOrDefault();
|
||||||
if (def is not null)
|
if(def is not null)
|
||||||
await _repository.UpdateAsync(def, t => t.Id == temp.Id, cancel);
|
await _repository.UpdateAsync(def, t => t.Id == temp.Id, cancel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,4 +113,4 @@ public class ResetEmailTemplateCommandHandler : IRequestHandler<ResetEmailTempla
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using MediatR;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Befehl zum Aktualisieren einer E-Mail-Vorlage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Body">
|
||||||
|
/// (Optional)Der neue Inhalt des E-Mail-Textkörpers. Wenn null, bleibt der vorhandene Inhalt unverändert.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="Subject">
|
||||||
|
/// (Optional) Der neue Betreff der E-Mail. Wenn null, bleibt der vorhandene Betreff unverändert.
|
||||||
|
/// </param>
|
||||||
|
public record UpdateEmailTemplateCommand(string? Body = null, string? Subject = null) : IRequest
|
||||||
|
{
|
||||||
|
/// <param>
|
||||||
|
/// Die Abfrage, die die E-Mail-Vorlage darstellt, die aktualisiert werden soll.
|
||||||
|
/// </param>
|
||||||
|
[JsonIgnore]
|
||||||
|
public EmailTemplateQuery? EmailTemplateQuery { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public DateTime ChangedWhen { get; init; } = DateTime.Now;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates.Commands.Update;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateEmailTemplateCommandHandler : IRequestHandler<UpdateEmailTemplateCommand>
|
||||||
|
{
|
||||||
|
private readonly IRepository<EmailTemplate> _repository;
|
||||||
|
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repository"></param>
|
||||||
|
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository, IMapper mapper)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
|
/// <exception cref="NotFoundException"></exception>
|
||||||
|
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
|
||||||
|
public async Task Handle(UpdateEmailTemplateCommand request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
EmailTemplateDto? tempDto;
|
||||||
|
|
||||||
|
if (request.EmailTemplateQuery?.Id is int id)
|
||||||
|
{
|
||||||
|
var temp = await _repository.ReadOnly().Where(t => t.Id == id).FirstOrDefaultAsync(cancel);
|
||||||
|
tempDto = _mapper.Map<EmailTemplateDto>(temp);
|
||||||
|
}
|
||||||
|
else if (request!.EmailTemplateQuery!.Type is EmailTemplateType type)
|
||||||
|
{
|
||||||
|
var temp = await _repository.ReadOnly().Where(t => t.Name == type.ToString()).FirstOrDefaultAsync(cancel);
|
||||||
|
tempDto = _mapper.Map<EmailTemplateDto>(temp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Both id and type is null. Id: " + request.EmailTemplateQuery.Id +". Type: " + request.EmailTemplateQuery.Type.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempDto == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Body is not null)
|
||||||
|
tempDto.Body = request.Body;
|
||||||
|
|
||||||
|
if (request.Subject is not null)
|
||||||
|
tempDto.Subject = request.Subject;
|
||||||
|
|
||||||
|
await _repository.UpdateAsync(tempDto, t => t.Id == tempDto.Id, cancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Application.Common.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EmailTemplates.Commands;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Body"></param>
|
|
||||||
/// <param name="Subject"></param>
|
|
||||||
public record EmailTemplateUpdateDto(string Body, string Subject);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Befehl zum Aktualisieren einer E-Mail-Vorlage.
|
|
||||||
/// </summary>
|
|
||||||
public record UpdateEmailTemplateCommand : UpdateCommand<EmailTemplateUpdateDto, EmailTemplate>, IEmailTemplateQuery
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Die eindeutige Kennung der E-Mail-Vorlage (optional).
|
|
||||||
/// </summary>
|
|
||||||
public int? Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional). Beispiele:<br/>
|
|
||||||
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.<br/>
|
|
||||||
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.<br/>
|
|
||||||
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.<br/>
|
|
||||||
/// 3 - DocumentCompleted: Benachrichtigung über den Abschluss eines Dokuments.<br/>
|
|
||||||
/// 4 - DocumentAccessCodeReceived: Benachrichtigung über den Erhalt eines Zugangscodes.<br/>
|
|
||||||
/// 5 - DocumentShared: Benachrichtigung über das Teilen eines Dokuments.<br/>
|
|
||||||
/// 6 - TotpSecret: Benachrichtigung über ein TOTP-Geheimnis.<br/>
|
|
||||||
/// 7 - DocumentRejected_ADM (für den Absender): Mail an den Absender, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 8 - DocumentRejected_REC (für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 9 - DocumentRejected_REC_2 (für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
|
|
||||||
/// </summary>
|
|
||||||
public EmailTemplateType? Type { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override Expression<Func<EmailTemplate, bool>> BuildQueryExpression()
|
|
||||||
=> Id is int id
|
|
||||||
? temp => temp.Id == id
|
|
||||||
: temp => temp!.Name == Type.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class UpdateEmailTemplateCommandHandler : UpdateCommandHandler<UpdateEmailTemplateCommand, EmailTemplateUpdateDto, EmailTemplate>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
public UpdateEmailTemplateCommandHandler(IRepository<EmailTemplate> repository) : base(repository)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repräsentiert eine Abfrage für E-Mail-Vorlagen, die für Absender und Empfänger von Umschlägen verwendet werden.
|
||||||
|
/// Die Standardkultur ist "de-DE".
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Id">Die eindeutige Kennung der E-Mail-Vorlage (optional).</param>
|
||||||
|
/// <param name="Type">Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional). Beispiele:
|
||||||
|
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.
|
||||||
|
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.
|
||||||
|
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.
|
||||||
|
/// 3 - DocumentCompleted: Benachrichtigung über den Abschluss eines Dokuments.
|
||||||
|
/// 4 - DocumentAccessCodeReceived: Benachrichtigung über den Erhalt eines Zugangscodes.
|
||||||
|
/// 5 - DocumentShared: Benachrichtigung über das Teilen eines Dokuments.
|
||||||
|
/// 6 - TotpSecret: Benachrichtigung über ein TOTP-Geheimnis.
|
||||||
|
/// 7 - DocumentRejected_ADM (Für den Absender): Mail an den Absender, wenn das Dokument abgelehnt wird.
|
||||||
|
/// 8 - DocumentRejected_REC (Für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.
|
||||||
|
/// 9 - DocumentRejected_REC_2 (Für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
|
||||||
|
/// </param>
|
||||||
|
public record EmailTemplateQuery(int? Id = null, EmailTemplateType? Type = null)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EmailTemplates;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stellt eine Schnittstelle für Abfragen von E-Mail-Vorlagen dar, die für Absender und Empfänger von Umschlägen verwendet werden.
|
|
||||||
/// Die Standardkultur ist "de-DE".
|
|
||||||
/// </summary>
|
|
||||||
public interface IEmailTemplateQuery
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Die eindeutige Kennung der E-Mail-Vorlage (optional).
|
|
||||||
/// </summary>
|
|
||||||
int? Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional). Beispiele:<br/>
|
|
||||||
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.<br/>
|
|
||||||
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.<br/>
|
|
||||||
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.<br/>
|
|
||||||
/// 3 - DocumentCompleted: Benachrichtigung über den Abschluss eines Dokuments.<br/>
|
|
||||||
/// 4 - DocumentAccessCodeReceived: Benachrichtigung über den Erhalt eines Zugangscodes.<br/>
|
|
||||||
/// 5 - DocumentShared: Benachrichtigung über das Teilen eines Dokuments.<br/>
|
|
||||||
/// 6 - TotpSecret: Benachrichtigung über ein TOTP-Geheimnis.<br/>
|
|
||||||
/// 7 - DocumentRejected_ADM (für den Absender): Mail an den Absender, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 8 - DocumentRejected_REC (für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 9 - DocumentRejected_REC_2 (für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
|
|
||||||
/// </summary>
|
|
||||||
EmailTemplateType? Type { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.EmailTemplates.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EmailTemplates;
|
namespace EnvelopeGenerator.Application.EmailTemplates;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MappingProfile : Profile
|
public class MappingProfile : Profile
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MappingProfile()
|
public MappingProfile()
|
||||||
{
|
{
|
||||||
CreateMap<EmailTemplate, EmailTemplateDto>();
|
CreateMap<EmailTemplate, ReadEmailTemplateResponse>();
|
||||||
|
|
||||||
CreateMap<EmailTemplateUpdateDto, EmailTemplate>()
|
|
||||||
.MapChangedWhen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen.
|
||||||
|
/// Diese Klasse erbt von <see cref="EmailTemplateQuery"/>.
|
||||||
|
/// </summary>
|
||||||
|
public record ReadEmailTemplateQuery : EmailTemplateQuery, IRequest<ReadEmailTemplateResponse?>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using EnvelopeGenerator.Application.Common.Interfaces.Repositories;
|
||||||
|
using EnvelopeGenerator.Domain.Constants;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, ReadEmailTemplateResponse?>
|
||||||
|
{
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
|
||||||
|
private readonly IEmailTemplateRepository _repository;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mapper">
|
||||||
|
/// <param name="repository">
|
||||||
|
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
|
||||||
|
/// </param>
|
||||||
|
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
|
||||||
|
public ReadEmailTemplateQueryHandler(IMapper mapper, IEmailTemplateRepository repository)
|
||||||
|
{
|
||||||
|
_mapper = mapper;
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
|
[Obsolete("Use IRepository")]
|
||||||
|
public async Task<ReadEmailTemplateResponse?> Handle(ReadEmailTemplateQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var temp = request.Id is int id
|
||||||
|
? await _repository.ReadByIdAsync(id)
|
||||||
|
: request.Type is EmailTemplateType type
|
||||||
|
? await _repository.ReadByNameAsync(type)
|
||||||
|
: throw new InvalidOperationException("Either a valid integer ID or a valid EmailTemplateType must be provided in the request.");
|
||||||
|
|
||||||
|
var res = _mapper.Map<ReadEmailTemplateResponse>(temp);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
namespace EnvelopeGenerator.Application.EmailTemplates.Queries.Read;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stellt die Antwort für eine Abfrage von E-Mail-Vorlagen bereit.
|
||||||
|
/// </summary>
|
||||||
|
public class ReadEmailTemplateResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Die eindeutige Kennung der E-Mail-Vorlage.
|
||||||
|
/// </summary>
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name des Typs
|
||||||
|
/// </summary>
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Das Datum und die Uhrzeit, wann die Vorlage hinzugefügt wurde.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime AddedWhen { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Der Inhalt (Body) der E-Mail-Vorlage. Kann null sein.
|
||||||
|
/// </summary>
|
||||||
|
public string? Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Der Betreff der E-Mail-Vorlage. Kann null sein.
|
||||||
|
/// </summary>
|
||||||
|
public string? Subject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Das Datum und die Uhrzeit, wann die Vorlage zuletzt geändert wurde. Kann null sein.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? ChangedWhen { get; set; }
|
||||||
|
}
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EmailTemplates.Queries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stellt eine Abfrage dar, um eine E-Mail-Vorlage zu lesen.
|
|
||||||
/// Diese Klasse erbt von <see cref="IEmailTemplateQuery"/>.
|
|
||||||
/// </summary>
|
|
||||||
public record ReadEmailTemplateQuery : IEmailTemplateQuery, IRequest<IEnumerable<EmailTemplateDto>>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Die eindeutige Kennung der E-Mail-Vorlage (optional).
|
|
||||||
/// </summary>
|
|
||||||
public int? Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Der Typ der E-Mail-Vorlage, z. B. <see cref="EmailTemplateType"/> (optional). Beispiele:<br/>
|
|
||||||
/// 0 - DocumentReceived: Benachrichtigung über den Empfang eines Dokuments.<br/>
|
|
||||||
/// 1 - DocumentSigned: Benachrichtigung über die Unterzeichnung eines Dokuments.<br/>
|
|
||||||
/// 2 - DocumentDeleted: Benachrichtigung über das Löschen eines Dokuments.<br/>
|
|
||||||
/// 3 - DocumentCompleted: Benachrichtigung über den Abschluss eines Dokuments.<br/>
|
|
||||||
/// 4 - DocumentAccessCodeReceived: Benachrichtigung über den Erhalt eines Zugangscodes.<br/>
|
|
||||||
/// 5 - DocumentShared: Benachrichtigung über das Teilen eines Dokuments.<br/>
|
|
||||||
/// 6 - TotpSecret: Benachrichtigung über ein TOTP-Geheimnis.<br/>
|
|
||||||
/// 7 - DocumentRejected_ADM (für den Absender): Mail an den Absender, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 8 - DocumentRejected_REC (für den ablehnenden Empfänger): Mail an den ablehnenden Empfänger, wenn das Dokument abgelehnt wird.<br/>
|
|
||||||
/// 9 - DocumentRejected_REC_2 (für sonstige Empfänger): Mail an andere Empfänger (Brief), wenn das Dokument abgelehnt wird.
|
|
||||||
/// </summary>
|
|
||||||
public EmailTemplateType? Type { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public string? LangCode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public static class ReadEmailTemplateQueryExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <param name="langCode"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<EmailTemplateDto?> ReadEmailTemplateAsync(this ISender sender, int id, string langCode, CancellationToken cancel = default)
|
|
||||||
{
|
|
||||||
var result = await sender.Send(new ReadEmailTemplateQuery { Id = id, LangCode = langCode }, cancel);
|
|
||||||
return result.FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="langCode"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task<EmailTemplateDto?> ReadEmailTemplateAsync(this ISender sender, EmailTemplateType type, string langCode, CancellationToken cancel = default)
|
|
||||||
{
|
|
||||||
var result = await sender.Send(new ReadEmailTemplateQuery { Type = type, LangCode = langCode }, cancel);
|
|
||||||
return result.FirstOrDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ReadEmailTemplateQueryHandler : IRequestHandler<ReadEmailTemplateQuery, IEnumerable<EmailTemplateDto>>
|
|
||||||
{
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
private readonly IRepository<EmailTemplate> _repo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialisiert eine neue Instanz der <see cref="EmailTemplateController"/>-Klasse.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mapper">
|
|
||||||
/// <param name="repo">
|
|
||||||
/// Die AutoMapper-Instanz, die zum Zuordnen von Objekten verwendet wird.
|
|
||||||
/// </param>
|
|
||||||
public ReadEmailTemplateQueryHandler(IMapper mapper, IRepository<EmailTemplate> repo)
|
|
||||||
{
|
|
||||||
_mapper = mapper;
|
|
||||||
_repo = repo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="InvalidOperationException"></exception>
|
|
||||||
public async Task<IEnumerable<EmailTemplateDto>> Handle(ReadEmailTemplateQuery request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var query = _repo.Query;
|
|
||||||
|
|
||||||
if (request.Id is int id)
|
|
||||||
query = query.Where(temp => temp.Id == id);
|
|
||||||
|
|
||||||
if (request.Type is EmailTemplateType type)
|
|
||||||
query = query.Where(temp => temp.Name == type.ToString());
|
|
||||||
|
|
||||||
if (request.LangCode is string langCode)
|
|
||||||
query = query.Where(temp => temp.LangCode == langCode);
|
|
||||||
|
|
||||||
var entity = await query.ToListAsync(cancel);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<EmailTemplateDto>>(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +1,61 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
|
<TargetFrameworks>net7.0;net8.0;net9.0;net8.0-windows</TargetFrameworks>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="Resources\Model.Designer.vb" />
|
<None Remove="Resources\Model.Designer.vb" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="2.1.66" />
|
<PackageReference Include="Dapper" Version="2.1.66" />
|
||||||
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.6.0" />
|
<PackageReference Include="DevExpress.Reporting.Core" Version="21.2.4" />
|
||||||
|
<PackageReference Include="DigitalData.Core.Abstraction.Application" Version="1.5.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
<PackageReference Include="DigitalData.Core.Application" Version="3.4.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
|
<PackageReference Include="DigitalData.Core.Client" Version="2.1.0" />
|
||||||
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
<PackageReference Include="DigitalData.Core.Exceptions" Version="1.1.0" />
|
||||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
|
<PackageReference Include="DigitalData.EmailProfilerDispatcher" Version="3.1.1" />
|
||||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
<PackageReference Include="HtmlSanitizer" Version="8.0.865" />
|
||||||
<PackageReference Include="MediatR" Version="12.5.0" />
|
<PackageReference Include="MediatR" Version="12.5.0" />
|
||||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.18" />
|
||||||
<PackageReference Include="Otp.NET" Version="1.4.0" />
|
<PackageReference Include="Otp.NET" Version="1.4.0" />
|
||||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
|
<PackageReference Include="QRCoder-ImageSharp" Version="0.10.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||||
<PackageReference Include="System.Formats.Asn1" Version="10.0.3" />
|
|
||||||
<PackageReference Include="System.Security.AccessControl" Version="6.0.1" />
|
|
||||||
<PackageReference Include="UserManager" Version="1.1.3" />
|
<PackageReference Include="UserManager" Version="1.1.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
|
||||||
|
<PackageReference Include="GdPicture" Version="14.3.19.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net8.0-windows'">
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
||||||
|
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
|
||||||
|
<PackageReference Include="GdPicture" Version="14.3.19.1" />
|
||||||
|
<PackageReference Include="GdPicture.runtimes.windows" Version="14.3.19.1" />
|
||||||
|
<PackageReference Include="GdPicture.runtimes.linux" Version="14.3.19.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
||||||
|
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
|
||||||
|
<PackageReference Include="GdPicture" Version="14.3.19.1" />
|
||||||
|
<PackageReference Include="GdPicture.runtimes.windows" Version="14.3.19.1" />
|
||||||
|
<PackageReference Include="GdPicture.runtimes.linux" Version="14.3.19.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.CommonServices\EnvelopeGenerator.CommonServices.vbproj" />
|
||||||
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
<ProjectReference Include="..\EnvelopeGenerator.Domain\EnvelopeGenerator.Domain.csproj" />
|
||||||
|
<ProjectReference Include="..\EnvelopeGenerator.PdfEditor\EnvelopeGenerator.PdfEditor.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -66,9 +77,6 @@
|
|||||||
<LastGenOutput>Model.Designer.cs</LastGenOutput>
|
<LastGenOutput>Model.Designer.cs</LastGenOutput>
|
||||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Update="Resources\Resource.fr-FR.resx">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<EmbeddedResource Update="Resources\Resource.de-DE.resx">
|
<EmbeddedResource Update="Resources\Resource.de-DE.resx">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
@@ -78,25 +86,27 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.2" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.3" />
|
||||||
<PackageReference Include="CommandDotNet">
|
<PackageReference Include="CommandDotNet">
|
||||||
<Version>7.0.5</Version>
|
<Version>7.0.5</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net8.0-windows'">
|
||||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
<PackageReference Include="CommandDotNet">
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
||||||
<Version>8.1.1</Version>
|
<PackageReference Include="CommandDotNet">
|
||||||
</PackageReference>
|
<Version>8.1.1</Version>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
|
||||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
<PackageReference Include="CommandDotNet">
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
|
||||||
<Version>8.1.1</Version>
|
<PackageReference Include="CommandDotNet">
|
||||||
</PackageReference>
|
<Version>8.1.1</Version>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public record CreateEnvelopeReceiverCommand : CreateEnvelopeCommand, IRequest<Cr
|
|||||||
/// <param name="X">X-Position</param>
|
/// <param name="X">X-Position</param>
|
||||||
/// <param name="Y">Y-Position</param>
|
/// <param name="Y">Y-Position</param>
|
||||||
/// <param name="Page">Seite, auf der sie sich befindet</param>
|
/// <param name="Page">Seite, auf der sie sich befindet</param>
|
||||||
public record DocReceiverElementCreateDto([Required] double X, [Required] double Y, [Required] int Page);
|
public record Signature([Required] double X, [Required] double Y, [Required] int Page);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
|
/// DTO für Empfänger, die erstellt oder abgerufen werden sollen.
|
||||||
@@ -41,7 +41,7 @@ public class ReceiverGetOrCreateCommand
|
|||||||
/// Unterschriften auf Dokumenten.
|
/// Unterschriften auf Dokumenten.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public List<DocReceiverElementCreateDto> DocReceiverElements { get; init; } = new();
|
public List<Signature> Signatures { get; init; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Der Name, mit dem der Empfänger angesprochen werden soll.
|
/// Der Name, mit dem der Empfänger angesprochen werden soll.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
using DigitalData.Core.Exceptions;
|
using DigitalData.Core.Exceptions;
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
using EnvelopeGenerator.Application.Envelopes.Queries;
|
||||||
@@ -47,13 +47,7 @@ namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
|||||||
/// Die Antwort enthält Details wie den Include, die Zuordnung zwischen Umschlag und Empfänger
|
/// Die Antwort enthält Details wie den Include, die Zuordnung zwischen Umschlag und Empfänger
|
||||||
/// sowie zusätzliche Metadaten.
|
/// sowie zusätzliche Metadaten.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public record ReadEnvelopeReceiverQuery : EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>, IRequest<IEnumerable<EnvelopeReceiverDto>>
|
public record ReadEnvelopeReceiverQuery : EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>, IRequest<IEnumerable<EnvelopeReceiverDto>>;
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Optionaler Benutzernamefilter, um Ergebnisse auf Umschläge eines bestimmten Besitzers einzuschränken.
|
|
||||||
/// </summary>
|
|
||||||
public string? Username { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -67,11 +61,10 @@ public static class Extensions
|
|||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string key, CancellationToken cancel = default)
|
public static Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string key, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var q = new ReadEnvelopeReceiverQuery() { Key = key };
|
var q = new ReadEnvelopeReceiverQuery() { Key = key };
|
||||||
var envRcvs = await mediator.Send(q, cancel);
|
return mediator.Send(q, cancel).Then(envRcvs => envRcvs.FirstOrDefault());
|
||||||
return envRcvs.FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -82,82 +75,80 @@ public static class Extensions
|
|||||||
/// <param name="signature"></param>
|
/// <param name="signature"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static async Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string uuid, string signature, CancellationToken cancel = default)
|
public static Task<EnvelopeReceiverDto?> ReadEnvelopeReceiverAsync(this IMediator mediator, string uuid, string signature, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
var q = new ReadEnvelopeReceiverQuery();
|
var q = new ReadEnvelopeReceiverQuery();
|
||||||
q.Envelope.Uuid = uuid;
|
q.Envelope.Uuid = uuid;
|
||||||
q.Receiver.Signature = signature;
|
q.Receiver.Signature = signature;
|
||||||
var envRcvs = await mediator.Send(q, cancel);
|
return mediator.Send(q, cancel).Then(envRcvs => envRcvs.FirstOrDefault());
|
||||||
return envRcvs.FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class ReadEnvelopeReceiverQueryHandler : IRequestHandler<ReadEnvelopeReceiverQuery, IEnumerable<EnvelopeReceiverDto>>
|
||||||
|
{
|
||||||
|
private readonly IRepository<EnvelopeReceiver> _repo;
|
||||||
|
|
||||||
|
private readonly IRepository<Receiver> _rcvRepo;
|
||||||
|
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verarbeitet <see cref="ReadEnvelopeReceiverQuery"/> und liefert passende <see cref="EnvelopeReceiverDto"/>-Ergebnisse.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ReadEnvelopeReceiverQueryHandler : IRequestHandler<ReadEnvelopeReceiverQuery, IEnumerable<EnvelopeReceiverDto>>
|
/// <param name="envelopeReceiver"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
public ReadEnvelopeReceiverQueryHandler(IRepository<EnvelopeReceiver> envelopeReceiver, IRepository<Receiver> rcvRepo, IMapper mapper)
|
||||||
{
|
{
|
||||||
private readonly IRepository<EnvelopeReceiver> _repo;
|
_repo = envelopeReceiver;
|
||||||
private readonly IRepository<Receiver> _rcvRepo;
|
_mapper = mapper;
|
||||||
private readonly IMapper _mapper;
|
_rcvRepo = rcvRepo;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiver"></param>
|
|
||||||
/// <param name="rcvRepo"></param>
|
|
||||||
/// <param name="mapper"></param>
|
|
||||||
public ReadEnvelopeReceiverQueryHandler(IRepository<EnvelopeReceiver> envelopeReceiver, IRepository<Receiver> rcvRepo, IMapper mapper)
|
|
||||||
{
|
|
||||||
_repo = envelopeReceiver;
|
|
||||||
_mapper = mapper;
|
|
||||||
_rcvRepo = rcvRepo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancel"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BadRequestException"></exception>
|
|
||||||
public async Task<IEnumerable<EnvelopeReceiverDto>> Handle(ReadEnvelopeReceiverQuery request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var q = _repo.Query.Where(request, notnull: false);
|
|
||||||
|
|
||||||
if (request.Username is string username)
|
|
||||||
q = q.Where(er => er.Envelope!.User.Username == username);
|
|
||||||
|
|
||||||
if (request.Envelope.Status is not null)
|
|
||||||
{
|
|
||||||
var status = request.Envelope.Status;
|
|
||||||
if (status.Min is not null)
|
|
||||||
q = q.Where(er => er.Envelope!.Status >= status.Min);
|
|
||||||
|
|
||||||
if (status.Max is not null)
|
|
||||||
q = q.Where(er => er.Envelope!.Status <= status.Max);
|
|
||||||
|
|
||||||
if (status.Include?.Length > 0)
|
|
||||||
q = q.Where(er => status.Include.Contains(er.Envelope!.Status));
|
|
||||||
|
|
||||||
if (status.Ignore is not null)
|
|
||||||
q = q.Where(er => !status.Ignore.Contains(er.Envelope!.Status));
|
|
||||||
}
|
|
||||||
|
|
||||||
var envRcvs = await q
|
|
||||||
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
|
|
||||||
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
|
|
||||||
.Include(er => er.Envelope).ThenInclude(e => e!.User)
|
|
||||||
.Include(er => er.Receiver)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
if (request.Receiver.HasAnyCriteria && envRcvs.Count != 0)
|
|
||||||
{
|
|
||||||
var receiver = await _rcvRepo.Query.Where(request.Receiver).FirstAsync(cancel);
|
|
||||||
|
|
||||||
foreach (var envRcv in envRcvs)
|
|
||||||
envRcv.Envelope?.Documents?.FirstOrDefault()?.Elements?.RemoveAll(s => s.ReceiverId != receiver.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _mapper.Map<List<EnvelopeReceiverDto>>(envRcvs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="BadRequestException"></exception>
|
||||||
|
public async Task<IEnumerable<EnvelopeReceiverDto>> Handle(ReadEnvelopeReceiverQuery request, CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var q = _repo.ReadOnly().Where(request, notnull: false);
|
||||||
|
|
||||||
|
if (request.Envelope.Status is not null)
|
||||||
|
{
|
||||||
|
var status = request.Envelope.Status;
|
||||||
|
if (status.Min is not null)
|
||||||
|
q = q.Where(er => er.Envelope!.Status >= status.Min);
|
||||||
|
|
||||||
|
if (status.Max is not null)
|
||||||
|
q = q.Where(er => er.Envelope!.Status <= status.Max);
|
||||||
|
|
||||||
|
if (status.Include?.Length > 0)
|
||||||
|
q = q.Where(er => status.Include.Contains(er.Envelope!.Status));
|
||||||
|
|
||||||
|
if (status.Ignore is not null)
|
||||||
|
q = q.Where(er => !status.Ignore.Contains(er.Envelope!.Status));
|
||||||
|
}
|
||||||
|
|
||||||
|
var envRcvs = await q
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
|
||||||
|
.Include(er => er.Envelope).ThenInclude(e => e!.User)
|
||||||
|
.Include(er => er.Receiver)
|
||||||
|
.ToListAsync(cancel);
|
||||||
|
|
||||||
|
if (request.Receiver.HasAnyCriteria && envRcvs.Any())
|
||||||
|
{
|
||||||
|
var receiver = await _rcvRepo.ReadOnly().Where(request.Receiver).FirstAsync(cancel);
|
||||||
|
|
||||||
|
foreach (var envRcv in envRcvs)
|
||||||
|
envRcv.Envelope?.Documents?.First().Elements.RemoveAll(s => s.ReceiverId != receiver.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mapper.Map<List<EnvelopeReceiverDto>>(envRcvs);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
using AutoMapper;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
||||||
using EnvelopeGenerator.Application.Receivers.Queries;
|
|
||||||
using MediatR;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
|
|
||||||
using EnvelopeGenerator.Application.Common.Query;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a query for reading an envelope receiver including sensitive fields
|
|
||||||
/// (access code, phone number) that are excluded from the standard <see cref="ReadEnvelopeReceiverQuery"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Returns a single <see cref="EnvelopeReceiverSecretDto"/> matched by UUID and receiver signature.
|
|
||||||
/// Equivalent to the legacy <c>ReadWithSecretByUuidSignatureAsync</c> service method.
|
|
||||||
/// </remarks>
|
|
||||||
public record ReadEnvelopeReceiverSecretQuery
|
|
||||||
: EnvelopeReceiverQueryBase<ReadEnvelopeQuery, ReadReceiverQuery>,
|
|
||||||
IRequest<EnvelopeReceiverSecretDto?>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for dispatching <see cref="ReadEnvelopeReceiverSecretQuery"/> via <see cref="IMediator"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static class ReadEnvelopeReceiverSecretQueryExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using the composite key (uuid::signature).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mediator">The mediator instance.</param>
|
|
||||||
/// <param name="key">Composite key in the format <c>uuid::signature</c>.</param>
|
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
|
||||||
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
|
|
||||||
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
|
|
||||||
this IMediator mediator,
|
|
||||||
string key,
|
|
||||||
CancellationToken cancel = default)
|
|
||||||
=> mediator.Send(new ReadEnvelopeReceiverSecretQuery { Key = key }, cancel);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a <see cref="ReadEnvelopeReceiverSecretQuery"/> using UUID and receiver signature.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mediator">The mediator instance.</param>
|
|
||||||
/// <param name="uuid">Envelope UUID.</param>
|
|
||||||
/// <param name="signature">Receiver signature.</param>
|
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
|
||||||
/// <returns>The matching <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if not found.</returns>
|
|
||||||
public static Task<EnvelopeReceiverSecretDto?> ReadEnvelopeReceiverSecretAsync(
|
|
||||||
this IMediator mediator,
|
|
||||||
string uuid,
|
|
||||||
string signature,
|
|
||||||
CancellationToken cancel = default)
|
|
||||||
{
|
|
||||||
var q = new ReadEnvelopeReceiverSecretQuery();
|
|
||||||
q.Envelope.Uuid = uuid;
|
|
||||||
q.Receiver.Signature = signature;
|
|
||||||
return mediator.Send(q, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles <see cref="ReadEnvelopeReceiverSecretQuery"/> and returns a
|
|
||||||
/// <see cref="EnvelopeReceiverSecretDto"/> containing sensitive fields.
|
|
||||||
/// </summary>
|
|
||||||
public class ReadEnvelopeReceiverSecretQueryHandler
|
|
||||||
: IRequestHandler<ReadEnvelopeReceiverSecretQuery, EnvelopeReceiverSecretDto?>
|
|
||||||
{
|
|
||||||
private readonly IRepository<EnvelopeReceiver> _repo;
|
|
||||||
private readonly IRepository<Receiver> _rcvRepo;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of <see cref="ReadEnvelopeReceiverSecretQueryHandler"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="envelopeReceiver">Repository for <see cref="EnvelopeReceiver"/>.</param>
|
|
||||||
/// <param name="rcvRepo">Repository for <see cref="Receiver"/>.</param>
|
|
||||||
/// <param name="mapper">AutoMapper instance.</param>
|
|
||||||
public ReadEnvelopeReceiverSecretQueryHandler(
|
|
||||||
IRepository<EnvelopeReceiver> envelopeReceiver,
|
|
||||||
IRepository<Receiver> rcvRepo,
|
|
||||||
IMapper mapper)
|
|
||||||
{
|
|
||||||
_repo = envelopeReceiver;
|
|
||||||
_rcvRepo = rcvRepo;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the query and returns the matching <see cref="EnvelopeReceiverSecretDto"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">The query containing filter criteria.</param>
|
|
||||||
/// <param name="cancel">Cancellation token.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// The matched <see cref="EnvelopeReceiverSecretDto"/>, or <c>null</c> if no record is found.
|
|
||||||
/// </returns>
|
|
||||||
public async Task<EnvelopeReceiverSecretDto?> Handle(
|
|
||||||
ReadEnvelopeReceiverSecretQuery request,
|
|
||||||
CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var q = _repo.Query.Where(request, notnull: false);
|
|
||||||
|
|
||||||
var envRcvs = await q
|
|
||||||
.Include(er => er.Envelope).ThenInclude(e => e!.Documents!).ThenInclude(d => d.Elements)
|
|
||||||
.Include(er => er.Envelope).ThenInclude(e => e!.Histories)
|
|
||||||
.Include(er => er.Envelope).ThenInclude(e => e!.User)
|
|
||||||
.Include(er => er.Receiver)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
if (request.Receiver.HasAnyCriteria && envRcvs.Count != 0)
|
|
||||||
{
|
|
||||||
var receiver = await _rcvRepo.Query.Where(request.Receiver).FirstAsync(cancel);
|
|
||||||
|
|
||||||
foreach (var item in envRcvs)
|
|
||||||
item.Envelope?.Documents?.FirstOrDefault()?.Elements?.RemoveAll(s => s.ReceiverId != receiver.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
var envRcv = envRcvs.FirstOrDefault();
|
|
||||||
if (envRcv is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return _mapper.Map<EnvelopeReceiverSecretDto>(envRcv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,6 +68,6 @@ public class ReceiverAlreadySignedQueryHandler : IRequestHandler<ReceiverAlready
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> Handle(ReceiverAlreadySignedQuery request, CancellationToken cancel = default)
|
public async Task<bool> Handle(ReceiverAlreadySignedQuery request, CancellationToken cancel = default)
|
||||||
{
|
{
|
||||||
return await _repo.Query.Where(request).Where(h => h.Status == EnvelopeStatus.DocumentSigned).AnyAsync(cancel);
|
return await _repo.ReadOnly().Where(request).Where(h => h.Status == EnvelopeStatus.DocumentSigned).AnyAsync(cancel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using DigitalData.Core.Exceptions;
|
||||||
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace EnvelopeGenerator.Application.EnvelopeReports;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public record ReadEnvelopeReportQuery(int EnvelopeId) : IRequest<IEnumerable<EnvelopeReport>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
[NotMapped]
|
||||||
|
public bool ThrowIfNotFound { get; init; } = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class ReadEnvelopeReportQueryExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="envelopeId"></param>
|
||||||
|
/// <param name="throwIfNotFound"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Task<IEnumerable<EnvelopeReport>> ReadEnvelopeReportAsync(this ISender sender, int envelopeId, bool throwIfNotFound = true, CancellationToken cancel = default)
|
||||||
|
=> sender.Send(new ReadEnvelopeReportQuery(envelopeId)
|
||||||
|
{
|
||||||
|
ThrowIfNotFound = throwIfNotFound
|
||||||
|
}, cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class ReadEnvelopeReportQueryHandler : IRequestHandler<ReadEnvelopeReportQuery, IEnumerable<EnvelopeReport>>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
private readonly IRepository<EnvelopeReport> _repo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="repo"></param>
|
||||||
|
/// <param name="mapper"></param>
|
||||||
|
public ReadEnvelopeReportQueryHandler(IRepository<EnvelopeReport> repo, IMapper mapper)
|
||||||
|
{
|
||||||
|
_repo = repo;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="cancel"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<IEnumerable<EnvelopeReport>> Handle(ReadEnvelopeReportQuery request, CancellationToken cancel = default)
|
||||||
|
{
|
||||||
|
var reports = await _repo.Where(r => r.EnvelopeId == request.EnvelopeId).ToListAsync(cancel);
|
||||||
|
var reportDtos = _mapper.Map<IEnumerable<EnvelopeReport>>(reports);
|
||||||
|
|
||||||
|
if(request.ThrowIfNotFound && !reportDtos.Any())
|
||||||
|
throw new NotFoundException($"EnvelopeReport with EnvelopeId '{request.EnvelopeId}' was not found.");
|
||||||
|
|
||||||
|
return reportDtos;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
|
||||||
using MediatR;
|
|
||||||
using AutoMapper;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.EnvelopeTypes.Queries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public record ReadEnvelopeTypesQuery : IRequest<IEnumerable<EnvelopeTypeDto>>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public class ReadEnvelopeTypesQueryHandler : IRequestHandler<ReadEnvelopeTypesQuery, IEnumerable<EnvelopeTypeDto>>
|
|
||||||
{
|
|
||||||
private readonly IRepository<EnvelopeType> _repository;
|
|
||||||
private readonly IMapper _mapper;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="repository"></param>
|
|
||||||
/// <param name="mapper"></param>
|
|
||||||
public ReadEnvelopeTypesQueryHandler(IRepository<EnvelopeType> repository, IMapper mapper)
|
|
||||||
{
|
|
||||||
_repository = repository;
|
|
||||||
_mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<IEnumerable<EnvelopeTypeDto>> Handle(ReadEnvelopeTypesQuery request, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var types = await _repository.Query.ToListAsync(cancellationToken);
|
|
||||||
return _mapper.Map<IEnumerable<EnvelopeTypeDto>>(types);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
@@ -12,44 +13,27 @@ namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
|
public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
|
||||||
{
|
{
|
||||||
public int? EnvelopeId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Der Titel des Umschlags. Dies ist ein Pflichtfeld.
|
/// Der Titel des Umschlags. Dies ist ein Pflichtfeld.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Title { get; set; }
|
[Required]
|
||||||
|
public required string Title { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Die Nachricht, die im Umschlag enthalten sein soll. Dies ist ein Pflichtfeld.
|
/// Die Nachricht, die im Umschlag enthalten sein soll. Dies ist ein Pflichtfeld.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Message { get; set; }
|
[Required]
|
||||||
|
public required string Message { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gibt an, ob die Zwei-Faktor-Authentifizierung für den Umschlag aktiviert ist. Standardmäßig false.
|
/// Gibt an, ob die Zwei-Faktor-Authentifizierung für den Umschlag aktiviert ist. Standardmäßig false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TFAEnabled { get; set; } = false;
|
public bool TFAEnabled { get; set; } = false;
|
||||||
|
|
||||||
public bool Send { get; init; }
|
|
||||||
|
|
||||||
public CreateEnvelopeDocumentDto? Document { get; init; }
|
|
||||||
|
|
||||||
public List<CreateEnvelopeReceiverDto> Receivers { get; init; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID des Absenders
|
/// ID des Absenders
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal int UserId { get; private set; }
|
public int UserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public CreateEnvelopeCommand WithAuth(int userId)
|
|
||||||
{
|
|
||||||
UserId = userId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines which component is used for envelope processing.
|
/// Determines which component is used for envelope processing.
|
||||||
@@ -60,14 +44,4 @@ public record CreateEnvelopeCommand : IRequest<EnvelopeDto?>
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public bool UseSQLExecutor { get; set; } = true;
|
public bool UseSQLExecutor { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record CreateEnvelopeDocumentDto(string DataAsBase64);
|
|
||||||
|
|
||||||
public record CreateEnvelopeFieldDto(double X, double Y, int Page);
|
|
||||||
|
|
||||||
public record CreateEnvelopeReceiverDto(
|
|
||||||
string EmailAddress,
|
|
||||||
string? Name,
|
|
||||||
string? PhoneNumber,
|
|
||||||
List<CreateEnvelopeFieldDto> Fields);
|
|
||||||
@@ -1,20 +1,10 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using EnvelopeGenerator.Application.Common.Configurations;
|
|
||||||
using EnvelopeGenerator.Application.Common.Extensions;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.Services;
|
|
||||||
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
using EnvelopeGenerator.Application.Common.Interfaces.SQLExecutor;
|
||||||
using EnvelopeGenerator.Application.Histories.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
||||||
|
|
||||||
@@ -29,26 +19,6 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
|
|||||||
|
|
||||||
private IRepository<Envelope> Repository => _provider.GetRequiredService<IRepository<Envelope>>();
|
private IRepository<Envelope> Repository => _provider.GetRequiredService<IRepository<Envelope>>();
|
||||||
|
|
||||||
private IRepository<Document> DocumentRepository => _provider.GetRequiredService<IRepository<Document>>();
|
|
||||||
|
|
||||||
private IRepository<EnvelopeReceiver> EnvelopeReceiverRepository => _provider.GetRequiredService<IRepository<EnvelopeReceiver>>();
|
|
||||||
|
|
||||||
private IRepository<Receiver> ReceiverRepository => _provider.GetRequiredService<IRepository<Receiver>>();
|
|
||||||
|
|
||||||
private IRepository<DocReceiverElement> DocReceiverElementRepository => _provider.GetRequiredService<IRepository<DocReceiverElement>>();
|
|
||||||
|
|
||||||
private IRepository<EmailTemplate> EmailTemplateRepository => _provider.GetRequiredService<IRepository<EmailTemplate>>();
|
|
||||||
|
|
||||||
private IRepository<EmailOut> EmailOutRepository => _provider.GetRequiredService<IRepository<EmailOut>>();
|
|
||||||
|
|
||||||
private MailParams MailParams => _provider.GetRequiredService<IOptions<MailParams>>().Value;
|
|
||||||
|
|
||||||
private DispatcherParams DispatcherParams => _provider.GetRequiredService<IOptions<DispatcherParams>>().Value;
|
|
||||||
|
|
||||||
private IConfigService ConfigService => _provider.GetRequiredService<IConfigService>();
|
|
||||||
|
|
||||||
private IMediator Mediator => _provider.GetRequiredService<IMediator>();
|
|
||||||
|
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -70,385 +40,10 @@ public class CreateEnvelopeCommandHandler : IRequestHandler<CreateEnvelopeComman
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<EnvelopeDto?> Handle(CreateEnvelopeCommand request, CancellationToken cancel)
|
public async Task<EnvelopeDto?> Handle(CreateEnvelopeCommand request, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var envelope = request.UseSQLExecutor
|
||||||
var title = request.Title?.Trim() ?? string.Empty;
|
? await Executor.CreateEnvelopeAsync(request.UserId, request.Title, request.Message, request.TFAEnabled, cancel)
|
||||||
var message = request.Message?.Trim() ?? string.Empty;
|
: await Repository.CreateAsync(request, cancel);
|
||||||
|
|
||||||
Envelope envelope;
|
|
||||||
if (request.EnvelopeId is int envelopeId)
|
|
||||||
{
|
|
||||||
envelope = await Repository.Query
|
|
||||||
.Include(e => e.User)
|
|
||||||
.FirstOrDefaultAsync(e => e.Id == envelopeId, cancel)
|
|
||||||
?? throw new BadRequestException($"Envelope {envelopeId} not found.");
|
|
||||||
|
|
||||||
if (envelope.UserId != request.UserId)
|
|
||||||
throw new ForbiddenException();
|
|
||||||
|
|
||||||
await Repository.UpdateAsync(e =>
|
|
||||||
{
|
|
||||||
e.Title = title;
|
|
||||||
e.Message = message;
|
|
||||||
e.TfaEnabled = request.TFAEnabled;
|
|
||||||
e.Status = EnvelopeStatus.EnvelopeSaved;
|
|
||||||
e.ChangedWhen = now;
|
|
||||||
}, e => e.Id == envelopeId, cancel);
|
|
||||||
|
|
||||||
envelope.Title = title;
|
|
||||||
envelope.Message = message;
|
|
||||||
envelope.TfaEnabled = request.TFAEnabled;
|
|
||||||
envelope.Status = EnvelopeStatus.EnvelopeSaved;
|
|
||||||
envelope.ChangedWhen = now;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
envelope = request.UseSQLExecutor
|
|
||||||
? await Executor.CreateEnvelopeAsync(request.UserId, title, message, request.TFAEnabled, cancel)
|
|
||||||
: await Repository.CreateAsync(new Envelope
|
|
||||||
{
|
|
||||||
UserId = request.UserId,
|
|
||||||
Status = EnvelopeStatus.EnvelopeCreated,
|
|
||||||
Uuid = Guid.NewGuid().ToString(),
|
|
||||||
Title = title,
|
|
||||||
Message = message,
|
|
||||||
AddedWhen = now,
|
|
||||||
ChangedWhen = now,
|
|
||||||
Comment = string.Empty,
|
|
||||||
Language = "de-DE",
|
|
||||||
TfaEnabled = request.TFAEnabled,
|
|
||||||
UseAccessCode = false,
|
|
||||||
SendReminderEmails = false
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
await Mediator.Send(new CreateHistoryCommand
|
|
||||||
{
|
|
||||||
EnvelopeId = envelope.Id,
|
|
||||||
UserReference = await ResolveSenderReferenceAsync(envelope, cancel),
|
|
||||||
Status = EnvelopeStatus.EnvelopeCreated
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
await UpsertDocumentAsync(envelope.Id, request.Document, now, cancel);
|
|
||||||
await UpsertReceiversAndFieldsAsync(envelope.Id, request.Receivers, now, cancel);
|
|
||||||
|
|
||||||
if (request.Send)
|
|
||||||
await SendEnvelopeAsync(envelope, cancel);
|
|
||||||
|
|
||||||
envelope = await Repository.Query
|
|
||||||
.Include(e => e.User)
|
|
||||||
.Include(e => e.EnvelopeReceivers)
|
|
||||||
.ThenInclude(er => er.Receiver)
|
|
||||||
.FirstAsync(e => e.Id == envelope.Id, cancel);
|
|
||||||
|
|
||||||
return _mapper.Map<EnvelopeDto>(envelope);
|
return _mapper.Map<EnvelopeDto>(envelope);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private async Task<string> ResolveSenderReferenceAsync(Envelope envelope, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(envelope.User?.Email))
|
|
||||||
return envelope.User.Email;
|
|
||||||
|
|
||||||
var senderEmail = await Repository.Query
|
|
||||||
.Where(e => e.Id == envelope.Id)
|
|
||||||
.Select(e => e.User.Email)
|
|
||||||
.FirstOrDefaultAsync(cancel);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(senderEmail))
|
|
||||||
throw new BadRequestException("Envelope sender email is missing.");
|
|
||||||
|
|
||||||
return senderEmail;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpsertDocumentAsync(int envelopeId, CreateEnvelopeDocumentDto? document, DateTime now, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if (document is null || string.IsNullOrWhiteSpace(document.DataAsBase64))
|
|
||||||
return;
|
|
||||||
|
|
||||||
byte[] bytes;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytes = Convert.FromBase64String(document.DataAsBase64);
|
|
||||||
}
|
|
||||||
catch (FormatException)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Document payload is not valid base64.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingDocumentId = await DocumentRepository.Query
|
|
||||||
.Where(d => d.EnvelopeId == envelopeId)
|
|
||||||
.Select(d => (int?)d.Id)
|
|
||||||
.FirstOrDefaultAsync(cancel);
|
|
||||||
|
|
||||||
if (existingDocumentId is null)
|
|
||||||
{
|
|
||||||
await DocumentRepository.CreateAsync(new Document
|
|
||||||
{
|
|
||||||
EnvelopeId = envelopeId,
|
|
||||||
AddedWhen = now,
|
|
||||||
ByteData = bytes,
|
|
||||||
Filename = string.Empty,
|
|
||||||
Filepath = string.Empty,
|
|
||||||
FileNameOriginal = string.Empty
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await DocumentRepository.UpdateAsync(d =>
|
|
||||||
{
|
|
||||||
d.ByteData = bytes;
|
|
||||||
}, d => d.Id == existingDocumentId.Value, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpsertReceiversAndFieldsAsync(int envelopeId, IReadOnlyCollection<CreateEnvelopeReceiverDto> receivers, DateTime now, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var filteredReceivers = receivers
|
|
||||||
.Where(r => !string.IsNullOrWhiteSpace(r.EmailAddress))
|
|
||||||
.Select(r => r with
|
|
||||||
{
|
|
||||||
EmailAddress = r.EmailAddress.Trim().ToLowerInvariant(),
|
|
||||||
Name = r.Name?.Trim(),
|
|
||||||
PhoneNumber = string.IsNullOrWhiteSpace(r.PhoneNumber) ? null : r.PhoneNumber.Trim(),
|
|
||||||
Fields = r.Fields ?? new List<CreateEnvelopeFieldDto>()
|
|
||||||
})
|
|
||||||
.GroupBy(r => r.EmailAddress, StringComparer.OrdinalIgnoreCase)
|
|
||||||
.Select(g => g.First())
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (filteredReceivers.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var document = await DocumentRepository.Query
|
|
||||||
.FirstOrDefaultAsync(d => d.EnvelopeId == envelopeId, cancel);
|
|
||||||
|
|
||||||
if (document is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var currentLinks = await EnvelopeReceiverRepository.Query
|
|
||||||
.Include(er => er.Receiver)
|
|
||||||
.Where(er => er.EnvelopeId == envelopeId)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
var linksByEmail = currentLinks
|
|
||||||
.Where(er => !string.IsNullOrWhiteSpace(er.Receiver?.EmailAddress))
|
|
||||||
.GroupBy(er => er.Receiver!.EmailAddress, StringComparer.OrdinalIgnoreCase)
|
|
||||||
.ToDictionary(g => g.Key.ToLowerInvariant(), g => g.First(), StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var sequence = currentLinks.Count > 0 ? currentLinks.Max(er => er.Sequence) : 0;
|
|
||||||
|
|
||||||
foreach (var receiverDraft in filteredReceivers)
|
|
||||||
{
|
|
||||||
var linkResult = await GetOrCreateEnvelopeReceiverAsync(envelopeId, receiverDraft, linksByEmail, now, sequence, cancel);
|
|
||||||
var link = linkResult.Link;
|
|
||||||
sequence = linkResult.NextSequence;
|
|
||||||
|
|
||||||
await DocReceiverElementRepository.DeleteAsync(query => query
|
|
||||||
.Where(e => e.DocumentId == document.Id)
|
|
||||||
.Where(e => e.ReceiverId == link.ReceiverId), cancel);
|
|
||||||
|
|
||||||
foreach (var field in receiverDraft.Fields.Where(f => f.Page > 0))
|
|
||||||
{
|
|
||||||
await DocReceiverElementRepository.CreateAsync(new DocReceiverElement
|
|
||||||
{
|
|
||||||
DocumentId = document.Id,
|
|
||||||
ReceiverId = link.ReceiverId,
|
|
||||||
ElementType = 1,
|
|
||||||
X = field.X,
|
|
||||||
Y = field.Y,
|
|
||||||
Width = 1.77,
|
|
||||||
Height = 1.96,
|
|
||||||
Page = field.Page,
|
|
||||||
Required = true,
|
|
||||||
ReadOnly = false,
|
|
||||||
AnnotationIndex = 0,
|
|
||||||
AddedWhen = now,
|
|
||||||
FullName = link.Name,
|
|
||||||
Position = string.Empty,
|
|
||||||
Place = string.Empty,
|
|
||||||
Tooltip = string.Empty
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<(EnvelopeReceiver Link, int NextSequence)> GetOrCreateEnvelopeReceiverAsync(
|
|
||||||
int envelopeId,
|
|
||||||
CreateEnvelopeReceiverDto receiverDraft,
|
|
||||||
IDictionary<string, EnvelopeReceiver> linksByEmail,
|
|
||||||
DateTime now,
|
|
||||||
int sequence,
|
|
||||||
CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if (linksByEmail.TryGetValue(receiverDraft.EmailAddress, out var existingLink))
|
|
||||||
{
|
|
||||||
var nextName = string.IsNullOrWhiteSpace(receiverDraft.Name) ? existingLink.Name : receiverDraft.Name!;
|
|
||||||
var nextPhoneNumber = receiverDraft.PhoneNumber ?? existingLink.PhoneNumber;
|
|
||||||
|
|
||||||
await EnvelopeReceiverRepository.UpdateAsync(er =>
|
|
||||||
{
|
|
||||||
er.Name = nextName;
|
|
||||||
er.PhoneNumber = nextPhoneNumber;
|
|
||||||
er.ChangedWhen = now;
|
|
||||||
}, er => er.EnvelopeId == existingLink.EnvelopeId && er.ReceiverId == existingLink.ReceiverId, cancel);
|
|
||||||
|
|
||||||
existingLink.Name = nextName;
|
|
||||||
existingLink.PhoneNumber = nextPhoneNumber;
|
|
||||||
return (existingLink, sequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
var receiver = await ReceiverRepository.Query
|
|
||||||
.FirstOrDefaultAsync(r => r.EmailAddress != null && r.EmailAddress.ToLower() == receiverDraft.EmailAddress, cancel);
|
|
||||||
|
|
||||||
if (receiver is null)
|
|
||||||
{
|
|
||||||
receiver = await ReceiverRepository.CreateAsync(new Receiver
|
|
||||||
{
|
|
||||||
EmailAddress = receiverDraft.EmailAddress,
|
|
||||||
Signature = receiverDraft.EmailAddress.ToUpperInvariant().GetChecksum(),
|
|
||||||
AddedWhen = now,
|
|
||||||
TotpSecretkey = string.Empty,
|
|
||||||
TfaRegDeadline = null
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence++;
|
|
||||||
var link = await EnvelopeReceiverRepository.CreateAsync(new EnvelopeReceiver
|
|
||||||
{
|
|
||||||
EnvelopeId = envelopeId,
|
|
||||||
ReceiverId = receiver.Id,
|
|
||||||
Sequence = sequence,
|
|
||||||
Name = receiverDraft.Name ?? receiverDraft.EmailAddress,
|
|
||||||
JobTitle = string.Empty,
|
|
||||||
CompanyName = string.Empty,
|
|
||||||
PrivateMessage = string.Empty,
|
|
||||||
AccessCode = string.Empty,
|
|
||||||
AddedWhen = now,
|
|
||||||
ChangedWhen = now,
|
|
||||||
PhoneNumber = receiverDraft.PhoneNumber ?? string.Empty
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
link.Receiver = receiver;
|
|
||||||
linksByEmail[receiverDraft.EmailAddress] = link;
|
|
||||||
return (link, sequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var hasDocument = await DocumentRepository.Query
|
|
||||||
.AnyAsync(d => d.EnvelopeId == envelope.Id && d.ByteData != null && d.ByteData.Length > 0, cancel);
|
|
||||||
|
|
||||||
if (!hasDocument)
|
|
||||||
throw new BadRequestException("Cannot send envelope without a document.");
|
|
||||||
|
|
||||||
var links = await EnvelopeReceiverRepository.Query
|
|
||||||
.Include(er => er.Receiver)
|
|
||||||
.Where(er => er.EnvelopeId == envelope.Id)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
if (links.Count == 0)
|
|
||||||
throw new BadRequestException("Cannot send envelope without receivers.");
|
|
||||||
|
|
||||||
var receiverIds = links.Select(l => l.ReceiverId).Distinct().ToList();
|
|
||||||
var documentIds = await DocumentRepository.Query
|
|
||||||
.Where(d => d.EnvelopeId == envelope.Id)
|
|
||||||
.Select(d => d.Id)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
var receiverFieldCounts = await DocReceiverElementRepository.Query
|
|
||||||
.Where(e => documentIds.Contains(e.DocumentId))
|
|
||||||
.Where(e => receiverIds.Contains(e.ReceiverId))
|
|
||||||
.GroupBy(e => e.ReceiverId)
|
|
||||||
.Select(g => new { ReceiverId = g.Key, Count = g.Count() })
|
|
||||||
.ToDictionaryAsync(x => x.ReceiverId, x => x.Count, cancel);
|
|
||||||
|
|
||||||
var receiversWithoutFields = links
|
|
||||||
.Where(link => !receiverFieldCounts.TryGetValue(link.ReceiverId, out var count) || count <= 0)
|
|
||||||
.Select(link => link.Name)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (receiversWithoutFields.Count > 0)
|
|
||||||
throw new BadRequestException("Cannot send envelope because some receivers have no signature fields.");
|
|
||||||
|
|
||||||
var changedWhen = DateTime.Now;
|
|
||||||
await Repository.UpdateAsync(e =>
|
|
||||||
{
|
|
||||||
e.Status = EnvelopeStatus.EnvelopeQueued;
|
|
||||||
e.ChangedWhen = changedWhen;
|
|
||||||
}, e => e.Id == envelope.Id, cancel);
|
|
||||||
|
|
||||||
var userReference = await ResolveSenderReferenceAsync(envelope, cancel);
|
|
||||||
|
|
||||||
await Mediator.Send(new CreateHistoryCommand
|
|
||||||
{
|
|
||||||
EnvelopeId = envelope.Id,
|
|
||||||
UserReference = userReference,
|
|
||||||
Status = EnvelopeStatus.EnvelopeQueued
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
await QueueInvitationEmailsAsync(envelope, links, userReference, cancel);
|
|
||||||
|
|
||||||
envelope.Status = EnvelopeStatus.EnvelopeQueued;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task QueueInvitationEmailsAsync(
|
|
||||||
Envelope envelope,
|
|
||||||
IReadOnlyCollection<EnvelopeReceiver> links,
|
|
||||||
string senderReference,
|
|
||||||
CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var template = await EmailTemplateRepository.Query
|
|
||||||
.SingleOrDefaultAsync(x => x.Name == EmailTemplateType.DocumentReceived.ToString(), cancel)
|
|
||||||
?? throw new InvalidOperationException($"Email template not found: {EmailTemplateType.DocumentReceived}");
|
|
||||||
|
|
||||||
var signatureHost = await ConfigService.ReadDefaultSignatureHost();
|
|
||||||
var senderName = senderReference;
|
|
||||||
|
|
||||||
foreach (var link in links)
|
|
||||||
{
|
|
||||||
var receiver = link.Receiver;
|
|
||||||
if (receiver is null || string.IsNullOrWhiteSpace(receiver.EmailAddress) || string.IsNullOrWhiteSpace(receiver.Signature))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var envelopeKey = (envelope.Uuid, receiver.Signature).ToEnvelopeKey();
|
|
||||||
var documentLink = $"{signatureHost.TrimEnd('/')}/envelope/login/{Uri.EscapeDataString(envelopeKey)}";
|
|
||||||
|
|
||||||
var placeholders = new Dictionary<string, string>(MailParams.Placeholders)
|
|
||||||
{
|
|
||||||
["[NAME_RECEIVER]"] = link.Name ?? string.Empty,
|
|
||||||
["[NAME_SENDER]"] = senderName ?? string.Empty,
|
|
||||||
["[DOCUMENT_TITLE]"] = envelope.Title ?? string.Empty,
|
|
||||||
["[MESSAGE]"] = envelope.Message ?? string.Empty,
|
|
||||||
["[LINK_TO_DOCUMENT]"] = documentLink,
|
|
||||||
["[LINK_TO_DOCUMENT_TEXT]"] = documentLink[..Math.Min(40, documentLink.Length)] + ".."
|
|
||||||
};
|
|
||||||
|
|
||||||
var subject = ReplacePlaceHolders(template.Subject, placeholders);
|
|
||||||
var body = ReplacePlaceHolders(template.Body, placeholders);
|
|
||||||
|
|
||||||
var emailOut = new EmailOut
|
|
||||||
{
|
|
||||||
EmailAddress = receiver.EmailAddress,
|
|
||||||
EmailSubj = subject,
|
|
||||||
EmailBody = body,
|
|
||||||
AddedWhen = DateTime.Now,
|
|
||||||
AddedWho = DispatcherParams.AddedWho,
|
|
||||||
SendingProfile = DispatcherParams.SendingProfile,
|
|
||||||
ReminderTypeId = DispatcherParams.ReminderTypeId,
|
|
||||||
EmailAttmt1 = DispatcherParams.EmailAttmt1,
|
|
||||||
WfId = (int)EnvelopeStatus.MessageInvitationSent,
|
|
||||||
ReferenceString = receiver.EmailAddress,
|
|
||||||
ReferenceId = link.ReceiverId
|
|
||||||
};
|
|
||||||
|
|
||||||
await EmailOutRepository.CreateAsync(emailOut, cancel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ReplacePlaceHolders(string text, Dictionary<string, string> placeHolders)
|
|
||||||
{
|
|
||||||
foreach (var placeHolder in placeHolders)
|
|
||||||
text = text.Replace(placeHolder.Key, placeHolder.Value);
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
|
||||||
|
|
||||||
public record DeleteEnvelopeResultDto(int EnvelopeId, EnvelopeStatus AppliedStatus, bool IsNoOp);
|
|
||||||
|
|
||||||
public record DeleteEnvelopeCommand : IRequest<DeleteEnvelopeResultDto>
|
|
||||||
{
|
|
||||||
[Required]
|
|
||||||
public int EnvelopeId { get; init; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
[MinLength(1)]
|
|
||||||
public string Reason { get; init; } = string.Empty;
|
|
||||||
|
|
||||||
internal int UserId { get; private set; }
|
|
||||||
|
|
||||||
public DeleteEnvelopeCommand WithAuth(int userId)
|
|
||||||
{
|
|
||||||
UserId = userId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
using EnvelopeGenerator.Application.Common.Notifications.EnvelopeDeleted;
|
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
|
||||||
using DigitalData.Core.Exceptions;
|
|
||||||
using EnvelopeGenerator.Application.Histories.Commands;
|
|
||||||
using EnvelopeGenerator.Domain.Constants;
|
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace EnvelopeGenerator.Application.Envelopes.Commands;
|
|
||||||
|
|
||||||
public class DeleteEnvelopeCommandHandler : IRequestHandler<DeleteEnvelopeCommand, DeleteEnvelopeResultDto>
|
|
||||||
{
|
|
||||||
private readonly IRepository<Envelope> _envelopeRepository;
|
|
||||||
private readonly IMediator _mediator;
|
|
||||||
|
|
||||||
public DeleteEnvelopeCommandHandler(
|
|
||||||
IRepository<Envelope> envelopeRepository,
|
|
||||||
IMediator mediator)
|
|
||||||
{
|
|
||||||
_envelopeRepository = envelopeRepository;
|
|
||||||
_mediator = mediator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<DeleteEnvelopeResultDto> Handle(DeleteEnvelopeCommand request, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
var reason = request.Reason?.Trim();
|
|
||||||
if (string.IsNullOrWhiteSpace(reason))
|
|
||||||
throw new BadRequestException("Please provide a reason.");
|
|
||||||
|
|
||||||
var envelope = await _envelopeRepository.Query
|
|
||||||
.Include(e => e.User)
|
|
||||||
.Include(e => e.EnvelopeReceivers)
|
|
||||||
.ThenInclude(er => er.Receiver)
|
|
||||||
.FirstOrDefaultAsync(e => e.Id == request.EnvelopeId, cancel)
|
|
||||||
?? throw new NotFoundException($"Envelope {request.EnvelopeId} not found.");
|
|
||||||
|
|
||||||
if (envelope.UserId != request.UserId)
|
|
||||||
throw new ForbiddenException();
|
|
||||||
|
|
||||||
if (envelope.Status is EnvelopeStatus.EnvelopeDeleted or EnvelopeStatus.EnvelopeWithdrawn)
|
|
||||||
return new DeleteEnvelopeResultDto(envelope.Id, envelope.Status, IsNoOp: true);
|
|
||||||
|
|
||||||
var targetStatus = envelope.Status > EnvelopeStatus.EnvelopeSaved
|
|
||||||
? EnvelopeStatus.EnvelopeWithdrawn
|
|
||||||
: EnvelopeStatus.EnvelopeDeleted;
|
|
||||||
|
|
||||||
var changedWhen = DateTime.Now;
|
|
||||||
envelope.Status = targetStatus;
|
|
||||||
envelope.Comment = reason;
|
|
||||||
envelope.ChangedWhen = changedWhen;
|
|
||||||
|
|
||||||
await _envelopeRepository.UpdateAsync(e =>
|
|
||||||
{
|
|
||||||
e.Status = targetStatus;
|
|
||||||
e.Comment = reason;
|
|
||||||
e.ChangedWhen = changedWhen;
|
|
||||||
}, e => e.Id == envelope.Id, cancel);
|
|
||||||
|
|
||||||
var userReference = envelope.User?.Email;
|
|
||||||
if (string.IsNullOrWhiteSpace(userReference))
|
|
||||||
throw new BadRequestException("Envelope sender email is missing.");
|
|
||||||
|
|
||||||
await _mediator.Send(new CreateHistoryCommand
|
|
||||||
{
|
|
||||||
EnvelopeId = envelope.Id,
|
|
||||||
UserReference = userReference,
|
|
||||||
Status = targetStatus,
|
|
||||||
Comment = reason
|
|
||||||
}, cancel);
|
|
||||||
|
|
||||||
foreach (var envelopeReceiver in envelope.EnvelopeReceivers ?? Enumerable.Empty<EnvelopeReceiver>())
|
|
||||||
{
|
|
||||||
if (envelopeReceiver.Receiver is null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
await _mediator.Publish(new EnvelopeDeletedNotification
|
|
||||||
{
|
|
||||||
Envelope = envelope,
|
|
||||||
EnvelopeReceiver = envelopeReceiver,
|
|
||||||
Reason = reason,
|
|
||||||
SenderReference = userReference
|
|
||||||
}, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DeleteEnvelopeResultDto(envelope.Id, targetStatus, IsNoOp: false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ using EnvelopeGenerator.Application.Common.Query;
|
|||||||
using EnvelopeGenerator.Application.Common.Dto;
|
using EnvelopeGenerator.Application.Common.Dto;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using DigitalData.Core.Abstraction.Application.Repository;
|
using DigitalData.Core.Abstraction.Application.Repository;
|
||||||
|
using EnvelopeGenerator.Application.Common.Extensions;
|
||||||
using EnvelopeGenerator.Domain.Entities;
|
using EnvelopeGenerator.Domain.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -14,30 +15,15 @@ namespace EnvelopeGenerator.Application.Envelopes.Queries;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest<IEnumerable<EnvelopeDto>>
|
public record ReadEnvelopeQuery : EnvelopeQueryBase, IRequest<IEnumerable<EnvelopeDto>>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool OnlyActive { get; init; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public bool OnlyCompleted { get; init; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Abfrage des Include des Umschlags
|
/// Abfrage des Include des Umschlags
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EnvelopeStatusQuery? Status { get; init; }
|
public EnvelopeStatusQuery? Status { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optionaler Benutzerfilter; wenn gesetzt, werden nur Umschläge des Benutzers geladen.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal int? UserId { get; init; }
|
public bool? HasDocResult { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setzt den Benutzerkontext für die Abfrage.
|
|
||||||
/// </summary>
|
|
||||||
public ReadEnvelopeQuery Authorize(int userId) => this with { UserId = userId };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -93,22 +79,23 @@ public record EnvelopeStatusQuery
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verarbeitet <see cref="ReadEnvelopeQuery"/> und liefert passende <see cref="EnvelopeDto"/>-Ergebnisse.
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnumerable<EnvelopeDto>>
|
public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnumerable<EnvelopeDto>>
|
||||||
{
|
{
|
||||||
private readonly IRepository<Envelope> _repository;
|
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
private readonly IRepository<Envelope> _repo;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="repository"></param>
|
|
||||||
/// <param name="mapper"></param>
|
/// <param name="mapper"></param>
|
||||||
public ReadEnvelopeQueryHandler(IRepository<Envelope> repository, IMapper mapper)
|
/// <param name="repo"></param>
|
||||||
|
public ReadEnvelopeQueryHandler(IMapper mapper, IRepository<Envelope> repo)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
|
_repo = repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -117,43 +104,25 @@ public class ReadEnvelopeQueryHandler : IRequestHandler<ReadEnvelopeQuery, IEnum
|
|||||||
/// <param name="request"></param>
|
/// <param name="request"></param>
|
||||||
/// <param name="cancel"></param>
|
/// <param name="cancel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
public async Task<IEnumerable<EnvelopeDto>> Handle(ReadEnvelopeQuery request, CancellationToken cancel)
|
public async Task<IEnumerable<EnvelopeDto>> Handle(ReadEnvelopeQuery request, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var query = _repository.Query;
|
var envelopesQ = _repo.Query.Where(request, notnull: false);
|
||||||
|
|
||||||
if (request.UserId is int userId)
|
EnvelopeStatusQuery? statusQ = request.Status;
|
||||||
query = query.Where(e => e.UserId == userId);
|
bool? hasDocResult = request.HasDocResult;
|
||||||
|
|
||||||
if (request.Id is int id)
|
var filtered =
|
||||||
query = query.Where(e => e.Id == id);
|
from envelope in envelopesQ
|
||||||
|
where (statusQ == null || statusQ.Include == null || statusQ.Include.Contains(envelope.Status))
|
||||||
|
&& (statusQ == null || statusQ.Ignore == null || !statusQ.Ignore.Contains(envelope.Status))
|
||||||
|
&& (statusQ == null || statusQ.Min == null || envelope.Status > statusQ.Min)
|
||||||
|
&& (statusQ == null || statusQ.Max == null || envelope.Status < statusQ.Max)
|
||||||
|
&& (!hasDocResult.HasValue || (hasDocResult.Value ? envelope.DocResult != null : envelope.DocResult == null))
|
||||||
|
select envelope;
|
||||||
|
|
||||||
if (request.Uuid is string uuid)
|
var envelopes = await filtered.ToListAsync(cancel);
|
||||||
query = query.Where(e => e.Uuid == uuid);
|
|
||||||
|
|
||||||
if (request.Status is { } status)
|
|
||||||
{
|
|
||||||
if (status.Min is not null)
|
|
||||||
query = query.Where(e => e.Status >= status.Min);
|
|
||||||
if (status.Max is not null)
|
|
||||||
query = query.Where(e => e.Status <= status.Max);
|
|
||||||
if (status.Include?.Length > 0)
|
|
||||||
query = query.Where(e => status.Include.Contains(e.Status));
|
|
||||||
if (status.Ignore?.Length > 0)
|
|
||||||
query = query.Where(e => !status.Ignore.Contains(e.Status));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(request is { OnlyActive: true })
|
|
||||||
query = query.Where(e => Status.Active.Contains(e.Status));
|
|
||||||
|
|
||||||
if (request is { OnlyCompleted: true })
|
|
||||||
query = query.Where(e => Status.Completed.Contains(e.Status));
|
|
||||||
|
|
||||||
var envelopes = await query
|
|
||||||
.Include(e => e.Type)
|
|
||||||
.Include(e => e.EnvelopeReceivers).ThenInclude(er => er.Receiver)
|
|
||||||
.Include(e => e.Histories)
|
|
||||||
.ToListAsync(cancel);
|
|
||||||
|
|
||||||
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
|
return _mapper.Map<IEnumerable<EnvelopeDto>>(envelopes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user