Solutech Engineering

Email Integration LPO Reader

Comprehensive guide to the Email Integration LPO Reader system for automated email processing and client synchronization

Email Integration LPO Reader

The Email Integration LPO (Local Purchase Order) Reader system is a comprehensive solution that enables automatic processing and synchronization of email-based Local Purchase Orders between the Solutech Admin system and client applications. The system integrates with Microsoft Outlook/Azure Email services to read emails with LPO attachments and forwards them to the appropriate client systems.

Key Features

  • Automated Email Processing: Reads emails from configured mailboxes with LPO attachments
  • Client-Based Routing: Routes emails to appropriate clients based on sender/recipient configuration
  • Bulk Synchronization: Supports batch processing of email logs for efficient client synchronization
  • Error Handling: Comprehensive error tracking and retry mechanisms
  • Audit Trail: Complete logging of all email processing activities
  • Real-time Monitoring: API endpoints for monitoring email processing status
  • Flexible Configuration: Support for multiple email integration types and client configurations

System Architecture

The Email Integration LPO Reader system consists of several interconnected components:

graph TD
    A[Microsoft Outlook/Azure] --> B[MsOutlookController]
    B --> C[Email Processing Engine]
    C --> D[EmailIntegrationClientDetail]
    C --> E[LogEmailIntegrationClientSync]
    E --> F[SyncEmailLogsCommand]
    F --> G[Client Systems via SAT]
    H[Admin Dashboard] --> I[EmailIntegrationClientDetailsController]
    I --> D
    H --> J[LogEmailIntegrationClientSyncController]
    J --> E

Components Overview

  1. Email Reader: Connects to Microsoft Outlook/Azure to fetch emails
  2. Processing Engine: Processes emails, extracts attachments, and creates log entries
  3. Client Management: Manages client configurations for email routing
  4. Synchronization System: Handles bulk synchronization of processed emails to client systems
  5. Monitoring Dashboard: Provides real-time monitoring and management capabilities

Core Components

1. MsOutlookController

File: app/Http/Controllers/API/Internal/Integration/EmailIntegration/MsOutlookController.php

The main controller responsible for Microsoft Outlook/Azure email integration.

Key Methods

  • authenticate(Request $request): Handles OAuth authentication with Microsoft Azure
  • readLpoEmails(Request $request): Reads and processes LPO emails from the configured mailbox
  • viewLpoEmails(Request $request): Retrieves emails for viewing without processing
  • processLpoFromEmail(array $inbox): Core email processing logic
  • getUserMessages($client, $access_token, $today): Fetches messages from Microsoft Graph API

Authentication Flow

// OAuth authentication with Azure
$response = $client->post("https://login.microsoftonline.com/$tenant_id/oauth2/v2.0/token", [
    'form_params' => [
        'grant_type' => 'authorization_code',
        'client_id' => $this->lpo_details['client_id'],
        'client_secret' => $this->lpo_details['client_secret'],
        'scope' => $this->lpo_details['scope'],
        'code' => $request->code,
        'response_mode' => 'query',
        'response_type' => 'code',
    ]
]);

2. EmailIntegrationClientDetailsController

File: app/Http/Controllers/API/Internal/Integration/EmailIntegration/EmailIntegrationClientDetailsController.php

Manages client configurations for email integration.

Key Methods

  • store(StoreRequest $request): Creates new email integration client configurations
  • index(Request $request): Lists all client configurations with filtering
  • update(UpdateRequest $request, int $id): Updates existing configurations
  • sync(Request $request): Synchronizes configurations to client systems
  • syncFromSat(Request $request): Handles synchronization requests from client systems
  • deleteFromSat(Request $request): Handles deletion requests from client systems

Client Configuration Structure

[
    'email' => 'client@example.com',
    'email_integration_type' => 'lpo_reader',
    'client_sub_accounts_id' => 123,
    'status' => 'active',
    'sync_status' => 'pending',
    'synced_at' => null
]

3. LogEmailIntegrationClientSyncController

File: app/Http/Controllers/API/Internal/Integration/EmailIntegration/LogEmailIntegrationClientSyncController.php

Manages email processing logs and provides monitoring capabilities.

Key Methods

  • index(Request $request): Retrieves email processing logs with filtering and pagination
  • getClients(): Returns list of available clients
  • triggerSync(Request $request): Manually triggers email log synchronization

4. SyncEmailLogsCommand

File: app/Console/Commands/Integration/EmailIntegration/SyncEmailLogsCommand.php

Artisan command for synchronizing processed email logs to client systems.

Command Signature

php artisan email-integration:sync-email-logs 
    {--client-code= : Sync logs for a specific client code only} 
    {--bulk-size=15 : Number of logs to send in each bulk API request} 
    {--date= : Date range to sync (e.g., 2024-01-01 or 2024-01-01,2024-01-31). Default is today}

Key Features

  • Batch Processing: Processes logs in configurable batch sizes
  • Bulk Synchronization: Sends multiple logs in single API requests
  • Error Recovery: Fallback to individual sync on bulk failures
  • Progress Tracking: Real-time progress reporting
  • Date Range Support: Flexible date-based filtering

Usage Examples

# Sync today's records for all clients
php artisan email-integration:sync-email-logs

# Sync for specific client
php artisan email-integration:sync-email-logs --client-code=ABC123

# Sync specific date
php artisan email-integration:sync-email-logs --date=2024-01-15

# Sync date range
php artisan email-integration:sync-email-logs --date=2024-01-01,2024-01-31

# Custom bulk size
php artisan email-integration:sync-email-logs --bulk-size=25

# Combined options
php artisan email-integration:sync-email-logs --client-code=ABC123 --date=2024-01-15 --bulk-size=10

Email Processing Workflow

Complete Process Flow

sequenceDiagram
    participant Admin as Admin System
    participant Azure as Microsoft Azure
    participant Controller as MsOutlookController
    participant DB as Database
    participant Client as Client System

    Admin->>Controller: Trigger readLpoEmails
    Controller->>Azure: Authenticate & Get Access Token
    Azure-->>Controller: Access Token
    Controller->>Azure: Fetch Emails with Attachments
    Azure-->>Controller: Email Data
    Controller->>Controller: processLpoFromEmail
    Controller->>DB: Store LogEmailIntegrationClientSync
    Controller->>Client: Send LPO Data via SAT
    Client-->>Controller: Response
    Controller->>DB: Update Log Status

Email Processing Logic

  1. Authentication: Refresh OAuth token with Microsoft Azure
  2. Email Retrieval: Fetch emails from configured mailbox using Microsoft Graph API
  3. Filtering: Process only emails with allowed attachment types (PDF, octet-stream)
  4. Client Mapping: Match sender/recipient emails to configured clients
  5. Deduplication: Check for already processed messages
  6. Data Extraction: Extract email metadata and attachment information
  7. Storage: Store processing log in log_email_integration_client_syncs
  8. Client Notification: Send LPO data to client system via SAT transport
  9. Status Update: Update log with sync status and response

Supported Attachment Types

  • application/pdf
  • application/octet-stream (for specific configured emails)

Email Routing Logic

The system determines the target client using the following priority:

  1. Sender Email Match: Check if sender email is configured for any client
  2. Recipient Email Match: If sender not found, check recipient emails
  3. Fallback: Skip email if no matching client configuration found

Configuration

Azure/Outlook Configuration

Set the following environment variables in your .env file:

# Azure Configuration
AZURE_TENANT_ID=your-tenant-id
AZURE_MAIL_CLIENT_ID=your-client-id
AZURE_MAIL_CLIENT_SECRET=your-client-secret
AZURE_MAIL_REDIRECT_URI=your-redirect-uri
AZURE_MAIL_SCOPE=https://graph.microsoft.com/.default
AZURE_LPO_READER_EMAIL=lpo-reader@yourdomain.com

Application Configuration

Configure in config/azure.php:

return [
    'tenant_id' => env('AZURE_TENANT_ID'),
    'lpo_reader_email' => env('AZURE_LPO_READER_EMAIL'),
    'mail' => [
        'client_id' => env('AZURE_MAIL_CLIENT_ID'),
        'client_secret' => env('AZURE_MAIL_CLIENT_SECRET'),
        'redirect_uri' => env('AZURE_MAIL_REDIRECT_URI'),
        'scope' => env('AZURE_MAIL_SCOPE'),
    ],
];

Email Integration Types

Configure allowed email integration types:

const EMAIL_INTEGRATION_TYPE = [
    'LPO READER' => 'lpo_reader'
];

API Endpoints

Authentication Required Endpoints

All endpoints require auth:api middleware unless specified otherwise.

Client Configuration Management

GET /api/email-integration/index
POST /api/email-integration/store
PUT /api/email-integration/update/{id}
DELETE /api/email-integration/delete/{id}
GET /api/email-integration/clients

Synchronization

POST /api/email-integration/sync-to-sat

Log Management

GET /api/email-integration/logs
POST /api/email-integration/sync-logs

Client Middleware Endpoints

These endpoints use client middleware for external client access:

POST /api/email-integration/logs
POST /api/email-integration/sync-new-email-integration
POST /api/email-integration/sync-delete-email-integration

Azure Email Integration Endpoints

GET /api/v1/azure-email-integration/view-lpo-emails
GET /api/v1/azure-email-integration/message-logs
GET /api/v1/azure-email-integration/access-token
GET /api/v1/azure-email-integration/generate-auth-url
GET /api/v1/azure-email-integration/read-lpo-emails

API Examples

Create Email Integration Configuration

POST /api/email-integration/store
Content-Type: application/json
Authorization: Bearer {token}

{
    "email": "client@example.com",
    "email_integration_type": "lpo_reader",
    "client_sub_accounts_id": 123
}

Retrieve Email Logs

GET /api/email-integration/logs?page=1&per_page=10&search=subject&start_date=2024-01-01&end_date=2024-01-31
Authorization: Bearer {token}

Trigger Manual Sync

POST /api/email-integration/sync-logs
Content-Type: application/json
Authorization: Bearer {token}

{
    "client_code": "ABC123"
}

Database Schema

email_integration_client_details

Stores client configuration for email integration.

ColumnTypeDescription
idbigintPrimary key
client_sub_accounts_idbigintForeign key to client_sub_accounts
email_integration_typevarcharType of integration (e.g., 'lpo_reader')
emailvarcharEmail address for integration
added_bybigintForeign key to solutes (user who added)
statusenumActive/Inactive status
synced_attimestampLast sync timestamp
sync_statusvarcharSync status (pending/success/failed)
sync_error_messagetextError message if sync failed
request_jsonjsonRequest payload for sync
response_jsonjsonResponse from client system
created_attimestampRecord creation time
updated_attimestampRecord update time

log_email_integration_client_syncs

Stores processed email logs for client synchronization.

ColumnTypeDescription
idbigintPrimary key
statusvarcharProcessing status (pending/sent/failed)
message_idvarcharUnique email message ID
attachment_idbigintEmail attachment ID
client_sub_accounts_idbigintTarget client sub account
fromvarcharSender email address
totextRecipient email addresses
subjectvarcharEmail subject
bodytextEmail body content
sent_attimestampEmail sent timestamp
received_attimestampEmail received timestamp
typevarcharEmail type (inbox/sent)
error_messagetextError message if processing failed
request_payloadtextRequest sent to client
file_typetextAttachment content type
attachment_linktextAttachment content/link
synced_to_client_attimestampClient sync timestamp
client_sync_responsetextResponse from client system
created_attimestampRecord creation time
updated_attimestampRecord update time
deleted_attimestampSoft delete timestamp

Data Models

EmailIntegrationClientDetail

File: app/Models/Internal/Integration/EmailIntegration/EmailIntegrationClientDetail.php

Stores client configuration for email integration.

protected $fillable = [
    'client_sub_accounts_id',
    'email_integration_type',
    'email',
    'added_by',
    'status',
    'synced_at',
    'request_json',
    'response_json',
    'sync_status',
    'sync_error_message'
];

LogEmailIntegrationClientSync

File: app/Models/Internal/Integration/EmailIntegration/LogEmailIntegrationClientSync.php

Stores processed email logs for synchronization to client systems.

protected $fillable = [
    'status',
    'message_id',
    'attachment_id',
    'client_sub_accounts_id',
    'from',
    'to',
    'subject',
    'body',
    'sent_at',
    'received_at',
    'type',
    'error_message',
    'request_payload',
    'file_type',
    'attachment_link',
    'synced_to_client_at',
    'client_sync_response'
];

Error Handling

Error Types and Solutions

1. Authentication Errors

Symptoms: Failed OAuth token refresh, 401 responses from Microsoft Graph API

Handling:

  • Automatic token refresh using refresh token
  • Logging of authentication failures
  • Graceful degradation to prevent service disruption
public function authenticateUsingRefreshToken()
{
    try {
        $response = $client->post("https://login.microsoftonline.com/$this->tenant_id/oauth2/v2.0/token", [
            'form_params' => [
                'grant_type' => 'refresh_token',
                'refresh_token' => $IntegrationCredentials->refresh_token,
                // ... other parameters
            ]
        ]);
        
        if ($response->getStatusCode() == 200 || $response->getStatusCode() == 201) {
            // Update stored credentials
            $IntegrationCredentials->update($responseData);
            return $responseData['access_token'];
        }
    } catch (\Exception $e) {
        Log::error('Token refresh failed', ['error' => $e->getMessage()]);
        return false;
    }
}

2. Email Processing Errors

Symptoms: Failed email fetching, malformed email data, attachment processing errors

Handling:

  • Skip problematic emails and continue processing
  • Log detailed error information
  • Update email log status to 'failed'
try {
    $this->processLpoFromEmail($inbox);
} catch (\Throwable $th) {
    Log::channel('email-integration')->error('Email processing failed', [
        'error' => $th->getMessage(),
        'trace' => $th->getTraceAsString()
    ]);
}

Monitoring and Logging

Real-time Monitoring

Dashboard Endpoints

  1. Email Logs Overview: /api/email-integration/logs

    • Paginated list of all email processing logs
    • Filtering by date, client, status, and account
    • Search functionality across multiple fields
    • Supports date range filtering with start_date and end_date
  2. Manual Sync Trigger: /api/email-integration/sync-logs

    • Trigger synchronization for specific clients or all clients
    • Returns command output and execution status

Log Status Constants

The system uses predefined status constants for tracking email processing:

const STATUS = [
    'SUCCESS' => 'sent',      // Email successfully processed and ready for sync
    'PENDING' => 'pending',   // Email processing in progress
    'FAILED' => 'failed'      // Email processing failed
];

const TYPE = [
    'INBOX' => 'inbox',       // Incoming emails
    'SENT' => 'sent'          // Outgoing emails
];

Key Monitoring Fields

The LogEmailIntegrationClientSync model tracks comprehensive data for monitoring:

  • Processing Status: status (pending/sent/failed)
  • Sync Status: synced_to_client_at (NULL if not synced)
  • Error Tracking: error_message for failed operations
  • Client Response: client_sync_response (JSON) for sync results
  • Email Metadata: subject, from, to, body, attachments
  • Timing Data: sent_at, received_at, created_at, synced_to_client_at

Search and Filtering

The model includes a comprehensive search scope that searches across:

public function scopeSearch(Builder $query, string $search): Builder
{
    return $query->where(function($query) use($search) {
        $query->where('log_email_integration_client_syncs.from', 'like', "%$search%")
            ->orWhere('log_email_integration_client_syncs.to', 'like', "%$search%")
            ->orWhere('log_email_integration_client_syncs.subject', 'like', "%$search%")
            ->orWhere('log_email_integration_client_syncs.error_message', 'like', "%$search%")
            ->orWhere('client_accounts.name', 'like', "%$search%");
    });
}

Synchronization Command Monitoring

Command Execution Tracking

The SyncEmailLogsCommand provides detailed real-time monitoring during execution:

Starting email logs sync for client code: ABC123...
Using bulk size: 15 logs per API request
Date range: 2024-01-15 00:00:00 to 2024-01-15 23:59:59
Found 3 client(s) with pending email logs.

Processing logs for client: Client A (Code: ABC123)
  Batch 1: Processing 100 logs
    Bulk chunk 1: Success: 15, Failures: 0
    Bulk chunk 2: Success: 15, Failures: 0
  Batch 1 completed. Success: 85, Failures: 15

Client Client A completed. Total Success: 85, Total Failures: 15
Overall sync completed in 45 seconds. Total Success: 250, Total Failures: 25

Stuck Process Detection

The command includes sophisticated monitoring for stuck processes:

// Track previous batch to detect stuck processing
$previousBatchIds = [];
$consecutiveFailedAttempts = 0;
$maxFailedAttempts = 3; // Maximum number of consecutive failures before skipping

// Check if we're processing the same logs repeatedly (stuck)
$currentBatchIds = $pendingLogs->pluck('id')->toArray();
$isReprocessingSameBatch = !empty($previousBatchIds) &&
                           count($previousBatchIds) === count($currentBatchIds) &&
                           count(array_diff($previousBatchIds, $currentBatchIds)) === 0;

if ($isReprocessingSameBatch) {
    $consecutiveFailedAttempts++;
    
    if ($consecutiveFailedAttempts >= $maxFailedAttempts) {
        // Mark these logs as failed and skip
        LogEmailIntegrationClientSync::whereIn('id', $currentBatchIds)->update([
            'synced_to_client_at' => now(),
            'client_sync_response' => json_encode(['error' => 'Automatic skip after repeated failures'])
        ]);
    }
}

Performance Throttling

Built-in delays prevent API overwhelming:

// Small delay between batches to avoid overwhelming the API
if ($pendingLogs->count() === self::BATCH_SIZE) {
    usleep(100000); // 100ms delay
}

Logging Strategy

Structured Logging Channels

The system uses dedicated logging channels for different aspects:

  1. email-integration: Primary channel for email processing activities
  2. General logs: Application-wide tracking in tbl_logs table

Command-Level Logging

Log::channel('email-integration')->error('Error syncing email logs', [
    'error' => $e->getMessage(),
    'trace' => $e->getTraceAsString()
]);

Log::channel('email-integration')->error('Skipped stuck logs after repeated failures', [
    'client_account_id' => $client->client_account_id,
    'log_ids' => $currentBatchIds
]);

Log::channel('email-integration')->warning('Email log sync failed', [
    'log_id' => $log->id,
    'client_account_id' => $clientAccount->id,
    'client_name' => $clientAccount->name,
    'error' => $errorMessage,
    'response' => $response
]);

Database Activity Logging

All sync activities are logged to the tbl_logs table:

// Successful bulk sync
DB::table('tbl_logs')->insert([
    'log_content' => "Bulk email logs (" . count($logIds) . " logs) synced successfully to client {$clientAccount->name}. Log IDs: " . implode(',', $logIds) . ". Response: " . json_encode($response)
]);

// Individual sync success
DB::table('tbl_logs')->insert([
    'log_content' => "Email log {$log->id} synced successfully to client {$clientAccount->name}. Response: " . json_encode($response)
]);

// Sync failure
DB::table('tbl_logs')->insert([
    'log_content' => "Email log {$log->id} sync failed for client {$clientAccount->name}. Error: {$errorMessage}. Response: " . json_encode($response)
]);