darvisherp
Prompt
You are a Senior C# / .NET 8 Architect working on "DarvishERP", an Offline-First WPF desktop accounting system using local SQLite (WAL mode) and a 2-Way Sync engine with a central server to maintain an exact DB Mirror. Write a single, self-contained, production-ready C# file implementing `VoucherSyncCoordinator` that solves the "Doomed Local Voucher" scenario under strict Offline-First, 2-Way Sync, and DB Mirror rules: ### Scenario & Architecture Constraints: 1. **Offline-First & Outbox Pattern**: - A user creates or edits an Accounting Voucher (`VoucherId: Guid`, `VoucherNumber: string?`, `TotalAmount: decimal`, `RowVersion: long`, `SyncStatus: Pending | Synced | Quarantined`) while offline. - Local changes are saved atomically with an `OutboxEntry` (`OpId: Guid`, `EntityId: Guid`, `PayloadJson: string`, `BaseRowVersion: long`) in the same local SQLite transaction. 2. **The "Doomed Local Voucher" Trap (2-Way Sync Rejection)**: - During the Push phase, the server may permanently reject a local `OutboxEntry` with `ServerRejectReason.FiscalPeriodClosed` or `ServerRejectReason.AccountDeactivated` (HTTP 422 Business Rule Violation, NOT a transient 5xx network error). - **Rule A (No Data Loss / Offline-First)**: You MUST NOT silently delete the user's offline voucher work. - **Rule B (Strict DB Mirror)**: The active `Vouchers` table MUST mirror valid server state and must not remain permanently polluted by an un-syncable ("doomed") voucher or block subsequent valid `Outbox` items (head-of-line blocking). - **Rule C (Resolution)**: Atomically move or mark the doomed voucher into a local `QuarantinedVouchers` state/table (preserving the user's original edits + server error reason for manual recovery in WPF UI), revert the active row in `Vouchers` to the last known `ServerSnapshotJson` (if it was an edit) or remove it from active `Vouchers` (if it was a local-only insert), and mark the `OutboxEntry` as `DeadLettered` so the sync queue continues. 3. **2-Way Sync Pull Interleaving (Concurrency Trap)**: - During the Pull phase (Delta Pull using a `ServerWatermark: long`), if the server sends an update for a `VoucherId` that currently has a still-valid `Pending` local `OutboxEntry`, you must NOT overwrite the user's unsynced local changes in the active `Vouchers` table; instead, update only the stored `ServerSnapshotJson` baseline and flag a conflict if `ServerRowVersion > BaseRowVersion`. 4. **WPF Thread-Safety**: - The coordinator must prevent concurrent sync runs (`SemaphoreSlim`) and expose an `IProgress<SyncProgressReport>` (or event) safe for WPF UI binding without blocking the UI thread. ### Deliverable Requirements: - Provide ONLY valid, compilable C# (.NET 8) code using `Microsoft.Data.Sqlite` (raw SQL or lightweight Dapper-style commands, NO heavy EF Core magic that hides transactions). - Include the SQLite DDL (`CREATE TABLE` statements) inside an `InitializeSchemaAsync` method. - No placeholders like `// TODO` or `// implement logic here`. Every SQL transaction and edge case above must be fully written.