#!/usr/bin/env bash
# =====================================================================
# seed-orders-returns.sh — demo sales orders in mixed states, shipments, invoices, voided orders
# and RETURNS (RMA + customer return receipt) for one merchant org, with demo photos attached to
# orders and returns. Everything goes through the app's routes; attachments use the app's Strapi
# attachment model (POST /api/attachments/<Table>/<id> → ad-attachment, then Strapi upload with
# ref/refId/field=attachment).
#
#   ADMIN_USER=demo ADMIN_PASS=… MERCHANT_SHORT=kazaar CONSIGNEES=/root/demo-setup/kazaar/consignees.json ./seed-orders-returns.sh
#
# Status mix per order number n — MIX=default (n % 10): 0 draft · 1 prepared (IP) · 2 completed then VOIDED ·
# 3,4 completed, not yet shipped (picking) · 5…9 completed + shipped · 5,6 also invoiced.
# MIX=shipped ("mostly shipped, some in progress, very few voided"): n%25==7 voided (4 %) · n%10==0 draft ·
# n%10==1 prepared · n%10==2 completed/picking · n%20==13 shipment generated + printed, no tracking yet
# ("In Bearbeitung") · everything else shipped WITH a dummy DHL tracking number, parcel type, shipping date
# and IsCommissioned ("Verschickt"); n%10 in 5,6 also invoiced.
# Lines 1–5 per order, qty 1–3, order dates spread over the last 30 days, consignees cycle through
# CONSIGNEES (anonymised list from build-consignees.py: name/street/postal/city/countryId/email).
#
# Env (defaults): BASE [http://127.0.0.1:3001], ORDERS [100], RETURNS [8], ATTACH_EVERY [10 = photo on
# every 10th order], PREFIX [<SHORT upper>-ORD], MIX [default|shipped], PAKET_TYPES [1000204,1000205,1000206,1000213
# = Paket S/M/L/XL products rotated per shipment], TRACKING_PREFIX [0034043416 → 20-digit DHL-style numbers],
# REASONS_FILE [lines "reason|rmaTypeId"; default = the fragrance list below], CLIENT_ID/ROLE_ID/ORG_ID/WH_ID (sign-in context),
# PRICE_LIST_ID [1000003], PLV_GROSS [1000003], DOCTYPE_SO [1000032], DOCTYPE_RMA [1000031 Customer Return
# Material], DOCTYPE_CR [1000015 MM Customer Return], RMA_TYPE_RETURN [1000001 Rückgabe], RMA_TYPE_BOUNCE
# [1000000 Rückläufer], LOCATOR_TYPE_RET [1000004 Retouren], SHIPPER_ID [1000000], CURRENCY_ID [102],
# UOM_ID [100], PAYMENT_TERM_ID [1000000], TAX_ID [1000000], BP_GROUP_STD [1000000],
# IMG_DIR [/root/demo-setup/images/<short>] product image cache, CATALOG [/root/demo-setup/<short>/catalog.json]
# (for product images), PHOTO_DIR [/root/demo-setup/photos/<short>], STRAPI_UPLOAD [http://127.0.0.1:1337/api/upload],
# STRAPI_TOKEN [STRAPITOKEN from .env].
# Idempotent: orders by POReference, consignees by name, RMAs by name, uploads by file name.
# =====================================================================
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
BASE="${BASE:-http://127.0.0.1:3001}"; ADMIN_USER="${ADMIN_USER:?}"; ADMIN_PASS="${ADMIN_PASS:?}"; MERCHANT_SHORT="${MERCHANT_SHORT:?}"
CONSIGNEES="${CONSIGNEES:?path to consignees.json}"
ORDERS="${ORDERS:-100}"; RETURNS="${RETURNS:-8}"; ATTACH_EVERY="${ATTACH_EVERY:-10}"; PREFIX="${PREFIX:-$(echo "$MERCHANT_SHORT" | tr a-z A-Z)-ORD}"
MIX="${MIX:-default}"; PAKET_TYPES="${PAKET_TYPES:-1000204,1000205,1000206,1000213}"; TRACKING_PREFIX="${TRACKING_PREFIX:-0034043416}"; REASONS_FILE="${REASONS_FILE:-}"
CLIENT_ID="${CLIENT_ID:-1000000}"; ROLE_ID="${ROLE_ID:-1000000}"; ORG_ID="${ORG_ID:-1000000}"; WH_ID="${WH_ID:-1000000}"
PRICE_LIST_ID="${PRICE_LIST_ID:-1000003}"; PLV_GROSS="${PLV_GROSS:-1000003}"; DOCTYPE_SO="${DOCTYPE_SO:-1000032}"
DOCTYPE_RMA="${DOCTYPE_RMA:-1000031}"; DOCTYPE_CR="${DOCTYPE_CR:-1000015}"; RMA_TYPE_RETURN="${RMA_TYPE_RETURN:-1000001}"; RMA_TYPE_BOUNCE="${RMA_TYPE_BOUNCE:-1000000}"
LOCATOR_TYPE_RET="${LOCATOR_TYPE_RET:-1000004}"; SHIPPER_ID="${SHIPPER_ID:-1000000}"; CURRENCY_ID="${CURRENCY_ID:-102}"
UOM_ID="${UOM_ID:-100}"; PAYMENT_TERM_ID="${PAYMENT_TERM_ID:-1000000}"; TAX_ID="${TAX_ID:-1000000}"; BP_GROUP_STD="${BP_GROUP_STD:-1000000}"
IMG_DIR="${IMG_DIR:-/root/demo-setup/images/$MERCHANT_SHORT}"; CATALOG="${CATALOG:-/root/demo-setup/$MERCHANT_SHORT/catalog.json}"; PHOTO_DIR="${PHOTO_DIR:-/root/demo-setup/photos/$MERCHANT_SHORT}"
STRAPI_UPLOAD="${STRAPI_UPLOAD:-http://127.0.0.1:1337/api/upload}"
STRAPI_TOKEN="${STRAPI_TOKEN:-$(grep -E '^STRAPITOKEN=' /opt/erp-nuxt-frontend/.env | cut -d= -f2- | tr -d "'\"")}"
TODAY="$(date +%Y-%m-%d)"
JAR="$(mktemp)"; trap 'rm -f "$JAR"' EXIT; mkdir -p "$PHOTO_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 'if type=="array" then .[0].id elif has("records") then .records[0].id else empty end // empty' 2>/dev/null; }
ok() { local st; st="$(echo "$1" | jq -r '.status // 200' 2>/dev/null || echo 500)"; [ "$st" = 200 ] || { echo "  FAILED: $(echo "$1" | cut -c1-300)" >&2; return 1; }; }
esc() { printf "%s" "$1" | sed "s/'/''/g"; }
dayago() { date -d "-$1 days" +%Y-%m-%d 2>/dev/null || date -v-"$1"d +%Y-%m-%d; }
dayplus() { date -d "+$1 days" +%Y-%m-%d 2>/dev/null || date -v+"$1"d +%Y-%m-%d; }

# attach <Table> <recordId> <recordUid> <file> — creates/gets the ad-attachment, uploads the file once
attach() {
  local table="$1" rid="$2" uid="$3" file="$4" fname; fname="$(basename "$file")"
  local a; a="$(api POST "/api/attachments/$table/$rid" "$(jq -n --arg u "$uid" '{uid:$u}')")"
  local sid; sid="$(echo "$a" | jq -r '.Strapi_Attachment_ID // empty')"
  [ -n "$sid" ] || { echo "  attachment record failed for $table/$rid: $(echo "$a" | cut -c1-160)"; return 0; }
  if curl -sSg -H "Authorization: Bearer $STRAPI_TOKEN" "$STRAPI_UPLOAD/files?filters[name][\$eq]=$fname&pagination[limit]=1" | jq -e 'type=="array" and length > 0' >/dev/null 2>&1; then return 0; fi
  local up; up="$(curl -sS -X POST "$STRAPI_UPLOAD" -H "Authorization: Bearer $STRAPI_TOKEN" -F "files=@$file;type=image/jpeg" -F "ref=api::ad-attachment.ad-attachment" -F "refId=$sid" -F "field=attachment")"
  echo "$up" | jq -e '.[0].id' >/dev/null 2>&1 && echo "  attached $fname -> $table $rid" || echo "  upload failed for $fname: $(echo "$up" | cut -c1-160)"
}
photo() { python3 "$HERE/make-demo-photo.py" "$@" >/dev/null; }
img_for() { local f; f="$(jq -r --arg v "$1" '.[] | select(.value==$v) | .imageUrl // empty' "$CATALOG" 2>/dev/null | head -1)"; [ -n "$f" ] && f="$IMG_DIR/$(printf '%s' "$f" | md5sum | cut -c1-16).img"; [ -s "${f:-/nonexistent}" ] && echo "$f" || true; }

# --- 0. sign in -------------------------------------------------------------------------------------------
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
ME="$(api GET /api/idempiere-auth/authenticated | jq -r '.user.id // empty')"; [ -n "$ME" ] || { echo "session not authenticated" >&2; exit 1; }

# --- 1. merchant context -----------------------------------------------------------------------------------
say "merchant org $MERCHANT_SHORT"
MORG="$(odata ad_org "Value eq '$MERCHANT_SHORT'" | first_id)"; [ -n "$MORG" ] || { echo "org $MERCHANT_SHORT not found" >&2; exit 1; }
MWH="$(odata m_warehouse "AD_Org_ID eq $MORG" | first_id)"
RETLOC="$(odata m_locator "M_Warehouse_ID eq $MWH and Value eq 'RET-01-01'" | first_id)"
if [ -z "$RETLOC" ]; then
  res="$(api POST /api/materials/locators/store "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --argjson t "$LOCATOR_TYPE_RET" '{organizationId:$o, warehouseId:$w, value:"RET-01-01", priorityNo:90, isActive:true, isDefault:false, x:"RET", y:"01", z:"01", locatorTypeId:$t}')")"; ok "$res"; RETLOC="$(echo "$res" | jq -r .id)"
fi
api GET "/api/filters/m_product/$(jq -rn --arg s "\$filter=AD_Org_ID eq $MORG and IsActive eq true&\$top=2000" '$s|@uri')" | jq -r '.records[] | "\(.id)|\(.Value)|\(.Name | gsub("\\|"; "/"))"' > "$PHOTO_DIR/products.txt"   # names may contain "|" (VONROC)
api GET "/api/filters/m_productprice/$(jq -rn --arg s "\$filter=M_PriceList_Version_ID eq $PLV_GROSS and AD_Org_ID eq $MORG&\$top=3000" '$s|@uri')" | jq -r '.records[] | "\(.M_Product_ID.id)|\(.PriceStd)"' > "$PHOTO_DIR/prices.txt"
join -t'|' <(sort -t'|' -k1,1 "$PHOTO_DIR/products.txt") <(sort -t'|' -k1,1 "$PHOTO_DIR/prices.txt") | awk -F'|' '$4+0 > 0' > "$PHOTO_DIR/articles.txt"
NART="$(wc -l < "$PHOTO_DIR/articles.txt")"; echo "  org $MORG warehouse $MWH return bin $RETLOC, $NART priced articles"
[ "$NART" -gt 0 ] || { echo "no priced articles" >&2; exit 1; }
pick() { sed -n "$(( ($1 * 7919 + $2 * 104729) % NART + 1 ))p" "$PHOTO_DIR/articles.txt"; }

# --- 2. consignees ------------------------------------------------------------------------------------------
NC="$(jq length "$CONSIGNEES")"; say "$NC consignees from $CONSIGNEES"
CUST_BP=(); CUST_LOC=(); created=0
for i in $(seq 0 $((NC-1))); do
  c="$(jq -c ".[$i]" "$CONSIGNEES")"; name="$(echo "$c" | jq -r .name)"
  bp="$(odata c_bpartner "AD_Org_ID eq $MORG and Name eq '$(esc "$name")'" | first_id)"
  if [ -z "$bp" ]; then
    loc="$(api POST /api/settings/locations/store "$(echo "$c" | jq -c --argjson o "$MORG" '{organizationId:$o, address1:.street, postal:.postal, city:.city, countryId:.countryId, isActive:true, isValid:true}')")"; ok "$loc"; locid="$(echo "$loc" | jq -r .id)"
    res="$(api POST /api/partners/partners/store "$(echo "$c" | jq -c --argjson o "$MORG" --argjson g "$BP_GROUP_STD" --argjson pl "$PRICE_LIST_ID" '{organizationId:$o, name:.name, EMail:.email, isActive:true, isCustomer:true, isVendor:false, partnerGroupId:$g, priceListId:$pl, languageId:"de_DE"}')")"; ok "$res"; bp="$(echo "$res" | jq -r .id)"
    bl="$(api POST "/api/partners/partners/$bp/locations" "$(jq -n --argjson o "$MORG" --argjson bp "$bp" --argjson l "$locid" --arg n "$name" '{organizationId:$o, partnerId:$bp, locationId:$l, name:$n, isShipTo:true, isBillTo:true, isPayFrom:true, isRemitTo:true, isActive:true}')")"; ok "$bl"; created=$((created+1))
  fi
  CUST_BP+=("$bp"); CUST_LOC+=("$(odata c_bpartner_location "C_BPartner_ID eq $bp" | first_id)")
done
echo "  $created created, ${#CUST_BP[@]} available"

# --- 3. orders ------------------------------------------------------------------------------------------------
say "$ORDERS orders"
SHIP_CUST=(); SHIP_ORDERS=(); INV_ORDERS=(); counts=""
for n in $(seq 1 "$ORDERS"); do
  ref="$(printf '%s-%04d' "$PREFIX" $((1000 + n)))"; ci=$(( (n * 37) % ${#CUST_BP[@]} )); bp="${CUST_BP[$ci]}"; bpl="${CUST_LOC[$ci]}"
  if [ "$MIX" = shipped ]; then
    if [ $(( n % 25 )) = 7 ]; then action=VO; else case $(( n % 10 )) in 0) action=DR;; 1) action=PR;; 2) action=CO;; *) action=SHIP;; esac; fi
  else
    case $(( n % 10 )) in 0) action=DR;; 1) action=PR;; 2) action=VO;; 3|4) action=CO;; *) action=SHIP;; esac
  fi
  d="$(dayago $(( (n * 13) % 30 )))"; dp="$(dayplus $(( n % 4 )))"
  oid="$(odata c_order "AD_Org_ID eq $MORG and POReference eq '$ref'" | first_id)"
  if [ -z "$oid" ]; then
    nl=$(( (n * 7) % 5 + 1 ))
    ol="$(for k in $(seq 1 $nl); do IFS='|' read -r pid val pname price <<<"$(pick "$n" "$k")"; q=$(( (n + k) % 3 + 1 )); jq -n --argjson p "$pid" --argjson q "$q" --argjson pr "$price" --argjson ln "$((k*10))" \
      --arg d "$d" --arg dp "$dp" --argjson u "$UOM_ID" --argjson w "$MWH" --argjson cur "$CURRENCY_ID" --argjson bp "$bp" --argjson bpl "$bpl" --argjson tax "$TAX_ID" \
      '{line:$ln, isActive:true, dateOrdered:$d, datePromised:$dp, qtyOrdered:$q, qtyEntered:$q, qtyReserved:0, qtyDelivered:0, qtyInvoiced:0, qtyLostSales:0,
        priceActual:$pr, priceEntered:$pr, priceList:$pr, priceLimit:$pr, discount:0, lineNetAmt:($pr*$q),
        M_Product_ID:{id:$p}, C_UOM_ID:{id:$u}, C_Tax_ID:{id:$tax}, M_Warehouse_ID:{id:$w}, C_Currency_ID:{id:$cur}, C_BPartner_ID:{id:$bp}, C_BPartner_Location_ID:{id:$bpl}}'; done | jq -s .)"
    res="$(api POST /api/orders/store "$(jq -n --argjson o "$MORG" --argjson dt "$DOCTYPE_SO" --arg d "$d" --arg dp "$dp" --argjson bp "$bp" --argjson bpl "$bpl" \
      --argjson w "$MWH" --argjson pl "$PRICE_LIST_ID" --argjson cur "$CURRENCY_ID" --argjson sh "$SHIPPER_ID" --arg ref "$ref" --argjson ol "$ol" --argjson pt "$PAYMENT_TERM_ID" \
      '{organizationId:$o, isActive:true, isSOTrx:true, docTypeTargetId:$dt, docStatusId:"DR", dateOrdered:$d, datePromised:$dp, dateAcct:$d,
        partnerId:$bp, partnerLocationId:$bpl, billPartnerId:$bp, billPartnerLocationId:$bpl, warehouseId:$w, priceListId:$pl, currencyId:$cur, paymentTermId:$pt,
        deliveryRuleId:"A", deliveryViaRuleId:"D", invoiceRuleId:"D", priorityRuleId:"5", paymentRuleId:"P", shipperId:$sh, poReference:$ref,
        description:"Demo-Bestellung Onlineshop", orderLines:$ol}')")"; ok "$res"; oid="$(echo "$res" | jq -r .id)"; printf '  %3d/%d %s (%s) %d lines -> %s\n' "$n" "$ORDERS" "$ref" "$oid" "$nl" "$action"
  fi
  rec="$(odata c_order "C_Order_ID eq $oid")"; st="$(echo "$rec" | jq -r '.records[0].DocStatus.id // "DR"')"; uid="$(echo "$rec" | jq -r '.records[0].uid')"
  case "$action" in
    PR) [ "$st" = DR ] && { da="$(api POST "/api/orders/$oid/document-action" '{"documentAction":"PR"}')"; ok "$da"; } ;;
    CO|SHIP) [ "$st" = DR ] && { da="$(api POST "/api/orders/$oid/document-action" '{"documentAction":"CO"}')"; ok "$da"; } ;;
    VO) [ "$st" = DR ] && { da="$(api POST "/api/orders/$oid/document-action" '{"documentAction":"CO"}')"; ok "$da"; st=CO; }
        [ "$st" = CO ] && { da="$(api POST "/api/orders/$oid/document-action" '{"documentAction":"VO"}')"; ok "$da"; } ;;
  esac
  [ "$action" = SHIP ] && { SHIP_CUST+=("$ci"); SHIP_ORDERS+=("$n|$oid|$d"); }; [ $(( n % 10 )) = 5 ] || [ $(( n % 10 )) = 6 ] && INV_ORDERS+=("$oid")
  if [ $(( n % ATTACH_EVERY )) = 1 ]; then
    IFS='|' read -r pid val pname price <<<"$(pick "$n" 1)"; f="$PHOTO_DIR/order-$ref-kundenfoto.jpg"; im="$(img_for "$val")"
    [ -s "$f" ] || photo "$f" ${im:+--image "$im"} "Kundenanfrage zu Bestellung $ref" "Artikel: $pname" "Foto vom Kunden per E-Mail erhalten"
    attach C_Order "$oid" "$uid" "$f"
  fi
done

# --- 4. shipments (one generate per customer with open completed orders) + invoices --------------------------------------
say "shipments"
for ci in $(printf '%s\n' "${SHIP_CUST[@]}" | sort -un); do
  bp="${CUST_BP[$ci]}"
  [ -n "$(odata c_order "AD_Org_ID eq $MORG and C_BPartner_ID eq $bp and DocStatus eq 'CO' and IsDelivered eq false" | first_id)" ] || continue
  res="$(api POST /api/processes/inouts/generate "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --argjson bp "$bp" '{organizationId:$o, warehouseId:$w, docStatusId:"CO", partnerId:$bp}')")"; ok "$res" || true
  echo "  customer $bp: $(echo "$res" | jq -r '.summary // .logs[0].log // "ok"' | cut -c1-60)"
done
if [ "$MIX" = shipped ]; then
  say "tracking numbers / printed shipments"
  IFS=',' read -r -a PT <<<"$PAKET_TYPES"; shipped=0; printed=0
  for rec in "${SHIP_ORDERS[@]}"; do
    IFS='|' read -r n oid d <<<"$rec"
    io="$(odata m_inout "C_Order_ID eq $oid and MovementType eq 'C-' and DocStatus eq 'CO'" | jq -r '.records[0]')"; ioid="$(echo "$io" | jq -r '.id // empty')"; [ -n "$ioid" ] || continue
    if [ $(( n % 20 )) = 13 ]; then          # picked + label printed, carrier not yet scanned → "In Bearbeitung"
      # iDempiere refuses IsPrinted on a processed record via REST ("Cannot update IsPrinted on processed record") → SQL
      [ "$(echo "$io" | jq -r '.IsPrinted // false')" = true ] || sudo -u postgres psql -q -d idempiere -c "set search_path to adempiere, public" -c "update adempiere.m_inout set isprinted='Y', updated=now() where m_inout_id=$ioid" 2>/dev/null || echo "  isPrinted SQL failed for $ioid"   # search_path: ES trigger
      printed=$((printed+1)); continue
    fi
    [ -n "$(echo "$io" | jq -r '.TrackingNo // empty')" ] && { shipped=$((shipped+1)); continue; }
    trk="$(printf '%s%010d' "$TRACKING_PREFIX" $(( (n * 7919 + oid) % 10000000000 )))"
    pt="${PT[$(( n % ${#PT[@]} ))]}"; sd="$(date -d "$d +1 day" +%Y-%m-%dT10:00:00Z 2>/dev/null || date -j -v+1d -f %Y-%m-%d "$d" +%Y-%m-%dT10:00:00Z)"   # REST wants yyyy-MM-dd'T'HH:mm:ss'Z'
    res="$(api PUT /api/inouts/tracking "$(jq -n --argjson id "$ioid" --arg t "$trk" --arg sd "$sd" --argjson pt "$pt" '{id:$id, trackingNo:$t, isCommissioned:true, shippingDate:$sd, paketTypeId:$pt}')")"
    ok "$res" && shipped=$((shipped+1)) || true
  done
  echo "  $shipped shipments with tracking number, $printed printed (in progress)"
fi
say "invoices"
for oid in "${INV_ORDERS[@]}"; do
  io="$(odata m_inout "C_Order_ID eq $oid and MovementType eq 'C-' and DocStatus eq 'CO'" | first_id)"; [ -n "$io" ] || continue
  [ -z "$(odata c_invoice "C_Order_ID eq $oid" | first_id)" ] || continue
  res="$(api POST /api/processes/c_invoice_generate "$(jq -n --argjson o "$MORG" --argjson io "$io" --arg d "$TODAY" '{organizationId:$o, inOutId:$io, docAction:"CO", dateInvoiced:$d, consolidateDocument:false}')")"; ok "$res" || true; echo "  order $oid invoiced"
done

# --- 5. returns: RMA (completed) + customer return receipt (completed) + photos ------------------------------------------
say "$RETURNS returns"
[ -n "$REASONS_FILE" ] && mapfile -t REASONS < <(grep -v '^\s*$' "$REASONS_FILE" | sed "s/|RETURN$/|$RMA_TYPE_RETURN/; s/|BOUNCE$/|$RMA_TYPE_BOUNCE/") || REASONS=("Widerruf – Duft gefällt nicht|$RMA_TYPE_RETURN" "Flasche beim Transport beschädigt|$RMA_TYPE_RETURN" "Unzustellbar – Annahme verweigert|$RMA_TYPE_BOUNCE" "Falscher Artikel bestellt|$RMA_TYPE_RETURN" "Zerstäuber defekt|$RMA_TYPE_RETURN" "Adresse unbekannt – Rückläufer|$RMA_TYPE_BOUNCE" "Doppelt bestellt|$RMA_TYPE_RETURN" "Geschenk nicht gewünscht|$RMA_TYPE_RETURN")
r=0
for io in $(odata m_inout "AD_Org_ID eq $MORG and IsSOTrx eq true and MovementType eq 'C-' and DocStatus eq 'CO'" 200 | jq -r '.records[] | .id' | awk 'NR % 3 == 1'); do
  r=$((r+1)); [ "$r" -le "$RETURNS" ] || break
  IFS='|' read -r reason rtype <<<"${REASONS[$(( (r-1) % ${#REASONS[@]} ))]}"; rname="$PREFIX-RMA-$(printf '%02d' "$r")"
  iorec="$(odata m_inout "M_InOut_ID eq $io" | jq -r '.records[0]')"; bp="$(echo "$iorec" | jq -r .C_BPartner_ID.id)"; bpl="$(echo "$iorec" | jq -r .C_BPartner_Location_ID.id)"; oid="$(echo "$iorec" | jq -r .C_Order_ID.id)"
  iol="$(odata m_inoutline "M_InOut_ID eq $io" | jq -r '.records[0]')"; iolid="$(echo "$iol" | jq -r .id)"; pid="$(echo "$iol" | jq -r .M_Product_ID.id)"
  pline="$(grep "^$pid|" "$PHOTO_DIR/articles.txt" | head -1)"; pval="$(echo "$pline" | cut -d'|' -f2)"; pname="$(echo "$pline" | cut -d'|' -f3)"; price="$(echo "$pline" | cut -d'|' -f4)"; price="${price:-0}"
  rma="$(odata m_rma "AD_Org_ID eq $MORG and Name eq '$rname'" | first_id)"
  if [ -z "$rma" ]; then
    res="$(api POST /api/rma/store "$(jq -n --argjson o "$MORG" --argjson bp "$bp" --argjson io "$io" --argjson oid "$oid" --argjson dt "$DOCTYPE_RMA" --argjson rt "$rtype" --arg n "$rname" --arg desc "$reason" --argjson iol "$iolid" --argjson pr "$price" --argjson cur "$CURRENCY_ID" --argjson me "$ME" \
      '{organizationId:$o, isActive:true, isSOTrx:true, name:$n, description:$desc, partnerId:$bp, inOutId:$io, orderId:$oid, docTypeId:$dt, rmaTypeId:$rt, currencyId:$cur, amt:$pr, salesRepId:$me,
        rmaLines:[{AD_Org_ID:{id:$o, tableName:"AD_Org"}, line:10, isActive:true, qty:1, amt:$pr, description:$desc, M_InOutLine_ID:{id:$iol, tableName:"M_InOutLine"}}]}')")"; ok "$res" || continue; rma="$(echo "$res" | jq -r .id)"
    echo "  RMA $rname ($rma) for shipment $io: $reason"
  fi
  rrec="$(odata m_rma "M_RMA_ID eq $rma")"; rst="$(echo "$rrec" | jq -r '.records[0].DocStatus.id // "DR"')"; ruid="$(echo "$rrec" | jq -r '.records[0].uid')"; rdoc="$(echo "$rrec" | jq -r '.records[0].DocumentNo')"
  # an existing RMA defines shipment/product/customer for the receipt (the shipment list above shifts between runs)
  rio="$(echo "$rrec" | jq -r '.records[0].InOut_ID.id // empty')"
  if [ -n "$rio" ] && [ "$rio" != "$io" ]; then
    io="$rio"; iorec="$(odata m_inout "M_InOut_ID eq $io" | jq -r '.records[0]')"; bp="$(echo "$iorec" | jq -r .C_BPartner_ID.id)"; bpl="$(echo "$iorec" | jq -r .C_BPartner_Location_ID.id)"
    pid="$(odata m_rmaline "M_RMA_ID eq $rma" 5 | jq -r '.records[0].M_Product_ID.id // empty')"; [ -n "$pid" ] || pid="$(odata m_inoutline "M_InOut_ID eq $io" | jq -r '.records[0].M_Product_ID.id')"
    pline="$(grep "^$pid|" "$PHOTO_DIR/articles.txt" | head -1)"; pval="$(echo "$pline" | cut -d'|' -f2)"; pname="$(echo "$pline" | cut -d'|' -f3)"
  fi
  if [ "$rst" = DR ] || [ "$rst" = IP ]; then da="$(api POST "/api/rma/$rma/document-action" '{"documentAction":"CO"}')"; ok "$da" || continue; echo "  RMA $rdoc completed"; fi
  rl="$(odata m_rmaline "M_RMA_ID eq $rma" 5 | first_id)"
  # (no '+' in filters: the generic filter route turns it into a space) → by RMA + customer-return doc type
  cr="$(odata m_inout "M_RMA_ID eq $rma and C_DocType_ID eq $DOCTYPE_CR and DocStatus eq 'CO'" | first_id)"
  if [ -z "$cr" ]; then
    res="$(api POST /api/inouts/store "$(jq -n --argjson o "$MORG" --argjson w "$MWH" --argjson bp "$bp" --argjson bpl "$bpl" --argjson rma "$rma" --argjson dt "$DOCTYPE_CR" --arg d "$TODAY" --arg desc "Retoure $rdoc – $reason" \
      --argjson rl "$rl" --argjson p "$pid" --argjson loc "$RETLOC" --argjson u "$UOM_ID" \
      '{organizationId:$o, isActive:true, isSOTrx:true, movementTypeId:"C+", docTypeId:$dt, rmaId:$rma, partnerId:$bp, partnerLocationId:$bpl, warehouseId:$w, movementDate:$d, dateAcct:$d, description:$desc,
        inOutLines:[{AD_Org_ID:{id:$o, tableName:"AD_Org"}, line:10, isActive:true, movementQty:1, qtyEntered:1, M_RMALine_ID:{id:$rl, tableName:"M_RMALine"}, M_Product_ID:{id:$p, tableName:"M_Product"}, M_Locator_ID:{id:$loc, tableName:"M_Locator"}, C_UOM_ID:{id:$u, tableName:"C_UOM"}}]}')")"; ok "$res" || continue; cr="$(echo "$res" | jq -r .id)"
    da="$(api POST "/api/inouts/$cr/document-action" '{"documentAction":"CO"}')"; ok "$da" || true; echo "  return receipt $cr -> $(echo "$da" | jq -r '.DocStatus.identifier // .DocStatus.id // "?"')"
  fi
  cruid="$(odata m_inout "M_InOut_ID eq $cr" | jq -r '.records[0].uid')"
  f1="$PHOTO_DIR/rma-$rname-artikel.jpg"; f2="$PHOTO_DIR/rma-$rname-paket.jpg"; im="$(img_for "$pval")"
  [ -s "$f1" ] || photo "$f1" ${im:+--image "$im"} "Retoure $rdoc – Wareneingang" "Artikel: $pname" "Grund: $reason"
  [ -s "$f2" ] || photo "$f2" --parcel "$rdoc" "Retourenpaket $rdoc" "Zustand bei Annahme dokumentiert"
  f3="$PHOTO_DIR/rma-$rname-wareneingang-$cr.jpg"; [ -s "$f3" ] || cp "$f2" "$f3"
  attach M_RMA "$rma" "$ruid" "$f1"; attach M_RMA "$rma" "$ruid" "$f2"; attach M_InOut "$cr" "$cruid" "$f3"
done
say "done — org $MORG: $ORDERS orders, $r returns, photos in $PHOTO_DIR"
