feat(molde): tarefas do financeiro

This commit is contained in:
deploy
2026-09-29 02:10:59 +00:00
parent 35740bb257
commit 1581c5b8c5
@@ -0,0 +1,220 @@
# =============================================================================
# Os tres processos do Financeiro que ninguem dispara sozinho: sem eles nasce
# mensalidade nenhuma (dia 1, 06h), nota fiscal nenhuma processa (de hora em
# hora) e a conciliacao nunca roda (6 em 6 horas). O financeiro nao tem
# @Scheduled de proposito: com mais de uma replica, duas varreduras correriam.
#
# NO MOLDE desde 2026-09-29. Antes so os quatro tenants antigos tinham isto, e
# um tenant VENDIDO nascia sem cobrar os alunos. O client `operacao` e o Secret
# `operacao-credentials` nascem no provisionamento (EtapaSelarSegredos +
# ClientDaOperacao, captacao 4.20).
#
# Tenant sem o financeiro no plano (bronze): o runtime responde 403 "plano nao
# contratado" e o script termina OK ("nada a fazer") — nao e falha, e nao
# alerta. Comprado o modulo avulso, as mesmas tarefas passam a valer sozinhas.
# =============================================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: financeiro-cron
namespace: {{TENANT}}-prod
data:
disparar.sh: |
#!/bin/sh
set -eu
: "${KEYCLOAK_URL:?falta KEYCLOAK_URL}"
: "${ALVO:?falta ALVO}"
: "${CLIENT_SECRET:?falta CLIENT_SECRET}"
: "${ISSUER_HOST:?falta ISSUER_HOST}"
echo "[$(date -u +%FT%TZ)] alvo: ${ALVO}"
i=0
until curl -sS -o /dev/null -m 5 "${KEYCLOAK_URL}" 2>/dev/null; do
i=$((i+1))
if [ "$i" -ge 24 ]; then
echo "ERRO: ${KEYCLOAK_URL} inalcancavel depois de 2 min." >&2
echo " Se for pod novo, e a politica de rede ainda nao programada;" >&2
echo " se persistir, confira o Service keycloak e a deny-cross-tenant." >&2
exit 1
fi
sleep 5
done
RESP="$(printf 'grant_type=client_credentials&client_id=operacao&client_secret=%s' \
"${CLIENT_SECRET}" \
| curl -sS --fail-with-body -X POST "${KEYCLOAK_URL}" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'X-Forwarded-Proto: https' \
-H 'X-Forwarded-Port: 443' \
-H "X-Forwarded-Host: ${ISSUER_HOST}" -d @-)"
TOKEN="$(printf '%s' "${RESP}" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')"
if [ -z "${TOKEN}" ]; then
echo "ERRO: nao consegui token pelo client 'operacao'." >&2
echo " Confira o Secret operacao-credentials e se o client existe no realm." >&2
exit 1
fi
# Sem --fail-with-body: o codigo e lido aqui, para separar "plano nao
# contratado" (tenant bronze sem o financeiro — nada a fazer, NAO e falha)
# de erro de verdade. Antes, todo bronze gerava um KubeJobFailed por hora
# (demo, 2026-09-28) e cada alerta gastava a cota de e-mail.
CODIGO="$(printf 'header = "Authorization: Bearer %s"\n' "${TOKEN}" \
| curl -sS -K - -X POST "${ALVO}" -o /tmp/saida.json -w '%{http_code}')"
echo "${CODIGO}"
echo "resposta:"
cat /tmp/saida.json
echo
if [ "${CODIGO}" = "403" ] && grep -q "plano nao contratado" /tmp/saida.json; then
echo "financeiro fora do plano deste tenant: nada a fazer."
exit 0
fi
case "${CODIGO}" in
2*) ;;
*) echo "ERRO: ${ALVO} respondeu ${CODIGO}." >&2; exit 1 ;;
esac
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: financeiro-mensalidades
namespace: {{TENANT}}-prod
spec:
schedule: "0 6 1 * *"
timeZone: America/Sao_Paulo
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
activeDeadlineSeconds: 900
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: disparar
image: docker.io/curlimages/curl:8.9.1
imagePullPolicy: Never
command: ["/bin/sh", "-c"]
args:
- |
export ALVO="http://runtime-operacao/api/financeiro/matriculas/gerar-mensalidades?competencia=$(date +%Y-%m)"
exec /bin/sh /script/disparar.sh
env:
- name: KEYCLOAK_URL
value: http://keycloak.plataforma-prod.svc:8080/realms/{{REALM}}/protocol/openid-connect/token
- name: ISSUER_HOST
value: auth.athleticmap.com
- name: CLIENT_SECRET
valueFrom:
secretKeyRef:
name: operacao-credentials
key: client-secret
resources:
requests: { cpu: 10m, memory: 32Mi }
limits: { cpu: 200m, memory: 64Mi }
volumeMounts:
- name: script
mountPath: /script
volumes:
- name: script
configMap:
name: financeiro-cron
defaultMode: 0555
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: financeiro-notas
namespace: {{TENANT}}-prod
spec:
schedule: "15 * * * *"
timeZone: America/Sao_Paulo
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
activeDeadlineSeconds: 600
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: disparar
image: docker.io/curlimages/curl:8.9.1
imagePullPolicy: Never
command: ["/bin/sh", "/script/disparar.sh"]
env:
- name: KEYCLOAK_URL
value: http://keycloak.plataforma-prod.svc:8080/realms/{{REALM}}/protocol/openid-connect/token
- name: ISSUER_HOST
value: auth.athleticmap.com
- name: ALVO
value: http://runtime-operacao/api/financeiro/notas/processar?limite=50
- name: CLIENT_SECRET
valueFrom:
secretKeyRef:
name: operacao-credentials
key: client-secret
resources:
requests: { cpu: 10m, memory: 32Mi }
limits: { cpu: 200m, memory: 64Mi }
volumeMounts:
- name: script
mountPath: /script
volumes:
- name: script
configMap:
name: financeiro-cron
defaultMode: 0555
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: financeiro-conciliacao
namespace: {{TENANT}}-prod
spec:
schedule: "30 */6 * * *"
timeZone: America/Sao_Paulo
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
activeDeadlineSeconds: 900
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: disparar
image: docker.io/curlimages/curl:8.9.1
imagePullPolicy: Never
command: ["/bin/sh", "/script/disparar.sh"]
env:
- name: KEYCLOAK_URL
value: http://keycloak.plataforma-prod.svc:8080/realms/{{REALM}}/protocol/openid-connect/token
- name: ISSUER_HOST
value: auth.athleticmap.com
- name: ALVO
value: http://runtime-operacao/api/financeiro/conciliacao?limiteEventos=100&limiteCobrancas=100
- name: CLIENT_SECRET
valueFrom:
secretKeyRef:
name: operacao-credentials
key: client-secret
resources:
requests: { cpu: 10m, memory: 32Mi }
limits: { cpu: 200m, memory: 64Mi }
volumeMounts:
- name: script
mountPath: /script
volumes:
- name: script
configMap:
name: financeiro-cron
defaultMode: 0555