Provider-Specific Workflows¶
Complete documentation of all provider sync workflows with visual diagrams.
KPN SP16 Complete Sync¶
Complexity: High (4-command chain)
Duration: 3-8 minutes
Trigger: SyncProfileHandler routes by type
Flow Diagram¶
flowchart TD
Start[KpnSp16SyncCommand] --> Handler[KpnSp16SyncHandler]
Handler --> Queue{Check Queue}
Queue -->|Has pending| Import[KpnSp16ImportCommand]
Queue -->|No pending| Skip[Skip - nothing to import]
Import --> Process[Process import queue]
Process --> Puppet[PupeteerKpnSp16SyncCommand]
Puppet --> Browser[Launch Puppeteer]
Browser --> Login[Login to KPN Portal]
Login --> Download[Download CDR files]
Download --> Store[Store files locally]
Store --> Totals[DispatchTotalsCommand]
Totals --> Calc[CalculateTotalsCommand]
Calc --> Backup[DispatchTotalsBackupCommand]
Backup --> Done[SyncTask: COMPLETED]
Skip --> Done
style Start fill:#e3f2fd
style Puppet fill:#ffebee
style Done fill:#e8f5e9
Database Operations¶
Reads:
- kpn_import_queue - Pending import jobs
- sync_profile - Configuration
- customer - Customer details
Writes:
- call_detail_record - Imported CDRs (bulk insert)
- subscription - Subscription updates
- invoice - Generated invoices
- totals - Calculated billing
- totals_backup - Historical backup
Updates:
- kpn_import_queue.status → processed
- sync_task.status → completed
Chain Execution¶
$command = new KpnSp16ImportCommand($queueId);
$command->withChain([
new PupeteerKpnSp16SyncCommand($syncProfileId, $billingCycle),
new DispatchTotalsCommand($customerId, $billingCycle),
new DispatchTotalsBackupCommand($customerId, $billingCycle)
]);
Handler locations:
- KpnSp16SyncHandler.php:113
- KpnSp16ImportHandler.php
- PupeteerKpnSp16SyncHandler.php
- DispatchTotalsHandler.php
- DispatchTotalsBackupHandler.php
Vodafone Sync¶
Complexity: Medium (2-command chain)
Duration: 2-5 minutes
Trigger: SyncProfileHandler routes MY_VODAFONE type
Flow Diagram¶
sequenceDiagram
participant Router as SyncProfileHandler
participant Sync as VodafoneSyncCommand
participant Handler as VodafoneSyncHandler
participant Import as VodafoneImportService
participant Portal as Vodafone Portal
participant DB as Database
participant Totals as DispatchTotalsCommand
Router->>Sync: Dispatch
Sync->>Handler: Handle
Handler->>Import: handleImport()
Import->>Portal: Scrape data
Portal-->>Import: CDRs + Subscriptions
Import->>DB: Store data
Handler->>Totals: Dispatch with chain
Totals->>Handler: DispatchTotalsBackupCommand
Implementation¶
// VodafoneSyncHandler.php:119
public function __invoke(VodafoneSyncCommand $command) {
try {
$this->vodafoneImportService->handleImport(
$customer,
$billingCycle
);
} catch (SyncAuthenticationFailedException $e) {
$this->setStatus(STATUS_INVALID_CREDENTIALS);
return;
}
$totalsCommand = new DispatchTotalsCommand(
$customer->getId(),
$billingCycle,
false,
$syncProfile->getId()
);
$totalsCommand->withChain([
new DispatchTotalsBackupCommand(
$customer->getId(),
$billingCycle
)
]);
$this->commandBus->dispatch($totalsCommand);
if ($customer->canSendSyncEmail()) {
$this->syncManagementService
->sendSuccessfulSyncEmailToCustomer($customer);
}
}
Database Operations¶
Queue Processing:
- Table: vodafone_import_queue
- Status transitions: pending → processing → completed
Data Import: - CDRs: ~1,000-10,000 per sync - Subscriptions: ~10-100 updates - Devices: ~50-500 updates
T-Mobile Calvi Sync¶
Complexity: Medium (2-command chain)
Duration: 2-4 minutes
Trigger: SyncProfileHandler routes T_MOBILE type
Flow Diagram¶
flowchart LR
A[TMobileCalviSyncCommand] --> B[TMobileCalviSyncHandler]
B --> C[TMobileCalviImportService]
C --> D[Calvi API Client]
D --> E[T-Mobile Calvi Portal]
B --> F[DispatchTotalsCommand]
F --> G[CalculateTotalsCommand]
G --> H[DispatchTotalsBackupCommand]
style A fill:#e3f2fd
style E fill:#ffebee
style H fill:#e8f5e9
Special Features¶
Calvi Partner Handling: - Supports multi-partner structure - Calculates partner-level totals - Monthly scheduled calculation (18th of month)
Console Command:
KPN EEN Sync¶
Complexity: Medium (2-command chain)
Duration: 2-5 minutes
Trigger: SyncProfileHandler routes KPN_EEN type
Flow Diagram¶
graph TD
A[KpnEenSyncCommand] --> B[KpnEenSyncHandler]
B --> C[KPN EEN Platform API]
C --> D[Import CDRs]
D --> E[Import Subscriptions]
E --> F[DispatchTotalsCommand]
F --> G[DispatchTotalsBackupCommand]
style A fill:#e3f2fd
style C fill:#fff3e0
style G fill:#e8f5e9
Implementation¶
Handler: KpnEenSyncHandler.php:181
Chain:
$totalsCommand = new DispatchTotalsCommand($customerId, $billingCycle);
$totalsCommand->withChain([
new DispatchTotalsBackupCommand($customerId, $billingCycle)
]);
$this->commandBus->dispatch($totalsCommand);
Yielder (Odido) Sync¶
Complexity: Medium (2-command chain)
Duration: 2-6 minutes
Trigger: SyncProfileHandler routes YIELDER type
Flow Diagram¶
flowchart TD
Start[YielderSyncCommand] --> Handler[YielderSyncHandler]
Handler --> Import[YielderImportService]
Import --> API[Yielder/Odido API]
API --> Subs[Import Subscriptions]
API --> Usage[Import Usage Data]
Handler --> Totals[DispatchTotalsCommand]
Totals --> Backup[DispatchTotalsBackupCommand]
style Start fill:#e3f2fd
style API fill:#fff3e0
style Backup fill:#e8f5e9
Queue Handling¶
Console Command:
Purpose: Process Yielder/Odido SIM card activation queue
Database:
- Table: yielder_queue
- Operations: Activation, modification, cancellation
Routit Mobile Integration¶
Complexity: High (multi-phase)
Duration: Variable (depends on CDR volume)
Trigger: Manual or scheduled
Complete Flow¶
graph TB
subgraph "Phase 1: Request"
A[RoutitRequestCdrsCommand] --> B[Request CDRs from API]
B --> C{CDRs Ready?}
C -->|Yes| D[RoutitDownloadCdrsCommand]
C -->|No| E[Wait for next run]
end
subgraph "Phase 2: Download"
D --> F[Download CDR files]
F --> G[Store files locally]
G --> H[RoutitImportCdrsCommand]
end
subgraph "Phase 3: Process"
H --> I[Parse CSV files]
I --> J[Insert CDRs]
J --> K[Link to devices]
end
subgraph "Phase 4: Billing"
K --> L[RoutitInvoiceCommand]
L --> M[Generate invoice]
M --> N[CalculateTotalsCommand]
N --> O[BillingInsightTotalsCommand]
O --> P[SaveStatusCommand]
end
style A fill:#e3f2fd
style L fill:#fff3e0
style P fill:#e8f5e9
See detailed workflow: CDR Processing →
Common Pattern Across All Providers¶
All provider syncs follow this pattern:
graph LR
A[Sync Command] --> B[Import Service]
B --> C[External Provider]
C --> D[Store Data]
D --> E[Calculate Totals]
E --> F[Backup Totals]
F --> G[Update Status]
style A fill:#e3f2fd
style C fill:#ffebee
style G fill:#e8f5e9
Standard Chain:
1. Provider-specific sync
2. DispatchTotalsCommand (routes to calculation)
3. CalculateTotalsCommand OR CalculateTotalsRateplanCommand
4. DispatchTotalsBackupCommand (historical backup)
Provider Comparison¶
| Provider | Complexity | Chain Length | CDR Volume | Special Features |
|---|---|---|---|---|
| KPN SP16 | High | 4 commands | 10k-50k | Puppeteer scraping, import queue |
| Vodafone | Medium | 2 commands | 5k-20k | Queue processing |
| T-Mobile | Medium | 2 commands | 5k-15k | Calvi partner support |
| KPN EEN | Medium | 2 commands | 3k-10k | Platform API |
| Yielder | Medium | 2 commands | 2k-8k | Queue handling |
| Routit | High | 6-phase | 10k-30k | Mobile integration, multi-phase |
Error Handling Patterns¶
Authentication Failures¶
try {
$importService->handleImport($customer, $billingCycle);
} catch (SyncAuthenticationFailedException $e) {
$this->syncManagementService->setStatus(
$syncProfile,
$billingCycle,
SyncTask::STATUS_INVALID_CREDENTIALS,
$e->getMessage()
);
return;
}
Action: Sync stops, task marked as INVALID_CREDENTIALS, email sent
Invoice Not Available¶
catch (SyncInvoiceNotAvailableException $e) {
$this->syncManagementService->setStatus(
$syncProfile,
$billingCycle,
SyncTask::STATUS_INVOICE_NOT_YET_AVAILABLE,
$e->getMessage()
);
return;
}
Action: Task will retry on next cron run (5 minutes)
Retry Logic¶
catch (\Exception $e) {
$this->logger->error("Sync failed: {$e->getMessage()}");
if ($syncTask->getRetries() >= 5) {
// Calculate totals anyway after 5 failures
$this->dispatchTotals($customer, $billingCycle);
}
}
Action: Retries up to 5 times, then calculates with existing data
Monitoring Each Provider¶
-- Check sync status by provider
SELECT
sp.sync_type,
st.status,
COUNT(*) as count,
AVG(TIMESTAMPDIFF(SECOND, st.created_at, st.updated_at)) as avg_duration
FROM sync_task st
JOIN sync_profile sp ON st.sync_profile_id = sp.id
WHERE st.created_at > NOW() - INTERVAL 1 DAY
GROUP BY sp.sync_type, st.status;