#!/usr/bin/env bash # Enumerate Arcodange recurring invoice templates. # # The Dolibarr API doesn't expose a list endpoint for templates. We probe # ids 1..MAX and stop after N consecutive empty hits. An "empty" template # has `id == null` (Dolibarr returns 200 with a hollow object for unknown ids). # # Usage: # list-templates.sh [--max-id N] # default 50 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOL_CURL="${SCRIPT_DIR}/../../dolibarr/scripts/dol-curl.sh" MAX_ID=50 EMPTY_TOLERANCE=5 # stop after this many consecutive empties while [[ $# -gt 0 ]]; do case "$1" in --max-id) MAX_ID="$2"; shift 2 ;; -h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "list-templates.sh: unknown arg: $1" >&2; exit 2 ;; esac done WORK="$(mktemp -d -t dtmpl.XXXXXX)" trap 'rm -rf "${WORK}"' EXIT CONSECUTIVE_EMPTY=0 FOUND=() for id in $(seq 1 "${MAX_ID}"); do if ! "${DOL_CURL}" "/invoices/templates/${id}" > "${WORK}/${id}.json" 2>/dev/null; then # Network / auth error — bail rather than mis-classify echo "list-templates.sh: error fetching id=${id}, stopping probe" >&2 break fi REAL=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print('1' if d.get('id') else '0')" "${WORK}/${id}.json") if [[ "${REAL}" == "1" ]]; then FOUND+=("${id}") CONSECUTIVE_EMPTY=0 else CONSECUTIVE_EMPTY=$((CONSECUTIVE_EMPTY+1)) rm "${WORK}/${id}.json" if [[ ${CONSECUTIVE_EMPTY} -ge ${EMPTY_TOLERANCE} ]]; then break fi fi done echo "# Probed ids 1..${id}, found ${#FOUND[@]} template(s) (stopped after ${EMPTY_TOLERANCE} consecutive empties)" echo if [[ ${#FOUND[@]} -eq 0 ]]; then echo "# (no recurring templates found)" exit 0 fi python3 - "${WORK}" "${FOUND[@]}" <<'PY' import json, sys, os, datetime work = sys.argv[1] ids = sys.argv[2:] print(f"{'id':>3} {'ref':<28} {'socid':>5} {'freq':<10} {'gen':>6} {'next':<12} {'last':<12} {'sus':>3} {'auto_val':>8} {'total_ht':>10}") print("-" * 115) for iid in ids: d = json.load(open(os.path.join(work, f"{iid}.json"))) freq_n = int(d.get("frequency") or 0) freq_u = d.get("unit_frequency") or "" freq_str = f"every {freq_n}{freq_u}" if freq_n else "OFF (0)" nb_done = d.get("nb_gen_done") or "0" nb_max = d.get("nb_gen_max") or "0" gen = f"{nb_done}/{nb_max}" if nb_max != "0" else f"{nb_done}/∞" nextd = d.get("date_when") or "-" lastd = d.get("date_last_gen") or "-" sus = d.get("suspended") or "0" autoval = d.get("auto_validate") or "0" print(f"{iid:>3} {(d.get('ref') or d.get('title') or '-'):<28} {str(d.get('socid') or '-'):>5} {freq_str:<10} {gen:>6} {nextd[:10]:<12} {lastd[:10]:<12} {sus:>3} {autoval:>8} {d.get('total_ht','-'):>10}") PY