cai-exos-systems/daveadmin-exos-demo:ops/suitecrm-exos/seed-suitecrm.sh
./seed-suitecrm.sh [--url=https://crm.exos.bluenotelogic.com] [--client-id=...] [--client-secret=...]
set -e
CRM_URL="https://crm.exos.bluenotelogic.com"
CLIENT_ID=""
CLIENT_SECRET="ExosCrmApiSecret26!"
for arg in "$@"; do
case $arg in
--url=*) CRM_URL="${arg#*=}" ;;
--client-id=*) CLIENT_ID="${arg#*=}" ;;
--client-secret=*) CLIENT_SECRET="${arg#*=}" ;;
esac
done
if [ -z "$CLIENT_ID" ]; then
echo "ERROR: --client-id is required."
exit 1
fi
echo "SuiteCRM Exos Seed — Acme Holdings Hierarchy"
echo "URL: $CRM_URL"
echo ""
── Get access token ─────────────────────────────────────────────────────────── TOKEN=$(curl -s -X POST "$CRM_URL/legacy/Api/index.php/access_token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \ | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") if [ -z "$TOKEN" ]; then echo "ERROR: Failed to get access token." exit 1 fi echo "Access token obtained" POST /V8/module — create any module record crm_create() { local data="$1" curl -s -X POST "$CRM_URL/legacy/Api/index.php/V8/module" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/vnd.api+json" \ -d "$data" } POST /V8/module/{Module}/{id}/relationships — link two records crm_relate() { local module="$1" local id="$2" local link="$3" local data="$4" curl -s -X POST "$CRM_URL/legacy/Api/index.php/V8/module/$module/$id/relationships/$link" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/vnd.api+json" \ -d "$data" }
── Create Acme Holdings (parent) ────────────────────────────────────────────── echo "Creating Acme Holdings (parent)..." HOLDINGS_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme Holdings", "account_type": "Customer", "industry": "Technology", "billing_address_country": "Ireland", "description": "Enterprise Billing Expert demo PARENT. Consolidated Mar 2026 spend: EUR 22,547. Subsidiaries: Acme Nordics, Acme UK, Acme EMEA." } } }') HOLDINGS_ID=$(echo "$HOLDINGS_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$HOLDINGS_ID" ]; then echo " ERROR creating Holdings: $HOLDINGS_RESP" exit 1 fi echo " Holdings ID: $HOLDINGS_ID"
── Create Acme Nordics (child) ──────────────────────────────────────────────── echo "Creating Acme Nordics..." NORDICS_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme Nordics", "account_type": "Customer", "industry": "Technology", "billing_address_country": "Norway", "description": "Cost Centres: Engineering (100 SIMs), Sales (73 SIMs + roaming). Contract: 150 min SIMs Jan 2025-Dec 2026 (173 active - above commit). Mar 2026: EUR 6,842." } } }') NORDICS_ID=$(echo "$NORDICS_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$NORDICS_ID" ]; then echo " ERROR: $NORDICS_RESP"; exit 1; fi echo " Nordics ID: $NORDICS_ID"
── Create Acme UK (child) ───────────────────────────────────────────────────── echo "Creating Acme UK..." UK_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme UK", "account_type": "Customer", "industry": "Technology", "billing_address_country": "United Kingdom", "description": "Cost Centre: IT Operations. ANOMALY: 31 unused PBX extensions (EUR 372/mo waste). 1 legacy SIP trunk low-usage. Contract: 200 PBX seats Mar 2025-Feb 2026 (214 active). Mar 2026: EUR 5,931." } } }') UK_ID=$(echo "$UK_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$UK_ID" ]; then echo " ERROR: $UK_RESP"; exit 1; fi echo " UK ID: $UK_ID"
── Create Acme EMEA (child) ─────────────────────────────────────────────────── echo "Creating Acme EMEA..." EMEA_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme EMEA", "account_type": "Customer", "industry": "Technology", "billing_address_country": "Germany", "description": "Cost Centre: Human Resources. BILL SHOCK: Cloud Compute +32% Mar 2026 (analytics workload spike). Contract: EUR 5,000/mo Cloud Jun 2025-May 2026 (OVERAGE: EUR 6,248 actual vs EUR 5,000 commit). Mar 2026: EUR 9,774." } } }') EMEA_ID=$(echo "$EMEA_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$EMEA_ID" ]; then echo " ERROR: $EMEA_RESP"; exit 1; fi echo " EMEA ID: $EMEA_ID"
── Link children to Holdings via member_of relationship ───────────────────── echo "Linking subsidiaries to Holdings..." crm_relate "Accounts" "$HOLDINGS_ID" "member_of" "{ \"data\": [{\"type\": \"Accounts\", \"id\": \"$NORDICS_ID\"}] }" > /dev/null && echo " Nordics linked" crm_relate "Accounts" "$HOLDINGS_ID" "member_of" "{ \"data\": [{\"type\": \"Accounts\", \"id\": \"$UK_ID\"}] }" > /dev/null && echo " UK linked" crm_relate "Accounts" "$HOLDINGS_ID" "member_of" "{ \"data\": [{\"type\": \"Accounts\", \"id\": \"$EMEA_ID\"}] }" > /dev/null && echo " EMEA linked"
── Create Contracts ─────────────────────────────────────────────────────────── echo "" echo "Creating contracts..." NORDICS_CONTRACT_RESP=$(crm_create "{ \"data\": { \"type\": \"AOS_Contracts\", \"attributes\": { \"name\": \"Mr Glenn Acme Nordics Mobile SIM Commitment\", \"status\": \"Active\", \"start_date\": \"2025-01-01\", \"end_date\": \"2026-12-31\", \"description\": \"Minimum 150 Mobile SIMs. Current: 173 SIMs (above commit). Includes Mobile 5G Standard (Engineering CC) and Pro+Roaming (Sales CC).\", \"total_contract_value\": \"82104.00\", \"currency_id\": \"-99\" } } }") NORDICS_CONTRACT_ID=$(echo "$NORDICS_CONTRACT_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -n "$NORDICS_CONTRACT_ID" ]; then crm_relate "AOS_Contracts" "$NORDICS_CONTRACT_ID" "accounts" "{\"data\": [{\"type\": \"Accounts\", \"id\": \"$NORDICS_ID\"}]}" > /dev/null echo " Nordics SIM contract created and linked" else echo " WARNING: Nordics contract failed: $NORDICS_CONTRACT_RESP" fi UK_CONTRACT_RESP=$(crm_create "{ \"data\": { \"type\": \"AOS_Contracts\", \"attributes\": { \"name\": \"Mr Glenn Acme UK PBX Seat Commitment\", \"status\": \"Active\", \"start_date\": \"2025-03-01\", \"end_date\": \"2026-02-28\", \"description\": \"200 PBX Extension seats commitment. IT Operations CC. Current: 214 active (31 UNUSED = EUR 372/mo waste). EXPIRING Feb 2026.\", \"total_contract_value\": \"71172.00\", \"currency_id\": \"-99\" } } }") UK_CONTRACT_ID=$(echo "$UK_CONTRACT_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -n "$UK_CONTRACT_ID" ]; then crm_relate "AOS_Contracts" "$UK_CONTRACT_ID" "accounts" "{\"data\": [{\"type\": \"Accounts\", \"id\": \"$UK_ID\"}]}" > /dev/null echo " UK PBX contract created and linked" else echo " WARNING: UK contract failed: $UK_CONTRACT_RESP" fi EMEA_CONTRACT_RESP=$(crm_create "{ \"data\": { \"type\": \"AOS_Contracts\", \"attributes\": { \"name\": \"Mr Glenn Acme EMEA Cloud Compute Commitment\", \"status\": \"Active\", \"start_date\": \"2025-06-01\", \"end_date\": \"2026-05-31\", \"description\": \"EUR 5,000/month Cloud Compute commitment. HR Cost Centre. Mar 2026 actual: EUR 6,248 (OVERAGE +EUR 1,248 = +32%). Analytics workload spike not covered by commitment.\", \"total_contract_value\": \"60000.00\", \"currency_id\": \"-99\" } } }") EMEA_CONTRACT_ID=$(echo "$EMEA_CONTRACT_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -n "$EMEA_CONTRACT_ID" ]; then crm_relate "AOS_Contracts" "$EMEA_CONTRACT_ID" "accounts" "{\"data\": [{\"type\": \"Accounts\", \"id\": \"$EMEA_ID\"}]}" > /dev/null echo " EMEA Cloud contract created and linked" else echo " WARNING: EMEA contract failed: $EMEA_CONTRACT_RESP" fi echo "" echo "── Seed complete ─────────────────────────────────────────────────" echo "Account IDs (save these for agent.php):" echo " Acme Holdings (parent): $HOLDINGS_ID" echo " Acme Nordics: $NORDICS_ID" echo " Acme UK: $UK_ID" echo " Acme EMEA: $EMEA_ID" echo "" echo "Add to exos-demo/.env:" echo " SUITECRM_EXOS_URL=https://crm.exos.bluenotelogic.com" echo " SUITECRM_EXOS_CLIENT_ID=$CLIENT_ID" echo " SUITECRM_EXOS_CLIENT_SECRET=$CLIENT_SECRET" echo " SUITECRM_EXOS_HOLDINGS_ID=$HOLDINGS_ID" echo " SUITECRM_EXOS_NORDICS_ID=$NORDICS_ID" echo " SUITECRM_EXOS_UK_ID=$UK_ID" echo " SUITECRM_EXOS_EMEA_ID=$EMEA_ID" ```
── Get access token ─────────────────────────────────────────────────────────── TOKEN=$(curl -s -X POST "$CRM_URL/legacy/Api/index.php/access_token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \ | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") if [ -z "$TOKEN" ]; then echo "ERROR: Failed to get access token." exit 1 fi echo "Access token obtained" POST /V8/module — create any module record crm_create() { local data="$1" curl -s -X POST "$CRM_URL/legacy/Api/index.php/V8/module" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/vnd.api+json" \ -d "$data" } POST /V8/module/{Module}/{id}/relationships — link two records crm_relate() { local module="$1" local id="$2" local link="$3" local data="$4" curl -s -X POST "$CRM_URL/legacy/Api/index.php/V8/module/$module/$id/relationships/$link" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/vnd.api+json" \ -d "$data" }
── Create Acme Holdings (parent) ────────────────────────────────────────────── echo "Creating Acme Holdings (parent)..." HOLDINGS_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme Holdings", "account_type": "Customer", "industry": "Technology", "billing_address_country": "Ireland", "description": "Enterprise Billing Expert demo PARENT. Consolidated Mar 2026 spend: EUR 22,547. Subsidiaries: Acme Nordics, Acme UK, Acme EMEA." } } }') HOLDINGS_ID=$(echo "$HOLDINGS_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$HOLDINGS_ID" ]; then echo " ERROR creating Holdings: $HOLDINGS_RESP" exit 1 fi echo " Holdings ID: $HOLDINGS_ID"
── Create Acme Nordics (child) ──────────────────────────────────────────────── echo "Creating Acme Nordics..." NORDICS_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme Nordics", "account_type": "Customer", "industry": "Technology", "billing_address_country": "Norway", "description": "Cost Centres: Engineering (100 SIMs), Sales (73 SIMs + roaming). Contract: 150 min SIMs Jan 2025-Dec 2026 (173 active - above commit). Mar 2026: EUR 6,842." } } }') NORDICS_ID=$(echo "$NORDICS_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$NORDICS_ID" ]; then echo " ERROR: $NORDICS_RESP"; exit 1; fi echo " Nordics ID: $NORDICS_ID"
── Create Acme UK (child) ───────────────────────────────────────────────────── echo "Creating Acme UK..." UK_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme UK", "account_type": "Customer", "industry": "Technology", "billing_address_country": "United Kingdom", "description": "Cost Centre: IT Operations. ANOMALY: 31 unused PBX extensions (EUR 372/mo waste). 1 legacy SIP trunk low-usage. Contract: 200 PBX seats Mar 2025-Feb 2026 (214 active). Mar 2026: EUR 5,931." } } }') UK_ID=$(echo "$UK_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$UK_ID" ]; then echo " ERROR: $UK_RESP"; exit 1; fi echo " UK ID: $UK_ID"
── Create Acme EMEA (child) ─────────────────────────────────────────────────── echo "Creating Acme EMEA..." EMEA_RESP=$(crm_create '{ "data": { "type": "Accounts", "attributes": { "name": "Mr Glenn Acme EMEA", "account_type": "Customer", "industry": "Technology", "billing_address_country": "Germany", "description": "Cost Centre: Human Resources. BILL SHOCK: Cloud Compute +32% Mar 2026 (analytics workload spike). Contract: EUR 5,000/mo Cloud Jun 2025-May 2026 (OVERAGE: EUR 6,248 actual vs EUR 5,000 commit). Mar 2026: EUR 9,774." } } }') EMEA_ID=$(echo "$EMEA_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -z "$EMEA_ID" ]; then echo " ERROR: $EMEA_RESP"; exit 1; fi echo " EMEA ID: $EMEA_ID"
── Link children to Holdings via member_of relationship ───────────────────── echo "Linking subsidiaries to Holdings..." crm_relate "Accounts" "$HOLDINGS_ID" "member_of" "{ \"data\": [{\"type\": \"Accounts\", \"id\": \"$NORDICS_ID\"}] }" > /dev/null && echo " Nordics linked" crm_relate "Accounts" "$HOLDINGS_ID" "member_of" "{ \"data\": [{\"type\": \"Accounts\", \"id\": \"$UK_ID\"}] }" > /dev/null && echo " UK linked" crm_relate "Accounts" "$HOLDINGS_ID" "member_of" "{ \"data\": [{\"type\": \"Accounts\", \"id\": \"$EMEA_ID\"}] }" > /dev/null && echo " EMEA linked"
── Create Contracts ─────────────────────────────────────────────────────────── echo "" echo "Creating contracts..." NORDICS_CONTRACT_RESP=$(crm_create "{ \"data\": { \"type\": \"AOS_Contracts\", \"attributes\": { \"name\": \"Mr Glenn Acme Nordics Mobile SIM Commitment\", \"status\": \"Active\", \"start_date\": \"2025-01-01\", \"end_date\": \"2026-12-31\", \"description\": \"Minimum 150 Mobile SIMs. Current: 173 SIMs (above commit). Includes Mobile 5G Standard (Engineering CC) and Pro+Roaming (Sales CC).\", \"total_contract_value\": \"82104.00\", \"currency_id\": \"-99\" } } }") NORDICS_CONTRACT_ID=$(echo "$NORDICS_CONTRACT_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -n "$NORDICS_CONTRACT_ID" ]; then crm_relate "AOS_Contracts" "$NORDICS_CONTRACT_ID" "accounts" "{\"data\": [{\"type\": \"Accounts\", \"id\": \"$NORDICS_ID\"}]}" > /dev/null echo " Nordics SIM contract created and linked" else echo " WARNING: Nordics contract failed: $NORDICS_CONTRACT_RESP" fi UK_CONTRACT_RESP=$(crm_create "{ \"data\": { \"type\": \"AOS_Contracts\", \"attributes\": { \"name\": \"Mr Glenn Acme UK PBX Seat Commitment\", \"status\": \"Active\", \"start_date\": \"2025-03-01\", \"end_date\": \"2026-02-28\", \"description\": \"200 PBX Extension seats commitment. IT Operations CC. Current: 214 active (31 UNUSED = EUR 372/mo waste). EXPIRING Feb 2026.\", \"total_contract_value\": \"71172.00\", \"currency_id\": \"-99\" } } }") UK_CONTRACT_ID=$(echo "$UK_CONTRACT_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -n "$UK_CONTRACT_ID" ]; then crm_relate "AOS_Contracts" "$UK_CONTRACT_ID" "accounts" "{\"data\": [{\"type\": \"Accounts\", \"id\": \"$UK_ID\"}]}" > /dev/null echo " UK PBX contract created and linked" else echo " WARNING: UK contract failed: $UK_CONTRACT_RESP" fi EMEA_CONTRACT_RESP=$(crm_create "{ \"data\": { \"type\": \"AOS_Contracts\", \"attributes\": { \"name\": \"Mr Glenn Acme EMEA Cloud Compute Commitment\", \"status\": \"Active\", \"start_date\": \"2025-06-01\", \"end_date\": \"2026-05-31\", \"description\": \"EUR 5,000/month Cloud Compute commitment. HR Cost Centre. Mar 2026 actual: EUR 6,248 (OVERAGE +EUR 1,248 = +32%). Analytics workload spike not covered by commitment.\", \"total_contract_value\": \"60000.00\", \"currency_id\": \"-99\" } } }") EMEA_CONTRACT_ID=$(echo "$EMEA_CONTRACT_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('id',''))") if [ -n "$EMEA_CONTRACT_ID" ]; then crm_relate "AOS_Contracts" "$EMEA_CONTRACT_ID" "accounts" "{\"data\": [{\"type\": \"Accounts\", \"id\": \"$EMEA_ID\"}]}" > /dev/null echo " EMEA Cloud contract created and linked" else echo " WARNING: EMEA contract failed: $EMEA_CONTRACT_RESP" fi echo "" echo "── Seed complete ─────────────────────────────────────────────────" echo "Account IDs (save these for agent.php):" echo " Acme Holdings (parent): $HOLDINGS_ID" echo " Acme Nordics: $NORDICS_ID" echo " Acme UK: $UK_ID" echo " Acme EMEA: $EMEA_ID" echo "" echo "Add to exos-demo/.env:" echo " SUITECRM_EXOS_URL=https://crm.exos.bluenotelogic.com" echo " SUITECRM_EXOS_CLIENT_ID=$CLIENT_ID" echo " SUITECRM_EXOS_CLIENT_SECRET=$CLIENT_SECRET" echo " SUITECRM_EXOS_HOLDINGS_ID=$HOLDINGS_ID" echo " SUITECRM_EXOS_NORDICS_ID=$NORDICS_ID" echo " SUITECRM_EXOS_UK_ID=$UK_ID" echo " SUITECRM_EXOS_EMEA_ID=$EMEA_ID" ```