Achiziție client
Achiziția unui client traversează un funnel cu lifecycle-uri separate: oportunitate CRM (CrmOpportunity),
ofertă comercială (Offer) și contract (Contract). Fiecare are propria stare; trecerea de la unul la
altul declanșează automatizări (creare partener, programare facturi recurente). Pagina descrie pas cu pas
cine ce face, cu stările exacte din cod.
Vezi și corespondentul vizual (swimlane) pe board-ul FigJam: ASYDATA pe Figma.
- Firma de contabilitate — rolul de vânzări / contabil care lucrează pipeline-ul (vezi actori și roluri).
- Clientul (prospect) — compania țintă, încă fără cont în ASYDATA.
- ASYDATA (System) — WebApi + serviciile de aplicație care aplică automatizările.
- MSign — semnătura electronică (prin Prasada), pentru contractul semnat de ambele părți.
Pas cu pas
Secțiune intitulată „Pas cu pas”1. Lead — oportunitate în CRM
Secțiune intitulată „1. Lead — oportunitate în CRM”Firma creează un deal în pipeline. Entitatea e CrmOpportunity (asydata-app-backend-dotnet/src/Asy.Domain/Crm/CrmOpportunity.cs).
Un deal se leagă fie de un client existent (RelationshipId), fie de un prospect rece prin
ProspectCompanyName — înainte să existe un partener în baza firmei.
StatusporneșteOpen(enumOpportunityStatus { Open, Won, Lost }).- Etapele vin din
pipeline_stages;Statusse ține sincron cu tipul etapei (serviciulIOpportunitiesService.MoveStageAsync, înasydata-app-backend-dotnet/src/Asy.Application/Crm/IOpportunitiesService.cs). Probabilityurmărește etapa cât timp nu e setată manual;WeightedValue = Value × Probability/100.
2. Ofertă — cotație de preț
Secțiune intitulată „2. Ofertă — cotație de preț”Pentru un lead, firma emite o ofertă. Entitatea e Offer (asydata-app-backend-dotnet/src/Asy.Domain/Offers/Offer.cs),
cu lifecycle propriu, separat de contract:
Stare (OfferStatus) | Înțeles | Tranziție |
|---|---|---|
Draft | În lucru, editabilă | implicit la creare |
Sent | Trimisă clientului | MarkSent |
Accepted | Acceptată de client | MarkAccepted |
Rejected | Respinsă de client | MarkRejected |
Expired | A depășit ValidUntil fără răspuns | — |
Oferta se face de obicei pentru un lead care nu e încă partener (ClientName, text liber).
Liniile se împart în recurente (OfferBillingType.Recurring, cu OfferRecurrence Monthly/Quarterly/Yearly)
și one-time, iar totalurile se recalculează pe cele două fluxuri (RecomputeTotals). Oferta poate fi
generată dintr-un DocumentTemplate, semnată MSign și trimisă prin Document Hub — la fel ca un contract
(vezi comunicare și semnare).
3. ConvertToContract — automatizarea cheie
Secțiune intitulată „3. ConvertToContract — automatizarea cheie”Când oferta e câștigată, firma o convertește în contract: IOffersService.ConvertToContractAsync
(asydata-app-backend-dotnet/src/Asy.Infrastructure/Offers/OffersService.cs). Aici se întâmplă mai multe
automatizări într-o singură tranzacție:
- Lead → partener. Dacă oferta nu are partener legat (
PartnerMasterIdnull), serviciul creează automat unPartnerMaster(dinClientName/Title) și unPartnerRelationshipfirmă↔master, cu conturi contabile implicite (DefaultAccountDb = "221.1",DefaultAccountCr = "611.1"). Așa lead-ul intră în baza de parteneri la conversie. - Creează contractul. Numerotat din Codificări (
CommercialContract, prefixCTR), cu liniile copiate din ofertă (recurente/one-time, cu recurența mapată). - Marchează oferta
Accepted(MarkAccepted) și completeazăConvertedToContractId— conversie idempotentă (a doua oară e respinsă).
4. Contract — semnare și activare
Secțiune intitulată „4. Contract — semnare și activare”Contractul (asydata-app-backend-dotnet/src/Asy.Domain/Contracts/Contract.cs) pornește Draft
(ContractStatus { Draft, Active, Suspended, Expired, Terminated }).
- Semnare. Contractul are două timestamp-uri de semnătură:
SignedByFirmAt(firma semnează prin MSign; setat de callback-ul MSign care identifică contractul) șiSignedByPartnerAt(partenerul semnează printr-un link public cu MSign — vezi comunicare și semnare). - Activare. Trecerea la
Activese face prinIContractsService.SetStatusAsync(asydata-app-backend-dotnet/src/Asy.Application/Contracts/Services/IContractsService.cs), care apeleazăContract.Activate()/Suspend()/Resume()/Terminate().
Automatizare — facturi recurente. Dacă contractul are cel puțin o linie recurentă, serviciul creează un
RecurringInvoiceSchedule (asydata-app-backend-dotnet/src/Asy.Domain/Contracts/RecurringInvoiceSchedule.cs),
1:1 cu contractul. Un worker scanează WHERE is_active AND next_due_date <= today și generează facturi draft
periodic, avansând NextDueDate cu un ciclu (AdvanceToNext).
5. Deal Won — companie activă
Secțiune intitulată „5. Deal Won — companie activă”La câștig, firma marchează deal-ul prin IOpportunitiesService.WinAsync (sau MoveStageAsync pe o etapă de
tip won): Status = Won, Probability = 100, WonAt setat, ValueBase înghețat. Dacă deal-ul era pe un
prospect rece, serviciul promovează prospectul la partener (PartnerMaster + PartnerRelationship Active) și
completează RelationshipId.
Compania client trece în LifecycleStage.Active (asydata-app-backend-dotnet/src/Asy.Domain/Companies/Company.cs,
enum LifecycleStage { Lead, Prospect, Onboarding, Active, Problematic, Churned }). Companiile client se creează
prin Company.CreateClient(...), care le pune inițial în Onboarding.
Ramura de respingere
Secțiune intitulată „Ramura de respingere”Dacă prospectul refuză, fluxul se închide simetric:
- Ofertă respinsă —
Offer.MarkRejected→OfferStatus.Rejected(sauExpireddacă a trecutValidUntil). - Deal pierdut —
IOpportunitiesService.LoseAsynccere obligatoriuLostReasonId; seteazăStatus = Lost,Probability = 0,LostAt, și opționalLostReasonNote. Motivele vin dinCrmLostReason(asydata-app-backend-dotnet/src/Asy.Domain/Crm/CrmLostReason.cs). - Partenerul, dacă exista, poate trece în
PartnerLifecycleStage.Churned(cuLostReasonpe relație).
Rezumat tranziții
Secțiune intitulată „Rezumat tranziții”Lead Ofertă Contract Deal / Companie─────────────────────────────────────────────────────────────────────────────────────Opportunity=Open → Offer Draft→Sent→Accepted → Contract Draft → Opportunity=Won │ (ConvertToContract) → semnat MSign Company.LifecycleStage │ + PartnerMaster → Active = Active │ + PartnerRelationship Active (+ RecurringInvoiceSchedule │ dacă are linii recurente) └─ Rejected / Expired ─────→ (deal Lost + LostReason)- Stări și lifecycle — referința consolidată a tuturor enum-urilor.
- Operare zilnică — ce urmează după ce clientul e activ.
- Monetizare — cum se facturează recurent serviciul.
- Integrarea MSign — fluxul de semnătură electronică.