6.6 KiB
6.6 KiB
sales_service Risk Points
Step 4 Event Outbox Status
Current state after Step 4:
sales_servicepublishes domain events to the sharedevent_outbox.- Publisher:
services/sales_service/event_publisher.py(SalesEventPublisher). - Registry:
services/sales_service/sales_events.py. - Events are appended before
session.commit(), in the same transaction as the business change. tenant_idis included in event envelope payload for every sales event.
Published MVP events:
lead.entered_crmlead.enrichment_completeddeal.stage_changeddeal.scenario_selecteddeal.next_action_scheduleddeal.closeddeal.lostdeal.woncommunication.startedcommunication.summary_createdmessage.receivedmessage.sentcall.receivedcall.completedoffer.createdoffer.sentoffer.acceptedoffer.rejecteddeal.conditions_confirmedcounterparty.completeddocument.createddocument.sentdocument.confirmeddocument.signedinvoice.createdinvoice.sentinvoice.overduepayment.receivedinvoice.paid
Remaining risks:
- No sales-specific consumers/handlers yet; outbox rows are ready for the shared dispatcher, but sales automation/analytics/post-sale reactions are future steps.
- The shared
event_outboxschema has no dedicatedtenant_id,actor_type,actor_id, orcausation_idcolumns. These values are stored insidepayload_json.payload. - Event payload schemas are registry constants plus tests, not yet formal JSON Schema files.
DEFAULT_TENANT_ID
Current state after Step 2:
- Production sales API flow no longer uses
DEFAULT_TENANT_ID. _tenant_id(actor)requires tenant context from JWT/legacy dev header.- Webhooks resolve tenant from auth/header or
tenant_integrationsprovider mapping. - Sales reads/writes are tenant-scoped.
Remaining risk:
- Tenant entity/settings are MVP-level, not full subscription/billing model.
- Webhook provider signature verification is still separate from tenant resolution.
stage_id Changes
Current state after Step 3:
sales_pipelinesandsales_pipeline_stagesexist.Deal.pipeline_idstores realpip_*public id.Deal.stage_idstores realpst_*public id.STAGE_LABELSis no longer the source of truth._apply_stage(...)resolves stable stage code to tenant/pipeline stage id and writesSalesStageHistoryRow.- Step 4 also publishes
deal.stage_changedtoevent_outbox.
Initial stage writes:
_create_lead_and_deal_for_contactresolves default stage codenew_qualified_leadto realpst_*id and records history._resolve_deal_for_inboundcreates inbound lead/deal, resolves real stage id and records history.create_leadcreates a lead and auto-deal, then records history with reasonlead.entered_crm.create_dealresolves stage code/id input to realpst_*id and records history with reasondeal.created.
Endpoint-driven stage changes:
POST /api/v1/leads/{lead_id}/enrichPOST /api/v1/deals/{deal_id}/change-stagePOST /api/v1/deals/{deal_id}/schedule-next-actionPOST /api/v1/deals/{deal_id}/closePOST /api/v1/deals/{deal_id}/escalatePOST /api/v1/deals/{deal_id}/communications/textPOST /api/v1/deals/{deal_id}/communications/voicePOST /api/v1/communications/{communication_id}/switch-channelPOST /api/v1/communications/{communication_id}/bind-external- Offer actions: create/send/accept/reject.
- Conditions upsert.
- Counterparty create/patch.
- Document create/send/confirm.
- Invoice create/send/mark-overdue.
- Payment webhook success/partial.
Risk:
- No transition validation/state machine yet.
- Stage customization is API/MVP-level; no UI/admin migration workflow for moving active deals between stages.
AutomationTask Creation
Central helper:
_schedule_task(session, deal, task_type, run_at, payload)createsSalesAutomationTaskRow.
Creation points:
| Code area | task_type |
|---|---|
_start_communication |
payload.next_action_type |
POST /api/v1/deals/{deal_id}/schedule-next-action |
payload.next_action_type |
POST /api/v1/communications/{communication_id}/summary |
payload.next_action_type |
POST /api/v1/communications/{communication_id}/switch-channel |
switch_to_{to_channel} |
POST /api/v1/invoices/{invoice_id}/send |
invoice_follow_up |
Task fields:
statusdefaults topending.retry_countdefaults to0.last_errorexists.- Index exists on
(run_at, status).
Risk:
- No worker/dispatcher found for pending tasks.
- No API found to list/update/execute/cancel/retry automation tasks.
- No overdue task handling found.
- Status values allow
running,completed,failed,canceled, but code only createspending.
Payment Webhook
Endpoint:
POST /api/v1/payments/webhook
Input:
- Required
deal_id. - Optional
invoice_id. - Optional
external_payment_id. payment_provider,amount,currency,status,paid_at,payment_method,failure_reason,metadata.
Current flow:
- Load deal by
payload.deal_id. - If
invoice_idis present, load invoice by id. - Create or update
SalesPaymentRowidempotently by(tenant_id, payment_provider, external_payment_id)when external id is provided. - If invoice exists and status is
success:- invoice ->
paid - invoice.paid_at set
- deal.status ->
won - deal.closed_at set
- deal.final_amount set to payment amount
- stage ->
won - ensure CRM customer
- invoice ->
- If invoice exists and status is
partial:- invoice ->
partially_paid - stage ->
partially_paid
- invoice ->
- If invoice exists and status is
failed:- only invoice.updated_at changes.
Risk:
- No provider signature verification.
- Payment idempotency depends on provider sending
external_payment_id. - Payment totals are not reconciled against invoice amount before marking paid.
- Endpoint still lacks provider-specific webhook signature authentication.
Notes
Current state:
SalesNoteRowandsales_notesexist.- No
SalesNoteIn/Outschemas found. - No notes API routes found.
SalesWorkspaceOutdoes not include notes.
Risk:
- Notes are table-only, not product-ready.
- System/user notes cannot be created or retrieved through current sales API.
Escalation
Current state:
POST /api/v1/deals/{deal_id}/escalatecreatesSalesEscalationRow.- It sets
deal.assigned_human_user_id. - It sets
deal.scenario_type = "custom_human_escalation". - It moves stage to
transferred_to_support.
Risk:
- No resolve/reassign API found.
- No integration with queue/routing SLA in sales-service.
- No status transition beyond initial
open.