455 lines
12 KiB
Bash
Executable File
455 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
WECLAPP_API_KEY="3fcf8ca6-bc33-4239-9033-8f8f78a10019"
|
||
WECLAPP_ENDPOINT="https://nextenterprise.weclapp.com/webapp/api/v1"
|
||
|
||
# jq erforderlich
|
||
if ! command -v jq >/dev/null 2>&1; then
|
||
echo "FEHLER: 'jq' ist nicht fehlt" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if [[ -z "$WECLAPP_API_KEY" ]]; then
|
||
echo "FEHLER: WECLAPP_API_KEY nicht gesetzt." >&2
|
||
exit 1
|
||
fi
|
||
|
||
|
||
|
||
log_file="$OUTPUT_FOLDER/weclapp_auswertung_$(date '+%d-%m-%Y').txt"
|
||
|
||
exec > >(tee -a "$log_file") 2>&1
|
||
|
||
echo "Logfile: $log_file"
|
||
echo "--------------------------------------------------------------"
|
||
|
||
|
||
# Alle Kunden abrufen und prüfen, ob die Kunden eine Klassifizierung haben.
|
||
ENDPOINT="customer"
|
||
weclapp_response=$(curl -sS --compressed -X GET \
|
||
-H "AuthenticationToken: ${WECLAPP_API_KEY}" \
|
||
-H "Accept: application/json" \
|
||
"${WECLAPP_ENDPOINT}/${ENDPOINT}?pageSize=1000")
|
||
|
||
# Anzahl Kunden ohne Klassifizierung
|
||
no_class_count=$(echo "$weclapp_response" | jq '
|
||
[.result[]
|
||
| select((.customerRatingName // "") | tostring | length == 0)
|
||
] | length
|
||
')
|
||
|
||
# Anzahl MSP-Kunden (Klasse B)
|
||
msp_count=$(echo "$weclapp_response" | jq '
|
||
[.result[]
|
||
| select(.customerRatingName == "B")
|
||
] | length
|
||
')
|
||
|
||
|
||
# Kunden ohne Klassifizierung
|
||
echo "--------------------------------------------------------------"
|
||
echo "--------------Fehlende-Zuordnung------------------------------"
|
||
echo "--------------------------------------------------------------"
|
||
echo "Für ${no_class_count} Kunden ist keine Klassifizierung vorgenommen:"
|
||
echo "$weclapp_response" | jq -r '
|
||
.result[]
|
||
| {company, customerNumber, customerRatingName}
|
||
| select((.customerRatingName // "") | tostring | length == 0)
|
||
| "- \(.company) (Kundennr: \(.customerNumber))"
|
||
'
|
||
|
||
echo ""
|
||
echo ""
|
||
echo "--------------------------------------------------------------"
|
||
echo "--------------MSP-Kunden--------------------------------------"
|
||
echo "--------------------------------------------------------------"
|
||
echo "${msp_count} Kunden haben einen MSP-Vertrag:"
|
||
|
||
# >>> ERWEITERUNG: MSP-Kunden-Company in Array speichern (ohne declare -A)
|
||
declare -a msp_companies=()
|
||
msp_map_file="/tmp/msp_customer_map.$$"
|
||
: > "$msp_map_file"
|
||
|
||
# Liste + Array befüllen
|
||
while IFS= read -r line; do
|
||
# line kommt als Tab-getrennt: company<TAB>customerNumber<TAB>customerRatingName
|
||
company="${line%%$'\t'*}"
|
||
rest="${line#*$'\t'}"
|
||
customerNumber="${rest%%$'\t'*}"
|
||
rating="${rest#*$'\t'}"
|
||
|
||
echo "- ${company} (Kundennr: ${customerNumber}, Klasse: ${rating})"
|
||
|
||
# nur sinnvolle Company-Namen speichern (nicht leer / nicht "null")
|
||
if [[ -n "$company" && "$company" != "null" ]]; then
|
||
msp_companies+=("$company")
|
||
printf '%s\t%s\n' "$company" "$customerNumber" >> "$msp_map_file"
|
||
fi
|
||
done < <(
|
||
echo "$weclapp_response" | jq -r '
|
||
.result[]
|
||
| {company, customerNumber, customerRatingName}
|
||
| select(.customerRatingName == "B")
|
||
| "\(.company)\t\(.customerNumber)\t\(.customerRatingName)"
|
||
'
|
||
)
|
||
|
||
|
||
echo ""
|
||
echo "Zu prüfende Kunden (Array msp_companies) – Anzahl: ${#msp_companies[@]}"
|
||
for c in "${msp_companies[@]}"; do
|
||
echo "- $c"
|
||
done
|
||
|
||
|
||
ENDPOINT="contract"
|
||
weclapp_response=$(curl -sS --compressed -X GET \
|
||
-H "AuthenticationToken: ${WECLAPP_API_KEY}" \
|
||
-H "Accept: application/json" \
|
||
"${WECLAPP_ENDPOINT}/${ENDPOINT}?pageSize=1000")
|
||
|
||
# Ziel-Datei
|
||
tmp_contract_file="/tmp/weclapp_contracts.json"
|
||
|
||
# Falls Datei existiert, zuerst löschen
|
||
if [ -f "$tmp_contract_file" ]; then
|
||
rm -f "$tmp_contract_file"
|
||
fi
|
||
|
||
# JSON unter /tmp speichern
|
||
printf '%s\n' "$weclapp_response" > "$tmp_contract_file"
|
||
|
||
echo "Contract-JSON wurde gespeichert unter: $tmp_contract_file"
|
||
|
||
|
||
|
||
|
||
echo ""
|
||
echo "--------------------------------------------------------------"
|
||
echo "--------------MSP-Kunden: Vertrag aus Datei-------------------"
|
||
echo "--------------------------------------------------------------"
|
||
|
||
tmp_contract_file="/tmp/weclapp_contracts.json"
|
||
|
||
if [ ! -f "$tmp_contract_file" ]; then
|
||
echo "Fehler: $tmp_contract_file existiert nicht."
|
||
rm -f "$msp_map_file"
|
||
exit 1
|
||
fi
|
||
|
||
now_s=$(date +%s)
|
||
|
||
normalize_company() {
|
||
printf '%s' "$1" \
|
||
| tr '\n' ' ' \
|
||
| sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' \
|
||
| tr '[:upper:]' '[:lower:]'
|
||
}
|
||
|
||
found_count=0
|
||
not_found_count=0
|
||
|
||
|
||
for company in "${msp_companies[@]}"; do
|
||
[ -z "$company" ] && continue
|
||
[ "$company" = "null" ] && continue
|
||
|
||
key="$(normalize_company "$company")"
|
||
|
||
# jq wählt den "besten" Vertrag gemäß Regeln:
|
||
# 1) gültig (unbefristet oder endDate in Zukunft)
|
||
# 2) innerhalb gültig: UNBEFRISTET, sonst größtes endDate, tie-breaker lastModifiedDate
|
||
# 3) wenn kein gültiger: größtes endDate (abgelaufen), tie-breaker lastModifiedDate
|
||
result=$(
|
||
jq -r --arg key "$key" --argjson now "$now_s" '
|
||
def norm:
|
||
tostring
|
||
| gsub("\\s+"; " ")
|
||
| gsub("^\\s+|\\s+$"; "")
|
||
| gsub("\\s*&\\s*"; "&")
|
||
| ascii_downcase;
|
||
|
||
def endMs:
|
||
if (.endDate // null) == null or ((.endDate|tostring|length)==0) then null else .endDate end;
|
||
|
||
def endStr:
|
||
if (.endDate // null) == null or ((.endDate|tostring|length)==0) then
|
||
"UNBEFRISTET"
|
||
else
|
||
((.endDate/1000) | strftime("%d.%m.%Y"))
|
||
end;
|
||
|
||
def isValid($now):
|
||
(endMs == null) or ((endMs/1000) >= $now);
|
||
|
||
# alle möglichen Company-Felder als Keys
|
||
def companyKeys:
|
||
[
|
||
.invoiceAddress.company?,
|
||
.deliveryAddress.company?,
|
||
.correspondenceAddress.company?
|
||
]
|
||
| map(select(. != null and (. | tostring | length) > 0))
|
||
| map(norm)
|
||
| unique;
|
||
|
||
($key | norm) as $custKey
|
||
|
||
# Kandidaten für diese Company (Match wenn irgendein Key passt)
|
||
| (.result
|
||
| map({
|
||
keys: companyKeys,
|
||
id: .id,
|
||
interval: (.contractItems[0].interval // ""),
|
||
lastModifiedDateMs: (.lastModifiedDate // 0),
|
||
endDateMs: endMs,
|
||
endDateStr: endStr,
|
||
valid: (isValid($now)),
|
||
contractStatus: (.contractStatus // "")
|
||
})
|
||
| map(select(.keys | any(. == $custKey)))
|
||
) as $matches
|
||
|
||
| if ($matches|length) == 0 then
|
||
empty
|
||
else
|
||
($matches | map(select(.valid))) as $valids
|
||
| if ($valids|length) > 0 then
|
||
# gültige: UNBEFRISTET bevorzugen, sonst max endDateMs, tie-breaker lastModifiedDateMs
|
||
(
|
||
($valids | map(select(.endDateMs == null)) | sort_by(.lastModifiedDateMs) | last)
|
||
// ($valids
|
||
| map(select(.endDateMs != null))
|
||
| sort_by([.endDateMs, .lastModifiedDateMs])
|
||
| last
|
||
)
|
||
) as $best
|
||
| {
|
||
id: $best.id,
|
||
interval: $best.interval,
|
||
end: $best.endDateStr,
|
||
status: $best.contractStatus
|
||
}
|
||
else
|
||
# kein gültiger -> letzter abgelaufener (max endDateMs), tie-breaker lastModifiedDateMs
|
||
($matches
|
||
| map(select(.endDateMs != null))
|
||
| sort_by([.endDateMs, .lastModifiedDateMs])
|
||
| last
|
||
) as $best2
|
||
| if $best2 == null then empty
|
||
else {
|
||
id: $best2.id,
|
||
interval: $best2.interval,
|
||
end: $best2.endDateStr,
|
||
status: $best2.contractStatus
|
||
}
|
||
end
|
||
end
|
||
end
|
||
' "$tmp_contract_file"
|
||
)
|
||
|
||
if [ -z "$result" ]; then
|
||
echo "Kunde: $company | KEIN_VERTRAG_GEFUNDEN"
|
||
not_found_count=$((not_found_count + 1))
|
||
else
|
||
cid=$(printf '%s\n' "$result" | jq -r '.id')
|
||
interval=$(printf '%s\n' "$result" | jq -r '.interval')
|
||
end=$(printf '%s\n' "$result" | jq -r '.end')
|
||
status=$(printf '%s\n' "$result" | jq -r '.status')
|
||
|
||
cust_nr=$(awk -F'\t' -v c="$company" '$1==c {print $2; exit}' "$msp_map_file")
|
||
[ -z "$cust_nr" ] && cust_nr="UNBEKANNT"
|
||
|
||
echo "Kunde: $company | Kundennr: $cust_nr | VertragsID: $cid | Interval: $interval | Ende: $end | Status: $status"
|
||
found_count=$((found_count + 1))
|
||
fi
|
||
done
|
||
|
||
|
||
echo ""
|
||
echo "--------------------------------------------------------------"
|
||
echo "Zusammenfassung:"
|
||
echo "Gefundene Verträge: $found_count"
|
||
echo "Kein Vertrag gefunden: $not_found_count"
|
||
echo "Gesamt MSP-Kunden geprüft: $((found_count + not_found_count))"
|
||
echo "--------------------------------------------------------------"
|
||
|
||
|
||
echo ""
|
||
echo "--------------------------------------------------------------"
|
||
echo "--------------MSP-Verträge: Laufzeit endet bald---------------"
|
||
echo "--------------------------------------------------------------"
|
||
|
||
# Ziel-Datei muss existieren
|
||
tmp_contract_file="/tmp/weclapp_contracts.json"
|
||
if [ ! -f "$tmp_contract_file" ]; then
|
||
echo "Fehler: $tmp_contract_file existiert nicht."
|
||
else
|
||
# Nächster + übernächster Monat (YYYY-MM)
|
||
next_ym=$(date -v+1m "+%Y-%m")
|
||
overnext_ym=$(date -v+2m "+%Y-%m")
|
||
|
||
# Monatsnamen (z.B. "März 2026")
|
||
next_label=$(date -j -f "%Y-%m" "$next_ym" "+%B %Y")
|
||
overnext_label=$(date -j -f "%Y-%m" "$overnext_ym" "+%B %Y")
|
||
|
||
# Temp-Listen
|
||
exp_next_file="/tmp/weclapp_exp_next.$$"
|
||
exp_overnext_file="/tmp/weclapp_exp_overnext.$$"
|
||
: > "$exp_next_file"
|
||
: > "$exp_overnext_file"
|
||
|
||
now_s=$(date +%s)
|
||
|
||
# Für jeden MSP-Kunden den "besten" Vertrag bestimmen (identische Logik wie zuvor),
|
||
# dann prüfen, ob endDate im nächsten/übernächsten Monat liegt
|
||
for company in "${msp_companies[@]}"; do
|
||
[ -z "$company" ] && continue
|
||
[ "$company" = "null" ] && continue
|
||
|
||
key="$(normalize_company "$company")"
|
||
|
||
result=$(
|
||
jq -r --arg key "$key" --argjson now "$now_s" '
|
||
def norm:
|
||
tostring
|
||
| gsub("\\s+"; " ")
|
||
| gsub("^\\s+|\\s+$"; "")
|
||
| gsub("\\s*&\\s*"; "&")
|
||
| ascii_downcase;
|
||
|
||
def endMs:
|
||
if (.endDate // null) == null or ((.endDate|tostring|length)==0) then null else .endDate end;
|
||
|
||
def endStr:
|
||
if (.endDate // null) == null or ((.endDate|tostring|length)==0) then
|
||
"UNBEFRISTET"
|
||
else
|
||
((.endDate/1000) | strftime("%d.%m.%Y"))
|
||
end;
|
||
|
||
def endYM:
|
||
if (endMs == null) then
|
||
null
|
||
else
|
||
((endMs/1000) | strftime("%Y-%m"))
|
||
end;
|
||
|
||
def isValid($now):
|
||
(endMs == null) or ((endMs/1000) >= $now);
|
||
|
||
def companyKeys:
|
||
[
|
||
.invoiceAddress.company?,
|
||
.deliveryAddress.company?,
|
||
.correspondenceAddress.company?
|
||
]
|
||
| map(select(. != null and (. | tostring | length) > 0))
|
||
| map(norm)
|
||
| unique;
|
||
|
||
($key | norm) as $custKey
|
||
|
||
| (.result
|
||
| map({
|
||
keys: companyKeys,
|
||
id: .id,
|
||
interval: (.contractItems[0].interval // ""),
|
||
lastModifiedDateMs: (.lastModifiedDate // 0),
|
||
endDateMs: endMs,
|
||
endDateStr: endStr,
|
||
endDateYM: endYM,
|
||
valid: (isValid($now)),
|
||
contractStatus: (.contractStatus // "")
|
||
})
|
||
| map(select(.keys | any(. == $custKey)))
|
||
) as $matches
|
||
|
||
| if ($matches|length) == 0 then
|
||
empty
|
||
else
|
||
($matches | map(select(.valid))) as $valids
|
||
| if ($valids|length) > 0 then
|
||
(
|
||
($valids | map(select(.endDateMs == null)) | sort_by(.lastModifiedDateMs) | last)
|
||
// ($valids
|
||
| map(select(.endDateMs != null))
|
||
| sort_by([.endDateMs, .lastModifiedDateMs])
|
||
| last
|
||
)
|
||
) as $best
|
||
| {
|
||
id: $best.id,
|
||
interval: $best.interval,
|
||
end: $best.endDateStr,
|
||
endYM: $best.endDateYM,
|
||
status: $best.contractStatus
|
||
}
|
||
else
|
||
($matches
|
||
| map(select(.endDateMs != null))
|
||
| sort_by([.endDateMs, .lastModifiedDateMs])
|
||
| last
|
||
) as $best2
|
||
| if $best2 == null then empty
|
||
else {
|
||
id: $best2.id,
|
||
interval: $best2.interval,
|
||
end: $best2.endDateStr,
|
||
endYM: $best2.endDateYM,
|
||
status: $best2.contractStatus
|
||
}
|
||
end
|
||
end
|
||
end
|
||
' "$tmp_contract_file"
|
||
)
|
||
|
||
# Wenn kein Vertrag gefunden oder UNBEFRISTET -> überspringen
|
||
[ -z "$result" ] && continue
|
||
|
||
endYM=$(printf '%s\n' "$result" | jq -r '.endYM // empty')
|
||
[ -z "$endYM" ] && continue # UNBEFRISTET oder keine endDate
|
||
|
||
cid=$(printf '%s\n' "$result" | jq -r '.id')
|
||
interval=$(printf '%s\n' "$result" | jq -r '.interval')
|
||
end=$(printf '%s\n' "$result" | jq -r '.end')
|
||
status=$(printf '%s\n' "$result" | jq -r '.status')
|
||
|
||
cust_nr=$(awk -F'\t' -v c="$company" '$1==c {print $2; exit}' "$msp_map_file")
|
||
[ -z "$cust_nr" ] && cust_nr="UNBEKANNT"
|
||
|
||
line="- $company (Kundennr: $cust_nr | VertragsID: $cid | Interval: $interval | Ende: $end | Status: $status)"
|
||
|
||
if [ "$endYM" = "$next_ym" ]; then
|
||
printf '%s\n' "$line" >> "$exp_next_file"
|
||
elif [ "$endYM" = "$overnext_ym" ]; then
|
||
printf '%s\n' "$line" >> "$exp_overnext_file"
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
echo "Die folgenden Verträge laufen im nächsten Monat ($next_label) aus:"
|
||
if [ -s "$exp_next_file" ]; then
|
||
cat "$exp_next_file"
|
||
else
|
||
echo "- (keine)"
|
||
fi
|
||
|
||
echo ""
|
||
echo "Die folgenden Verträge laufen im übernächsten Monat ($overnext_label) aus:"
|
||
if [ -s "$exp_overnext_file" ]; then
|
||
cat "$exp_overnext_file"
|
||
else
|
||
echo "- (keine)"
|
||
fi
|
||
|
||
rm -f "$exp_next_file" "$exp_overnext_file"
|
||
fi
|
||
|
||
# Cleanup
|
||
rm -f "$msp_map_file"
|
||
rm -f "$tmp_contract_file"
|
||
|