First two of an expected family of dolibarr-* skills: - dolibarr/: platform reference — DOLAPIKEY auth, the voir_tous ACL trap, endpoint catalogue, the dol-curl.sh wrapper, .env credentials layout (gitignored, mode 600). Every future workflow skill depends on this one. - dolibarr-invoice-audit/: first workflow — list KissMetrics invoices, audit one invoice end-to-end (JSON facts + PDF mandatory-mention checklist against the French legal corpus), audit the KissMetrics thirdparty record. Live captures in examples/ include real audit findings to surface to the Arcodange × KissMetrics cohort review: PDFs are missing capital social, L.441-10 penalties, 40 € indemnity, L.123-22 / R.123-237; KissMetrics thirdparty has no EIN (idprof1..6 all empty); static/config/company.json holds placeholder values and a wrong forme juridique (claims SAS, the real Dolibarr is SARL). .gitignore hardened with *.credentials, secrets/, *.key, and an explicit .claude/skills/**/.env pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
2.1 KiB
Bash
Executable File
73 lines
2.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Audit the KissMetrics thirdparty record (socid=1) for completeness.
|
|
#
|
|
# Usage:
|
|
# audit-km-thirdparty.sh
|
|
#
|
|
# Checks the contact fields and the idprof1..idprof6 slots where the US EIN
|
|
# should live for a non-EU customer. Today EIN is missing — this script's
|
|
# exit-1 IS the finding to surface to the cohort review.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DOL_CURL="${SCRIPT_DIR}/../../dolibarr/scripts/dol-curl.sh"
|
|
KM_SOCID=1
|
|
|
|
TMP_JSON="$(mktemp -t doltp.XXXXXX.json)"
|
|
trap 'rm -f "${TMP_JSON}"' EXIT
|
|
|
|
"${DOL_CURL}" "/thirdparties/${KM_SOCID}" > "${TMP_JSON}"
|
|
|
|
python3 - "${TMP_JSON}" <<'PY'
|
|
import json, sys
|
|
with open(sys.argv[1]) as f:
|
|
d = json.load(f)
|
|
|
|
print("=" * 80)
|
|
print(f" KissMetrics thirdparty audit — socid={d.get('id')}")
|
|
print("=" * 80)
|
|
|
|
required = [
|
|
("name", d.get("name")),
|
|
("client flag", "yes" if str(d.get("client")) == "1" else None),
|
|
("country_code", d.get("country_code")),
|
|
("state_id", d.get("state_id")),
|
|
("address", d.get("address")),
|
|
("zip", d.get("zip")),
|
|
("town", d.get("town")),
|
|
("email", d.get("email")),
|
|
("phone", d.get("phone")),
|
|
("url", d.get("url")),
|
|
]
|
|
idprofs = [(f"idprof{i}", d.get(f"idprof{i}")) for i in range(1, 7)]
|
|
ein_present = any(v not in (None, "", "0") for _, v in idprofs)
|
|
|
|
passes = fails = 0
|
|
def check(label, value):
|
|
global passes, fails
|
|
ok = value not in (None, "", "0")
|
|
print(f" [{'OK' if ok else 'XX'}] {label:<18} = {value!r}")
|
|
if ok: passes += 1
|
|
else: fails += 1
|
|
|
|
for label, value in required:
|
|
check(label, value)
|
|
|
|
print()
|
|
print(" idprof1..idprof6 (EIN slot for non-EU customers):")
|
|
for label, value in idprofs:
|
|
print(f" {label:<10} = {value!r}")
|
|
print(f" [{'OK' if ein_present else 'XX'}] EIN present (any idprof populated)")
|
|
if ein_present: passes += 1
|
|
else: fails += 1
|
|
|
|
ao = d.get("array_options") or {}
|
|
print()
|
|
print(f" array_options (Dolibarr extrafields): {ao if ao else '(none)'}")
|
|
|
|
print()
|
|
print(f" {passes} pass / {fails} fail")
|
|
sys.exit(0 if fails == 0 else 1)
|
|
PY
|