#!/usr/bin/env bash # Country-aware completeness audit for one Arcodange thirdparty. # # Usage: # audit-thirdparty.sh # # Replaces the V1 dolibarr-invoice-audit/scripts/audit-km-thirdparty.sh # (which hardcoded socid=1) with a generalized version that: # - Detects whether the thirdparty is a client / supplier / both # - Applies country-specific completeness rules # FR → SIREN (idprof1) + SIRET (idprof2) + tva_intra (if VAT-registered) # EU non-FR → tva_intra required for B2B autoliquidation # Extra-EU → EIN-equivalent in idprof1 (or note that there's no enforceable rule) # - Checks email / phone / url / IBAN where applicable # # Exits 0 if every applicable field is populated, 1 otherwise. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOL_CURL="${SCRIPT_DIR}/../../dolibarr/scripts/dol-curl.sh" if [[ $# -lt 1 ]]; then echo "audit-thirdparty.sh: missing socid. Usage: audit-thirdparty.sh " >&2 exit 2 fi SOCID="$1" TMP_JSON="$(mktemp -t doltp.XXXXXX.json)" trap 'rm -f "${TMP_JSON}"' EXIT "${DOL_CURL}" "/thirdparties/${SOCID}" > "${TMP_JSON}" python3 - "${TMP_JSON}" <<'PY' import json, sys with open(sys.argv[1]) as f: d = json.load(f) if not d.get("id"): print(f"audit-thirdparty.sh: no thirdparty at id={sys.argv[1]}", file=sys.stderr) sys.exit(2) EU = set("AT BE BG HR CY CZ DK EE FI FR DE GR HU IE IT LV LT LU MT NL PL PT RO SK SI ES SE".split()) socid = d.get("id") name = d.get("name") or d.get("ref") cnty = d.get("country_code") or "" is_client = str(d.get("client") or "0") in ("1", "2", "3") # 1=client, 2=prospect, 3=both is_supplier = str(d.get("fournisseur") or "0") == "1" role_bits = [] if is_client: role_bits.append("client") if is_supplier: role_bits.append("supplier") role = "/".join(role_bits) or "neither" # What completeness rules apply depends on country + role def rules_for(cnty, is_client, is_supplier): rules = [] # Universal rules.append(("name", d.get("name"), True)) rules.append(("address", d.get("address"), True)) rules.append(("zip", d.get("zip"), True)) rules.append(("town", d.get("town"), True)) rules.append(("country_code", d.get("country_code"), True)) # Country-specific identifiers if cnty == "FR": rules.append(("idprof1 (SIREN)", d.get("idprof1"), True)) rules.append(("idprof2 (SIRET)", d.get("idprof2"), True)) rules.append(("idprof3 (APE)", d.get("idprof3"), False)) # nice-to-have rules.append(("tva_intra (VAT)", d.get("tva_intra"), is_supplier)) # mandatory if supplier elif cnty in EU and cnty: rules.append(("tva_intra (VAT EU)", d.get("tva_intra"), True)) # autoliquidation requires it rules.append(("idprof1 (national reg)", d.get("idprof1"), False)) elif cnty: # Non-EU rules.append(("idprof1 (tax id / EIN)", d.get("idprof1"), True)) rules.append(("idprof2", d.get("idprof2"), False)) # Contact rules.append(("email", d.get("email"), False)) rules.append(("phone", d.get("phone"), False)) rules.append(("url", d.get("url"), False)) return rules rules = rules_for(cnty, is_client, is_supplier) print("=" * 80) print(f" Thirdparty {socid} — {name} [{role}, country={cnty or '?'}]") print("=" * 80) mandatory_fails = 0 optional_fails = 0 for label, value, mandatory in rules: ok = value not in (None, "", "0") flag = "OK" if ok else ("XX" if mandatory else "--") suffix = "" if mandatory else " (optional)" print(f" [{flag}] {label:<22} = {value!r}{suffix}") if not ok: if mandatory: mandatory_fails += 1 else: optional_fails += 1 # Surface what bank account info we have, if any account_iban = d.get("iban") or "" if account_iban: print(f" [OK] iban = {account_iban!r}") elif is_client or is_supplier: print(f" [--] iban = (not set) (optional)") ao = d.get("array_options") or {} if ao: print(f" array_options (extrafields): {ao}") print() print(f" {len(rules) - mandatory_fails - optional_fails} pass, {mandatory_fails} mandatory fail(s), {optional_fails} optional unset") sys.exit(0 if mandatory_fails == 0 else 1) PY