Saltar a contenido

3. Despliegue del ERP

3.1. Clonar repositorio

cd /opt
git clone <URL_REPO> tienda
cd /opt/tienda

# Crear usuario de sistema dedicado
useradd -r -d /opt/tienda -s /bin/bash tienda
chown -R tienda:tienda /opt/tienda

3.2. Instalar dependencias

sudo -u tienda bash
cd /opt/tienda
source .venv/bin/activate
pip install -r requirements.txt

Dependencias clave verificadas:

Paquete Propósito
Django==5.1.4 Framework
channels==4.3.2, daphne==4.2.1 WebSocket / ASGI
psycopg2-binary==2.9.9 Driver Postgres
WeasyPrint==66.0 Generación PDF
Pillow>=10.4.0 Imágenes
celery[redis]==5.4.0, django-celery-beat==2.7.0 Tareas async + cron
djangorestframework==3.15.2 API REST (tienda externa, opcional)
lxml==5.3.0 Parser XML rápido
signxml==4.0.4 Firma XAdES-BES
cryptography>=43 Manejo de claves/cert
qrcode==7.4.2 QR timbre fiscal
requests==2.33.1 HTTP cliente DGII
pytesseract>=0.3.13, pdfplumber>=0.11.0 OCR factura proveedor

Verificar:

python -c "import signxml, lxml, cryptography, qrcode; print('OK firma')"
python -c "import pdfplumber, pytesseract; print('OK OCR')"

3.3. Variables de entorno

Crear /opt/tienda/.env con permisos restrictivos:

touch /opt/tienda/.env
chown tienda:tienda /opt/tienda/.env
chmod 600 /opt/tienda/.env

Contenido (estructura — reemplazar valores reales):

# Django
DJANGO_SETTINGS_MODULE=tienda.settings
DJANGO_SECRET_KEY=<48+ caracteres random>
DJANGO_ENV=production
DJANGO_DEBUG=0
DJANGO_ALLOWED_HOSTS=erp.tuempresa.com.do,www.erp.tuempresa.com.do,127.0.0.1
DJANGO_CSRF_TRUSTED_ORIGINS=https://erp.tuempresa.com.do

# Cookies/Headers seguros
DJANGO_SECURE_SSL_REDIRECT=1
DJANGO_HSTS_SECONDS=31536000
DJANGO_CSRF_COOKIE_SECURE=1
DJANGO_SESSION_COOKIE_SECURE=1

# Base de datos
DB_ENGINE=django.db.backends.postgresql
DB_NAME=tienda_db
DB_USER=tienda
DB_PASSWORD=<password fuerte>
DB_HOST=127.0.0.1
DB_PORT=5432

# Redis / Celery
REDIS_URL=redis://:<password_redis>@127.0.0.1:6379/0
CELERY_BROKER_URL=redis://:<password_redis>@127.0.0.1:6379/1
CELERY_RESULT_BACKEND=redis://:<password_redis>@127.0.0.1:6379/2

# DGII e-CF
DGII_PUBLIC_BASE_URL=https://erp.tuempresa.com.do
DGII_SOFTWARE_NAME=Artistica Garcia
DGII_SOFTWARE_VERSION_PUBLIC=2.0
DGII_AMBIENTE_DEFAULT=CERT
DGII_CERTIFICADO_P12=/opt/tienda/secrets/ccpsd_RNC.p12
DGII_CERTIFICADO_PASSWORD=<password p12>
DGII_RNC_EMISOR=130768021
DGII_CADENA_CCPSD=/opt/tienda/secrets/CCPSD_cadena.crt

# Endpoints DGII
DGII_API_BASE_CERT=https://ecf.dgii.gov.do/CerteCF
DGII_API_BASE_PROD=https://ecf.dgii.gov.do/eCF

# Seguridad bearer/mTLS
ECF_BEARER_REQUIRED=1
ECF_MTLS_REQUIRED=0
ECF_VALIDATE_XSD_BEFORE_SEND=1

# API tienda virtual (opcional)
INTERNAL_API_TOKEN=<token random 48+ chars>

Buena práctica: gestionar .env con git-crypt, sops o Ansible Vault. NUNCA versionarlo plano.

3.4. Migraciones, collectstatic, superusuario

sudo -u tienda bash -c 'cd /opt/tienda && set -a && . .env && set +a && source .venv/bin/activate && \
    python manage.py migrate && \
    python manage.py collectstatic --noinput && \
    python manage.py createsuperuser'

El comando set -a; . .env; set +a es obligatorio al correr manage.py desde SSH directo. Sin él, Django usa los defaults (SQLite) y la migración se aplica al archivo equivocado.

3.5. Gunicorn (HTTP) + Daphne (WebSocket) con systemd

/etc/systemd/system/tienda.service (Gunicorn → puerto 8000):

[Unit]
Description=Tienda ERP (Gunicorn HTTP)
After=network.target postgresql.service redis-server.service

[Service]
User=tienda
Group=tienda
WorkingDirectory=/opt/tienda
EnvironmentFile=/opt/tienda/.env
ExecStart=/opt/tienda/.venv/bin/gunicorn tienda.wsgi:application \
    --bind 127.0.0.1:8000 \
    --workers 4 \
    --threads 2 \
    --timeout 60 \
    --access-logfile /opt/tienda/logs/gunicorn-access.log \
    --error-logfile /opt/tienda/logs/gunicorn-error.log
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

/etc/systemd/system/tienda-daphne.service (WebSocket → puerto 8002):

[Unit]
Description=Tienda ERP (Daphne WebSocket)
After=network.target redis-server.service

[Service]
User=tienda
Group=tienda
WorkingDirectory=/opt/tienda
EnvironmentFile=/opt/tienda/.env
ExecStart=/opt/tienda/.venv/bin/daphne -b 127.0.0.1 -p 8002 tienda.asgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

Activar:

systemctl daemon-reload
systemctl enable --now tienda.service tienda-daphne.service
systemctl status tienda tienda-daphne

Validar: curl -I https://erp.tuempresa.com.do/ debe devolver 200/302 desde Gunicorn.