didi-website-erp-crm/erp_crm/build-from-zero.sh

188 lines
8.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Build de la 0 ERP/CRM DiDi - one-shot installer
# Versiune 2.0, iulie 2026
#
# Cerinte: docker, docker compose v2. Reteaua Docker `didi-network` trebuie sa existe
# (o creeaza scriptul daca lipseste).
#
# NOTA: toata logica de setup ruleaza IN container, prin `bench execute` pe functiile
# din app-ul didi_custom. Scripturile din scripts/setup/ sunt LEGACY (prima versiune,
# rulate pe host) si NU mai sunt folosite de acest installer.
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$DIR"
log() { printf '\033[1;36m[%(%H:%M:%S)T]\033[0m %s\n' -1 "$*"; }
err() { printf '\033[1;31m[%(%H:%M:%S)T] ERROR:\033[0m %s\n' -1 "$*" >&2; exit 1; }
# ── Verificari ──────────────────────────────────────────────
command -v docker >/dev/null || err "docker nu e instalat"
docker compose version >/dev/null || err "docker compose v2 nu e instalat"
[ -f .env ] || err ".env nu exista. Copiaza din .env.example si configureaza."
source .env
SITE_NAME="${SITE_NAME:-didi-erp}"
ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin}"
MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:?seteaza MYSQL_ROOT_PASSWORD in .env}"
ERPNEXT_PORT="${ERPNEXT_PORT:-8080}"
if ! docker network inspect didi-network >/dev/null 2>&1; then
log "Creez reteaua didi-network..."
docker network create didi-network
fi
# Helper: ruleaza o functie Python din bench, IN container (cu venv-ul frappe).
# NU folosi `python3` pe host sau in container pentru logica frappe — esueaza tacut.
bexec() {
log " bench execute $1"
docker exec didi-erpnext bash -c \
"cd /home/frappe/frappe-bench && bench --site $SITE_NAME execute $1" \
|| log " ⚠️ $1 a esuat — verifica manual"
}
# Helper: insert/set-value tolerant la 'exista deja'
bexec_kw() {
docker exec didi-erpnext bash -c \
"cd /home/frappe/frappe-bench && bench --site $SITE_NAME execute $1 --kwargs '$2'" \
>/dev/null 2>&1 || true
}
# ── Pas 1. Pornire containere ───────────────────────────────
log "Pas 1/10. Pornire containere..."
docker compose up -d
log " Astept MariaDB sa fie healthy..."
for i in $(seq 1 30); do
status=$(docker inspect --format='{{.State.Health.Status}}' didi-mariadb 2>/dev/null || echo "starting")
[ "$status" = "healthy" ] && break
sleep 2
done
[ "$status" = "healthy" ] || err "MariaDB nu a devenit healthy dupa 60s"
log " OK"
# ── Pas 2. Astepta ERPNext gunicorn ────────────────────────
log "Pas 2/10. Astept ERPNext gunicorn..."
for i in $(seq 1 30); do
if docker exec didi-erpnext curl -sf http://127.0.0.1:8000 >/dev/null 2>&1; then
log " OK"
break
fi
sleep 3
done
# ── Pas 3. Config Redis ─────────────────────────────────────
log "Pas 3/10. Configurare Redis in common_site_config..."
docker exec didi-erpnext bash -c "
cd /home/frappe/frappe-bench &&
bench set-config -g redis_cache 'redis://redis-cache:6379' &&
bench set-config -g redis_queue 'redis://redis-queue:6379' &&
bench set-config -g redis_socketio 'redis://redis-queue:6379'
" >/dev/null
# ── Pas 4. Creare site daca nu exista ──────────────────────
log "Pas 4/10. Verificare/creare site $SITE_NAME..."
if docker exec didi-erpnext test -f "/home/frappe/frappe-bench/sites/${SITE_NAME}/site_config.json"; then
log " Site $SITE_NAME exista deja"
else
log " Creez site $SITE_NAME (dureaza ~3 min)..."
docker exec didi-erpnext bash -c "
cd /home/frappe/frappe-bench &&
bench new-site $SITE_NAME \
--mariadb-root-password '$MYSQL_ROOT_PASSWORD' \
--admin-password '$ADMIN_PASSWORD' \
--db-host mariadb \
--install-app erpnext
"
fi
# ── Pas 5. Fix MariaDB user @ wildcard ──────────────────────
log "Pas 5/10. Fix DB user pentru orice host..."
DB_NAME=$(docker exec didi-erpnext python3 -c "
import json
with open('/home/frappe/frappe-bench/sites/${SITE_NAME}/site_config.json') as f:
print(json.load(f)['db_name'])
")
DB_PWD=$(docker exec didi-erpnext python3 -c "
import json
with open('/home/frappe/frappe-bench/sites/${SITE_NAME}/site_config.json') as f:
print(json.load(f)['db_password'])
")
docker exec didi-mariadb mariadb -uroot -p"$MYSQL_ROOT_PASSWORD" -e "
CREATE USER IF NOT EXISTS '$DB_NAME'@'%' IDENTIFIED BY '$DB_PWD';
GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_NAME'@'%';
FLUSH PRIVILEGES;
" 2>/dev/null
# ── Pas 6. Patch portal_user + install didi_custom ─────────
log "Pas 6/10. Patch portal_users + install didi_custom..."
docker exec didi-erpnext sed -i \
's|for portal_user in self.portal_users:|for portal_user in (getattr(self, "portal_users", None) or []):|' \
/home/frappe/frappe-bench/apps/erpnext/erpnext/selling/doctype/customer/customer.py 2>/dev/null || true
docker exec didi-erpnext bash -c "
cd /home/frappe/frappe-bench &&
printf 'frappe\nerpnext\ndidi_custom\n' > sites/apps.txt &&
env/bin/pip install -e apps/didi_custom 2>&1 | tail -2 &&
bench --site $SITE_NAME install-app didi_custom 2>&1 | tail -2 || true
" >/dev/null
# Fix installed_apps registry
docker exec didi-mariadb mariadb -uroot -p"$MYSQL_ROOT_PASSWORD" "$DB_NAME" -e "
UPDATE tabDefaultValue SET defvalue='[\"frappe\",\"erpnext\",\"didi_custom\"]' WHERE defkey='installed_apps';
" 2>/dev/null
# ── Pas 7. Companie + plan de conturi Romania ──────────────
log "Pas 7/10. Companie TOP CLOSSERS SRL + plan de conturi RO..."
# Warehouse Type "Transit" e cerut la crearea companiei si lipseste in instalarea curata
bexec_kw frappe.client.insert '{"doc":{"doctype":"Warehouse Type","name":"Transit"}}'
bexec didi_custom.make_company_ro3.execute
bexec didi_custom.setup_ro_coa.execute
# An fiscal curent — lipseste complet dupa instalarea curata; facturile pica fara el
YEAR=$(date +%Y)
log " An fiscal $YEAR..."
bexec_kw frappe.client.insert "{\"doc\":{\"doctype\":\"Fiscal Year\",\"year\":\"$YEAR\",\"year_start_date\":\"$YEAR-01-01\",\"year_end_date\":\"$YEAR-12-31\"}}"
# Price list implicit in RON
bexec_kw frappe.client.set_value '{"doctype":"Price List","name":"Standard Selling","fieldname":"currency","value":"RON"}'
# ── Pas 8. Doctypes, campuri custom, continut CMS ──────────
log "Pas 8/10. Doctypes + campuri custom + CMS..."
bexec didi_custom.setup_doctypes.execute
bexec didi_custom.integrations.migrations.setup_custom_fields.execute
bexec didi_custom.fix_sa.execute
bexec didi_custom.populate_content.execute
# ── Pas 9. Integrare website, print format, email, workspace ─
log "Pas 9/10. Items DIDI + user API + print format + email + workspace..."
bexec didi_custom.setup_website_integration.execute
bexec didi_custom.setup_phase2_final.setup_print_format
bexec didi_custom.setup_website_integration.fix_print_format
bexec didi_custom.setup_email.execute
bexec didi_custom.setup_workspaces.execute
bexec didi_custom.setup_shortcuts.execute
# ── Pas 10. Build + restart ────────────────────────────────
log "Pas 10/10. Build assets + restart..."
docker exec didi-erpnext bash -c 'cd /home/frappe/frappe-bench && bench build --force' 2>&1 | tail -2
docker exec didi-redis-cache redis-cli FLUSHALL >/dev/null
docker exec didi-redis-queue redis-cli FLUSHALL >/dev/null
docker restart didi-erpnext >/dev/null
sleep 8
# ── Verificare finala ───────────────────────────────────────
HTTP=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${ERPNEXT_PORT}")
log "============================================="
if [ "$HTTP" = "200" ] || [ "$HTTP" = "301" ] || [ "$HTTP" = "302" ]; then
log " ERPNext UP: http://localhost:${ERPNEXT_PORT}"
log " Login: Administrator / ${ADMIN_PASSWORD}"
log " Site: ${SITE_NAME}"
else
log " HTTP ${HTTP} - verifica log-urile cu: docker compose logs erpnext"
fi
log "============================================="
log ""
log "Credentialele API pentru website (user website_api@didi-erp.local) se genereaza"
log "la Pas 9 — vezi output-ul lui setup_website_integration.execute."
log "Pentru date de test (smoke testing): bash seed-data.sh"