Comunicare și semnare
Comunicarea cu clientul (și semnarea documentelor) trece prin patru sub-fluxuri decuplate: legarea unui chat Telegram prin token, partajarea unui document printr-un link public cu semnare MSign fără cont ASYDATA, butoanele cardului e-Factură și notificările in-app / web push. Pagina descrie pas cu pas actorii și stările.
Corespondentul vizual (swimlane) e pe board-ul FigJam: ASYDATA pe Figma.
- Firma de contabilitate — inițiază bind-ul, partajează documente, operează cardurile.
- ASYDATA (System) — WebApi + worker-ul Telegram + endpoint-urile publice.
- Clientul — primește link-uri, vizualizează și semnează (fără cont ASYDATA).
- Telegram — botul prin care vin cardurile și aprobările.
- MSign — semnătura electronică (prin Prasada).
(a) Bind chat Telegram prin token
Secțiune intitulată „(a) Bind chat Telegram prin token”Pentru a primi carduri într-un chat, firma leagă chat-ul printr-un token de o singură folosire.
- Firma generează un token (TTL 30 minute —
BindTokenTtl = TimeSpan.FromMinutes(30)înasydata-app-backend-dotnet/src/Asy.Infrastructure/Telegram/TelegramService.cs). - Utilizatorul deschide
t.me/<bot>?start={token}; worker-ul de polling consumă comanda și apeleazăRedeemBindTokenAsync, care creează/actualizează unTelegramChat(asydata-app-backend-dotnet/src/Asy.Domain/Telegram/TelegramChat.cs). - Un chat = o firmă:
TelegramChat.FirmIde obligatoriu și imuabil; un chat poate fi re-legat ulterior la altă firmă (rândul se înlocuiește), dar la un moment dat servește o singură firmă.
(b) Partajare document cu semnare publică
Secțiune intitulată „(b) Partajare document cu semnare publică”Orice document partajabil (ShareableDocumentType { Contract, Invoice, HrContract, Act, Offer }) poate fi
trimis printr-un link public. Entitatea e DocumentShare
(asydata-app-backend-dotnet/src/Asy.Domain/Sharing/DocumentShare.cs).
- Firma creează share-ul (
IDocumentShareService,asydata-app-backend-dotnet/src/Asy.Application/Sharing/IDocumentShareService.cs): unTokenlung URL-safe (~43 caractere base64url) și un email destinatar. Link-ul public are forma/share/{token}. Share-ul e activ câtRevokedAte null și nu a expirat (IsActive). - View public. Clientul deschide link-ul fără cont. Endpoint-ul public marchează
FirstViewedAt, bumpeazăLastViewedAtșiViewCountși scrie unDocumentEventShareViewed. - Semnare publică MSign — fără cont ASYDATA. Clientul semnează prin MSign; serviciul inițiază semnarea cu
InitiatedByUserIdnull (partenerul nu are cont). La callback-ul MSign reușit, se seteazăDocumentShare.SignedAt, plusSignerName/SignerIdnp/SignerOrgIdnodin certificat, și se scrie unDocumentEventSignedByPartner. Pentru contracte, se completeazăContract.SignedByPartnerAt. - Respingere. Clientul poate respinge documentul (
RejectedAt+RejectionReason,DocumentEventShareRejected) — stare terminală, distinctă de revocarea inițiată de firmă (RevokedAt).
Stările unui DocumentShare (câmpuri-timestamp, nu un enum):
| Câmp | Înțeles |
|---|---|
CreatedAt | link creat |
FirstViewedAt / LastViewedAt / ViewCount | audit de vizualizare |
SignedAt | semnat de client prin MSign |
RejectedAt / RejectionReason | respins de client (terminal) |
RevokedAt | revocat de firmă (terminal) |
ExpiresAt | expirare opțională |
DocumentEvent (asydata-app-backend-dotnet/src/Asy.Domain/Sharing/DocumentEvent.cs) e log-ul append-only al
acțiunilor pe document, cu DocumentEventKind { Created, Updated, LinesReplaced, PdfGenerated, FileUploaded, StatusChanged, ShareSent, ShareViewed, ShareRevoked, SignedByFirm, SignedByPartner, Terminated, ShareRejected }.
Acțiunile publice anonime populează ActorEmail, Ip, UserAgent (nu ActorUserId).
(c) Butoanele cardului e-Factură
Secțiune intitulată „(c) Butoanele cardului e-Factură”Cardul facturii (renderat de EfacturaCardRenderer) are butoane inline cu callback efactura:{invoiceId}:{action}:
- Aprobă / Respinge (
approve/decline) — aprobare internă, nu atinge SFS (vezi operare zilnică). În stare aprobată/respinsă, butonul devine un toggle (unset). - Vezi PDF (
pdf) — trimite PDF-ul ca răspuns la mesaj. - Vezi atașamentul (
attach) — dacă factura are atașament. - Semnează (MSign) — buton URL (nu callback), spre endpoint-ul care inițiază semnarea și redirecționează la MSign (zero mesaje noi). Ascuns la ciclu scurt, când cumpărătorul nu are ce semna.
- Plătește (
queue) — pune plata în coadă.
(d) Cereri de permisiune în Telegram
Secțiune intitulată „(d) Cereri de permisiune în Telegram”Când un utilizator declanșează o acțiune pentru care nu are drept, se creează un PermissionRequest
(asydata-app-backend-dotnet/src/Asy.Domain/Telegram/PermissionRequest.cs), cu state machine
PermissionRequestStatus { Pending, Approved, Denied, Expired }. Un card de aprobare apare în chat; un admin
apasă Aprobă/Refuză (Approve / Deny), iar evenimentele (PermissionApprovedEvent / PermissionDeniedEvent)
trec prin outbox → Kafka. Cererile expiră după ExpiresAt.
(e) Notificări in-app + web push
Secțiune intitulată „(e) Notificări in-app + web push”În paralel cu Telegram, evenimentele alimentează notificări:
- In-app.
Notification(asydata-app-backend-dotnet/src/Asy.Domain/Notifications/Notification.cs), cu unTypeîn formă"{domeniu}.{acțiune}"(ex."efactura.incoming"),Title/Body,Link(rută SPA, ex./facturi/{id}) șiReadAt. - Web push.
PushSubscription(asydata-app-backend-dotnet/src/Asy.Domain/Notifications/PushSubscription.cs) țineEndpoint, cheileP256dh/Auth(ECDH P-256) șiFailureCount(curățat la 404/410 de la serviciul de push). Un consumer web push trimite notificarea în browser pe baza acelorași evenimente.
Vezi notificări pentru detalii.
Principiul de decuplare
Secțiune intitulată „Principiul de decuplare”Niciun flux operațional nu apelează direct canalele de comunicare. Un eveniment de domeniu trece prin
outbox → Kafka firm.events → consumeri independenți (Telegram, web push). Vezi
flux event-driven.
- Operare zilnică — de unde vin cardurile e-Factură.
- Monetizare — partajarea conturilor de plată și a facturilor.
- Stări și lifecycle — enum-urile complete.
- Integrarea Telegram și MSign.