Skip to content

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.statusprocessed - sync_task.statuscompleted

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: pendingprocessingcompleted

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:

php bin/console calculate-totals:calvi-partners 2025-01-01


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:

# Runs every minute
php bin/console odido:handle:queue

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;

Next: CDR Processing Workflow →