#!/usr/bin/env bash # ===================================================================== # create-master-articles.sh — master ("summary") articles for variation groups of a merchant, through # the app's own routes: quick-store (name + image, no prices) → products/update (search key, brand, # category, description) → [id]/flags (isSummary = true, isStocked/isSold/isPurchased = false), so the # master is a pure structure entity: never stocked, picked, ordered or priced. The variations are linked # afterwards with set-parent-ids.sh (M_Product.ParentId = master search key). # # ADMIN_USER=demo ADMIN_PASS=… MERCHANT_SHORT=hess MASTERS=/root/demo-setup/hess/masters.json ./create-master-articles.sh # # masters.json entry (hess/build-catalog.py): { value, name, group1, group2, classification, description, # documentNote, imageUrl, variants[] }. Env: BASE [http://127.0.0.1:3001], CLIENT_ID/ROLE_ID/ORG_ID/WH_ID # (sign-in context, 1000000), IMG_DIR [/root/demo-setup/images/], IMG_DELAY [0.3], LIMIT [0]. # Idempotent: masters are looked up by search key first. # ===================================================================== set -euo pipefail; export LANG=C.UTF-8 BASE="${BASE:-http://127.0.0.1:3001}"; ADMIN_USER="${ADMIN_USER:?}"; ADMIN_PASS="${ADMIN_PASS:?}" MERCHANT_SHORT="${MERCHANT_SHORT:?}"; MASTERS="${MASTERS:?}" CLIENT_ID="${CLIENT_ID:-1000000}"; ROLE_ID="${ROLE_ID:-1000000}"; ORG_ID="${ORG_ID:-1000000}"; WH_ID="${WH_ID:-1000000}" IMG_DIR="${IMG_DIR:-/root/demo-setup/images/$MERCHANT_SHORT}"; IMG_DELAY="${IMG_DELAY:-0.3}"; LIMIT="${LIMIT:-0}" JAR="$(mktemp)"; trap 'rm -f "$JAR"' EXIT; mkdir -p "$IMG_DIR" say() { printf '\n== %s\n' "$*"; } api() { if [ $# -ge 3 ]; then curl -sS -b "$JAR" -c "$JAR" -X "$1" "$BASE$2" -H 'Content-Type: application/json' -d "$3"; else curl -sS -b "$JAR" -c "$JAR" -X "$1" "$BASE$2"; fi; } odata() { local enc; enc="$(jq -rn --arg s "\$filter=$2&\$top=${3:-50}" '$s|@uri')"; api GET "/api/filters/$1/$enc"; } first_id() { jq -r '(.records // .)[0].id // empty'; } ok() { local st; st="$(echo "$1" | jq -r '.status // 200' 2>/dev/null || echo 500)"; if [ "$st" != 200 ]; then echo " FAILED: $(echo "$1" | cut -c1-300)" >&2; return 1; fi; } esc() { printf "%s" "$1" | sed "s/'/''/g"; } say "sign in as $ADMIN_USER" login="$(api POST /api/idempiere-auth/login "$(jq -n --arg u "$ADMIN_USER" --arg p "$ADMIN_PASS" '{userName:$u,password:$p,selectRole:true}')")" echo "$login" | jq -e '.token' >/dev/null || { echo "login failed" >&2; exit 1; } api POST /api/idempiere-auth/select-role "$(jq -n --argjson c "$CLIENT_ID" --argjson r "$ROLE_ID" --argjson o "$ORG_ID" --argjson w "$WH_ID" '{clientId:$c,roleId:$r,organizationId:$o,warehouseId:$w,language:"de_DE"}')" >/dev/null api GET /api/idempiere-auth/authenticated | jq -e '.user.id' >/dev/null || { echo "session not authenticated" >&2; exit 1; } MORG="$(odata ad_org "Value eq '$(esc "$MERCHANT_SHORT")'" | first_id)"; [ -n "$MORG" ] || { echo "org $MERCHANT_SHORT not found" >&2; exit 1; } echo " ok — merchant org $MORG" say "master articles from $MASTERS" TOTAL="$(jq length "$MASTERS")"; [ "$LIMIT" -gt 0 ] && TOTAL=$(( LIMIT < TOTAL ? LIMIT : TOTAL )) created=0; existed=0; failed=0 for i in $(seq 0 $((TOTAL-1))); do e="$(jq -c ".[$i]" "$MASTERS")"; value="$(echo "$e" | jq -r .value)"; name="$(echo "$e" | jq -r .name)" id="$(odata m_product "AD_Org_ID eq $MORG and Value eq '$(esc "$value")'" | first_id)" if [ -n "$id" ]; then existed=$((existed+1)); continue; fi imgurl="$(echo "$e" | jq -r '.imageUrl // empty')"; imgarg=() if [ -n "$imgurl" ]; then f="$IMG_DIR/$(printf '%s' "$imgurl" | md5sum | cut -c1-16).img" [ -s "$f" ] || { curl -sSL -A "Mozilla/5.0" --max-time 30 -o "$f" "$imgurl" || true; sleep "$IMG_DELAY"; } if [ -s "$f" ]; then mime="$(file -b --mime-type "$f" 2>/dev/null || echo image/png)"; case "$mime" in image/*) imgarg=(-F "image=@$f;type=$mime;filename=$value.${mime#image/}");; esac; fi fi res="$(curl -sS -b "$JAR" -c "$JAR" -X POST "$BASE/api/materials/products/quick-store" -F "name=$name" -F "price=0" -F "organizationId=$MORG" "${imgarg[@]}")" if ! ok "$res" 2>/dev/null; then failed=$((failed+1)); echo " FAILED $value: $(echo "$res" | cut -c1-200)"; continue; fi id="$(echo "$res" | jq -r '.id // .product.id')" upd="$(api PUT /api/materials/products/update "$(echo "$e" | jq -c --argjson id "$id" --argjson o "$MORG" \ '{id:$id, organizationId:$o, value:.value, sku:"", upc:"", group1:(.group1 // ""), group2:(.group2 // ""), classification:(.classification // ""), description:(.description // ""), documentNote:(.documentNote // "")}')")" ok "$upd" 2>/dev/null || echo " (details not saved for $value: $(echo "$upd" | cut -c1-120))" fl="$(api PUT "/api/materials/products/$id/flags" '{"isSummary":true,"isStocked":false,"isSold":false,"isPurchased":false}')" ok "$fl" 2>/dev/null || echo " (flags not saved for $value: $(echo "$fl" | cut -c1-120))" created=$((created+1)); printf ' %3d/%d %s (%s) img=%s variants=%s\n' "$((i+1))" "$TOTAL" "$value" "$id" "$(echo "$res" | jq -r .imageUploaded)" "$(echo "$e" | jq '.variants|length')" done say "done — masters created $created / existing $existed / failed $failed (org $MORG)"