#!/usr/bin/env bash # OMNL — Create one new office (by name, parentId, externalId, openingDate) and fund it with a two-leg M1 transfer from HO. # Rail B: Leg 1 at HO (Dr 2100 / Cr 1410), Leg 2 at new office (Dr 1410 / Cr 2100). # Usage: # OFFICE_NAME="Crunchygalaxy Unip Lda - Portugal" EXTERNAL_ID="CRUNCHYGALAXY-515159573" \ # OPENING_DATE="2026-02-24" AMOUNT=1000000000 APPROVER="" \ # bash scripts/omnl/create-office-and-fund.sh # Optional: SKIP_OFFICE_CREATE=1 (use existing office by EXTERNAL_ID), SKIP_TRANSFER=1 (create office only), DRY_RUN=1, SKIP_INITIAL_CLOSURES=1 (do not run closures before legs; use when posting on same day as existing closure would block). # Requires: resolve_ids.sh, omnl-gl-closures-post.sh, omnl-je-maker.sh, omnl-je-checker.sh, omnl-audit-packet-office20.sh. set -euo pipefail REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" SKIP_OFFICE_CREATE="${SKIP_OFFICE_CREATE:-0}" SKIP_TRANSFER="${SKIP_TRANSFER:-0}" SKIP_INITIAL_CLOSURES="${SKIP_INITIAL_CLOSURES:-0}" DRY_RUN="${DRY_RUN:-0}" OPENING_DATE="${OPENING_DATE:-$(date +%Y-%m-%d)}" TX_DATE="${TX_DATE:-$(date +%Y-%m-%d)}" : "${OFFICE_NAME:?Set OFFICE_NAME}" : "${EXTERNAL_ID:?Set EXTERNAL_ID}" : "${AMOUNT:?Set AMOUNT (minor units, e.g. 1000000000 for 1B)}" # Material (≥10M) requires APPROVER if [ "${AMOUNT:-0}" -ge 10000000 ] 2>/dev/null; then : "${APPROVER:?Set APPROVER for material posting}" fi if [ -f "${REPO_ROOT}/omnl-fineract/.env" ]; then set +u; source "${REPO_ROOT}/omnl-fineract/.env" 2>/dev/null || true; set -u elif [ -f "${REPO_ROOT}/.env" ]; then set +u; source "${REPO_ROOT}/.env" 2>/dev/null || true; set -u fi BASE_URL="${OMNL_FINERACT_BASE_URL:-}" TENANT="${OMNL_FINERACT_TENANT:-omnl}" USER="${OMNL_FINERACT_USER:-app.omnl}" PASS="${OMNL_FINERACT_PASSWORD:-}" [ -z "$BASE_URL" ] || [ -z "$PASS" ] && { echo "Set OMNL_FINERACT_BASE_URL and OMNL_FINERACT_PASSWORD" >&2; exit 1; } CURL_OPTS=(-s -S -H "Fineract-Platform-TenantId: ${TENANT}" -H "Content-Type: application/json" -u "${USER}:${PASS}") # --- Resolve GL IDs echo "=== Resolve IDs ===" >&2 bash "${REPO_ROOT}/scripts/omnl/resolve_ids.sh" source "${REPO_ROOT}/ids.env" 2>/dev/null || source ids.env 2>/dev/null : "${ID_1410:?}" : "${ID_2100:?}" # --- Create or find office offices_json=$(curl "${CURL_OPTS[@]}" "${BASE_URL}/offices" 2>/dev/null) NEW_OFFICE_ID=$(echo "$offices_json" | jq -r --arg e "$EXTERNAL_ID" '.[] | select(.externalId == $e) | .id' 2>/dev/null | head -1) if [ -n "$NEW_OFFICE_ID" ] && [ "$NEW_OFFICE_ID" != "null" ]; then echo "Office already exists: officeId=$NEW_OFFICE_ID (externalId=$EXTERNAL_ID)" >&2 else if [ "$SKIP_OFFICE_CREATE" = "1" ]; then echo "SKIP_OFFICE_CREATE=1 and office not found for externalId=$EXTERNAL_ID" >&2 exit 1 fi payload=$(jq -n \ --arg name "$OFFICE_NAME" \ --arg openingDate "$OPENING_DATE" \ --arg externalId "$EXTERNAL_ID" \ '{ name: $name, parentId: 1, openingDate: $openingDate, externalId: $externalId, dateFormat: "yyyy-MM-dd", locale: "en" }') if [ "$DRY_RUN" = "1" ]; then echo "DRY_RUN: would POST /offices $payload" >&2 exit 0 fi res=$(curl "${CURL_OPTS[@]}" -X POST -d "$payload" "${BASE_URL}/offices" 2>/dev/null) || true NEW_OFFICE_ID=$(echo "$res" | jq -r '.resourceId // .officeId // empty') [ -z "$NEW_OFFICE_ID" ] && { echo "Failed to create office: $res" >&2; exit 1; } echo "Created office: officeId=$NEW_OFFICE_ID" >&2 fi if [ "$SKIP_TRANSFER" = "1" ]; then echo "SKIP_TRANSFER=1. NEW_OFFICE_ID=$NEW_OFFICE_ID" >&2 exit 0 fi # --- Closures (idempotent; skip if SKIP_INITIAL_CLOSURES=1 to allow same-day post when server blocks on closure date) if [ "$SKIP_INITIAL_CLOSURES" != "1" ]; then echo "=== GL closures ===" >&2 CLOSING_DATE="${TX_DATE}" bash "${REPO_ROOT}/scripts/omnl/omnl-gl-closures-post.sh" 2>/dev/null || true fi # --- Leg 1: HO (Office 1) Dr 2100 / Cr 1410 REF_L1="CRUNCHY-1-$(date +%Y%m%d)-TR1-1B-L1" REF_L2="CRUNCHY-${NEW_OFFICE_ID}-$(date +%Y%m%d)-TR1-1B-L2" echo "=== Leg 1 (HO reduction) ===" >&2 if [ "$DRY_RUN" = "1" ]; then echo "Would maker+checker REF=$REF_L1 OFFICE_ID=1 Dr 2100 Cr 1410 AMOUNT=$AMOUNT" >&2 else REQUIRES_APPROVAL=1 APPROVER="${APPROVER:-}" REFERENCE_NUMBER="$REF_L1" TX_DATE="$TX_DATE" OFFICE_ID=1 \ DEBIT_GL_ID="$ID_2100" CREDIT_GL_ID="$ID_1410" AMOUNT="$AMOUNT" \ bash "${REPO_ROOT}/scripts/omnl/omnl-je-maker.sh" PAYLOAD_FILE=$(ls "${REPO_ROOT}/reconciliation/je-${REF_L1}"*.payload.json 2>/dev/null | head -1) [ -z "$PAYLOAD_FILE" ] && PAYLOAD_FILE="${REPO_ROOT}/reconciliation/je-${REF_L1}.payload.json" PAYLOAD_FILE="$PAYLOAD_FILE" bash "${REPO_ROOT}/scripts/omnl/omnl-je-checker.sh" fi # --- Leg 2: New office Dr 1410 / Cr 2100 echo "=== Leg 2 (Office $NEW_OFFICE_ID funding) ===" >&2 if [ "$DRY_RUN" = "1" ]; then echo "Would maker+checker REF=$REF_L2 OFFICE_ID=$NEW_OFFICE_ID Dr 1410 Cr 2100 AMOUNT=$AMOUNT" >&2 else REQUIRES_APPROVAL=1 APPROVER="${APPROVER:-}" REFERENCE_NUMBER="$REF_L2" TX_DATE="$TX_DATE" OFFICE_ID="$NEW_OFFICE_ID" \ DEBIT_GL_ID="$ID_1410" CREDIT_GL_ID="$ID_2100" AMOUNT="$AMOUNT" \ bash "${REPO_ROOT}/scripts/omnl/omnl-je-maker.sh" PAYLOAD_FILE=$(ls "${REPO_ROOT}/reconciliation/je-${REF_L2}"*.payload.json 2>/dev/null | head -1) [ -z "$PAYLOAD_FILE" ] && PAYLOAD_FILE="${REPO_ROOT}/reconciliation/je-${REF_L2}.payload.json" PAYLOAD_FILE="$PAYLOAD_FILE" bash "${REPO_ROOT}/scripts/omnl/omnl-je-checker.sh" fi # --- Post-audit for new office echo "=== Post-audit (Office $NEW_OFFICE_ID) ===" >&2 if [ "$DRY_RUN" != "1" ]; then OUT_BASE="${REPO_ROOT}/reconciliation" OFFICE_ID="$NEW_OFFICE_ID" bash "${REPO_ROOT}/scripts/omnl/omnl-audit-packet-office20.sh" PACKET_DIR=$(find "${REPO_ROOT}/reconciliation" -maxdepth 1 -type d -name "audit-office${NEW_OFFICE_ID}-*" 2>/dev/null | sort -r | head -1) echo "Audit packet: $PACKET_DIR" >&2 fi # --- Re-lock echo "=== Re-lock closures ===" >&2 CLOSING_DATE="${TX_DATE}" bash "${REPO_ROOT}/scripts/omnl/omnl-gl-closures-post.sh" 2>/dev/null || true [ -n "${PACKET_DIR:-}" ] && echo "Package: cd reconciliation && zip -r ../CRUNCHY-${NEW_OFFICE_ID}-$(date +%Y%m%d)-TR1-1B-AUDIT.zip $(basename "$PACKET_DIR") && cd .." >&2 echo "Done. NEW_OFFICE_ID=$NEW_OFFICE_ID" >&2