008 Wts Setup Script

WTS Setup Script (Unified)

This document details the setup of the Windows Task Scheduler (WTS) for all ErpCrystal jobs using the Unified folder structure on the E: drive.

Overview

  • Deployment Path: E:\MFGReports\SchedulerLog
  • Setup Script: E:\MFGReports\SchedulerLog\setup-scheduler.ps1
  • Unified Wrapper: E:\MFGReports\SchedulerLog\run-job.ps1
  • Logs: E:\MFGReports\SchedulerLog\scheduler.log
  • Task Naming: ErpCrystal_<JobName>

Prerequisites

  1. PowerShell Administrator: Run scripts as Administrator.
  2. API Running: Ensure the API is accessible at http://localhost:5116.
  3. Permissions: Ensure the user account has rights to write to E:\.

Unified Setup Script (setup-scheduler.ps1)

This script configures all 7 jobs. It creates the directories, generates a single generic wrapper script (run-job.ps1), and registers the scheduled tasks to use it.

# ==========================================
# ErpCrystal Development Scheduler Setup (Unified)
# Target: E:\MFGReports\SchedulerLog
# ==========================================

$SchedulerDir = "E:\MFGReports\SchedulerLog"
$LogDir       = "E:\MFGReports\SchedulerLog"
$UserAccount  = "SYSTEM"
$BaseUrl      = "http://localhost:5116"
$ApiKey       = "ApiKey"

# --- CONFIGURATION ---
$UnifiedLogFile = "$LogDir\scheduler.log"
$GenericWrapper = "$SchedulerDir\run-job.ps1"

# --- JOB DEFINITIONS ---
# Available Jobs (Uncomment to enable):
# @{ Name = "ErpCrystal_InvoiceEmail";      Endpoint = "invoice-email";         Time = "09:30" },
# @{ Name = "ErpCrystal_ArEmail";           Endpoint = "ar-email";              Time = "10:30" },
# @{ Name = "ErpCrystal_VoucherPayment";    Endpoint = "voucher-payment-email"; Time = "11:00" },
# @{ Name = "ErpCrystal_VoucherReceipt";    Endpoint = "voucher-receipt-email"; Time = "11:30" },

$Jobs = @(
    @{ Name = "ErpCrystal_IndentEmail";       Endpoint = "indent-email";          Time = "11:34" },
    @{ Name = "ErpCrystal_SalesOrderEmail";   Endpoint = "sales-order-email";     Time = "11:37" },
    @{ Name = "ErpCrystal_AIInsights";        Endpoint = "ai-insights";           Time = "11:40" } 
)

# 1. Create Directories (Ignore errors if drive mapped differently in session, rely on script location)
if (-not (Test-Path $SchedulerDir)) {
    try { New-Item -ItemType Directory -Path $SchedulerDir -Force | Out-Null } catch {}
}

# 2. Create the Single Generic Wrapper Script
Write-Host "`nCreating Generic Wrapper: $GenericWrapper" -ForegroundColor Cyan

$WrapperContent = @"
# ======================================================
# ErpCrystal Generic Job Wrapper
# SYSTEM-safe, HTTPS-safe, localhost-safe
# ======================================================

param(
    [string]`$EndpointSuffix,
    [string]`$TaskName
)

`$ErrorActionPreference = "Stop"

# ---------- CONFIG ----------
`$BaseUrl = "$BaseUrl"
`$ApiKey  = "$ApiKey"
`$LogFile = "$UnifiedLogFile"

`$FullEndpoint = "`$BaseUrl/api/jobs/`$EndpointSuffix"

# ---------- HELPERS ----------
function Log(`$msg) {
    `$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    "`$timestamp | [`$TaskName] | `$msg" | Out-File -FilePath `$LogFile -Append -Encoding UTF8
}

Log "STARTING..."
Log "User: `$(whoami) | API: `$FullEndpoint"

try {
    # ---- LOAD REQUIRED ASSEMBLY ----
    Add-Type -AssemblyName System.Net.Http

    # ---- TLS ----
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

    # ---- HTTP CLIENT ----
    `$handler = New-Object System.Net.Http.HttpClientHandler
    `$handler.ServerCertificateCustomValidationCallback = { `$true }

    `$client = New-Object System.Net.Http.HttpClient(`$handler)
    `$client.Timeout = [TimeSpan]::FromMinutes(10)

    # FORCE HTTP/1.1
    `$request = New-Object System.Net.Http.HttpRequestMessage ``
        ([System.Net.Http.HttpMethod]::Post, `$FullEndpoint)
    `$request.Version = [Version]::Parse("1.1")
    `$request.Headers.Add("ApiKey", `$ApiKey)

    `$response = `$client.SendAsync(`$request).GetAwaiter().GetResult()

    if (-not `$response.IsSuccessStatusCode) {
        throw "HTTP `$(`$response.StatusCode) - `$(`$response.ReasonPhrase)"
    }

    `$body = `$response.Content.ReadAsStringAsync().GetAwaiter().GetResult()
    
    # Truncate body for log if too long
    `$logBody = if (`$body.Length -gt 500) { `$body.Substring(0, 500) + "..." } else { `$body }
    
    Log "SUCCESS: `$logBody"
    exit 0
}
catch {
    Log "ERROR: `$_.Exception.Message"
    exit 1
}
finally {
    if (`$client) { `$client.Dispose() }
}
"@

$WrapperContent | Out-File -FilePath $GenericWrapper -Encoding UTF8 -Force


# 3. Register Each Task
foreach ($Job in $Jobs) {
    $TaskName = $Job.Name
    $EndpointSuffix = $Job.Endpoint
    $JobTime = $Job.Time
    
    Write-Host "Registering Task: $TaskName ($JobTime)"

    $Action = New-ScheduledTaskAction `
        -Execute "powershell.exe" `
        -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$GenericWrapper`" -EndpointSuffix `"$EndpointSuffix`" -TaskName `"$TaskName`""

    $Trigger = New-ScheduledTaskTrigger -Daily -At $JobTime

    $Settings = New-ScheduledTaskSettingsSet `
        -AllowStartIfOnBatteries `
        -DontStopIfGoingOnBatteries `
        -StartWhenAvailable `
        -MultipleInstances IgnoreNew `
        -RestartCount 3 `
        -RestartInterval (New-TimeSpan -Minutes 1)

    try {
        Register-ScheduledTask `
            -Action $Action `
            -Trigger $Trigger `
            -Settings $Settings `
            -TaskName $TaskName `
            -Description "ErpCrystal Job: $TaskName" `
            -User $UserAccount `
            -Force | Out-Null
            
        Write-Host "  -> OK" -ForegroundColor Green
    }
    catch {
        Write-Error "  -> FAILED: $_"
    }
}

Write-Host "`nAll Jobs Configured Successfully." -ForegroundColor Green

Quickly Updating Task Times

To update the scheduled times without re-running the full setup script (and potentially overwriting the wrapper), run the following command in PowerShell as Administrator:

# Define your new times here
$Updates = @{
    "ErpCrystal_IndentEmail"     = "11:47"
    "ErpCrystal_SalesOrderEmail" = "11:50"
    "ErpCrystal_AIInsights"      = "11:45"
}

foreach ($TaskName in $Updates.Keys) {
    $Time = $Updates[$TaskName]
    Write-Host "Updating $TaskName to $Time..."
    
    $Trigger = New-ScheduledTaskTrigger -Daily -At $Time
    Set-ScheduledTask -TaskName $TaskName -Trigger $Trigger | Out-Null
    
    Write-Host "Success!" -ForegroundColor Green
}

Maintenance

  • Manual Run: Open Task Scheduler, right-click any ErpCrystal_* task, and select “Run”.
  • View Logs: Check E:\MFGReports\SchedulerLog\scheduler.log (all logs are consolidated here).
  • Edit Script: Modify the single wrapper script at E:\MFGReports\SchedulerLog\run-job.ps1.

Testing Steps

Testers should follow these detailed steps to verify the WTS setup:

  1. Verify Configuration:

    • Ensure the API is running at http://localhost:5116.
    • Ensure appsettings.Development.json has ApiKey set to "ApiKey" (or match the script).
  2. Create Setup Script:

    • Ensure the directory E:\MFGReports\SchedulerLog exists.
    • Create a new file named setup-scheduler.ps1 in that location.
    • Copy the script content from the “Unified Setup Script” section above.
    • Paste it into your new setup-scheduler.ps1 file and save.
  3. Run Setup Script:

    • Open PowerShell as Administrator.
    • Execute the command:
      E:\MFGReports\SchedulerLog\setup-scheduler.ps1
    • Success Criteria: Look for the message "All Jobs Configured Successfully" in green at the end of the output.
    • Verify Files: Confirm that a new file E:\MFGReports\SchedulerLog\run-job.ps1 has been created.
  4. Verify Task Scheduler:

    • Open Windows Task Scheduler (search for taskschd.msc).
    • Click on Task Scheduler Library in the left pane.
    • Check: You should see the following tasks listed:
      • ErpCrystal_IndentEmail
      • ErpCrystal_SalesOrderEmail
      • ErpCrystal_AIInsights
    • Status: Ensure their status is Ready.
  5. Manual Job Test:

    • In Task Scheduler, right-click ErpCrystal_IndentEmail and select Run.
    • Verify Logs: Open E:\MFGReports\SchedulerLog\scheduler.log.
    • Success Criteria: Look for a new entry starting with STARTING... followed by SUCCESS: ....
  6. Scheduled Run Test:

    • Wait for the scheduled trigger time (e.g., 19:50) or use the “Quick Update” script below to set a test time.
    • Verify the logs update automatically at the specified time.

Configuration Changes for Testing

The following configuration changes have been applied to facilitate testing and local development. Testers should verify these settings if connectivity issues arise.

1. VS Code Launch Configuration (.vscode/launch.json)

  • API Project: Configured to run on HTTP port 5116.
    • ASPNETCORE_URLS: "http://localhost:5116;"
    • This ensures the Scheduler scripts (which use HTTP by default) can connect without SSL certificate errors.

2. API Launch Settings (ErpCrystal_MFG.Api/Properties/launchSettings.json)

  • Profile ErpCrystal_MFG.Api:
    • applicationUrl: http://localhost:5116
    • Matches the VS Code configuration to ensure consistency regardless of how the project is launched.

3. Web App HTTP Client (ErpCrystal_MFG.Web/Program.cs)

  • Named Client “mfgapi”:
    • Logic updated to read BaseAddress and ApiKey directly from appsettings.json (Section: ApiConfiguration).
    • Important: Ensure your local appsettings.Development.json has the correct values:
      "ApiConfiguration": {
        "BaseAddress": "http://localhost:5116",
        "ApiKey": "ApiKey"
      }
    • Comment the app.UseHttpsRedirection() line to avoid forced HTTPS redirection during local testing.