Compare commits
10 Commits
573d9b7f6d
...
388d02051f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
388d02051f | ||
|
|
d0d82aa8e1 | ||
|
|
2e1ad5526c | ||
|
|
9bae910d3a | ||
|
|
3c58240206 | ||
|
|
4190c957ff | ||
|
|
7071fa2eb9 | ||
|
|
74fe447f73 | ||
|
|
b0067aeb6e | ||
|
|
64b26e494d |
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.env
|
||||||
|
*.md
|
||||||
|
docker-compose.yml
|
||||||
|
.DS_Store
|
||||||
|
daily_order_reports/*.csv
|
||||||
|
email.log
|
||||||
23
.env.example
Normal file
23
.env.example
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Crew Sportswear Database
|
||||||
|
DB_HOST_CREW=mysql
|
||||||
|
DB_PORT_CREW=3306
|
||||||
|
DB_NAME_CREW=custom_design
|
||||||
|
DB_USER_CREW=crew_user
|
||||||
|
DB_PASS_CREW=your_crew_password
|
||||||
|
|
||||||
|
# MerchBay Database
|
||||||
|
DB_HOST_MERCHBAY=mysql
|
||||||
|
DB_PORT_MERCHBAY=3306
|
||||||
|
DB_NAME_MERCHBAY=merchbay_laravel
|
||||||
|
DB_USER_MERCHBAY=merchbay_user
|
||||||
|
DB_PASS_MERCHBAY=your_merchbay_password
|
||||||
|
|
||||||
|
# SMTP Configuration
|
||||||
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
|
||||||
|
# Crew SMTP (mail@crewsportswear.com)
|
||||||
|
SMTP_PASS_CREW=your_crew_gmail_app_password
|
||||||
|
|
||||||
|
# MerchBay SMTP (support@merchbay.com)
|
||||||
|
SMTP_PASS_MERCHBAY=your_merchbay_gmail_app_password
|
||||||
62
.gitea/workflows/build-push.yml
Normal file
62
.gitea/workflows/build-push.yml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Build and Push to Registry
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo
|
||||||
|
cd /workspace/repo
|
||||||
|
git checkout $GITHUB_REF_NAME
|
||||||
|
|
||||||
|
- name: Extract version from tag
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
cd /workspace/repo
|
||||||
|
docker build -t email_reports_crew:${VERSION} .
|
||||||
|
docker tag email_reports_crew:${VERSION} email_reports_crew:latest
|
||||||
|
|
||||||
|
- name: Login to Docker Registry
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin $REGISTRY_URL
|
||||||
|
|
||||||
|
- name: Tag and Push images
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
|
||||||
|
run: |
|
||||||
|
docker tag email_reports_crew:${VERSION} ${REGISTRY_URL}/email_reports_crew:${VERSION}
|
||||||
|
docker tag email_reports_crew:${VERSION} ${REGISTRY_URL}/email_reports_crew:latest
|
||||||
|
|
||||||
|
docker push ${REGISTRY_URL}/email_reports_crew:${VERSION}
|
||||||
|
docker push ${REGISTRY_URL}/email_reports_crew:latest
|
||||||
|
|
||||||
|
echo "✓ Pushed images:"
|
||||||
|
echo " ${REGISTRY_URL}/email_reports_crew:${VERSION}"
|
||||||
|
echo " ${REGISTRY_URL}/email_reports_crew:latest"
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
docker image prune -af
|
||||||
|
echo "✓ Cleanup completed"
|
||||||
168
.gitea/workflows/deploy.yml
Normal file
168
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
name: Deploy Production Email Reports (Unified)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo
|
||||||
|
cd /workspace/repo
|
||||||
|
git checkout $GITHUB_REF_NAME
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
cd /workspace/repo
|
||||||
|
docker build -t email_reports_unified:latest .
|
||||||
|
docker save email_reports_unified:latest | gzip > email_reports_unified.tar.gz
|
||||||
|
|
||||||
|
- name: Setup SSH
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||||
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
|
||||||
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
- name: Upload image and compose
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||||
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
run: |
|
||||||
|
scp -i ~/.ssh/id_ed25519 \
|
||||||
|
/workspace/repo/email_reports_unified.tar.gz \
|
||||||
|
/workspace/repo/docker-compose.yml \
|
||||||
|
${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/
|
||||||
|
|
||||||
|
- name: Deploy on server
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||||
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
run: |
|
||||||
|
ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF'
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DEPLOY_DIR="/var/www/apps/email_reports"
|
||||||
|
sudo mkdir -p "$DEPLOY_DIR"
|
||||||
|
sudo chown $USER:$USER "$DEPLOY_DIR"
|
||||||
|
|
||||||
|
echo "Loading image"
|
||||||
|
docker load < /tmp/email_reports_unified.tar.gz
|
||||||
|
|
||||||
|
echo "Removing old email_reports images"
|
||||||
|
docker images | grep email_reports_unified | grep -v "$(docker images email_reports_unified:latest -q)" | awk '{print $3}' | xargs -r docker rmi -f || true
|
||||||
|
|
||||||
|
echo "Updating compose file"
|
||||||
|
cp /tmp/docker-compose.yml "$DEPLOY_DIR/docker-compose.yml"
|
||||||
|
|
||||||
|
cd "$DEPLOY_DIR"
|
||||||
|
|
||||||
|
echo "Checking .env file"
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
echo ".env file not found at $DEPLOY_DIR/.env"
|
||||||
|
echo "Please create it first with required variables:"
|
||||||
|
echo " Crew: DB_HOST_CREW, DB_NAME_CREW, DB_USER_CREW, DB_PASS_CREW, SMTP_PASS_CREW"
|
||||||
|
echo " MerchBay: DB_HOST_MERCHBAY, DB_NAME_MERCHBAY, DB_USER_MERCHBAY, DB_PASS_MERCHBAY, SMTP_PASS_MERCHBAY"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Fixing .env permissions"
|
||||||
|
sudo chown $USER:$USER .env
|
||||||
|
sudo chmod 600 .env
|
||||||
|
|
||||||
|
echo "Ensure networks"
|
||||||
|
docker network inspect crew-app-net >/dev/null 2>&1 || \
|
||||||
|
docker network create crew-app-net
|
||||||
|
|
||||||
|
echo "Creating required directories"
|
||||||
|
mkdir -p daily_order_reports_crew daily_order_reports_merchbay
|
||||||
|
touch email.log
|
||||||
|
chmod 666 email.log
|
||||||
|
chmod 755 daily_order_reports_crew daily_order_reports_merchbay
|
||||||
|
|
||||||
|
echo "Stopping existing containers"
|
||||||
|
docker compose down || true
|
||||||
|
docker rm -f email_reports_unified email_reports_crew email_reports_merchbay || true
|
||||||
|
|
||||||
|
echo "Starting unified container (env vars from .env file)"
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
echo "Waiting for container to start"
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q email_reports_unified; then
|
||||||
|
echo "✓ Container is running"
|
||||||
|
echo "Testing cron daemon"
|
||||||
|
docker exec email_reports_unified ps aux | grep -q crond && echo "✓ Cron is running"
|
||||||
|
|
||||||
|
echo "Cron schedule:"
|
||||||
|
docker exec email_reports_unified crontab -l
|
||||||
|
|
||||||
|
echo "Testing database connectivity (Crew)"
|
||||||
|
docker exec email_reports_unified ping -c 1 mysql && echo "✓ Can reach MySQL"
|
||||||
|
else
|
||||||
|
echo "✗ Container failed to start"
|
||||||
|
docker compose logs
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Cleanup"
|
||||||
|
rm -f /tmp/email_reports_unified.tar.gz /tmp/docker-compose.yml
|
||||||
|
|
||||||
|
echo "Docker cleanup"
|
||||||
|
docker image prune -af --filter "until=24h" || true
|
||||||
|
docker container prune -f || true
|
||||||
|
docker system df
|
||||||
|
|
||||||
|
echo "✓ Deployment completed!"
|
||||||
|
echo "Email reports container: email_reports_unified"
|
||||||
|
echo "Next scheduled runs:"
|
||||||
|
echo " - Crew: 23:55 CT"
|
||||||
|
echo " - MerchBay: 23:56 CT"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Verify deployment
|
||||||
|
shell: sh
|
||||||
|
env:
|
||||||
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||||
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
|
run: |
|
||||||
|
ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF'
|
||||||
|
echo "Container status:"unified --format "table {{.Names}}\t{{.Status}}\t{{.State}}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Recent logs:"
|
||||||
|
docker logs --tail 20 email_reports_unified
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "To test manually:"
|
||||||
|
echo " Crew: docker exec email_reports_unified BRAND=crew php /app/send_report.php"
|
||||||
|
echo " MerchBay: docker exec email_reports_unified BRAND=merchbay php /app/send_report.php"
|
||||||
|
echo ""
|
||||||
|
echo "To view logs:"
|
||||||
|
echo " docker logs -f email_reports_unified"
|
||||||
|
echo " docker exec email_reports_unified tail -f /app/email.log"
|
||||||
|
echo ""
|
||||||
|
echo "Filter by brand:"
|
||||||
|
echo " docker exec email_reports_unified grep '[CREW]' /app/email.log"
|
||||||
|
echo " docker exec email_reports_unified grep '[MERCHBAY]'
|
||||||
|
echo " docker exec email_reports_crew tail -f /app/email.log"
|
||||||
|
EOF
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.env
|
||||||
|
email.log
|
||||||
|
daily_order_reports/
|
||||||
|
daily_order_reports_crew/
|
||||||
|
daily_order_reports_merchbay/
|
||||||
|
.DS_Store
|
||||||
|
phpmailer/
|
||||||
417
DEPLOYMENT.md
Normal file
417
DEPLOYMENT.md
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
# Email Reports Deployment Checklist
|
||||||
|
|
||||||
|
## Pre-Deployment Setup
|
||||||
|
|
||||||
|
### 1. Configure Gitea Secrets
|
||||||
|
|
||||||
|
In your Gitea repository **Settings → Secrets**, add:
|
||||||
|
|
||||||
|
```
|
||||||
|
DEPLOY_SSH_KEY = <your-ssh-private-key>
|
||||||
|
DEPLOY_HOST = <server-ip-or-hostname>
|
||||||
|
DEPLOY_USER = <ssh-username>
|
||||||
|
REGISTRY_URL = <docker-registry-url> (optional, for version tags)
|
||||||
|
REGISTRY_USER = <registry-username> (optional)
|
||||||
|
REGISTRY_PASSWORD = <registry-password> (optional)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Server Preparation
|
||||||
|
|
||||||
|
SSH into your production server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@your-server
|
||||||
|
|
||||||
|
# Create deployment directory
|
||||||
|
sudo mkdir -p /var/www/apps/email_reports
|
||||||
|
sudo chown $USER:$USER /var/www/apps/email_reports
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
|
||||||
|
# Create .env file with production values
|
||||||
|
vim .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required .env variables:**
|
||||||
|
```env
|
||||||
|
DB_HOST=mysql # Your MySQL container name
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_NAME=custom_design
|
||||||
|
DB_USER=crew_user
|
||||||
|
DB_PASS=your_secure_password # CHANGE THIS
|
||||||
|
|
||||||
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=mail@crewsportswear.com
|
||||||
|
SMTP_PASS=your_gmail_app_password # Get from Gmail
|
||||||
|
|
||||||
|
EMAIL_TO=graphics@crewsportswear.com
|
||||||
|
EMAIL_BCC=webmaster@crewsportswear.com,angelo@crewsportswear.com,production@crewsportswear.com
|
||||||
|
|
||||||
|
APP_URL=https://www.crewsportswear.com
|
||||||
|
ADMIN_URL=https://admin.crewsportswear.app
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Secure .env
|
||||||
|
chmod 600 .env
|
||||||
|
|
||||||
|
# Create data directories
|
||||||
|
mkdir -p daily_order_reports
|
||||||
|
touch email.log
|
||||||
|
chmod 666 email.log
|
||||||
|
|
||||||
|
# Ensure Docker network exists
|
||||||
|
docker network create crew-app-net || true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Verify MySQL Access
|
||||||
|
|
||||||
|
Test database connectivity from server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# If MySQL is in a container:
|
||||||
|
docker exec mysql_container_name mysql -u crew_user -p -e "SHOW DATABASES;"
|
||||||
|
|
||||||
|
# Verify network connectivity
|
||||||
|
docker run --rm --network crew-app-net alpine ping -c 3 mysql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Get Gmail App Password
|
||||||
|
|
||||||
|
1. Go to https://myaccount.google.com/apppasswords
|
||||||
|
2. Create new app password for "Email Reports"
|
||||||
|
3. Copy the 16-character password
|
||||||
|
4. Add to `.env` as `SMTP_PASS`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Workflow
|
||||||
|
|
||||||
|
### Option 1: Automated (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From your local machine
|
||||||
|
cd email_reports
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Deploy email reports container"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
Gitea Actions will automatically:
|
||||||
|
- ✅ Build Docker image
|
||||||
|
- ✅ Transfer to server
|
||||||
|
- ✅ Deploy to /var/www/apps/email_reports
|
||||||
|
- ✅ Start container with cron
|
||||||
|
- ✅ Verify deployment
|
||||||
|
|
||||||
|
Watch the workflow in Gitea: **Actions** tab
|
||||||
|
|
||||||
|
### Option 2: Manual Deployment
|
||||||
|
|
||||||
|
SSH to server and deploy manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
|
||||||
|
# Pull latest code (if using git on server)
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# Or copy files manually via scp
|
||||||
|
scp docker-compose.yml user@server:/var/www/apps/email_reports/
|
||||||
|
|
||||||
|
# Build and deploy
|
||||||
|
docker-compose build
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
docker ps | grep email_reports
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Post-Deployment Verification
|
||||||
|
|
||||||
|
### 1. Check Container Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container is running
|
||||||
|
docker ps --filter name=email_reports_crew
|
||||||
|
|
||||||
|
# Expected output:
|
||||||
|
# CONTAINER ID IMAGE STATUS NAMES
|
||||||
|
# abc123... email_reports_crew:latest Up 2 minutes email_reports_crew
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Verify Cron is Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec email_reports_crew ps aux | grep crond
|
||||||
|
|
||||||
|
# Should show: crond -f -l 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Check Cron Schedule
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec email_reports_crew crontab -l
|
||||||
|
|
||||||
|
# Expected output:
|
||||||
|
# 55 23 * * * cd /app && php index.php >> /var/log/cron.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Test Database Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec email_reports_crew php -r "
|
||||||
|
\$host = getenv('DB_HOST');
|
||||||
|
\$db = getenv('DB_NAME');
|
||||||
|
\$user = getenv('DB_USER');
|
||||||
|
\$pass = getenv('DB_PASS');
|
||||||
|
new PDO(\"mysql:host=\$host;dbname=\$db\", \$user, \$pass);
|
||||||
|
echo 'Database connection: OK\n';
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Run Manual Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Execute report immediately (don't wait for cron)
|
||||||
|
docker exec email_reports_crew php /app/index.php
|
||||||
|
|
||||||
|
# Check output
|
||||||
|
docker exec email_reports_crew cat /app/email.log | tail -5
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected log entry:
|
||||||
|
```
|
||||||
|
2026-01-02 15:30:00 successfully sent
|
||||||
|
```
|
||||||
|
OR
|
||||||
|
```
|
||||||
|
2026-01-02 15:30:00 No order for today
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Verify Email Sent
|
||||||
|
|
||||||
|
Check your inbox at `graphics@crewsportswear.com` for test email.
|
||||||
|
|
||||||
|
### 7. Check Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container logs
|
||||||
|
docker logs email_reports_crew
|
||||||
|
|
||||||
|
# Cron logs
|
||||||
|
docker exec email_reports_crew tail -f /var/log/cron.log
|
||||||
|
|
||||||
|
# Email execution logs
|
||||||
|
docker exec email_reports_crew tail -f /app/email.log
|
||||||
|
|
||||||
|
# CSV files generated
|
||||||
|
docker exec email_reports_crew ls -lh /app/daily_order_reports/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### View Real-time Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Follow cron execution
|
||||||
|
docker logs -f email_reports_crew
|
||||||
|
|
||||||
|
# Follow email log
|
||||||
|
docker exec email_reports_crew tail -f /app/email.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Next Scheduled Run
|
||||||
|
|
||||||
|
Cron runs at **23:55 Central Time daily**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Current time in container
|
||||||
|
docker exec email_reports_crew date
|
||||||
|
|
||||||
|
# Should show: America/Chicago timezone
|
||||||
|
docker exec email_reports_crew cat /etc/timezone
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container health status
|
||||||
|
docker inspect email_reports_crew | grep -A 5 Health
|
||||||
|
|
||||||
|
# Manual health check
|
||||||
|
docker exec email_reports_crew ps aux | grep crond
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Container Won't Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View startup logs
|
||||||
|
docker logs email_reports_crew
|
||||||
|
|
||||||
|
# Check compose file
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
cat docker-compose.yml
|
||||||
|
|
||||||
|
# Verify .env exists
|
||||||
|
ls -la .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Failed
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test network connectivity
|
||||||
|
docker exec email_reports_crew ping mysql
|
||||||
|
|
||||||
|
# Verify database credentials in .env
|
||||||
|
docker exec email_reports_crew env | grep DB_
|
||||||
|
```
|
||||||
|
|
||||||
|
### Email Not Sending
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable debug mode
|
||||||
|
docker exec email_reports_crew sed -i 's|// \$mail->SMTPDebug = 3;|\$mail->SMTPDebug = 3;|' /app/index.php
|
||||||
|
|
||||||
|
# Run manually with debug output
|
||||||
|
docker exec email_reports_crew php /app/index.php
|
||||||
|
|
||||||
|
# Check SMTP credentials
|
||||||
|
docker exec email_reports_crew env | grep SMTP_
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cron Not Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check cron daemon
|
||||||
|
docker exec email_reports_crew ps aux | grep crond
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
docker restart email_reports_crew
|
||||||
|
|
||||||
|
# Verify crontab
|
||||||
|
docker exec email_reports_crew crontab -l
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wrong Timezone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check timezone
|
||||||
|
docker exec email_reports_crew date
|
||||||
|
docker exec email_reports_crew cat /etc/timezone
|
||||||
|
|
||||||
|
# Should be: America/Chicago
|
||||||
|
# If not, rebuild image
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
If deployment fails:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
|
||||||
|
# Stop and remove container
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Load previous image (if kept)
|
||||||
|
docker load < /tmp/email_reports_crew_backup.tar.gz
|
||||||
|
|
||||||
|
# Or pull from registry
|
||||||
|
docker pull your-registry/email_reports_crew:previous-version
|
||||||
|
|
||||||
|
# Start with old image
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Gitea secrets configured
|
||||||
|
- [ ] Server directory created: `/var/www/apps/email_reports`
|
||||||
|
- [ ] `.env` file created with all variables
|
||||||
|
- [ ] Database credentials tested
|
||||||
|
- [ ] Gmail app password obtained and configured
|
||||||
|
- [ ] Docker network `crew-app-net` exists
|
||||||
|
- [ ] Code pushed to `main` branch (triggers workflow)
|
||||||
|
- [ ] Workflow completed successfully (green checkmark)
|
||||||
|
- [ ] Container is running: `docker ps | grep email_reports`
|
||||||
|
- [ ] Cron is running: `ps aux | grep crond`
|
||||||
|
- [ ] Manual test executed successfully
|
||||||
|
- [ ] Email received by recipients
|
||||||
|
- [ ] Logs showing successful execution
|
||||||
|
- [ ] Scheduled for next automatic run at 23:55 CT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## For MerchBay Reports
|
||||||
|
|
||||||
|
Repeat the same process in a separate directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On server
|
||||||
|
sudo mkdir -p /var/www/apps/email_reports_merchbay
|
||||||
|
# ... follow same steps with merchbay database/credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Update Recipients
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Edit .env on server
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
vim .env
|
||||||
|
|
||||||
|
# Update EMAIL_TO or EMAIL_BCC
|
||||||
|
# Then restart container
|
||||||
|
docker-compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change Schedule
|
||||||
|
|
||||||
|
Edit Dockerfile, rebuild, and redeploy:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Change from 23:55 to 22:00
|
||||||
|
RUN echo "0 22 * * * cd /app && php index.php" > /etc/crontabs/root
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Historical Reports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all generated CSVs
|
||||||
|
docker exec email_reports_crew ls -lh /app/daily_order_reports/
|
||||||
|
|
||||||
|
# Download specific report
|
||||||
|
docker cp email_reports_crew:/app/daily_order_reports/daily_order_report_2026-01-02.csv .
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
✅ Container running continuously
|
||||||
|
✅ Cron daemon active
|
||||||
|
✅ Database connectivity confirmed
|
||||||
|
✅ Manual test sends email
|
||||||
|
✅ Recipients receive reports
|
||||||
|
✅ CSV files generated in `/app/daily_order_reports/`
|
||||||
|
✅ Logs show execution history
|
||||||
|
✅ Scheduled to run at 23:55 CT daily
|
||||||
|
|
||||||
|
**Deployment Complete! 🎉**
|
||||||
39
Dockerfile
Normal file
39
Dockerfile
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
FROM php:7.4-cli-alpine
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
dcron \
|
||||||
|
mysql-client \
|
||||||
|
&& docker-php-ext-install pdo pdo_mysql
|
||||||
|
|
||||||
|
# Set timezone
|
||||||
|
RUN apk add --no-cache tzdata && \
|
||||||
|
cp /usr/share/zoneinfo/America/Chicago /etc/localtime && \
|
||||||
|
echo "America/Chicago" > /etc/timezone && \
|
||||||
|
apk del tzdata
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy email reports scripts
|
||||||
|
COPY . /app/
|
||||||
|
|
||||||
|
# Create reports directories
|
||||||
|
RUN mkdir -p /app/daily_order_reports_crew /app/daily_order_reports_merchbay && \
|
||||||
|
chmod 755 /app/daily_order_reports_crew /app/daily_order_reports_merchbay
|
||||||
|
|
||||||
|
# Create crontab with both reports
|
||||||
|
RUN echo "55 23 * * * BRAND=crew php /app/send_report.php >> /var/log/cron.log 2>&1" > /etc/crontabs/root && \
|
||||||
|
echo "56 23 * * * BRAND=merchbay php /app/send_report.php >> /var/log/cron.log 2>&1" >> /etc/crontabs/root && \
|
||||||
|
chmod 0644 /etc/crontabs/root
|
||||||
|
|
||||||
|
# Create log file
|
||||||
|
RUN touch /var/log/cron.log && \
|
||||||
|
chmod 666 /var/log/cron.log
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD ps aux | grep -v grep | grep -q crond || exit 1
|
||||||
|
|
||||||
|
# Start cron in foreground
|
||||||
|
CMD crond -f -l 2
|
||||||
324
MIGRATION.md
Normal file
324
MIGRATION.md
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# Migration to Unified Email Reports
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
✅ **Merged `email_reports` and `email_reports_merchbay` into ONE container**
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
- Two separate directories
|
||||||
|
- Two separate containers
|
||||||
|
- Two separate cron jobs calling via HTTP
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
- Single unified container
|
||||||
|
- Handles both Crew & MerchBay
|
||||||
|
- Two cron jobs (23:55, 23:56) running internally
|
||||||
|
- No HTTP calls needed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What Changed
|
||||||
|
|
||||||
|
### New Unified Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
email_reports/
|
||||||
|
├── send_report.php # NEW: Unified script for both brands
|
||||||
|
├── index.php # KEPT: Legacy Crew script (backward compatible)
|
||||||
|
├── Dockerfile # UPDATED: Runs both cron jobs
|
||||||
|
├── docker-compose.yml # UPDATED: Unified container config
|
||||||
|
├── .env.example # UPDATED: Both Crew + MerchBay vars
|
||||||
|
└── .gitea/workflows/
|
||||||
|
└── deploy.yml # UPDATED: Deploys unified container
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Changes
|
||||||
|
|
||||||
|
1. **send_report.php** - Brand-agnostic report generator
|
||||||
|
- Uses `BRAND` environment variable (`crew` or `merchbay`)
|
||||||
|
- Auto-selects database, SMTP, recipients per brand
|
||||||
|
- Writes to separate CSV directories
|
||||||
|
|
||||||
|
2. **Cron Jobs** - Two jobs in one container:
|
||||||
|
```cron
|
||||||
|
55 23 * * * BRAND=crew php /app/send_report.php
|
||||||
|
56 23 * * * BRAND=merchbay php /app/send_report.php
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Separate CSV Directories**:
|
||||||
|
- `daily_order_reports_crew/`
|
||||||
|
- `daily_order_reports_merchbay/`
|
||||||
|
|
||||||
|
4. **Unified Logs** - Single `email.log` with brand prefixes:
|
||||||
|
```
|
||||||
|
2026-01-02 23:55:00 [CREW] successfully sent (5 orders)
|
||||||
|
2026-01-02 23:56:00 [MERCHBAY] No order for today
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Steps
|
||||||
|
|
||||||
|
### 1. Server Preparation
|
||||||
|
|
||||||
|
SSH to your production server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh user@server
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
|
||||||
|
# Backup existing setup
|
||||||
|
tar -czf email_reports_backup_$(date +%Y%m%d).tar.gz \
|
||||||
|
daily_order_reports/ email.log .env
|
||||||
|
|
||||||
|
# Create new directories
|
||||||
|
mkdir -p daily_order_reports_crew daily_order_reports_merchbay
|
||||||
|
|
||||||
|
# Move existing Crew reports (optional)
|
||||||
|
mv daily_order_reports/* daily_order_reports_crew/ 2>/dev/null || true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Update .env File
|
||||||
|
|
||||||
|
Replace your `.env` with unified configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
cat > .env << 'EOL'
|
||||||
|
# Crew Sportswear Database
|
||||||
|
DB_HOST_CREW=mysql
|
||||||
|
DB_PORT_CREW=3306
|
||||||
|
DB_NAME_CREW=custom_design
|
||||||
|
DB_USER_CREW=crew_user
|
||||||
|
DB_PASS_CREW=your_crew_password
|
||||||
|
|
||||||
|
# MerchBay Database
|
||||||
|
DB_HOST_MERCHBAY=mysql
|
||||||
|
DB_PORT_MERCHBAY=3306
|
||||||
|
DB_NAME_MERCHBAY=merchbay_laravel
|
||||||
|
DB_USER_MERCHBAY=merchbay_user
|
||||||
|
DB_PASS_MERCHBAY=your_merchbay_password
|
||||||
|
|
||||||
|
# SMTP Configuration
|
||||||
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
|
||||||
|
# Crew SMTP (mail@crewsportswear.com)
|
||||||
|
SMTP_PASS_CREW=your_crew_gmail_app_password
|
||||||
|
|
||||||
|
# MerchBay SMTP (support@merchbay.com)
|
||||||
|
SMTP_PASS_MERCHBAY=your_merchbay_gmail_app_password
|
||||||
|
EOL
|
||||||
|
|
||||||
|
chmod 600 .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Remove Old Cron Jobs
|
||||||
|
|
||||||
|
If you had host-level cron jobs calling via HTTP:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e
|
||||||
|
|
||||||
|
# REMOVE these lines:
|
||||||
|
# 55 23 * * * wget -qO- https://www.crewsportswear.com/email_reports/index.php &> /dev/null
|
||||||
|
# 55 23 * * * wget -qO- https://www.crewsportswear.com/email_reports_merchbay/index.php &> /dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
**The container now handles scheduling internally!**
|
||||||
|
|
||||||
|
### 4. Deploy Unified Container
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From your local machine
|
||||||
|
cd email_reports
|
||||||
|
git add .
|
||||||
|
git commit -m "Migrate to unified email reports container"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
Gitea workflow will automatically:
|
||||||
|
- Build unified image
|
||||||
|
- Deploy to server
|
||||||
|
- Start container with both cron jobs
|
||||||
|
|
||||||
|
### 5. Verify Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On server
|
||||||
|
docker ps | grep email_reports
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# email_reports_unified Up X minutes
|
||||||
|
|
||||||
|
# Check cron schedule
|
||||||
|
docker exec email_reports_unified crontab -l
|
||||||
|
|
||||||
|
# Expected output:
|
||||||
|
# 55 23 * * * BRAND=crew php /app/send_report.php >> /var/log/cron.log 2>&1
|
||||||
|
# 56 23 * * * BRAND=merchbay php /app/send_report.php >> /var/log/cron.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Test Both Reports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test Crew report
|
||||||
|
docker exec email_reports_unified BRAND=crew php /app/send_report.php
|
||||||
|
|
||||||
|
# Test MerchBay report
|
||||||
|
docker exec email_reports_unified BRAND=merchbay php /app/send_report.php
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker exec email_reports_unified tail /app/email.log
|
||||||
|
|
||||||
|
# Expected:
|
||||||
|
# 2026-01-02 15:30:00 [CREW] successfully sent
|
||||||
|
# 2026-01-02 15:30:15 [MERCHBAY] successfully sent
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Verify Email Delivery
|
||||||
|
|
||||||
|
- Check `graphics@crewsportswear.com` inbox
|
||||||
|
- Should receive **two separate emails**:
|
||||||
|
- "CREW Daily Order Report - 2026-01-02"
|
||||||
|
- "Merchbay Daily Order Report - 2026-01-02"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What to Delete
|
||||||
|
|
||||||
|
### On Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# The old email_reports_merchbay directory is no longer needed
|
||||||
|
# (if it exists on the server)
|
||||||
|
rm -rf /var/www/apps/email_reports_merchbay
|
||||||
|
|
||||||
|
# Old container (will be removed automatically by deployment)
|
||||||
|
docker rm -f email_reports_crew email_reports_merchbay
|
||||||
|
```
|
||||||
|
|
||||||
|
### In Git Repository
|
||||||
|
|
||||||
|
The `email_reports_merchbay/` directory in your repos can stay for reference, or archive it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /path/to/repos
|
||||||
|
tar -czf email_reports_merchbay_archive.tar.gz email_reports_merchbay/
|
||||||
|
# Optional: rm -rf email_reports_merchbay/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Both Reports Not Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check crontab
|
||||||
|
docker exec email_reports_unified crontab -l
|
||||||
|
|
||||||
|
# Manually trigger both
|
||||||
|
docker exec email_reports_unified BRAND=crew php /app/send_report.php
|
||||||
|
docker exec email_reports_unified BRAND=merchbay php /app/send_report.php
|
||||||
|
```
|
||||||
|
|
||||||
|
### MerchBay Database Connection Failed
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify MerchBay DB credentials in .env
|
||||||
|
docker exec email_reports_unified env | grep MERCHBAY
|
||||||
|
|
||||||
|
# Test connection
|
||||||
|
docker exec email_reports_unified php -r "
|
||||||
|
new PDO(
|
||||||
|
'mysql:host=' . getenv('DB_HOST_MERCHBAY') . ';dbname=' . getenv('DB_NAME_MERCHBAY'),
|
||||||
|
getenv('DB_USER_MERCHBAY'),
|
||||||
|
getenv('DB_PASS_MERCHBAY')
|
||||||
|
);
|
||||||
|
echo 'MerchBay DB: OK\n';
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Only One Email Received
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check which brand succeeded
|
||||||
|
docker exec email_reports_unified grep "$(date +%Y-%m-%d)" /app/email.log
|
||||||
|
|
||||||
|
# Look for [CREW] or [MERCHBAY] prefixes
|
||||||
|
# If one failed, check the error message
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSV Files Not Created
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check directories exist
|
||||||
|
docker exec email_reports_unified ls -la /app/ | grep daily_order
|
||||||
|
|
||||||
|
# Should show:
|
||||||
|
# daily_order_reports_crew/
|
||||||
|
# daily_order_reports_merchbay/
|
||||||
|
|
||||||
|
# Check permissions
|
||||||
|
docker exec email_reports_unified stat -c '%a' /app/daily_order_reports_crew
|
||||||
|
# Should be 755
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If something goes wrong:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On server
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
|
||||||
|
# Stop unified container
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# Restore backup
|
||||||
|
tar -xzf email_reports_backup_YYYYMMDD.tar.gz
|
||||||
|
|
||||||
|
# Restore old .env
|
||||||
|
# Start old container
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Benefits of Unified Container
|
||||||
|
|
||||||
|
✅ **Simpler Infrastructure**
|
||||||
|
- One container instead of two
|
||||||
|
- Single deployment process
|
||||||
|
- Shared codebase
|
||||||
|
|
||||||
|
✅ **Better Resource Usage**
|
||||||
|
- One cron daemon
|
||||||
|
- Shared Docker image
|
||||||
|
- Less memory overhead
|
||||||
|
|
||||||
|
✅ **Easier Maintenance**
|
||||||
|
- Update both brands at once
|
||||||
|
- Unified logging
|
||||||
|
- Single point of monitoring
|
||||||
|
|
||||||
|
✅ **Consistent Behavior**
|
||||||
|
- Same PHPMailer version
|
||||||
|
- Same PHP version
|
||||||
|
- Same timezone handling
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. ✅ Deploy unified container
|
||||||
|
2. ✅ Test both reports manually
|
||||||
|
3. ✅ Wait for automatic execution at 23:55/23:56
|
||||||
|
4. ✅ Verify emails received
|
||||||
|
5. ✅ Monitor logs for first week
|
||||||
|
6. ✅ Remove old cron jobs
|
||||||
|
7. ✅ Archive old email_reports_merchbay directory
|
||||||
|
|
||||||
|
**Migration Complete! 🎉**
|
||||||
467
README.md
Normal file
467
README.md
Normal file
@@ -0,0 +1,467 @@
|
|||||||
|
# Email Reports - Dockerized Container
|
||||||
|
|
||||||
|
Unified Docker container for automated daily order email reports for **both Crew Sportswear and MerchBay**.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This container runs cron jobs daily at **23:55 CT** to:
|
||||||
|
1. Query orders from both Crew and MerchBay databases
|
||||||
|
2. Generate separate CSV reports for each brand
|
||||||
|
3. Email reports to recipients via SMTP
|
||||||
|
|
||||||
|
**Cron Schedule:**
|
||||||
|
- **23:55** - Crew Sportswear report
|
||||||
|
- **23:56** - MerchBay report
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Create `.env` file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
vim .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure your environment variables:
|
||||||
|
```env
|
||||||
|
# Crew Database
|
||||||
|
DB_HOST_CREW=mysql
|
||||||
|
DB_NAME_CREW=custom_design
|
||||||
|
DB_USER_CREW=crew_user
|
||||||
|
DB_PASS_CREW=your_password
|
||||||
|
|
||||||
|
# MerchBay Database
|
||||||
|
DB_HOST_MERCHBAY=mysql
|
||||||
|
DB_NAME_MERCHBAY=merchbay_laravel
|
||||||
|
DB_USER_MERCHBAY=merchbay_user
|
||||||
|
DB_PASS_MERCHBAY=your_password
|
||||||
|
|
||||||
|
# SMTP
|
||||||
|
SMTP_PASS_CREW=crew_gmail_app_password
|
||||||
|
SMTP_PASS_MERCHBAY=merchbay_gmail_app_password
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Build and Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build image
|
||||||
|
docker-compose build
|
||||||
|
|
||||||
|
# Start container
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Check logs
|
||||||
|
docker logs -f email_reports_crew
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Test Manually
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test Crew report
|
||||||
|
docker exec email_reports_unified BRAND=crew php /app/send_report.php
|
||||||
|
|
||||||
|
# Test MerchBay report
|
||||||
|
docker exec email_reports_unified BRAND=merchbay php /app/send_report.php
|
||||||
|
|
||||||
|
# Check cron log
|
||||||
|
docker exec email_reports_unified tail -f /var/log/cron.log
|
||||||
|
|
||||||
|
# Check email log (shows both brands)
|
||||||
|
docker exec email_reports_unified tail -f /app/email.log
|
||||||
|
```
|
||||||
|
|
||||||
|
┌──────────────────────────────────────────┐
|
||||||
|
│ Email Reports Container (Unified) │
|
||||||
|
│ │
|
||||||
|
│ ┌────────────────────────────────────┐ │
|
||||||
|
│ │ Cron Jobs: │ │
|
||||||
|
│ │ 23:55 → BRAND=crew │ │
|
||||||
|
│ │ 23:56 → BRAND=merchbay │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ send_report.php │ │
|
||||||
|
│ │ └─ PHPMailer │ │
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
└─────────────┼────────────────────────────┘
|
||||||
|
│
|
||||||
|
├──> MySQL: custom_design (Crew)
|
||||||
|
├──> MySQL: merchbay_laravel (MerchBay)
|
||||||
|
└──> SMTP: Gmail (2 accounts)
|
||||||
|
│
|
||||||
|
├──────> MySQL (crew-app-net)
|
||||||
|
└──────> SMTP (smtp.gmail.com)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fsend_report.php** - Unified report generator (handles both brands)
|
||||||
|
- **index.php** - Legacy Crew report (kept for backward compatibility)
|
||||||
|
- **dbconfig.php** - Database connection (env-aware)
|
||||||
|
- **phpmailer/** - Email library
|
||||||
|
|
||||||
|
## Cron Schedule
|
||||||
|
|
||||||
|
```cron
|
||||||
|
55 23 * * * BRAND=crew php /app/send_report.php >> /var/log/cron.log 2>&1
|
||||||
|
56 23 * * * BRAND=merchbay php /app/send_report.php >> /var/log/cron.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
**23:55 CT** - Crew Sportswear
|
||||||
|
**23:56 CT** - MerchBay
|
||||||
|
|
||||||
|
Both run daily, 1 minute apart.
|
||||||
|
55 23 * * * cd /app && php index.php >> /var/log/cron.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs daily at **11:55 PM Central Time**
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### Crew Sportswear
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| DB_HOST_CREW | mysql | MySQL host for Crew |
|
||||||
|
| DB_PORT_CREW | 3306 | MySQL port |
|
||||||
|
| DB_NAME_CREW | custom_design | Crew database name |
|
||||||
|
| DB_USER_CREW | crew_user | Crew database username |
|
||||||
|
| DB_PASS_CREW | - | Crew database password |
|
||||||
|
| SMTP_PASS_CREW | - | Gmail app password for mail@crewsportswear.com |
|
||||||
|
|
||||||
|
### MerchBay
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| DB_HOST_MERCHBAY | mysql | MySQL host for MerchBay |
|
||||||
|
| DB_PORT_MERCHBAY | 3306 | MySQL port |
|
||||||
|
| DB_NAME_MERCHBAY | merchbay_laravel | MerchBay database name |
|
||||||
|
| DB_USER_MERCHBAY | merchbay_user | MerchBay database username |
|
||||||
|
| DB_PASS_MERCHBAY | - | MerchBay database password |
|
||||||
|
| SMTP_PASS_MERCHBAY | - | Gmail app password for support@merchbay.com |
|
||||||
|
|
||||||
|
### Shared
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| SMTP_HOST | smtp.gmail.com | SMTP server |
|
||||||
|
| SMTP_PORT | 587 | SMTP port |
|
||||||
|
|
||||||
|
## Brand Configuration
|
||||||
|
|
||||||
|
The unified container handles both brands using the `BRAND` environment variable:
|
||||||
|
|
||||||
|
**Crew Sportswear (`BRAND=crew`)**:
|
||||||
|
- Database: `custom_design`
|
||||||
|
- From: `orders@crewsportswear.com`
|
||||||
|
- To: `graphics@crewsportswear.com`
|
||||||
|
- BCC: webmaster, angelo, production @crewsportswear.com
|
||||||
|
- Admin: https://admin.crewsportswear.app
|
||||||
|
- CSV: `daily_order_reports_crew/`
|
||||||
|
|
||||||
|
**MerchBay (`BRAND=merchbay`)**:
|
||||||
|
- Database: `merchbay_laravel`
|
||||||
|
- From: `orders@merchbay.com`
|
||||||
|
- To: `graphics@crewsportswear.com`
|
||||||
|
- BCC: webmaster, production @crewsportswear.com
|
||||||
|
- Admin: https://merchbay.app
|
||||||
|
- CSV: `daily_order_reports_merchbay/`
|
||||||
|
|
||||||
|
## Networks
|
||||||
|
|
||||||
|
Container must be on **crew-app-net** to access MySQL:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
- crew-app-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
crew-app-net:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Create network if it doesn't exist:
|
||||||
|
```bash
|
||||||
|
docker network create crew-app-net
|
||||||
|
```
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
- ./daily_order_reports_crew:/app/daily_order_reports_crew # Crew CSV archives
|
||||||
|
- ./daily_order_reports_merchbay:/app/daily_order_reports_merchbay # MerchBay CSV archives
|
||||||
|
- ./email.log:/app/email.log # Combined execution log
|
||||||
|
```
|
||||||
|
|
||||||
|
Both brands write to the same `email.log` with prefixes `[CREW]` and `[MERCHBAY]`.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Automated Deployment (Gitea Actions)
|
||||||
|
|
||||||
|
**Production deployment** is automated via Gitea Actions when pushing to `main` or `master` branch.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Trigger deployment
|
||||||
|
git add .
|
||||||
|
git commit -m "Update email reports"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
The workflow will:
|
||||||
|
1. Build Docker image
|
||||||
|
2. Transfer to production server via SSH
|
||||||
|
3. Deploy to `/var/www/apps/email_reports`
|
||||||
|
4. Start container with cron
|
||||||
|
5. Verify container is running
|
||||||
|
|
||||||
|
**Registry build** is triggered by version tags:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create version tag
|
||||||
|
git tag v1.0.0
|
||||||
|
git push origin v1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
This builds and pushes to your Docker registry.
|
||||||
|
|
||||||
|
### Manual Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy via docker-compose
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# Or build specific image version
|
||||||
|
docker build -t email-reports:1.0.0 .
|
||||||
|
docker tag email-reports:1.0.0 your-registry/email-reports:1.0.0
|
||||||
|
docker push your-registry/email-reports:1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required Gitea Secrets
|
||||||
|
|
||||||
|
Configure in your repository settings:
|
||||||
|
|
||||||
|
- `DEPLOY_SSH_KEY` - SSH private key for deployment server
|
||||||
|
- `DEPLOY_HOST` - Deployment server hostname/IP
|
||||||
|
- `DEPLOY_USER` - SSH username
|
||||||
|
- `REGISTRY_URL` - Docker registry URL (for version tags)
|
||||||
|
- `REGISTRY_USER` - Registry username
|
||||||
|
- `REGISTRY_PASSWORD` - Registry password
|
||||||
|
|
||||||
|
### Server Setup (First Time)
|
||||||
|
|
||||||
|
On your production server, create the deployment directory and `.env` file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create directory
|
||||||
|
sudo mkdir -p /var/www/apps/email_reports
|
||||||
|
sudo chown $USER:$USER /var/www/apps/email_reports
|
||||||
|
cd /var/www/apps/email_reports
|
||||||
|
|
||||||
|
# Create .env file
|
||||||
|
cat > .env << 'EOL'
|
||||||
|
DB_HOST=mysql
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_NAME=custom_design
|
||||||
|
DB_USER=crew_user
|
||||||
|
DB_PASS=your_secure_password
|
||||||
|
|
||||||
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
SMTP_USER=mail@crewsportswear.com
|
||||||
|
SMTP_PASS=your_gmail_app_password
|
||||||
|
|
||||||
|
EMAIL_TO=graphics@crewsportswear.com
|
||||||
|
EMAIL_BCC=webmaster@crewsportswear.com,angelo@crewsportswear.com,production@crewsportswear.com
|
||||||
|
|
||||||
|
APP_URL=https://www.crewsportswear.com
|
||||||
|
ADMIN_URL=https://admin.crewsportswear.app
|
||||||
|
EOL
|
||||||
|
|
||||||
|
chmod 600 .env
|
||||||
|
|
||||||
|
# Create data directories
|
||||||
|
mkdir -p daily_order_reports
|
||||||
|
touch email.log
|
||||||
|
chmod 666 email.log
|
||||||
|
|
||||||
|
# Ensure network exists
|
||||||
|
docker network create crew-app-net || true
|
||||||
|
```
|
||||||
|
|
||||||
|
Now push to trigger the first deployment!
|
||||||
|
|
||||||
|
### Multiple Instances
|
||||||
|
|
||||||
|
For MerchBay, create similar setup in `email_reports_merchbay/`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml in email_reports_merchbay/
|
||||||
|
services:
|
||||||
|
email-reports-merchbay:
|
||||||
|
build: .
|
||||||
|
container_name: email_reports_merchbay
|
||||||
|
environment:
|
||||||
|
- DB_NAME=merchbay_db
|
||||||
|
- EMAIL_TO=orders@merchbay.com
|
||||||
|
# ... other env vars
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Check Container Health
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container status
|
||||||
|
docker ps | grep email_reports
|
||||||
|
|
||||||
|
# Should show email_reports_unified
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
docker inspect email_reports_unified | grep -A 5 Health
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Real-time cron logs (both brands)
|
||||||
|
docker exec email_reports_unified tail -f /var/log/cron.log
|
||||||
|
|
||||||
|
# Email execution logs (prefixed by brand)
|
||||||
|
docker exec email_reports_unified tail -f /app/email.log
|
||||||
|
|
||||||
|
# Example log output:
|
||||||
|
# 2026-01-02 23:55:00 [CREW] successfully sent (5 orders)
|
||||||
|
# 2026-01-02 23:56:00 [MERCHBAY] No order for today
|
||||||
|
|
||||||
|
# Last 50 lines
|
||||||
|
docker logs --tail 50 email_reports_unified
|
||||||
|
|
||||||
|
# Filter by brand
|
||||||
|
docker exec email_reports_unified grep "\[CREW\]" /app/email.log | tail -10
|
||||||
|
docker exec email_reports_unified grep "\[MERCHBAY\]" /app/email.log | tail -10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify Cron Schedule
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View active crontab (should show both jobs)
|
||||||
|
docker exec email_reports_unified crontab -l
|
||||||
|
|
||||||
|
# Expected:
|
||||||
|
# 55 23 * * * BRAND=crew php /app/send_report.php >> /var/log/cron.log 2>&1
|
||||||
|
# 56 23 * * * BRAND=merchbay php /app/send_report.php >> /var/log/cron.log 2>&1
|
||||||
|
|
||||||
|
# Check if cron is running
|
||||||
|
docker exec email_reports_unified ps aux | grep crond
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Database Connection Failed
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test MySQL connectivity
|
||||||
|
docker exec email_reports_crew ping mysql
|
||||||
|
|
||||||
|
# Test database connection
|
||||||
|
docker exec email_reports_crew php -r "new PDO('mysql:host=mysql;dbname=custom_design', 'user', 'pass');"
|
||||||
|
|
||||||
|
# Check network
|
||||||
|
docker network inspect crew-app-net
|
||||||
|
```
|
||||||
|
|
||||||
|
### Email Not Sending
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable debug mode
|
||||||
|
docker exec email_reports_crew sed -i 's|// \$mail->SMTPDebug|\$mail->SMTPDebug|' /app/index.php
|
||||||
|
|
||||||
|
# Run manually with debug
|
||||||
|
docker exec email_reports_crew php /app/index.php
|
||||||
|
|
||||||
|
# Check SMTP credentials in .env
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cron Not Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check cron daemon
|
||||||
|
docker exec email_reports_crew ps aux | grep crond
|
||||||
|
|
||||||
|
# Restart container
|
||||||
|
docker restart email_reports_crew
|
||||||
|
|
||||||
|
# View crontab
|
||||||
|
docker exec email_reports_crew cat /etc/crontabs/root
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wrong Timezone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify timezone
|
||||||
|
docker exec email_reports_crew date
|
||||||
|
docker exec email_reports_crew cat /etc/timezone
|
||||||
|
|
||||||
|
# Should show: America/Chicago
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permissions Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fix CSV directory permissions
|
||||||
|
docker exec email_reports_crew chmod 755 /app/daily_order_reports
|
||||||
|
|
||||||
|
# Fix log file
|
||||||
|
docker exec email_reports_crew chmod 666 /app/email.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Update Cron Schedule
|
||||||
|
|
||||||
|
Edit Dockerfile and rebuild:
|
||||||
|
```dockerfile
|
||||||
|
RUN echo "30 22 * * * cd /app && php index.php" > /etc/crontabs/root
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rotate Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Manually clear logs (optional)
|
||||||
|
docker exec email_reports_crew truncate -s 0 /var/log/cron.log
|
||||||
|
docker exec email_reports_crew truncate -s 0 /app/email.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Reports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Archive old CSV files
|
||||||
|
tar -czf daily_reports_backup_$(date +%Y%m%d).tar.gz daily_order_reports/
|
||||||
|
|
||||||
|
# Or sync to S3/backup location
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
1. **Never commit `.env`** - Use `.env.example` only
|
||||||
|
2. **Use Gmail App Passwords** - Not your main password
|
||||||
|
3. **Restrict network access** - Only join necessary networks
|
||||||
|
4. **Rotate credentials** - Change passwords regularly
|
||||||
|
5. **Monitor logs** - Check for unauthorized access
|
||||||
|
|
||||||
|
## Advantages of Separate Container
|
||||||
|
|
||||||
|
✅ **Isolation** - Reports run independently of web apps
|
||||||
|
✅ **Reusability** - Can serve multiple apps (Crew + MerchBay)
|
||||||
|
✅ **Simple Deployment** - Single purpose, easy to update
|
||||||
|
✅ **Resource Control** - Limit CPU/memory for cron jobs
|
||||||
|
✅ **Independent Scaling** - Restart without affecting web traffic
|
||||||
|
✅ **Clean Separation** - No coupling with Laravel/framework
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Test database connectivity
|
||||||
|
2. Verify SMTP credentials
|
||||||
|
3. Run manual test: `docker exec email_reports_crew php /app/index.php`
|
||||||
|
4. Wait for 23:55 or adjust cron for testing
|
||||||
|
5. Monitor logs for first automated run
|
||||||
|
6. Set up similar container for MerchBay reports
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
StoreName,ProductName,NAME,NUMBER,Size,JerseySize,ShortsSize,Price,Quantity,TotalPrice,Tax,DateCreated,InvoiceNumber,Payer_Email
|
|
||||||
"World Class Basketball",Socks,,,,,,12.00,2,24,2.4000000000000004,"2019-03-13 14:11:35",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Sleeveless Hoodie",,,M,,,25.00,1,25,2.5,"2019-03-13 14:14:14",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Wisconsin Long Sleeve",,,L,,,25.00,1,25,2.5,"2019-03-13 14:15:48",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,44,,YXL,YXL,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,3,,YXL,YXL,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,15,,M,M,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,11,,S,S,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,0,,YL,YL,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,2,,S,S,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,5,,YXL,YXL,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,20,,YL,YL,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Away Uniform",,4,,YL,YL,40.00,1,40,4,"2019-03-13 14:18:57",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,15,,M,M,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,11,,S,S,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,0,,YL,YL,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,2,,S,S,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,5,,YXL,YXL,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,20,,YL,YL,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,4,,YL,YL,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,44,,YXL,YXL,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
"World Class Basketball","Illinois Home Uniform",,3,,YXL,YXL,40.00,1,40,4,"2019-03-13 14:22:29",2019-5c8958991de3a,
|
|
||||||
|
@@ -1,180 +0,0 @@
|
|||||||
StoreName,ProductName,NAME,NUMBER,Size,JerseySize,ShortsSize,Price,Quantity,TotalPrice,Tax,DateCreated,InvoiceNumber,Payer_Email
|
|
||||||
Limitless,"Black Uniform",,12,,S,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,11,,YXL,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,6,,S,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,30,,YXL,YXL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,30,,S,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,15,,YXL,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,25,,S,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,1,,YXS,YS,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,8,,M,YXL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,18,,YM,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,0,,M,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,6,,YL,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,23,,M,XL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,17,,YL,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,1,,L,YXL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,7,,YL,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,13,,YL,YM,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,0,,S,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,4,,S,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,21,,YXL,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,0,,YXL,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,34,,S,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,10,,YXL,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,67,,S,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,3,,YXL,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,11,,M,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,29,,YXS,YM,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,7,,M,L,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,23,,YM,YS,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,7,,M,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,53,,YL,YXL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,10,,M,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,21,,YL,L,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,24,,L,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,2,,YL,L,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,27,,XL,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,23,,S,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,21,,S,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,23,,YXL,YXL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,33,,YXL,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,8,,S,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,11,,YXL,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,54,,S,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,7,,YXL,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,9,,M,YM,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,5,,YM,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,34,,M,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,10,,YM,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,2,,M,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,17,,YL,YL,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,0,,L,YS,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,0,,YL,M,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,19,,YL,YM,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Black Uniform",,34,,XL,S,45.00,1,45,4.5,"2019-08-31 22:22:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,22,L,,,25.00,1,25,2.5,"2019-08-31 22:27:51",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,40,L,,,25.00,1,25,2.5,"2019-08-31 22:27:51",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,34,L,,,25.00,1,25,2.5,"2019-08-31 22:28:24",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,5,L,,,25.00,1,25,2.5,"2019-08-31 22:28:24",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,1,L,,,25.00,1,25,2.5,"2019-08-31 22:29:26",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,24,L,,,25.00,1,25,2.5,"2019-08-31 22:29:26",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,5,M,,,25.00,1,25,2.5,"2019-08-31 22:33:16",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,L,,,25.00,1,25,2.5,"2019-08-31 22:33:16",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,42,M,,,25.00,1,25,2.5,"2019-08-31 22:35:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,35,M,,,25.00,1,25,2.5,"2019-08-31 22:35:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,M,,,25.00,1,25,2.5,"2019-08-31 22:35:44",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,M,,,25.00,1,25,2.5,"2019-08-31 22:35:44",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,12,M,,,25.00,1,25,2.5,"2019-08-31 22:36:13",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,9,M,,,25.00,1,25,2.5,"2019-08-31 22:36:13",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,4,M,,,25.00,1,25,2.5,"2019-08-31 22:36:29",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,4,M,,,25.00,1,25,2.5,"2019-08-31 22:36:29",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,10,M,,,25.00,1,25,2.5,"2019-08-31 22:40:17",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,42,M,,,25.00,1,25,2.5,"2019-08-31 22:40:17",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,2,M,,,25.00,1,25,2.5,"2019-08-31 22:40:56",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,M,,,25.00,1,25,2.5,"2019-08-31 22:40:56",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,7,M,,,25.00,1,25,2.5,"2019-08-31 22:42:48",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,M,,,25.00,1,25,2.5,"2019-08-31 22:42:48",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,7,M,,,25.00,1,25,2.5,"2019-08-31 22:44:29",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,34,M,,,25.00,1,25,2.5,"2019-08-31 22:44:29",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,9,M,,,25.00,1,25,2.5,"2019-08-31 22:45:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,8,M,,,25.00,1,25,2.5,"2019-08-31 22:45:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,11,M,,,25.00,1,25,2.5,"2019-08-31 22:45:59",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,35,S,,,25.00,1,25,2.5,"2019-08-31 22:45:59",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,33,S,,,25.00,1,25,2.5,"2019-08-31 22:47:01",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,3,S,,,25.00,1,25,2.5,"2019-08-31 22:47:01",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,S,,,25.00,1,25,2.5,"2019-08-31 22:47:29",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,8,S,,,25.00,1,25,2.5,"2019-08-31 22:47:29",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,15,S,,,25.00,1,25,2.5,"2019-08-31 22:48:30",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,S,,,25.00,1,25,2.5,"2019-08-31 22:48:30",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,20,S,,,25.00,1,25,2.5,"2019-08-31 22:49:02",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,1,S,,,25.00,1,25,2.5,"2019-08-31 22:49:02",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,12,S,,,25.00,1,25,2.5,"2019-08-31 22:49:42",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,31,S,,,25.00,1,25,2.5,"2019-08-31 22:49:42",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,11,S,,,25.00,1,25,2.5,"2019-08-31 22:50:16",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,9,S,,,25.00,1,25,2.5,"2019-08-31 22:50:16",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,5,S,,,25.00,1,25,2.5,"2019-08-31 22:51:24",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,1,S,,,25.00,1,25,2.5,"2019-08-31 22:51:24",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,35,S,,,25.00,1,25,2.5,"2019-08-31 22:51:59",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,1,S,,,25.00,1,25,2.5,"2019-08-31 22:51:59",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,14,S,,,25.00,1,25,2.5,"2019-08-31 22:52:28",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,S,,,25.00,1,25,2.5,"2019-08-31 22:52:28",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,12,S,,,25.00,1,25,2.5,"2019-08-31 22:53:20",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,25,S,,,25.00,1,25,2.5,"2019-08-31 22:53:20",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,67,S,,,25.00,1,25,2.5,"2019-08-31 22:56:45",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,54,S,,,25.00,1,25,2.5,"2019-08-31 22:56:45",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,8,S,,,25.00,1,25,2.5,"2019-08-31 22:58:19",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,30,S,,,25.00,1,25,2.5,"2019-08-31 22:58:19",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,34,S,,,25.00,1,25,2.5,"2019-08-31 23:09:10",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,6,S,,,25.00,1,25,2.5,"2019-08-31 23:09:10",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,4,S,,,25.00,1,25,2.5,"2019-08-31 23:09:44",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,S,,,25.00,1,25,2.5,"2019-08-31 23:09:44",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,S,,,25.00,1,25,2.5,"2019-08-31 23:11:18",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,12,S,,,25.00,1,25,2.5,"2019-08-31 23:11:18",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,S,,,25.00,1,25,2.5,"2019-08-31 23:11:57",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,26,XL,,,25.00,1,25,2.5,"2019-08-31 23:11:57",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,34,XL,,,25.00,1,25,2.5,"2019-08-31 23:13:25",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,2,XL,,,25.00,1,25,2.5,"2019-08-31 23:13:25",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,11,XL,,,25.00,1,25,2.5,"2019-08-31 23:13:55",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,20,XL,,,25.00,1,25,2.5,"2019-08-31 23:13:55",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,34,XL,,,25.00,1,25,2.5,"2019-08-31 23:15:34",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,27,XL,,,25.00,1,25,2.5,"2019-08-31 23:15:34",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,15,YL,,,25.00,1,25,2.5,"2019-08-31 23:16:17",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,3,YL,,,25.00,1,25,2.5,"2019-08-31 23:16:17",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,28,YL,,,25.00,1,25,2.5,"2019-08-31 23:16:59",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,YL,,,25.00,1,25,2.5,"2019-08-31 23:16:59",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,2,YL,,,25.00,1,25,2.5,"2019-08-31 23:18:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,36,YL,,,25.00,1,25,2.5,"2019-08-31 23:18:38",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,13,YL,,,25.00,1,25,2.5,"2019-08-31 23:19:09",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,22,YL,,,25.00,1,25,2.5,"2019-08-31 23:19:09",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,22,YL,,,25.00,1,25,2.5,"2019-08-31 23:20:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,10,YL,,,25.00,1,25,2.5,"2019-08-31 23:20:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,33,YL,,,25.00,1,25,2.5,"2019-08-31 23:20:57",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,30,YL,,,25.00,1,25,2.5,"2019-08-31 23:20:57",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,30,YL,,,25.00,1,25,2.5,"2019-08-31 23:21:28",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,33,YL,,,25.00,1,25,2.5,"2019-08-31 23:21:28",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,YL,,,25.00,1,25,2.5,"2019-08-31 23:22:05",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,30,YL,,,25.00,1,25,2.5,"2019-08-31 23:22:05",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,19,YL,,,25.00,1,25,2.5,"2019-08-31 23:22:43",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,13,YL,,,25.00,1,25,2.5,"2019-08-31 23:22:43",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,2,YL,,,25.00,1,25,2.5,"2019-08-31 23:23:07",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,7,YL,,,25.00,1,25,2.5,"2019-08-31 23:23:07",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,YL,,,25.00,1,25,2.5,"2019-08-31 23:24:00",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,YL,,,25.00,1,25,2.5,"2019-08-31 23:24:00",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,17,YL,,,25.00,1,25,2.5,"2019-08-31 23:24:33",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,17,YL,,,25.00,1,25,2.5,"2019-08-31 23:24:33",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,53,YL,,,25.00,1,25,2.5,"2019-08-31 23:25:07",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,6,YL,,,25.00,1,25,2.5,"2019-08-31 23:25:07",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,5,YM,,,25.00,1,25,2.5,"2019-08-31 23:25:33",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,3,YM,,,25.00,1,25,2.5,"2019-08-31 23:25:33",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,33,YM,,,25.00,1,25,2.5,"2019-08-31 23:26:05",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,YM,,,25.00,1,25,2.5,"2019-08-31 23:26:05",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,8,YM,,,25.00,1,25,2.5,"2019-08-31 23:26:49",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,34,YM,,,25.00,1,25,2.5,"2019-08-31 23:26:49",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,YM,,,25.00,1,25,2.5,"2019-08-31 23:27:19",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,2,YM,,,25.00,1,25,2.5,"2019-08-31 23:27:19",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,10,YM,,,25.00,1,25,2.5,"2019-08-31 23:27:51",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,28,YM,,,25.00,1,25,2.5,"2019-08-31 23:27:51",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,18,YM,,,25.00,1,25,2.5,"2019-08-31 23:29:05",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,YM,,,25.00,1,25,2.5,"2019-08-31 23:29:05",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,5,YM,,,25.00,1,25,2.5,"2019-08-31 23:29:36",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,29,YS,,,25.00,1,25,2.5,"2019-08-31 23:31:02",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,5,YS,,,25.00,1,25,2.5,"2019-08-31 23:31:19",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,1,YS,,,25.00,1,25,2.5,"2019-08-31 23:31:47",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,YXL,,,25.00,1,25,2.5,"2019-08-31 23:32:15",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,33,YXL,,,25.00,1,25,2.5,"2019-08-31 23:32:15",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,11,YXL,,,25.00,1,25,2.5,"2019-08-31 23:32:45",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,10,YXL,,,25.00,1,25,2.5,"2019-08-31 23:32:45",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,9,YXL,,,25.00,1,25,2.5,"2019-08-31 23:33:24",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,20,YXL,,,25.00,1,25,2.5,"2019-08-31 23:33:24",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,3,YXL,,,25.00,1,25,2.5,"2019-08-31 23:34:02",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,7,YXL,,,25.00,1,25,2.5,"2019-08-31 23:34:02",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,15,YXL,,,25.00,1,25,2.5,"2019-08-31 23:34:28",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,11,YXL,,,25.00,1,25,2.5,"2019-08-31 23:34:28",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,30,YXL,,,25.00,1,25,2.5,"2019-08-31 23:34:55",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,10,YXL,,,25.00,1,25,2.5,"2019-08-31 23:34:55",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,0,YXL,,,25.00,1,25,2.5,"2019-08-31 23:35:27",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,33,YXL,,,25.00,1,25,2.5,"2019-08-31 23:35:27",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,11,YXL,,,25.00,1,25,2.5,"2019-08-31 23:35:56",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,23,YXL,,,25.00,1,25,2.5,"2019-08-31 23:35:56",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
Limitless,"Reversible Jersey",,21,YXL,,,25.00,1,25,2.5,"2019-08-31 23:36:11",20190901-0181,dawaun@limitlessbasketball.com
|
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
StoreName,ProductName,NAME,NUMBER,Size,JerseySize,ShortsSize,Price,Quantity,TotalPrice,Tax,DateCreated,InvoiceNumber,Payer_Email
|
|
||||||
"Bruton Basketball","Long Sleeve T Shirt",,,2XL,,,45.00,1,45,4.5,"2019-09-01 17:38:41",20190901-0182,clare.davis@fire.tas.gov.au
|
|
||||||
"Bruton Basketball","T Shirt",,,2XL,,,35.00,1,35,3.5,"2019-09-01 17:39:01",20190901-0182,clare.davis@fire.tas.gov.au
|
|
||||||
"Bruton Basketball","Long Sleeve Hoodie",,,3XL,,,50.00,1,50,5,"2019-09-01 17:39:19",20190901-0182,clare.davis@fire.tas.gov.au
|
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
StoreName,ProductName,NAME,NUMBER,Size,JerseySize,ShortsSize,Price,Quantity,TotalPrice,Tax,DateCreated,InvoiceNumber,Payer_Email
|
|
||||||
Ice,"Practice Jersey Reversible",SIMON,55,YM,,,30.00,1,30,3,"2019-10-26 09:17:16",20191026-0275,simondj55@gmail.com
|
|
||||||
"San Diego Bulldogs","Long Sleeve Hoodie",,,YXL,,,45.00,1,45,4.5,"2019-10-26 17:50:31",20191026-0276,sjones023@att.net
|
|
||||||
"San Diego Bulldogs",Backpack,"MJ ",23,,,,70.00,1,70,7,"2019-10-26 17:51:14",20191026-0276,sjones023@att.net
|
|
||||||
Wauconda,"Game Day Reversible uniform",PIEHL,12,,L,L,65.00,1,65,6.5,"2019-10-26 18:12:46",20191026-0277,aprilo99@aol.com
|
|
||||||
Wauconda,"Dry fit Long Sleeve T Shirt",,,L,,,30.00,1,30,3,"2019-10-26 18:21:16",20191026-0277,aprilo99@aol.com
|
|
||||||
Wauconda,socks,,,,,,12.00,1,12,1.2000000000000002,"2019-10-26 18:21:33",20191026-0277,aprilo99@aol.com
|
|
||||||
|
@@ -48,630 +48,254 @@ $array_orders = getSubItem($dateToday);
|
|||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<title>Order Details</title>
|
<title>Order Details</title>
|
||||||
<style>
|
<link rel="stylesheet" href="styles.css">
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 10px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 880px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 880px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 880px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn>tbody>tr>td {
|
|
||||||
padding-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn table {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #3498db;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #3498db;
|
|
||||||
border-color: #3498db;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #34495e !important;
|
|
||||||
border-color: #34495e !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.table-bordered td,
|
|
||||||
th {
|
|
||||||
border: 1px solid black;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.previewImage {
|
|
||||||
height: 200px;
|
|
||||||
overflow: hidden;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
.items tr {
|
|
||||||
page-break-inside: avoid;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@page {
|
|
||||||
size: 18cm 26.7cm;
|
|
||||||
margin: 1cm;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tbl_subitem tr td {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="" onload="window.print()">
|
<body class="" onload="window.print()">
|
||||||
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
|
||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td class="container">
|
<td class="container">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
<!-- START CENTERED WHITE CONTAINER -->
|
||||||
<table role="presentation" class="main">
|
<table role="presentation" class="main">
|
||||||
<!-- START MAIN CONTENT AREA -->
|
<!-- START MAIN CONTENT AREA -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="wrapper">
|
<td class="wrapper">
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<h3 class="align-center" style="font-size: 15px;"><b>Daily Reports
|
<h3 class="align-center" style="font-size: 15px;"><b>Daily Reports <?php echo $dateToday ?></b></h3>
|
||||||
<?php echo $dateToday ?></b> </h3>
|
<br>
|
||||||
<br>
|
<?php
|
||||||
<?php
|
$q = $conn->prepare("SELECT t.StoreName, o.ProductId, o.FormUsed, o.ProductName, o.NAME, o.NUMBER, o.Size, o.JerseySize, o.ShortsSize, o.Price, o.Quantity, (o.Price * o.Quantity) AS TotalPrice, ((o.Price * o.Quantity) * 0.10) AS Tax,
|
||||||
$q = $conn->prepare("SELECT t.StoreName, o.ProductId, o.FormUsed, o.ProductName, o.NAME, o.NUMBER, o.Size, o.JerseySize, o.ShortsSize, o.Price, o.Quantity, (o.Price * o.Quantity) AS TotalPrice, ((o.Price * o.Quantity) * 0.10) AS Tax,
|
o.DateCreated, pd.InvoiceNumber, pd.Payer_Email FROM orders AS o
|
||||||
o.DateCreated, pd.InvoiceNumber, pd.Payer_Email FROM orders AS o
|
INNER JOIN payment_details AS pd ON pd.CartKey = o.CartKey
|
||||||
INNER JOIN payment_details AS pd ON pd.CartKey = o.CartKey
|
INNER JOIN teamstores AS t ON t.Id = o.StoreId
|
||||||
INNER JOIN teamstores AS t ON t.Id = o.StoreId
|
WHERE o.DateCreated BETWEEN '$dateToday 00:00:00' AND '$dateToday 23:59:00'
|
||||||
WHERE o.DateCreated BETWEEN '$dateToday 00:00:00' AND '$dateToday 23:59:00'
|
GROUP BY o.ProductId
|
||||||
GROUP BY o.ProductId
|
ORDER BY o.DateCreated");
|
||||||
ORDER BY o.DateCreated");
|
$q->execute();
|
||||||
$q->execute();
|
$result = $q->rowCount();
|
||||||
$result = $q->rowCount();
|
|
||||||
|
|
||||||
if($result > 0){
|
if($result > 0){
|
||||||
// $i = 1;
|
while ($row = $q->fetch()) {
|
||||||
while ($row = $q->fetch()) {
|
?>
|
||||||
|
<div class="items-parent" style="border: 1px solid #e2e2e2; padding: 10px; margin-bottom: 10px;">
|
||||||
?>
|
<table class="items" role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||||
<div class="items-parent"
|
<tbody>
|
||||||
style="border: 1px solid #e2e2e2; padding: 10px; margin-bottom: 10px;">
|
<tr>
|
||||||
|
<td>
|
||||||
|
<h2><b>Store: </b></h2>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<h2><?php echo $row['StoreName'] ?></h2>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<div style="font-size: 12px;">
|
||||||
|
<a href="#"><?php echo $row['ProductName'] ?></a>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<?php
|
||||||
|
$itemImages = getItemImages($row['ProductId']);
|
||||||
|
foreach($itemImages as $thumb_key => $thumb_val){
|
||||||
|
echo '<img style="height: 150px; overflow: hidden; object-fit: contain;" src="https://crewsportswear.app:5955/'.$thumb_val['Image'].'"> ';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<table id="tbl_subitem"class="table table-condensed table-bordered" style="width: 100%; border-collapse: collapse;">
|
||||||
|
<?php if($row['FormUsed']=="jersey-and-shorts-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<td><b>Name</b></td>
|
||||||
|
<td><b>Number</b></td>
|
||||||
|
<td><b>Jersey Size</b></td>
|
||||||
|
<td><b>Shorts Size</b></td>
|
||||||
|
<td><b>Quantity</b></td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="tshirt-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<td><b>Size</b></td>
|
||||||
|
<td><b>Quantity</b></td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="quantity-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<td>Quantity</b></td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-number-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<td><b>Name</b></td>
|
||||||
|
<td><b>Number</b></td>
|
||||||
|
<td><b>Quantity</b></td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-number-size-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Number</th>
|
||||||
|
<th>Size</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="number-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<th>Number</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-name2-size-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<td>Gamer Tag</td>
|
||||||
|
<td>Name</td>
|
||||||
|
<td>Size</td>
|
||||||
|
<td>Quantity</td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-size-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Size</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="jersey-and-shorts-quantity-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<th>Jersey Size</th>
|
||||||
|
<th>Shorts Size</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="number-jersey-shorts-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><b>#</b></td>
|
||||||
|
<td><b>Number</b></td>
|
||||||
|
<td><b>Jersey Size</b></td>
|
||||||
|
<td><b>Shorts Size</b></td>
|
||||||
|
<td><b>Quantity</b></td>
|
||||||
|
</tr>
|
||||||
|
<?php }else{ /* else */ }
|
||||||
|
|
||||||
<table class="items" role="presentation" border="0" cellpadding="0"
|
$i=1;
|
||||||
cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<h2><b>Store: </b></h2>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<h2><?php echo $row['StoreName'] ?></h2>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<div style="font-size: 12px;">
|
|
||||||
<a href="#">
|
|
||||||
<?php echo $row['ProductName'] ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<!-- -->
|
|
||||||
<?php
|
|
||||||
$itemImages = getItemImages($row['ProductId']);
|
|
||||||
foreach($itemImages as $thumb_key => $thumb_val){
|
|
||||||
// echo $thumb_val['Image'];
|
|
||||||
echo '<img style="height: 150px; overflow: hidden; object-fit: contain;" src="https://crewsportswear.app/images/'.$thumb_val['Image'].'"> ';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
<table id="tbl_subitem"
|
|
||||||
class="table table-condensed table-bordered"
|
|
||||||
style="width: 100%; border-collapse: collapse;">
|
|
||||||
<?php
|
|
||||||
|
|
||||||
if($row['FormUsed']=="jersey-and-shorts-form"){
|
foreach($array_orders as $key => $val){
|
||||||
?>
|
if($val['ProductId'] == $row['ProductId']){
|
||||||
<tr>
|
if($row['FormUsed']=="jersey-and-shorts-form"){ ?>
|
||||||
<td><b>#</b></td>
|
<tr>
|
||||||
<td><b>Name</b></td>
|
<td><?php echo $i++ ?></td>
|
||||||
<td><b>Number</b></td>
|
<td><?php echo ($val['Name'] !== "") ? : '--' ?></td>
|
||||||
<td><b>Jersey Size</b></td>
|
<td><?php echo $val['Number'] ?> </td>
|
||||||
<td><b>Shorts Size</b></td>
|
<td><?php echo $val['JerseySize'] ?> </td>
|
||||||
<td><b>Quantity</b></td>
|
<td><?php echo $val['ShortsSize'] ?> </td>
|
||||||
</tr>
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
<?php
|
</tr>
|
||||||
}elseif($row['FormUsed']=="tshirt-form"){
|
<?php }elseif($row['FormUsed']=="tshirt-form"){ ?>
|
||||||
?>
|
<tr>
|
||||||
<tr>
|
<td><?php echo $i++ ?> </td>
|
||||||
<td><b>#</b></td>
|
<td><?php echo $val['Size'] ?> </td>
|
||||||
<td><b>Size</b></td>
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
<td><b>Quantity</b></td>
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="quantity-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-number-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['Name'] ?> </td>
|
||||||
|
<td><?php echo $val['Number'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-number-size-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['Name'] ?> </td>
|
||||||
|
<td><?php echo $val['Number'] ?> </td>
|
||||||
|
<td><?php echo $val['Size'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="number-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['Number'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-name2-size-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['Name'] ?> </td>
|
||||||
|
<td><?php echo $val['Name2'] ?> </td>
|
||||||
|
<td><?php echo $val['Size'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="name-size-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['Name'] ?> </td>
|
||||||
|
<td><?php echo $val['Size'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="jersey-and-shorts-quantity-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?> </td>
|
||||||
|
<td><?php echo $val['JerseySize'] ?> </td>
|
||||||
|
<td><?php echo $val['ShortsSize'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }elseif($row['FormUsed']=="number-jersey-shorts-form"){ ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo $i++ ?></td>
|
||||||
|
<td><?php echo $val['Number'] ?> </td>
|
||||||
|
<td><?php echo $val['JerseySize'] ?> </td>
|
||||||
|
<td><?php echo $val['ShortsSize'] ?> </td>
|
||||||
|
<td><?php echo $val['Quantity'] ?> </td>
|
||||||
|
</tr>
|
||||||
|
<?php }else{ /* else */ }
|
||||||
|
}
|
||||||
|
} ?>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- END MAIN CONTENT AREA -->
|
||||||
|
</table>
|
||||||
|
<!-- END CENTERED WHITE CONTAINER -->
|
||||||
|
|
||||||
</tr>
|
<!-- START FOOTER -->
|
||||||
<?php
|
<div class="footer">
|
||||||
}elseif($row['FormUsed']=="quantity-form"){
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||||
?>
|
<tr>
|
||||||
<tr>
|
<td class="content-block">
|
||||||
<td>Quantity</b></td>
|
<span class="apple-link">Copyright © 2019 <a
|
||||||
</tr>
|
href="https://crewsportswear.com">crewsportswear.com</a>. All rights
|
||||||
<?php
|
reserved.</span>
|
||||||
}elseif($row['FormUsed']=="name-number-form"){
|
</td>
|
||||||
?>
|
</tr>
|
||||||
<tr>
|
</table>
|
||||||
<td><b>#</b></td>
|
</div>
|
||||||
<td><b>Name</b></td>
|
<!-- END FOOTER -->
|
||||||
<td><b>Number</b></td>
|
</div>
|
||||||
<td><b>Quantity</b></td>
|
</td>
|
||||||
</tr>
|
<td> </td>
|
||||||
<?php
|
</tr>
|
||||||
}elseif($row['FormUsed']=="name-number-size-form"){
|
</table>
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><b>#</b></td>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Number</th>
|
|
||||||
<th>Size</th>
|
|
||||||
<th>Quantity</th>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="number-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><b>#</b></td>
|
|
||||||
<th>Number</th>
|
|
||||||
<th>Quantity</th>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="name-size-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><b>#</b></td>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Size</th>
|
|
||||||
<th>Price</th>
|
|
||||||
<th>Quantity</th>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
}else{
|
|
||||||
// else
|
|
||||||
}
|
|
||||||
|
|
||||||
$i=1;
|
|
||||||
|
|
||||||
foreach($array_orders as $key => $val){
|
|
||||||
if($val['ProductId'] == $row['ProductId']){
|
|
||||||
|
|
||||||
if($row['FormUsed']=="jersey-and-shorts-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<?php echo $i++ ?>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<?php echo ($val['Name'] !== "") ? : '--' ?>
|
|
||||||
</td>
|
|
||||||
<td><?php echo $val['Number'] ?> </td>
|
|
||||||
<td><?php echo $val['JerseySize'] ?> </td>
|
|
||||||
<td><?php echo $val['ShortsSize'] ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="tshirt-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $i++ ?> </td>
|
|
||||||
<td><?php echo $val['Size'] ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="quantity-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $i++ ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="name-number-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $i++ ?> </td>
|
|
||||||
<td><?php echo $val['Name'] ?> </td>
|
|
||||||
<td><?php echo $val['Number'] ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="name-number-size-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $i++ ?> </td>
|
|
||||||
<td><?php echo $val['Name'] ?> </td>
|
|
||||||
<td><?php echo $val['Number'] ?> </td>
|
|
||||||
<td><?php echo $val['Size'] ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="number-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $i++ ?> </td>
|
|
||||||
<td><?php echo $val['Number'] ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}elseif($row['FormUsed']=="name-size-form"){
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo $i++ ?> </td>
|
|
||||||
<td><?php echo $val['Name'] ?> </td>
|
|
||||||
<td><?php echo $val['Size'] ?> </td>
|
|
||||||
<td><?php echo $val['Quantity'] ?> </td>
|
|
||||||
</tr>
|
|
||||||
<?php
|
|
||||||
}else{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
<!-- table header -->
|
|
||||||
<!-- table body -->
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td class="content-block">
|
|
||||||
<span class="apple-link">Copyright © 2019 <a
|
|
||||||
href="https://crewsportswear.com">crewsportswear.com</a>. All rights
|
|
||||||
reserved.</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
18
dbconfig.php
18
dbconfig.php
@@ -1,18 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$host = "localhost";
|
// Use environment variables for Docker deployment
|
||||||
$username = "root";
|
$host = getenv('DB_HOST') ?: 'localhost';
|
||||||
$password = "";
|
$username = getenv('DB_USER') ?: 'root';
|
||||||
$db = "custom_design";
|
$password = getenv('DB_PASS') ?: '';
|
||||||
|
$db = getenv('DB_NAME') ?: 'custom_designs';
|
||||||
|
$port = getenv('DB_PORT') ?: '3306';
|
||||||
|
|
||||||
try{
|
try{
|
||||||
// database connection
|
// database connection
|
||||||
$conn = new PDO("mysql:host=$host;dbname=$db",$username,$password);
|
$conn = new PDO("mysql:host=$host;port=$port;dbname=$db", $username, $password);
|
||||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
}
|
}
|
||||||
catch(PDOException $pe)
|
catch(PDOException $pe)
|
||||||
{
|
{
|
||||||
die('Connection error, because: ' .$pe->getMessage());
|
die('Connection error, because: ' .$pe->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$baseURL = 'http://'.$_SERVER['HTTP_HOST'] . '/bulacanlibrary/';
|
|
||||||
46
docker-compose.yml
Normal file
46
docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
email-reports:
|
||||||
|
build: .
|
||||||
|
container_name: email_reports_unified
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- TZ=America/Chicago
|
||||||
|
|
||||||
|
# Crew Sportswear Database
|
||||||
|
- DB_HOST_CREW=${DB_HOST_CREW:-mysql}
|
||||||
|
- DB_PORT_CREW=${DB_PORT_CREW:-3306}
|
||||||
|
- DB_NAME_CREW=${DB_NAME_CREW:-custom_design}
|
||||||
|
- DB_USER_CREW=${DB_USER_CREW:-crew_user}
|
||||||
|
- DB_PASS_CREW=${DB_PASS_CREW}
|
||||||
|
|
||||||
|
# MerchBay Database
|
||||||
|
- DB_HOST_MERCHBAY=${DB_HOST_MERCHBAY:-mysql}
|
||||||
|
- DB_PORT_MERCHBAY=${DB_PORT_MERCHBAY:-3306}
|
||||||
|
- DB_NAME_MERCHBAY=${DB_NAME_MERCHBAY:-merchbay_laravel}
|
||||||
|
- DB_USER_MERCHBAY=${DB_USER_MERCHBAY:-merchbay_user}
|
||||||
|
- DB_PASS_MERCHBAY=${DB_PASS_MERCHBAY}
|
||||||
|
|
||||||
|
# SMTP Configuration (shared)
|
||||||
|
- SMTP_HOST=${SMTP_HOST:-smtp.gmail.com}
|
||||||
|
- SMTP_PORT=${SMTP_PORT:-587}
|
||||||
|
|
||||||
|
# Crew SMTP
|
||||||
|
- SMTP_PASS_CREW=${SMTP_PASS_CREW}
|
||||||
|
|
||||||
|
# MerchBay SMTP
|
||||||
|
- SMTP_PASS_MERCHBAY=${SMTP_PASS_MERCHBAY}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ./daily_order_reports_crew:/app/daily_order_reports_crew
|
||||||
|
- ./daily_order_reports_merchbay:/app/daily_order_reports_merchbay
|
||||||
|
- ./email.log:/app/email.log
|
||||||
|
networks:
|
||||||
|
- crew-app-net
|
||||||
|
labels:
|
||||||
|
- "description=Unified Email Reports for Crew & MerchBay"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
crew-app-net:
|
||||||
|
external: true
|
||||||
27
index.php
27
index.php
@@ -4,6 +4,7 @@ include 'dbconfig.php';
|
|||||||
require("phpmailer/class.phpmailer.php");
|
require("phpmailer/class.phpmailer.php");
|
||||||
date_default_timezone_set('America/Chicago');
|
date_default_timezone_set('America/Chicago');
|
||||||
$dateToday = date('Y-m-d');
|
$dateToday = date('Y-m-d');
|
||||||
|
//$dateToday = "2019-12-12";
|
||||||
$dateTimeToday = date("Y-m-d H:i:s");
|
$dateTimeToday = date("Y-m-d H:i:s");
|
||||||
|
|
||||||
//grab all rows from the table where state is "new" and put into array
|
//grab all rows from the table where state is "new" and put into array
|
||||||
@@ -40,7 +41,7 @@ if($result > 0){
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$report_link = "https://www.crewsportswear.com/email_reports/daily_reports_with_image.php?d=". $dateToday;
|
$report_link = (getenv('APP_URL') ?: 'https://www.crewsportswear.com') . "/email_reports/daily_reports_with_image.php?d=". $dateToday;
|
||||||
$body = "<html>";
|
$body = "<html>";
|
||||||
$body .= "<body>";
|
$body .= "<body>";
|
||||||
$body .= "<p> Please download the attached file for today's order report.</p>";
|
$body .= "<p> Please download the attached file for today's order report.</p>";
|
||||||
@@ -51,7 +52,8 @@ if($result > 0){
|
|||||||
|
|
||||||
foreach($invoice_array as $invoice){
|
foreach($invoice_array as $invoice){
|
||||||
$v = explode("|", $invoice);
|
$v = explode("|", $invoice);
|
||||||
$body .= "<div>Invoice Number: <a href='http://admin.crewsportswear.com:23000/admin/orders/view/" . $v['0']."/print'>" . $v['1'] . "</a></div>";
|
$admin_url = getenv('ADMIN_URL') ?: 'https://admin.crewsportswear.app';
|
||||||
|
$body .= "<div>Invoice Number: <a href='" . $admin_url . "/admin/orders/view/" . $v['0']."/print'>" . $v['1'] . "</a></div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$body .= "</body>";
|
$body .= "</body>";
|
||||||
@@ -63,18 +65,23 @@ if($result > 0){
|
|||||||
// $mail->SMTPDebug = 3; // uncomment for debug mode.
|
// $mail->SMTPDebug = 3; // uncomment for debug mode.
|
||||||
|
|
||||||
$mail->SMTPSecure = 'tls';
|
$mail->SMTPSecure = 'tls';
|
||||||
$mail->Host = "smtp.gmail.com";
|
$mail->Host = getenv('SMTP_HOST') ?: 'smtp.gmail.com';
|
||||||
$mail->Port = "587";
|
$mail->Port = getenv('SMTP_PORT') ?: '587';
|
||||||
|
|
||||||
//gmail account
|
//gmail account credentials from environment
|
||||||
$mail->Username = 'no-reply@crewsportswear.com';
|
$mail->Username = getenv('SMTP_USER') ?: 'mail@crewsportswear.com';
|
||||||
$mail->Password = '20HustleHard19!';
|
$mail->Password = getenv('SMTP_PASS') ?: 'tjpwykpttvkjilxh';
|
||||||
|
|
||||||
$mail->SetFrom('orders@crewsportswear.com', 'CREW Daily Order Report');
|
$mail->SetFrom('orders@crewsportswear.com', 'CREW Daily Order Report');
|
||||||
|
|
||||||
$mail->addAddress('graphics@crewsportswear.com');
|
// Recipients from environment or defaults
|
||||||
$mail->addBCC("webmaster@crewsportswear.com");
|
$email_to = getenv('EMAIL_TO') ?: 'graphics@crewsportswear.com';
|
||||||
$mail->addBCC("angelo@crewsportswear.com");
|
$mail->addAddress($email_to);
|
||||||
|
|
||||||
|
$email_bcc = getenv('EMAIL_BCC') ?: 'webmaster@crewsportswear.com,angelo@crewsportswear.com,production@crewsportswear.com';
|
||||||
|
foreach (explode(',', $email_bcc) as $bcc_email) {
|
||||||
|
$mail->addBCC(trim($bcc_email));
|
||||||
|
}
|
||||||
|
|
||||||
$mail->addAttachment('daily_order_reports/'.$filename);
|
$mail->addAttachment('daily_order_reports/'.$filename);
|
||||||
|
|
||||||
|
|||||||
154
send_report.php
Normal file
154
send_report.php
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include 'dbconfig.php';
|
||||||
|
require("phpmailer/class.phpmailer.php");
|
||||||
|
date_default_timezone_set('America/Chicago');
|
||||||
|
$dateToday = date('Y-m-d');
|
||||||
|
$dateTimeToday = date("Y-m-d H:i:s");
|
||||||
|
|
||||||
|
// Determine which brand to process
|
||||||
|
$brand = getenv('BRAND') ?: 'crew'; // 'crew' or 'merchbay'
|
||||||
|
|
||||||
|
// Brand-specific configuration
|
||||||
|
$config = [
|
||||||
|
'crew' => [
|
||||||
|
'db_name' => getenv('DB_NAME_CREW') ?: 'custom_design',
|
||||||
|
'db_host' => getenv('DB_HOST_CREW') ?: getenv('DB_HOST') ?: 'mysql',
|
||||||
|
'db_user' => getenv('DB_USER_CREW') ?: 'crew_user',
|
||||||
|
'db_pass' => getenv('DB_PASS_CREW') ?: getenv('DB_PASS'),
|
||||||
|
'smtp_user' => 'mail@crewsportswear.com',
|
||||||
|
'smtp_pass' => getenv('SMTP_PASS_CREW') ?: getenv('SMTP_PASS'),
|
||||||
|
'email_from' => 'orders@crewsportswear.com',
|
||||||
|
'email_from_name' => 'CREW Daily Order Report',
|
||||||
|
'email_to' => 'graphics@crewsportswear.com',
|
||||||
|
'email_bcc' => ['webmaster@crewsportswear.com', 'angelo@crewsportswear.com', 'production@crewsportswear.com'],
|
||||||
|
'report_url' => 'https://www.crewsportswear.com/email_reports/daily_reports_with_image.php',
|
||||||
|
'admin_url' => 'https://admin.crewsportswear.app',
|
||||||
|
'csv_dir' => 'daily_order_reports_crew',
|
||||||
|
'log_prefix' => '[CREW]'
|
||||||
|
],
|
||||||
|
'merchbay' => [
|
||||||
|
'db_name' => getenv('DB_NAME_MERCHBAY') ?: 'merchbay_laravel',
|
||||||
|
'db_host' => getenv('DB_HOST_MERCHBAY') ?: getenv('DB_HOST') ?: 'mysql',
|
||||||
|
'db_user' => getenv('DB_USER_MERCHBAY') ?: 'merchbay_user',
|
||||||
|
'db_pass' => getenv('DB_PASS_MERCHBAY') ?: getenv('DB_PASS'),
|
||||||
|
'smtp_user' => 'support@merchbay.com',
|
||||||
|
'smtp_pass' => getenv('SMTP_PASS_MERCHBAY') ?: getenv('SMTP_PASS'),
|
||||||
|
'email_from' => 'orders@merchbay.com',
|
||||||
|
'email_from_name' => 'Merchbay Daily Order Report',
|
||||||
|
'email_to' => 'graphics@crewsportswear.com',
|
||||||
|
'email_bcc' => ['webmaster@crewsportswear.com', 'production@crewsportswear.com'],
|
||||||
|
'report_url' => 'https://www.crewsportswear.com/email_reports_merchbay/daily_reports_with_image.php',
|
||||||
|
'admin_url' => 'https://merchbay.app',
|
||||||
|
'csv_dir' => 'daily_order_reports_merchbay',
|
||||||
|
'log_prefix' => '[MERCHBAY]'
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$cfg = $config[$brand];
|
||||||
|
|
||||||
|
// Connect to brand-specific database
|
||||||
|
try {
|
||||||
|
$conn = new PDO(
|
||||||
|
"mysql:host={$cfg['db_host']};dbname={$cfg['db_name']}",
|
||||||
|
$cfg['db_user'],
|
||||||
|
$cfg['db_pass']
|
||||||
|
);
|
||||||
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch(PDOException $pe) {
|
||||||
|
$msg = $dateTimeToday . "\t\t\t{$cfg['log_prefix']} Database error: " . $pe->getMessage() . "\n";
|
||||||
|
file_put_contents('email.log', print_r($msg, true), FILE_APPEND);
|
||||||
|
die('Connection error, because: ' .$pe->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query orders
|
||||||
|
$q = $conn->prepare("SELECT t.StoreName, o.CartKey, o.ProductName, o.NAME, o.NUMBER, o.Size, o.JerseySize, o.ShortsSize, o.Price, o.Quantity, (o.Price * o.Quantity) AS TotalPrice, ((o.Price * o.Quantity) * 0.10) AS Tax,
|
||||||
|
o.DateCreated, pd.InvoiceNumber, pd.Payer_Email FROM orders AS o
|
||||||
|
INNER JOIN payment_details AS pd ON pd.CartKey = o.CartKey
|
||||||
|
INNER JOIN teamstores AS t ON t.Id = o.StoreId
|
||||||
|
WHERE o.DateCreated BETWEEN '$dateToday 00:00:00' AND '$dateToday 23:59:00'
|
||||||
|
ORDER BY o.DateCreated");
|
||||||
|
$q->execute();
|
||||||
|
$result = $q->rowCount();
|
||||||
|
|
||||||
|
if($result > 0){
|
||||||
|
$invoice_array = array();
|
||||||
|
|
||||||
|
// Create CSV directory if it doesn't exist
|
||||||
|
if (!file_exists($cfg['csv_dir'])) {
|
||||||
|
mkdir($cfg['csv_dir'], 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = $cfg['csv_dir'] . '/daily_order_report_'.$dateToday.'.csv';
|
||||||
|
$headers = array('StoreName', 'ProductName', 'NAME', 'NUMBER', 'Size', 'JerseySize', 'ShortsSize', 'Price', 'Quantity', 'TotalPrice', 'Tax', 'DateCreated', 'InvoiceNumber', 'Payer_Email');
|
||||||
|
$fp = fopen($filename, 'w');
|
||||||
|
fputcsv($fp, $headers);
|
||||||
|
|
||||||
|
while ($row = $q->fetch()) {
|
||||||
|
$value = $row['CartKey'] . "|" . $row['InvoiceNumber'];
|
||||||
|
if(!in_array($value, $invoice_array, true)){
|
||||||
|
array_push($invoice_array, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lineData = array($row['StoreName'], $row['ProductName'], $row['NAME'], $row['NUMBER'], $row['Size'], $row['JerseySize'], $row['ShortsSize'], $row['Price'], $row['Quantity'], $row['TotalPrice'], $row['Tax'], $row['DateCreated'], $row['InvoiceNumber'], $row['Payer_Email']);
|
||||||
|
fputcsv($fp, $lineData);
|
||||||
|
}
|
||||||
|
fclose($fp);
|
||||||
|
|
||||||
|
// Build email body
|
||||||
|
$report_link = $cfg['report_url'] . "?d=" . $dateToday;
|
||||||
|
$body = "<html>";
|
||||||
|
$body .= "<body>";
|
||||||
|
$body .= "<p>Please download the attached file for today's order report.</p>";
|
||||||
|
$body .= "<br><br>";
|
||||||
|
$body .= "<p>For the order report with image please <a href='".$report_link."'>click here</a>.</p>";
|
||||||
|
$body .= "<p>####################################</p>";
|
||||||
|
$body .= "<p>Order Details per Invoice</p>";
|
||||||
|
|
||||||
|
foreach($invoice_array as $invoice){
|
||||||
|
$v = explode("|", $invoice);
|
||||||
|
$body .= "<div>Invoice Number: <a href='" . $cfg['admin_url'] . "/admin/orders/view/" . $v['0'] . "/print'>" . $v['1'] . "</a></div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$body .= "</body>";
|
||||||
|
$body .= "</html>";
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
$mail = new PHPMailer();
|
||||||
|
$mail->IsSMTP();
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
// $mail->SMTPDebug = 3; // uncomment for debug mode.
|
||||||
|
|
||||||
|
$mail->SMTPSecure = 'tls';
|
||||||
|
$mail->Host = getenv('SMTP_HOST') ?: 'smtp.gmail.com';
|
||||||
|
$mail->Port = getenv('SMTP_PORT') ?: '587';
|
||||||
|
|
||||||
|
$mail->Username = $cfg['smtp_user'];
|
||||||
|
$mail->Password = $cfg['smtp_pass'];
|
||||||
|
|
||||||
|
$mail->SetFrom($cfg['email_from'], $cfg['email_from_name']);
|
||||||
|
$mail->addAddress($cfg['email_to']);
|
||||||
|
|
||||||
|
foreach ($cfg['email_bcc'] as $bcc) {
|
||||||
|
$mail->addBCC($bcc);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->addAttachment($filename);
|
||||||
|
|
||||||
|
$mail->Subject = "{$cfg['email_from_name']} - $dateToday";
|
||||||
|
$mail->Body = $body;
|
||||||
|
$mail->ErrorInfo;
|
||||||
|
$mail->IsHTML(true);
|
||||||
|
|
||||||
|
if(!$mail->Send()){
|
||||||
|
$msg = $dateTimeToday . "\t\t\t{$cfg['log_prefix']} Mailer Error: " . $mail->ErrorInfo . "\n";
|
||||||
|
file_put_contents('email.log', print_r($msg, true), FILE_APPEND);
|
||||||
|
} else {
|
||||||
|
$msg = $dateTimeToday . "\t\t\t{$cfg['log_prefix']} successfully sent ({$result} orders)\n";
|
||||||
|
file_put_contents('email.log', print_r($msg, true), FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$msg = $dateTimeToday . "\t\t\t{$cfg['log_prefix']} No order for today\n";
|
||||||
|
file_put_contents('email.log', print_r($msg, true), FILE_APPEND);
|
||||||
|
}
|
||||||
356
styles.css
Normal file
356
styles.css
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
img {
|
||||||
|
border: none;
|
||||||
|
-ms-interpolation-mode: bicubic;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
font-family: sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: separate;
|
||||||
|
mso-table-lspace: 0pt;
|
||||||
|
mso-table-rspace: 0pt;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 10px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
BODY & CONTAINER
|
||||||
|
------------------------------------- */
|
||||||
|
|
||||||
|
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||||
|
.container {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
/* makes it centered */
|
||||||
|
max-width: 880px;
|
||||||
|
padding: 10px;
|
||||||
|
width: 880px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||||
|
.content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 880px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
HEADER, FOOTER, MAIN
|
||||||
|
------------------------------------- */
|
||||||
|
.main {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-block {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
clear: both;
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer td,
|
||||||
|
.footer p,
|
||||||
|
.footer span,
|
||||||
|
.footer a {
|
||||||
|
color: #999999;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
TYPOGRAPHY
|
||||||
|
------------------------------------- */
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4 {
|
||||||
|
color: #000000;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 35px;
|
||||||
|
font-weight: 300;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p li,
|
||||||
|
ul li,
|
||||||
|
ol li {
|
||||||
|
list-style-position: inside;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #3498db;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
BUTTONS
|
||||||
|
------------------------------------- */
|
||||||
|
.btn {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn > tbody > tr > td {
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn table {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn table td {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn a {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: solid 1px #3498db;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #3498db;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px 25px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary table td {
|
||||||
|
background-color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary a {
|
||||||
|
background-color: #3498db;
|
||||||
|
border-color: #3498db;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
OTHER STYLES THAT MIGHT BE USEFUL
|
||||||
|
------------------------------------- */
|
||||||
|
.last {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt0 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb0 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preheader {
|
||||||
|
color: transparent;
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
max-height: 0;
|
||||||
|
max-width: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
mso-hide: all;
|
||||||
|
visibility: hidden;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.powered-by a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid #f6f6f6;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||||
|
------------------------------------- */
|
||||||
|
@media only screen and (max-width: 620px) {
|
||||||
|
table[class="body"] h1 {
|
||||||
|
font-size: 28px !important;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] p,
|
||||||
|
table[class="body"] ul,
|
||||||
|
table[class="body"] ol,
|
||||||
|
table[class="body"] td,
|
||||||
|
table[class="body"] span,
|
||||||
|
table[class="body"] a {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .wrapper,
|
||||||
|
table[class="body"] .article {
|
||||||
|
padding: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .content {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .container {
|
||||||
|
padding: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .main {
|
||||||
|
border-left-width: 0 !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border-right-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .btn table {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .btn a {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[class="body"] .img-responsive {
|
||||||
|
height: auto !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------
|
||||||
|
PRESERVE THESE STYLES IN THE HEAD
|
||||||
|
------------------------------------- */
|
||||||
|
@media all {
|
||||||
|
.ExternalClass {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ExternalClass,
|
||||||
|
.ExternalClass p,
|
||||||
|
.ExternalClass span,
|
||||||
|
.ExternalClass font,
|
||||||
|
.ExternalClass td,
|
||||||
|
.ExternalClass div {
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apple-link a {
|
||||||
|
color: inherit !important;
|
||||||
|
font-family: inherit !important;
|
||||||
|
font-size: inherit !important;
|
||||||
|
font-weight: inherit !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary table td:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary a:hover {
|
||||||
|
background-color: #34495e !important;
|
||||||
|
border-color: #34495e !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-bordered td,
|
||||||
|
th {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewImage {
|
||||||
|
height: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.items tr {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@page {
|
||||||
|
size: 18cm 26.7cm;
|
||||||
|
margin: 1cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tbl_subitem tr td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user