feat: Implement comprehensive database setup system
- Add complete database setup script (setup_complete_database.py) - Add quick deployment script (quick_deploy.sh) - Add comprehensive documentation (DATABASE_SETUP_README.md) - Move individual db scripts to backup_db_scripts folder - Update external_server.conf with correct database settings - Clean up obsolete documentation files - Streamline deployment process to single command Features: - One-script database creation for all tables and triggers - Automated permissions and roles setup - Complete verification and error handling - Production-ready deployment workflow - Maintains backward compatibility with individual scripts
This commit is contained in:
133
py_app/DATABASE_SETUP_README.md
Normal file
133
py_app/DATABASE_SETUP_README.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# Quick Database Setup for Trasabilitate Application
|
||||||
|
|
||||||
|
This script provides a complete one-step database setup for quick deployment of the Trasabilitate application.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before running the setup script, ensure:
|
||||||
|
|
||||||
|
1. **MariaDB is installed and running**
|
||||||
|
2. **Database and user are created**:
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE trasabilitate;
|
||||||
|
CREATE USER 'trasabilitate'@'localhost' IDENTIFIED BY 'Initial01!';
|
||||||
|
GRANT ALL PRIVILEGES ON trasabilitate.* TO 'trasabilitate'@'localhost';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
3. **Python virtual environment is activated**:
|
||||||
|
```bash
|
||||||
|
source ../recticel/bin/activate
|
||||||
|
```
|
||||||
|
4. **Python dependencies are installed**:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Quick Setup (Recommended)
|
||||||
|
```bash
|
||||||
|
cd /srv/quality_recticel/py_app
|
||||||
|
source ../recticel/bin/activate
|
||||||
|
python3 app/db_create_scripts/setup_complete_database.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### What the script creates:
|
||||||
|
|
||||||
|
#### MariaDB Tables:
|
||||||
|
- `scan1_orders` - Quality scanning data for process 1
|
||||||
|
- `scanfg_orders` - Quality scanning data for finished goods
|
||||||
|
- `order_for_labels` - Label printing orders
|
||||||
|
- `warehouse_locations` - Warehouse location management
|
||||||
|
- `permissions` - System permissions
|
||||||
|
- `role_permissions` - Role-permission mappings
|
||||||
|
- `role_hierarchy` - User role hierarchy
|
||||||
|
- `permission_audit_log` - Permission change audit trail
|
||||||
|
|
||||||
|
#### Database Triggers:
|
||||||
|
- Auto-increment approved/rejected quantities based on quality codes
|
||||||
|
- Triggers for both scan1_orders and scanfg_orders tables
|
||||||
|
|
||||||
|
#### SQLite Tables:
|
||||||
|
- `users` - User authentication (in instance/users.db)
|
||||||
|
- `roles` - User roles (in instance/users.db)
|
||||||
|
|
||||||
|
#### Configuration:
|
||||||
|
- Updates `instance/external_server.conf` with correct database settings
|
||||||
|
- Creates default superadmin user (username: `superadmin`, password: `superadmin123`)
|
||||||
|
|
||||||
|
#### Permission System:
|
||||||
|
- 7 user roles (superadmin, admin, manager, quality_manager, warehouse_manager, quality_worker, warehouse_worker)
|
||||||
|
- 25+ granular permissions for different application areas
|
||||||
|
- Complete role hierarchy with inheritance
|
||||||
|
|
||||||
|
## After Setup
|
||||||
|
|
||||||
|
1. **Start the application**:
|
||||||
|
```bash
|
||||||
|
python3 run.py
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Access the application**:
|
||||||
|
- Local: http://127.0.0.1:8781
|
||||||
|
- Network: http://192.168.0.205:8781
|
||||||
|
|
||||||
|
3. **Login with superadmin**:
|
||||||
|
- Username: `superadmin`
|
||||||
|
- Password: `superadmin123`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues:
|
||||||
|
|
||||||
|
1. **Database connection failed**:
|
||||||
|
- Check if MariaDB is running: `sudo systemctl status mariadb`
|
||||||
|
- Verify database exists: `sudo mysql -e "SHOW DATABASES;"`
|
||||||
|
- Check user privileges: `sudo mysql -e "SHOW GRANTS FOR 'trasabilitate'@'localhost';"`
|
||||||
|
|
||||||
|
2. **Import errors**:
|
||||||
|
- Ensure virtual environment is activated
|
||||||
|
- Install missing dependencies: `pip install -r requirements.txt`
|
||||||
|
|
||||||
|
3. **Permission denied**:
|
||||||
|
- Make script executable: `chmod +x app/db_create_scripts/setup_complete_database.py`
|
||||||
|
- Check file ownership: `ls -la app/db_create_scripts/`
|
||||||
|
|
||||||
|
### Manual Database Recreation:
|
||||||
|
|
||||||
|
If you need to completely reset the database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Drop and recreate database
|
||||||
|
sudo mysql -e "DROP DATABASE IF EXISTS trasabilitate; CREATE DATABASE trasabilitate; GRANT ALL PRIVILEGES ON trasabilitate.* TO 'trasabilitate'@'localhost'; FLUSH PRIVILEGES;"
|
||||||
|
|
||||||
|
# Remove SQLite database
|
||||||
|
rm -f instance/users.db
|
||||||
|
|
||||||
|
# Run setup script
|
||||||
|
python3 app/db_create_scripts/setup_complete_database.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Script Features
|
||||||
|
|
||||||
|
- ✅ **Comprehensive**: Creates all necessary database structure
|
||||||
|
- ✅ **Safe**: Uses `IF NOT EXISTS` clauses to prevent conflicts
|
||||||
|
- ✅ **Verified**: Includes verification step to confirm setup
|
||||||
|
- ✅ **Informative**: Detailed output showing each step
|
||||||
|
- ✅ **Error handling**: Clear error messages and troubleshooting hints
|
||||||
|
- ✅ **Idempotent**: Can be run multiple times safely
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
|
||||||
|
The script combines functionality from these individual scripts:
|
||||||
|
- `create_scan_1db.py`
|
||||||
|
- `create_scanfg_orders.py`
|
||||||
|
- `create_order_for_labels_table.py`
|
||||||
|
- `create_warehouse_locations_table.py`
|
||||||
|
- `create_permissions_tables.py`
|
||||||
|
- `create_roles_table.py`
|
||||||
|
- `create_triggers.py`
|
||||||
|
- `create_triggers_fg.py`
|
||||||
|
- `populate_permissions.py`
|
||||||
|
|
||||||
|
For development or debugging, you can still run individual scripts if needed.
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
# Enhanced Print Controller - Features & Usage
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
The print module now includes an advanced Print Controller with real-time monitoring, error detection, pause/resume functionality, and automatic reprint capabilities for handling printer issues like paper jams or running out of paper.
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
### 1. **Real-Time Progress Modal**
|
|
||||||
- Visual progress bar showing percentage completion
|
|
||||||
- Live counter showing "X / Y" labels printed
|
|
||||||
- Status messages updating in real-time
|
|
||||||
- Detailed event log with timestamps
|
|
||||||
|
|
||||||
### 2. **Print Status Log**
|
|
||||||
- Timestamped entries for all print events
|
|
||||||
- Color-coded status messages:
|
|
||||||
- **Green**: Successful operations
|
|
||||||
- **Yellow**: Warnings and paused states
|
|
||||||
- **Red**: Errors and failures
|
|
||||||
- Auto-scrolling to show latest events
|
|
||||||
- Scrollable history of all print activities
|
|
||||||
|
|
||||||
### 3. **Control Buttons**
|
|
||||||
|
|
||||||
#### **⏸️ Pause Button**
|
|
||||||
- Pauses printing between labels
|
|
||||||
- Useful for:
|
|
||||||
- Checking printer paper level
|
|
||||||
- Inspecting print quality
|
|
||||||
- Loading more paper
|
|
||||||
- Progress bar turns yellow when paused
|
|
||||||
|
|
||||||
#### **▶️ Resume Button**
|
|
||||||
- Resumes printing from where it was paused
|
|
||||||
- Appears when print job is paused
|
|
||||||
- Progress bar returns to normal green
|
|
||||||
|
|
||||||
#### **🔄 Reprint Last Button**
|
|
||||||
- Available after each successful print
|
|
||||||
- Reprints the last completed label
|
|
||||||
- Useful when:
|
|
||||||
- Label came out damaged
|
|
||||||
- Print quality was poor
|
|
||||||
- Label fell on the floor
|
|
||||||
|
|
||||||
#### **❌ Cancel Button**
|
|
||||||
- Stops the entire print job
|
|
||||||
- Shows how many labels were completed
|
|
||||||
- Progress bar turns red
|
|
||||||
- Database not updated on cancellation
|
|
||||||
|
|
||||||
### 4. **Automatic Error Detection**
|
|
||||||
- Detects when a label fails to print
|
|
||||||
- Automatically pauses the job
|
|
||||||
- Shows error message in log
|
|
||||||
- Progress bar turns red
|
|
||||||
- Prompts user to check printer
|
|
||||||
|
|
||||||
### 5. **Automatic Recovery**
|
|
||||||
When a print error occurs:
|
|
||||||
1. Job pauses automatically
|
|
||||||
2. Error is logged with timestamp
|
|
||||||
3. User checks and fixes printer issue (refill paper, clear jam)
|
|
||||||
4. User clicks "Resume"
|
|
||||||
5. Failed label is automatically retried
|
|
||||||
6. If retry succeeds, continues with next label
|
|
||||||
7. If retry fails, user can cancel or try again
|
|
||||||
|
|
||||||
### 6. **Smart Print Management**
|
|
||||||
- Tracks current label being printed
|
|
||||||
- Remembers last successfully printed label
|
|
||||||
- Maintains list of failed labels
|
|
||||||
- Prevents duplicate printing
|
|
||||||
- Sequential label numbering (CP00000777/001, 002, 003...)
|
|
||||||
|
|
||||||
## Usage Instructions
|
|
||||||
|
|
||||||
### Normal Printing Workflow
|
|
||||||
|
|
||||||
1. **Start Print Job**
|
|
||||||
- Select an order from the table
|
|
||||||
- Click "Print Label (QZ Tray)" button
|
|
||||||
- Print Controller modal appears
|
|
||||||
|
|
||||||
2. **Monitor Progress**
|
|
||||||
- Watch progress bar fill (green)
|
|
||||||
- Check "X / Y" counter
|
|
||||||
- Read status messages
|
|
||||||
- View timestamped log entries
|
|
||||||
|
|
||||||
3. **Completion**
|
|
||||||
- All labels print successfully
|
|
||||||
- Database updates automatically
|
|
||||||
- Table refreshes to show new status
|
|
||||||
- Modal closes automatically
|
|
||||||
- Success notification appears
|
|
||||||
|
|
||||||
### Handling Paper Running Out Mid-Print
|
|
||||||
|
|
||||||
**Scenario**: Printing 20 labels, paper runs out after label 12
|
|
||||||
|
|
||||||
1. **Detection**
|
|
||||||
- Label 13 fails to print
|
|
||||||
- Controller detects error
|
|
||||||
- Job pauses automatically
|
|
||||||
- Progress bar turns red
|
|
||||||
- Log shows: "✗ Label 13 failed: Print error"
|
|
||||||
- Status: "⚠️ ERROR - Check printer (paper jam/out of paper)"
|
|
||||||
|
|
||||||
2. **User Action**
|
|
||||||
- Check printer
|
|
||||||
- See paper is empty
|
|
||||||
- Load new paper roll
|
|
||||||
- Ensure paper is feeding correctly
|
|
||||||
|
|
||||||
3. **Resume Printing**
|
|
||||||
- Click "▶️ Resume" button
|
|
||||||
- Controller automatically retries label 13
|
|
||||||
- Log shows: "Retrying label 13..."
|
|
||||||
- If successful: "✓ Label 13 printed successfully (retry)"
|
|
||||||
- Continues with labels 14-20
|
|
||||||
- Job completes normally
|
|
||||||
|
|
||||||
### Handling Print Quality Issues
|
|
||||||
|
|
||||||
**Scenario**: Label 5 of 10 prints too light
|
|
||||||
|
|
||||||
1. **During Print**
|
|
||||||
- Wait for label 5 to complete
|
|
||||||
- "🔄 Reprint Last" button appears
|
|
||||||
- Click button
|
|
||||||
- Label 5 reprints with current settings
|
|
||||||
|
|
||||||
2. **Adjust & Continue**
|
|
||||||
- Adjust printer darkness setting
|
|
||||||
- Click "▶️ Resume" if paused
|
|
||||||
- Continue printing labels 6-10
|
|
||||||
|
|
||||||
### Manual Pause for Inspection
|
|
||||||
|
|
||||||
**Scenario**: Want to check label quality mid-batch
|
|
||||||
|
|
||||||
1. Click "⏸️ Pause" button
|
|
||||||
2. Progress bar turns yellow
|
|
||||||
3. Remove and inspect last printed label
|
|
||||||
4. If good: Click "▶️ Resume"
|
|
||||||
5. If bad:
|
|
||||||
- Click "🔄 Reprint Last"
|
|
||||||
- Adjust printer settings
|
|
||||||
- Click "▶️ Resume"
|
|
||||||
|
|
||||||
### Emergency Cancellation
|
|
||||||
|
|
||||||
**Scenario**: Wrong order selected or major printer malfunction
|
|
||||||
|
|
||||||
1. Click "❌ Cancel" button
|
|
||||||
2. Printing stops immediately
|
|
||||||
3. Log shows labels completed (e.g., "7 of 25")
|
|
||||||
4. Progress bar turns red
|
|
||||||
5. Modal stays open for 2 seconds
|
|
||||||
6. Warning notification appears
|
|
||||||
7. Database NOT updated
|
|
||||||
8. Can reprint the order later
|
|
||||||
|
|
||||||
## Technical Implementation
|
|
||||||
|
|
||||||
### Print Controller State
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
isPaused: false, // Whether job is paused
|
|
||||||
isCancelled: false, // Whether job is cancelled
|
|
||||||
currentLabel: 0, // Current label number being printed
|
|
||||||
totalLabels: 0, // Total labels in job
|
|
||||||
lastPrintedLabel: 0, // Last successfully printed label
|
|
||||||
failedLabels: [], // Array of failed label numbers
|
|
||||||
orderData: null, // Order information
|
|
||||||
printerName: null // Selected printer name
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Event Log Format
|
|
||||||
```
|
|
||||||
[17:47:15] Starting print job: 10 labels
|
|
||||||
[17:47:15] Printer: Thermal Printer A
|
|
||||||
[17:47:15] Order: CP00000777
|
|
||||||
[17:47:16] Sending label 1 to printer...
|
|
||||||
[17:47:16] ✓ Label 1 printed successfully
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Detection
|
|
||||||
- Try/catch around each print operation
|
|
||||||
- Errors trigger automatic pause
|
|
||||||
- Failed label number recorded
|
|
||||||
- Automatic retry on resume
|
|
||||||
|
|
||||||
### Progress Calculation
|
|
||||||
```javascript
|
|
||||||
percentage = (currentLabel / totalLabels) * 100
|
|
||||||
```
|
|
||||||
|
|
||||||
### Color States
|
|
||||||
- **Green**: Normal printing
|
|
||||||
- **Yellow**: Paused (manual or automatic)
|
|
||||||
- **Red**: Error or cancelled
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
1. ✅ **Prevents Wasted Labels**: Automatic recovery from errors
|
|
||||||
2. ✅ **Reduces Operator Stress**: Clear status and easy controls
|
|
||||||
3. ✅ **Handles Paper Depletion**: Auto-pause and retry on paper out
|
|
||||||
4. ✅ **Quality Control**: Easy reprint of damaged labels
|
|
||||||
5. ✅ **Transparency**: Full log of all print activities
|
|
||||||
6. ✅ **Flexibility**: Pause anytime for inspection
|
|
||||||
7. ✅ **Safety**: Cancel button for emergencies
|
|
||||||
8. ✅ **Accuracy**: Sequential numbering maintained even with errors
|
|
||||||
|
|
||||||
## Common Scenarios Handled
|
|
||||||
|
|
||||||
| Issue | Detection | Solution |
|
|
||||||
|-------|-----------|----------|
|
|
||||||
| Paper runs out | Print error on next label | Auto-pause, user refills, resume |
|
|
||||||
| Paper jam | Print error detected | Auto-pause, user clears jam, resume |
|
|
||||||
| Poor print quality | Visual inspection | Reprint last label |
|
|
||||||
| Wrong order selected | User realizes mid-print | Cancel job |
|
|
||||||
| Need to inspect labels | User decision | Pause, inspect, resume |
|
|
||||||
| Label falls on floor | Visual observation | Reprint last label |
|
|
||||||
| Printer offline | Print error | Auto-pause, user fixes, resume |
|
|
||||||
|
|
||||||
## Future Enhancements (Possible)
|
|
||||||
|
|
||||||
- Printer status monitoring (paper level, online/offline)
|
|
||||||
- Print queue for multiple orders
|
|
||||||
- Estimated time remaining
|
|
||||||
- Sound notifications on completion
|
|
||||||
- Email/SMS alerts for long jobs
|
|
||||||
- Print history logging to database
|
|
||||||
- Batch printing multiple orders
|
|
||||||
- Automatic printer reconnection
|
|
||||||
|
|
||||||
## Testing Recommendations
|
|
||||||
|
|
||||||
1. **Normal Job**: Print 5-10 labels, verify all complete
|
|
||||||
2. **Paper Depletion**: Remove paper mid-job, verify auto-pause and recovery
|
|
||||||
3. **Pause/Resume**: Manually pause mid-job, wait, resume
|
|
||||||
4. **Reprint**: Print job, reprint last label multiple times
|
|
||||||
5. **Cancel**: Start job, cancel after 2-3 labels
|
|
||||||
6. **Error Recovery**: Simulate error, verify automatic retry
|
|
||||||
|
|
||||||
## Browser Compatibility
|
|
||||||
|
|
||||||
- Chrome/Edge: ✅ Fully supported
|
|
||||||
- Firefox: ✅ Fully supported
|
|
||||||
- Safari: ✅ Fully supported
|
|
||||||
- Mobile browsers: ⚠️ Desktop recommended for QZ Tray
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
For issues or questions:
|
|
||||||
- Check browser console for detailed error messages
|
|
||||||
- Verify QZ Tray is running and connected
|
|
||||||
- Check printer is online and has paper
|
|
||||||
- Review print status log in modal
|
|
||||||
- Restart QZ Tray if connection issues persist
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
# Mobile-Responsive Login Page
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
The login page has been enhanced with comprehensive mobile-responsive CSS to provide an optimal user experience across all device types and screen sizes.
|
|
||||||
|
|
||||||
## Mobile-Responsive Features Added
|
|
||||||
|
|
||||||
### 1. **Responsive Breakpoints**
|
|
||||||
- **Tablet (≤768px)**: Column layout, optimized logo and form sizing
|
|
||||||
- **Mobile (≤480px)**: Enhanced touch targets, better spacing
|
|
||||||
- **Small Mobile (≤320px)**: Minimal padding, compact design
|
|
||||||
- **Landscape (height ≤500px)**: Horizontal layout for landscape phones
|
|
||||||
|
|
||||||
### 2. **Layout Adaptations**
|
|
||||||
|
|
||||||
#### Desktop (>768px)
|
|
||||||
- Side-by-side logo and form layout
|
|
||||||
- Large logo (90vh height)
|
|
||||||
- Fixed form width (600px)
|
|
||||||
|
|
||||||
#### Tablet (≤768px)
|
|
||||||
- Vertical stacked layout
|
|
||||||
- Logo height reduced to 30vh
|
|
||||||
- Form width becomes responsive (100%, max 400px)
|
|
||||||
|
|
||||||
#### Mobile (≤480px)
|
|
||||||
- Optimized touch targets (44px minimum)
|
|
||||||
- Increased padding and margins
|
|
||||||
- Better visual hierarchy
|
|
||||||
- Enhanced shadows and border radius
|
|
||||||
|
|
||||||
#### Small Mobile (≤320px)
|
|
||||||
- Minimal padding to maximize space
|
|
||||||
- Compact logo (20vh height)
|
|
||||||
- Reduced font sizes where appropriate
|
|
||||||
|
|
||||||
### 3. **Touch Optimizations**
|
|
||||||
|
|
||||||
#### iOS/Safari Specific
|
|
||||||
- `font-size: 16px` on inputs prevents automatic zoom
|
|
||||||
- Proper touch target sizing (44px minimum)
|
|
||||||
|
|
||||||
#### Touch Device Enhancements
|
|
||||||
- Active states for button presses
|
|
||||||
- Optimized image rendering for high DPI screens
|
|
||||||
- Hover effects disabled on touch devices
|
|
||||||
|
|
||||||
### 4. **Accessibility Improvements**
|
|
||||||
- Proper contrast ratios maintained
|
|
||||||
- Touch targets meet accessibility guidelines
|
|
||||||
- Readable font sizes across all devices
|
|
||||||
- Smooth transitions and animations
|
|
||||||
|
|
||||||
### 5. **Performance Considerations**
|
|
||||||
- CSS-only responsive design (no JavaScript required)
|
|
||||||
- Efficient media queries
|
|
||||||
- Optimized image rendering for retina displays
|
|
||||||
|
|
||||||
## Key CSS Features
|
|
||||||
|
|
||||||
### Flexible Layout
|
|
||||||
```css
|
|
||||||
.login-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column; /* Mobile */
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Responsive Images
|
|
||||||
```css
|
|
||||||
.login-logo {
|
|
||||||
max-height: 25vh; /* Mobile */
|
|
||||||
max-width: 85vw;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Touch-Friendly Inputs
|
|
||||||
```css
|
|
||||||
.form-container input {
|
|
||||||
padding: 12px;
|
|
||||||
font-size: 16px; /* Prevents iOS zoom */
|
|
||||||
min-height: 44px; /* Touch target size */
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Landscape Optimization
|
|
||||||
```css
|
|
||||||
@media screen and (max-height: 500px) and (orientation: landscape) {
|
|
||||||
.login-page {
|
|
||||||
flex-direction: row; /* Back to horizontal */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Recommendations
|
|
||||||
|
|
||||||
### Device Testing
|
|
||||||
- [ ] iPhone (various sizes)
|
|
||||||
- [ ] Android phones (various sizes)
|
|
||||||
- [ ] iPad/Android tablets
|
|
||||||
- [ ] Desktop browsers with responsive mode
|
|
||||||
|
|
||||||
### Orientation Testing
|
|
||||||
- [ ] Portrait mode on all devices
|
|
||||||
- [ ] Landscape mode on phones
|
|
||||||
- [ ] Landscape mode on tablets
|
|
||||||
|
|
||||||
### Browser Testing
|
|
||||||
- [ ] Safari (iOS)
|
|
||||||
- [ ] Chrome (Android/iOS)
|
|
||||||
- [ ] Firefox Mobile
|
|
||||||
- [ ] Samsung Internet
|
|
||||||
- [ ] Desktop browsers (Chrome, Firefox, Safari, Edge)
|
|
||||||
|
|
||||||
## Browser Support
|
|
||||||
- Modern browsers (ES6+ support)
|
|
||||||
- iOS Safari 12+
|
|
||||||
- Android Chrome 70+
|
|
||||||
- Desktop browsers (last 2 versions)
|
|
||||||
|
|
||||||
## Performance Impact
|
|
||||||
- **CSS Size**: Increased by ~2KB (compressed)
|
|
||||||
- **Load Time**: No impact (CSS only)
|
|
||||||
- **Rendering**: Optimized for mobile GPUs
|
|
||||||
- **Memory**: Minimal additional usage
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
1. **Dark mode mobile optimizations**
|
|
||||||
2. **Progressive Web App (PWA) features**
|
|
||||||
3. **Biometric authentication UI**
|
|
||||||
4. **Loading states and animations**
|
|
||||||
5. **Error message responsive design**
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
# Print Progress Modal Feature
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
Added a visual progress modal that displays during label printing operations via QZ Tray. The modal shows real-time progress, updates the database upon completion, and refreshes the table view automatically.
|
|
||||||
|
|
||||||
## Features Implemented
|
|
||||||
|
|
||||||
### 1. Progress Modal UI
|
|
||||||
- **Modal Overlay**: Full-screen semi-transparent overlay to focus user attention
|
|
||||||
- **Progress Bar**: Animated progress bar showing percentage completion
|
|
||||||
- **Status Messages**: Real-time status updates during printing
|
|
||||||
- **Label Counter**: Shows "X / Y" format for current progress (e.g., "5 / 10")
|
|
||||||
|
|
||||||
### 2. Print Flow Improvements
|
|
||||||
The printing process now follows these steps:
|
|
||||||
|
|
||||||
1. **Validation**: Check QZ Tray connection and printer selection
|
|
||||||
2. **Modal Display**: Show progress modal immediately
|
|
||||||
3. **Sequential Printing**: Print each label one by one with progress updates
|
|
||||||
- Update progress bar after each successful print
|
|
||||||
- Show current label number being printed
|
|
||||||
- 500ms delay between labels for printer processing
|
|
||||||
4. **Database Update**: Call `/update_printed_status/<order_id>` endpoint
|
|
||||||
- Marks the order as printed in the database
|
|
||||||
- Handles errors gracefully (prints still succeed even if DB update fails)
|
|
||||||
5. **Table Refresh**: Automatically click "Load Orders" button to refresh the view
|
|
||||||
6. **Modal Close**: Hide modal after completion
|
|
||||||
7. **Notification**: Show success notification to user
|
|
||||||
|
|
||||||
### 3. Progress Updates
|
|
||||||
The modal displays different status messages:
|
|
||||||
- "Preparing to print..." (initial)
|
|
||||||
- "Printing label X of Y..." (during printing)
|
|
||||||
- "✅ All labels printed! Updating database..." (after prints complete)
|
|
||||||
- "✅ Complete! Refreshing table..." (after DB update)
|
|
||||||
- "⚠️ Labels printed but database update failed" (on DB error)
|
|
||||||
|
|
||||||
### 4. Error Handling
|
|
||||||
- Modal automatically closes on any error
|
|
||||||
- Error notifications shown to user
|
|
||||||
- Database update failures don't prevent successful printing
|
|
||||||
- Graceful degradation if DB update fails
|
|
||||||
|
|
||||||
## Technical Details
|
|
||||||
|
|
||||||
### CSS Styling
|
|
||||||
- **Modal**: Fixed position, z-index 9999, centered layout
|
|
||||||
- **Content Card**: White background, rounded corners, shadow
|
|
||||||
- **Progress Bar**: Linear gradient blue, smooth transitions
|
|
||||||
- **Responsive**: Min-width 400px, max-width 500px
|
|
||||||
|
|
||||||
### JavaScript Functions Modified
|
|
||||||
|
|
||||||
#### `handleQZTrayPrint(selectedRow)`
|
|
||||||
**Changes:**
|
|
||||||
- Added modal element references
|
|
||||||
- Show modal before printing starts
|
|
||||||
- Update progress bar and counter in loop
|
|
||||||
- Call database update endpoint after printing
|
|
||||||
- Handle database update errors
|
|
||||||
- Refresh table automatically
|
|
||||||
- Close modal on completion or error
|
|
||||||
|
|
||||||
### Backend Integration
|
|
||||||
|
|
||||||
#### Endpoint Used: `/update_printed_status/<int:order_id>`
|
|
||||||
- **Method**: POST
|
|
||||||
- **Purpose**: Mark order as printed in database
|
|
||||||
- **Authentication**: Requires superadmin, warehouse_manager, or etichete role
|
|
||||||
- **Response**: JSON with success/error message
|
|
||||||
|
|
||||||
## User Experience Flow
|
|
||||||
|
|
||||||
1. User selects an order row in the table
|
|
||||||
2. User clicks "Print Label (QZ Tray)" button
|
|
||||||
3. Modal appears showing "Preparing to print..."
|
|
||||||
4. Progress bar fills as each label prints
|
|
||||||
5. Counter shows current progress (e.g., "7 / 10")
|
|
||||||
6. After all labels print: "✅ All labels printed! Updating database..."
|
|
||||||
7. Database updates with printed status
|
|
||||||
8. Modal shows "✅ Complete! Refreshing table..."
|
|
||||||
9. Modal closes automatically
|
|
||||||
10. Success notification appears
|
|
||||||
11. Table refreshes showing updated order status
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
✅ **Visual Feedback**: Users see real-time progress instead of a frozen UI
|
|
||||||
✅ **Status Clarity**: Clear messages about what's happening
|
|
||||||
✅ **Automatic Updates**: Database and UI update without manual intervention
|
|
||||||
✅ **Error Recovery**: Graceful handling of database update failures
|
|
||||||
✅ **Professional UX**: Modern, polished user interface
|
|
||||||
✅ **Non-Blocking**: Progress modal doesn't interfere with printing operation
|
|
||||||
|
|
||||||
## Files Modified
|
|
||||||
|
|
||||||
1. **print_module.html**
|
|
||||||
- Added modal HTML structure
|
|
||||||
- Added modal CSS styles
|
|
||||||
- Updated `handleQZTrayPrint()` function
|
|
||||||
- Added database update API call
|
|
||||||
- Added automatic table refresh
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Modal appears when printing starts
|
|
||||||
- [ ] Progress bar animates smoothly
|
|
||||||
- [ ] Counter updates correctly (1/10, 2/10, etc.)
|
|
||||||
- [ ] All labels print successfully
|
|
||||||
- [ ] Database updates after printing
|
|
||||||
- [ ] Table refreshes automatically
|
|
||||||
- [ ] Modal closes after completion
|
|
||||||
- [ ] Success notification appears
|
|
||||||
- [ ] Error handling works (if DB update fails)
|
|
||||||
- [ ] Modal closes on printing errors
|
|
||||||
|
|
||||||
## Future Enhancements
|
|
||||||
|
|
||||||
Potential improvements:
|
|
||||||
- Add "Cancel" button to stop printing mid-process
|
|
||||||
- Show estimated time remaining
|
|
||||||
- Add sound notification on completion
|
|
||||||
- Log printing history with timestamps
|
|
||||||
- Add printer status monitoring
|
|
||||||
- Show print queue if multiple orders selected
|
|
||||||
@@ -5,7 +5,7 @@ db_config = {
|
|||||||
"user": "trasabilitate",
|
"user": "trasabilitate",
|
||||||
"password": "Initial01!",
|
"password": "Initial01!",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"database": "trasabilitate_database"
|
"database": "trasabilitate"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Connect to the database
|
# Connect to the database
|
||||||
@@ -6,7 +6,7 @@ db_config = {
|
|||||||
"user": "trasabilitate",
|
"user": "trasabilitate",
|
||||||
"password": "Initial01!",
|
"password": "Initial01!",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"database": "trasabilitate_database"
|
"database": "trasabilitate"
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -5,7 +5,7 @@ db_config = {
|
|||||||
"user": "trasabilitate",
|
"user": "trasabilitate",
|
||||||
"password": "Initial01!",
|
"password": "Initial01!",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"database": "trasabilitate_database"
|
"database": "trasabilitate"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Connect to the database
|
# Connect to the database
|
||||||
@@ -5,7 +5,7 @@ db_config = {
|
|||||||
"user": "trasabilitate",
|
"user": "trasabilitate",
|
||||||
"password": "Initial01!",
|
"password": "Initial01!",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"database": "trasabilitate_database"
|
"database": "trasabilitate"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Connect to the database
|
# Connect to the database
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Database script to add the printed_labels column to the order_for_labels table
|
|
||||||
This column will track whether labels have been printed for each order (boolean: 0=false, 1=true)
|
|
||||||
Default value: 0 (false)
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import mariadb
|
|
||||||
from flask import Flask
|
|
||||||
|
|
||||||
# Add the app directory to the path
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
|
|
||||||
def get_db_connection():
|
|
||||||
"""Get database connection using settings from external_server.conf"""
|
|
||||||
# Go up two levels from this script to reach py_app directory, then to instance
|
|
||||||
app_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
settings_file = os.path.join(app_root, 'instance', 'external_server.conf')
|
|
||||||
|
|
||||||
settings = {}
|
|
||||||
with open(settings_file, 'r') as f:
|
|
||||||
for line in f:
|
|
||||||
key, value = line.strip().split('=', 1)
|
|
||||||
settings[key] = value
|
|
||||||
|
|
||||||
return mariadb.connect(
|
|
||||||
user=settings['username'],
|
|
||||||
password=settings['password'],
|
|
||||||
host=settings['server_domain'],
|
|
||||||
port=int(settings['port']),
|
|
||||||
database=settings['database_name']
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_printed_labels_column():
|
|
||||||
"""
|
|
||||||
Adds the printed_labels column to the order_for_labels table after the line_number column
|
|
||||||
Column type: TINYINT(1) (boolean: 0=false, 1=true)
|
|
||||||
Default value: 0 (false)
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
conn = get_db_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Check if table exists
|
|
||||||
cursor.execute("SHOW TABLES LIKE 'order_for_labels'")
|
|
||||||
result = cursor.fetchone()
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
print("❌ Table 'order_for_labels' does not exist. Please create it first.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check if column already exists
|
|
||||||
cursor.execute("SHOW COLUMNS FROM order_for_labels LIKE 'printed_labels'")
|
|
||||||
column_exists = cursor.fetchone()
|
|
||||||
|
|
||||||
if column_exists:
|
|
||||||
print("ℹ️ Column 'printed_labels' already exists.")
|
|
||||||
# Show current structure
|
|
||||||
cursor.execute("DESCRIBE order_for_labels")
|
|
||||||
columns = cursor.fetchall()
|
|
||||||
print("\n📋 Current table structure:")
|
|
||||||
for col in columns:
|
|
||||||
null_info = 'NULL' if col[2] == 'YES' else 'NOT NULL'
|
|
||||||
default_info = f" DEFAULT {col[4]}" if col[4] else ""
|
|
||||||
print(f" 📌 {col[0]:<25} {col[1]:<20} {null_info}{default_info}")
|
|
||||||
else:
|
|
||||||
# Add the column after line_number
|
|
||||||
alter_table_sql = """
|
|
||||||
ALTER TABLE order_for_labels
|
|
||||||
ADD COLUMN printed_labels TINYINT(1) NOT NULL DEFAULT 0
|
|
||||||
COMMENT 'Boolean flag: 0=labels not printed, 1=labels printed'
|
|
||||||
AFTER line_number
|
|
||||||
"""
|
|
||||||
|
|
||||||
cursor.execute(alter_table_sql)
|
|
||||||
conn.commit()
|
|
||||||
print("✅ Column 'printed_labels' added successfully!")
|
|
||||||
|
|
||||||
# Show the updated structure
|
|
||||||
cursor.execute("DESCRIBE order_for_labels")
|
|
||||||
columns = cursor.fetchall()
|
|
||||||
print("\n📋 Updated table structure:")
|
|
||||||
for col in columns:
|
|
||||||
null_info = 'NULL' if col[2] == 'YES' else 'NOT NULL'
|
|
||||||
default_info = f" DEFAULT {col[4]}" if col[4] else ""
|
|
||||||
highlight = "🆕 " if col[0] == 'printed_labels' else " "
|
|
||||||
print(f"{highlight}{col[0]:<25} {col[1]:<20} {null_info}{default_info}")
|
|
||||||
|
|
||||||
# Show count of existing records that will have printed_labels = 0
|
|
||||||
cursor.execute("SELECT COUNT(*) FROM order_for_labels")
|
|
||||||
count = cursor.fetchone()[0]
|
|
||||||
if count > 0:
|
|
||||||
print(f"\n📊 {count} existing records now have printed_labels = 0 (false)")
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except mariadb.Error as e:
|
|
||||||
print(f"❌ Database error: {e}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def verify_column():
|
|
||||||
"""Verify the column was added correctly"""
|
|
||||||
try:
|
|
||||||
conn = get_db_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Test the column functionality
|
|
||||||
cursor.execute("SELECT COUNT(*) as total, SUM(printed_labels) as printed FROM order_for_labels")
|
|
||||||
result = cursor.fetchone()
|
|
||||||
|
|
||||||
if result:
|
|
||||||
total, printed = result
|
|
||||||
print(f"\n🔍 Verification:")
|
|
||||||
print(f" 📦 Total orders: {total}")
|
|
||||||
print(f" 🖨️ Printed orders: {printed or 0}")
|
|
||||||
print(f" 📄 Unprinted orders: {total - (printed or 0)}")
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Verification failed: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🔧 Adding printed_labels column to order_for_labels table...")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
success = add_printed_labels_column()
|
|
||||||
|
|
||||||
if success:
|
|
||||||
print("\n🔍 Verifying column addition...")
|
|
||||||
verify_column()
|
|
||||||
print("\n✅ Database modification completed successfully!")
|
|
||||||
print("\n📝 Column Details:")
|
|
||||||
print(" • Name: printed_labels")
|
|
||||||
print(" • Type: TINYINT(1) (boolean)")
|
|
||||||
print(" • Default: 0 (false - labels not printed)")
|
|
||||||
print(" • Values: 0 = not printed, 1 = printed")
|
|
||||||
print(" • Position: After line_number column")
|
|
||||||
else:
|
|
||||||
print("\n❌ Database modification failed!")
|
|
||||||
|
|
||||||
print("="*60)
|
|
||||||
637
py_app/app/db_create_scripts/setup_complete_database.py
Executable file
637
py_app/app/db_create_scripts/setup_complete_database.py
Executable file
@@ -0,0 +1,637 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Complete Database Setup Script for Trasabilitate Application
|
||||||
|
This script creates all necessary database tables, triggers, and initial data
|
||||||
|
for quick deployment of the application.
|
||||||
|
|
||||||
|
Usage: python3 setup_complete_database.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import mariadb
|
||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
|
DB_CONFIG = {
|
||||||
|
"user": "trasabilitate",
|
||||||
|
"password": "Initial01!",
|
||||||
|
"host": "localhost",
|
||||||
|
"database": "trasabilitate"
|
||||||
|
}
|
||||||
|
|
||||||
|
def print_step(step_num, description):
|
||||||
|
"""Print formatted step information"""
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f"Step {step_num}: {description}")
|
||||||
|
print('='*60)
|
||||||
|
|
||||||
|
def print_success(message):
|
||||||
|
"""Print success message"""
|
||||||
|
print(f"✅ {message}")
|
||||||
|
|
||||||
|
def print_error(message):
|
||||||
|
"""Print error message"""
|
||||||
|
print(f"❌ {message}")
|
||||||
|
|
||||||
|
def test_database_connection():
|
||||||
|
"""Test if we can connect to the database"""
|
||||||
|
print_step(1, "Testing Database Connection")
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
print_success("Successfully connected to MariaDB database 'trasabilitate'")
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to connect to database: {e}")
|
||||||
|
print("\nPlease ensure:")
|
||||||
|
print("1. MariaDB is running")
|
||||||
|
print("2. Database 'trasabilitate' exists")
|
||||||
|
print("3. User 'trasabilitate' has been created with password 'Initial01!'")
|
||||||
|
print("4. User has all privileges on the database")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_scan_tables():
|
||||||
|
"""Create scan1_orders and scanfg_orders tables"""
|
||||||
|
print_step(2, "Creating Scan Tables (scan1_orders & scanfg_orders)")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create scan1_orders table
|
||||||
|
scan1_table_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS scan1_orders (
|
||||||
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
operator_code VARCHAR(4) NOT NULL,
|
||||||
|
CP_full_code VARCHAR(15) NOT NULL UNIQUE,
|
||||||
|
OC1_code VARCHAR(4) NOT NULL,
|
||||||
|
OC2_code VARCHAR(4) NOT NULL,
|
||||||
|
CP_base_code VARCHAR(10) GENERATED ALWAYS AS (LEFT(CP_full_code, 10)) STORED,
|
||||||
|
quality_code INT(3) NOT NULL,
|
||||||
|
date DATE NOT NULL,
|
||||||
|
time TIME NOT NULL,
|
||||||
|
approved_quantity INT DEFAULT 0,
|
||||||
|
rejected_quantity INT DEFAULT 0
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(scan1_table_query)
|
||||||
|
print_success("Table 'scan1_orders' created successfully")
|
||||||
|
|
||||||
|
# Create scanfg_orders table
|
||||||
|
scanfg_table_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS scanfg_orders (
|
||||||
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
operator_code VARCHAR(4) NOT NULL,
|
||||||
|
CP_full_code VARCHAR(15) NOT NULL UNIQUE,
|
||||||
|
OC1_code VARCHAR(4) NOT NULL,
|
||||||
|
OC2_code VARCHAR(4) NOT NULL,
|
||||||
|
CP_base_code VARCHAR(10) GENERATED ALWAYS AS (LEFT(CP_full_code, 10)) STORED,
|
||||||
|
quality_code INT(3) NOT NULL,
|
||||||
|
date DATE NOT NULL,
|
||||||
|
time TIME NOT NULL,
|
||||||
|
approved_quantity INT DEFAULT 0,
|
||||||
|
rejected_quantity INT DEFAULT 0
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(scanfg_table_query)
|
||||||
|
print_success("Table 'scanfg_orders' created successfully")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to create scan tables: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_order_for_labels_table():
|
||||||
|
"""Create order_for_labels table"""
|
||||||
|
print_step(3, "Creating Order for Labels Table")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
order_labels_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS order_for_labels (
|
||||||
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
comanda_productie VARCHAR(15) NOT NULL,
|
||||||
|
cod_articol VARCHAR(15) NULL,
|
||||||
|
descr_com_prod VARCHAR(50) NOT NULL,
|
||||||
|
cantitate INT(3) NOT NULL,
|
||||||
|
com_achiz_client VARCHAR(25) NULL,
|
||||||
|
nr_linie_com_client INT(3) NULL,
|
||||||
|
customer_name VARCHAR(50) NULL,
|
||||||
|
customer_article_number VARCHAR(25) NULL,
|
||||||
|
open_for_order VARCHAR(25) NULL,
|
||||||
|
line_number INT(3) NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(order_labels_query)
|
||||||
|
print_success("Table 'order_for_labels' created successfully")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to create order_for_labels table: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_warehouse_locations_table():
|
||||||
|
"""Create warehouse_locations table"""
|
||||||
|
print_step(4, "Creating Warehouse Locations Table")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
warehouse_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS warehouse_locations (
|
||||||
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
location_code VARCHAR(12) NOT NULL UNIQUE,
|
||||||
|
size INT,
|
||||||
|
description VARCHAR(250)
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(warehouse_query)
|
||||||
|
print_success("Table 'warehouse_locations' created successfully")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to create warehouse_locations table: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_permissions_tables():
|
||||||
|
"""Create permission management tables"""
|
||||||
|
print_step(5, "Creating Permission Management Tables")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create permissions table
|
||||||
|
permissions_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS permissions (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
permission_key VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
page VARCHAR(100) NOT NULL,
|
||||||
|
page_name VARCHAR(255) NOT NULL,
|
||||||
|
section VARCHAR(100) NOT NULL,
|
||||||
|
section_name VARCHAR(255) NOT NULL,
|
||||||
|
action VARCHAR(50) NOT NULL,
|
||||||
|
action_name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(permissions_query)
|
||||||
|
print_success("Table 'permissions' created successfully")
|
||||||
|
|
||||||
|
# Create role_permissions table
|
||||||
|
role_permissions_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS role_permissions (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
role_name VARCHAR(100) NOT NULL,
|
||||||
|
permission_id INT NOT NULL,
|
||||||
|
granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
granted_by VARCHAR(100),
|
||||||
|
FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE KEY unique_role_permission (role_name, permission_id)
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(role_permissions_query)
|
||||||
|
print_success("Table 'role_permissions' created successfully")
|
||||||
|
|
||||||
|
# Create role_hierarchy table
|
||||||
|
role_hierarchy_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS role_hierarchy (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
role_name VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
role_display_name VARCHAR(255) NOT NULL,
|
||||||
|
level INT NOT NULL,
|
||||||
|
parent_role VARCHAR(100),
|
||||||
|
description TEXT,
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(role_hierarchy_query)
|
||||||
|
print_success("Table 'role_hierarchy' created successfully")
|
||||||
|
|
||||||
|
# Create permission_audit_log table
|
||||||
|
audit_log_query = """
|
||||||
|
CREATE TABLE IF NOT EXISTS permission_audit_log (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
action VARCHAR(50) NOT NULL,
|
||||||
|
role_name VARCHAR(100),
|
||||||
|
permission_key VARCHAR(255),
|
||||||
|
user_id VARCHAR(100),
|
||||||
|
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
details TEXT,
|
||||||
|
ip_address VARCHAR(45)
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
cursor.execute(audit_log_query)
|
||||||
|
print_success("Table 'permission_audit_log' created successfully")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to create permissions tables: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_sqlite_tables():
|
||||||
|
"""Create SQLite tables for users and roles"""
|
||||||
|
print_step(6, "Creating SQLite User and Role Tables")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create instance folder if it doesn't exist
|
||||||
|
instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance'))
|
||||||
|
if not os.path.exists(instance_folder):
|
||||||
|
os.makedirs(instance_folder)
|
||||||
|
|
||||||
|
db_path = os.path.join(instance_folder, 'users.db')
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create users table
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
role TEXT NOT NULL
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Insert superadmin user if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO users (username, password, role)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', ('superadmin', 'superadmin123', 'superadmin'))
|
||||||
|
|
||||||
|
# Create roles table
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT UNIQUE NOT NULL,
|
||||||
|
access_level TEXT NOT NULL,
|
||||||
|
description TEXT
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Insert superadmin role if not exists
|
||||||
|
cursor.execute('''
|
||||||
|
INSERT OR IGNORE INTO roles (name, access_level, description)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
''', ('superadmin', 'full', 'Full access to all app areas and functions'))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
print_success("SQLite tables created and superadmin user initialized")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to create SQLite tables: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def create_database_triggers():
|
||||||
|
"""Create database triggers for automatic quantity calculations"""
|
||||||
|
print_step(7, "Creating Database Triggers")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Drop existing triggers if they exist
|
||||||
|
trigger_drops = [
|
||||||
|
"DROP TRIGGER IF EXISTS increment_approved_quantity;",
|
||||||
|
"DROP TRIGGER IF EXISTS increment_rejected_quantity;",
|
||||||
|
"DROP TRIGGER IF EXISTS increment_approved_quantity_fg;",
|
||||||
|
"DROP TRIGGER IF EXISTS increment_rejected_quantity_fg;"
|
||||||
|
]
|
||||||
|
|
||||||
|
for drop_query in trigger_drops:
|
||||||
|
cursor.execute(drop_query)
|
||||||
|
|
||||||
|
# Create trigger for scan1_orders approved quantity
|
||||||
|
scan1_approved_trigger = """
|
||||||
|
CREATE TRIGGER increment_approved_quantity
|
||||||
|
AFTER INSERT ON scan1_orders
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
IF NEW.quality_code = 000 THEN
|
||||||
|
UPDATE scan1_orders
|
||||||
|
SET approved_quantity = approved_quantity + 1
|
||||||
|
WHERE CP_base_code = NEW.CP_base_code;
|
||||||
|
ELSE
|
||||||
|
UPDATE scan1_orders
|
||||||
|
SET rejected_quantity = rejected_quantity + 1
|
||||||
|
WHERE CP_base_code = NEW.CP_base_code;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
"""
|
||||||
|
cursor.execute(scan1_approved_trigger)
|
||||||
|
print_success("Trigger 'increment_approved_quantity' created for scan1_orders")
|
||||||
|
|
||||||
|
# Create trigger for scanfg_orders approved quantity
|
||||||
|
scanfg_approved_trigger = """
|
||||||
|
CREATE TRIGGER increment_approved_quantity_fg
|
||||||
|
AFTER INSERT ON scanfg_orders
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
IF NEW.quality_code = 000 THEN
|
||||||
|
UPDATE scanfg_orders
|
||||||
|
SET approved_quantity = approved_quantity + 1
|
||||||
|
WHERE CP_base_code = NEW.CP_base_code;
|
||||||
|
ELSE
|
||||||
|
UPDATE scanfg_orders
|
||||||
|
SET rejected_quantity = rejected_quantity + 1
|
||||||
|
WHERE CP_base_code = NEW.CP_base_code;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
"""
|
||||||
|
cursor.execute(scanfg_approved_trigger)
|
||||||
|
print_success("Trigger 'increment_approved_quantity_fg' created for scanfg_orders")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to create database triggers: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def populate_permissions_data():
|
||||||
|
"""Populate permissions and roles with default data"""
|
||||||
|
print_step(8, "Populating Permissions and Roles Data")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Define all permissions
|
||||||
|
permissions_data = [
|
||||||
|
# Home page permissions
|
||||||
|
('home.view', 'home', 'Home Page', 'navigation', 'Navigation', 'view', 'View Home Page', 'Access to home page'),
|
||||||
|
|
||||||
|
# Scan1 permissions
|
||||||
|
('scan1.view', 'scan1', 'Scan1 Page', 'scanning', 'Scanning Operations', 'view', 'View Scan1', 'Access to scan1 page'),
|
||||||
|
('scan1.scan', 'scan1', 'Scan1 Page', 'scanning', 'Scanning Operations', 'scan', 'Perform Scan1', 'Ability to perform scan1 operations'),
|
||||||
|
('scan1.history', 'scan1', 'Scan1 Page', 'scanning', 'Scanning Operations', 'history', 'View Scan1 History', 'View scan1 operation history'),
|
||||||
|
|
||||||
|
# ScanFG permissions
|
||||||
|
('scanfg.view', 'scanfg', 'ScanFG Page', 'scanning', 'Scanning Operations', 'view', 'View ScanFG', 'Access to scanfg page'),
|
||||||
|
('scanfg.scan', 'scanfg', 'ScanFG Page', 'scanning', 'Scanning Operations', 'scan', 'Perform ScanFG', 'Ability to perform scanfg operations'),
|
||||||
|
('scanfg.history', 'scanfg', 'ScanFG Page', 'scanning', 'Scanning Operations', 'history', 'View ScanFG History', 'View scanfg operation history'),
|
||||||
|
|
||||||
|
# Warehouse permissions
|
||||||
|
('warehouse.view', 'warehouse', 'Warehouse Management', 'warehouse', 'Warehouse Operations', 'view', 'View Warehouse', 'Access to warehouse page'),
|
||||||
|
('warehouse.manage_locations', 'warehouse', 'Warehouse Management', 'warehouse', 'Warehouse Operations', 'manage', 'Manage Locations', 'Add, edit, delete warehouse locations'),
|
||||||
|
('warehouse.view_locations', 'warehouse', 'Warehouse Management', 'warehouse', 'Warehouse Operations', 'view_locations', 'View Locations', 'View warehouse locations'),
|
||||||
|
|
||||||
|
# Labels permissions
|
||||||
|
('labels.view', 'labels', 'Label Management', 'labels', 'Label Operations', 'view', 'View Labels', 'Access to labels page'),
|
||||||
|
('labels.print', 'labels', 'Label Management', 'labels', 'Label Operations', 'print', 'Print Labels', 'Print labels'),
|
||||||
|
('labels.manage_orders', 'labels', 'Label Management', 'labels', 'Label Operations', 'manage', 'Manage Label Orders', 'Manage label orders'),
|
||||||
|
|
||||||
|
# Print Module permissions
|
||||||
|
('print.view', 'print', 'Print Module', 'printing', 'Printing Operations', 'view', 'View Print Module', 'Access to print module'),
|
||||||
|
('print.execute', 'print', 'Print Module', 'printing', 'Printing Operations', 'execute', 'Execute Print', 'Execute print operations'),
|
||||||
|
('print.manage_queue', 'print', 'Print Module', 'printing', 'Printing Operations', 'manage_queue', 'Manage Print Queue', 'Manage print queue'),
|
||||||
|
|
||||||
|
# Settings permissions
|
||||||
|
('settings.view', 'settings', 'Settings', 'system', 'System Management', 'view', 'View Settings', 'Access to settings page'),
|
||||||
|
('settings.edit', 'settings', 'Settings', 'system', 'System Management', 'edit', 'Edit Settings', 'Modify application settings'),
|
||||||
|
('settings.database', 'settings', 'Settings', 'system', 'System Management', 'database', 'Database Settings', 'Manage database settings'),
|
||||||
|
|
||||||
|
# User Management permissions
|
||||||
|
('users.view', 'users', 'User Management', 'admin', 'Administration', 'view', 'View Users', 'View user list'),
|
||||||
|
('users.create', 'users', 'User Management', 'admin', 'Administration', 'create', 'Create Users', 'Create new users'),
|
||||||
|
('users.edit', 'users', 'User Management', 'admin', 'Administration', 'edit', 'Edit Users', 'Edit existing users'),
|
||||||
|
('users.delete', 'users', 'User Management', 'admin', 'Administration', 'delete', 'Delete Users', 'Delete users'),
|
||||||
|
|
||||||
|
# Permission Management permissions
|
||||||
|
('permissions.view', 'permissions', 'Permission Management', 'admin', 'Administration', 'view', 'View Permissions', 'View permissions'),
|
||||||
|
('permissions.assign', 'permissions', 'Permission Management', 'admin', 'Administration', 'assign', 'Assign Permissions', 'Assign permissions to roles'),
|
||||||
|
('permissions.audit', 'permissions', 'Permission Management', 'admin', 'Administration', 'audit', 'View Audit Log', 'View permission audit log'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Insert permissions
|
||||||
|
permission_insert_query = """
|
||||||
|
INSERT IGNORE INTO permissions
|
||||||
|
(permission_key, page, page_name, section, section_name, action, action_name, description)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.executemany(permission_insert_query, permissions_data)
|
||||||
|
print_success(f"Inserted {len(permissions_data)} permissions")
|
||||||
|
|
||||||
|
# Define role hierarchy
|
||||||
|
roles_data = [
|
||||||
|
('superadmin', 'Super Administrator', 1, None, 'Full system access with all permissions'),
|
||||||
|
('admin', 'Administrator', 2, 'superadmin', 'Administrative access with most permissions'),
|
||||||
|
('manager', 'Manager', 3, 'admin', 'Management level access'),
|
||||||
|
('quality_manager', 'Quality Manager', 4, 'manager', 'Quality control and scanning operations'),
|
||||||
|
('warehouse_manager', 'Warehouse Manager', 4, 'manager', 'Warehouse operations and management'),
|
||||||
|
('quality_worker', 'Quality Worker', 5, 'quality_manager', 'Basic quality scanning operations'),
|
||||||
|
('warehouse_worker', 'Warehouse Worker', 5, 'warehouse_manager', 'Basic warehouse operations'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Insert roles
|
||||||
|
role_insert_query = """
|
||||||
|
INSERT IGNORE INTO role_hierarchy
|
||||||
|
(role_name, role_display_name, level, parent_role, description)
|
||||||
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.executemany(role_insert_query, roles_data)
|
||||||
|
print_success(f"Inserted {len(roles_data)} roles")
|
||||||
|
|
||||||
|
# Assign permissions to roles
|
||||||
|
# Get all permission IDs
|
||||||
|
cursor.execute("SELECT id, permission_key FROM permissions")
|
||||||
|
permissions = {key: id for id, key in cursor.fetchall()}
|
||||||
|
|
||||||
|
# Define role-permission mappings
|
||||||
|
role_permissions = {
|
||||||
|
'superadmin': list(permissions.values()), # All permissions
|
||||||
|
'admin': [pid for key, pid in permissions.items() if not key.startswith('permissions.audit')], # All except audit
|
||||||
|
'manager': [permissions[key] for key in permissions.keys() if any(key.startswith(prefix) for prefix in ['home.', 'settings.view', 'users.view'])],
|
||||||
|
'quality_manager': [permissions[key] for key in permissions.keys() if any(key.startswith(prefix) for prefix in ['home.', 'scan1.', 'scanfg.', 'labels.', 'print.'])],
|
||||||
|
'warehouse_manager': [permissions[key] for key in permissions.keys() if any(key.startswith(prefix) for prefix in ['home.', 'warehouse.', 'labels.'])],
|
||||||
|
'quality_worker': [permissions[key] for key in permissions.keys() if any(key.startswith(prefix) for prefix in ['home.', 'scan1.view', 'scan1.scan', 'scanfg.view', 'scanfg.scan'])],
|
||||||
|
'warehouse_worker': [permissions[key] for key in permissions.keys() if any(key.startswith(prefix) for prefix in ['home.', 'warehouse.view', 'warehouse.view_locations'])],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Insert role permissions
|
||||||
|
for role, permission_ids in role_permissions.items():
|
||||||
|
for permission_id in permission_ids:
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT IGNORE INTO role_permissions (role_name, permission_id)
|
||||||
|
VALUES (%s, %s)
|
||||||
|
""", (role, permission_id))
|
||||||
|
|
||||||
|
print_success("Role permissions assigned successfully")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to populate permissions data: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_external_config():
|
||||||
|
"""Update external_server.conf with correct database settings"""
|
||||||
|
print_step(9, "Updating External Server Configuration")
|
||||||
|
|
||||||
|
try:
|
||||||
|
config_path = os.path.join(os.path.dirname(__file__), '../../instance/external_server.conf')
|
||||||
|
|
||||||
|
config_content = """server_domain=localhost
|
||||||
|
port=3306
|
||||||
|
database_name=trasabilitate
|
||||||
|
username=trasabilitate
|
||||||
|
password=Initial01!
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create instance directory if it doesn't exist
|
||||||
|
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||||
|
|
||||||
|
with open(config_path, 'w') as f:
|
||||||
|
f.write(config_content)
|
||||||
|
|
||||||
|
print_success("External server configuration updated")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to update external config: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def verify_database_setup():
|
||||||
|
"""Verify that all tables were created successfully"""
|
||||||
|
print_step(10, "Verifying Database Setup")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mariadb.connect(**DB_CONFIG)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Check MariaDB tables
|
||||||
|
cursor.execute("SHOW TABLES")
|
||||||
|
tables = [table[0] for table in cursor.fetchall()]
|
||||||
|
|
||||||
|
expected_tables = [
|
||||||
|
'scan1_orders',
|
||||||
|
'scanfg_orders',
|
||||||
|
'order_for_labels',
|
||||||
|
'warehouse_locations',
|
||||||
|
'permissions',
|
||||||
|
'role_permissions',
|
||||||
|
'role_hierarchy',
|
||||||
|
'permission_audit_log'
|
||||||
|
]
|
||||||
|
|
||||||
|
print("\n📊 MariaDB Tables Status:")
|
||||||
|
for table in expected_tables:
|
||||||
|
if table in tables:
|
||||||
|
print_success(f"Table '{table}' exists")
|
||||||
|
else:
|
||||||
|
print_error(f"Table '{table}' missing")
|
||||||
|
|
||||||
|
# Check triggers
|
||||||
|
cursor.execute("SHOW TRIGGERS")
|
||||||
|
triggers = [trigger[0] for trigger in cursor.fetchall()]
|
||||||
|
|
||||||
|
expected_triggers = [
|
||||||
|
'increment_approved_quantity',
|
||||||
|
'increment_approved_quantity_fg'
|
||||||
|
]
|
||||||
|
|
||||||
|
print("\n🔧 Database Triggers Status:")
|
||||||
|
for trigger in expected_triggers:
|
||||||
|
if trigger in triggers:
|
||||||
|
print_success(f"Trigger '{trigger}' exists")
|
||||||
|
else:
|
||||||
|
print_error(f"Trigger '{trigger}' missing")
|
||||||
|
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Check SQLite database
|
||||||
|
instance_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../instance'))
|
||||||
|
sqlite_path = os.path.join(instance_folder, 'users.db')
|
||||||
|
|
||||||
|
if os.path.exists(sqlite_path):
|
||||||
|
print_success("SQLite database 'users.db' exists")
|
||||||
|
else:
|
||||||
|
print_error("SQLite database 'users.db' missing")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print_error(f"Failed to verify database setup: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main function to orchestrate the complete database setup"""
|
||||||
|
print("🚀 Trasabilitate Application - Complete Database Setup")
|
||||||
|
print(f"Started at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
|
||||||
|
steps = [
|
||||||
|
test_database_connection,
|
||||||
|
create_scan_tables,
|
||||||
|
create_order_for_labels_table,
|
||||||
|
create_warehouse_locations_table,
|
||||||
|
create_permissions_tables,
|
||||||
|
create_sqlite_tables,
|
||||||
|
create_database_triggers,
|
||||||
|
populate_permissions_data,
|
||||||
|
update_external_config,
|
||||||
|
verify_database_setup
|
||||||
|
]
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
|
||||||
|
for step in steps:
|
||||||
|
if step():
|
||||||
|
success_count += 1
|
||||||
|
else:
|
||||||
|
print(f"\n❌ Setup failed at step: {step.__name__}")
|
||||||
|
print("Please check the error messages above and resolve the issues.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print("🎉 DATABASE SETUP COMPLETED SUCCESSFULLY!")
|
||||||
|
print(f"{'='*60}")
|
||||||
|
print(f"✅ All {success_count} steps completed successfully")
|
||||||
|
print(f"📅 Completed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
print("\n📋 Setup Summary:")
|
||||||
|
print(" • MariaDB tables created with triggers")
|
||||||
|
print(" • SQLite user database initialized")
|
||||||
|
print(" • Permissions system fully configured")
|
||||||
|
print(" • Default superadmin user created (username: superadmin, password: superadmin123)")
|
||||||
|
print(" • Configuration files updated")
|
||||||
|
print("\n🚀 Your application is ready to run!")
|
||||||
|
print(" Run: python3 run.py")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
server_domain=localhost
|
server_domain=localhost
|
||||||
port=3602
|
port=3306
|
||||||
database_name=trasabilitate_database
|
database_name=trasabilitate
|
||||||
username=trasabilitate
|
username=trasabilitate
|
||||||
password=Initial01!
|
password=Initial01!
|
||||||
|
|||||||
BIN
py_app/instance/users.db
Executable file → Normal file
BIN
py_app/instance/users.db
Executable file → Normal file
Binary file not shown.
142
py_app/quick_deploy.sh
Executable file
142
py_app/quick_deploy.sh
Executable file
@@ -0,0 +1,142 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Trasabilitate Application - Quick Deployment Script
|
||||||
|
# This script handles the complete deployment process
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
echo "🚀 Trasabilitate Application - Quick Deployment"
|
||||||
|
echo "=============================================="
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
print_step() {
|
||||||
|
echo -e "\n${BLUE}📋 Step $1: $2${NC}"
|
||||||
|
echo "----------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}✅ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}⚠️ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}❌ $1${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [[ $EUID -eq 0 ]]; then
|
||||||
|
print_error "This script should not be run as root for security reasons"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_step 1 "Checking Prerequisites"
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if [[ ! -f "run.py" ]]; then
|
||||||
|
print_error "Please run this script from the py_app directory"
|
||||||
|
print_error "Expected location: /srv/quality_recticel/py_app"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Running from correct directory"
|
||||||
|
|
||||||
|
# Check if MariaDB is running
|
||||||
|
if ! systemctl is-active --quiet mariadb; then
|
||||||
|
print_warning "MariaDB is not running. Attempting to start..."
|
||||||
|
if sudo systemctl start mariadb; then
|
||||||
|
print_success "MariaDB started successfully"
|
||||||
|
else
|
||||||
|
print_error "Failed to start MariaDB. Please start it manually:"
|
||||||
|
print_error "sudo systemctl start mariadb"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_success "MariaDB is running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if virtual environment exists
|
||||||
|
if [[ ! -d "../recticel" ]]; then
|
||||||
|
print_error "Virtual environment 'recticel' not found"
|
||||||
|
print_error "Please create it first:"
|
||||||
|
print_error "python3 -m venv ../recticel"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Virtual environment found"
|
||||||
|
|
||||||
|
print_step 2 "Setting up Database and User"
|
||||||
|
|
||||||
|
# Create database and user
|
||||||
|
print_warning "You may be prompted for the MySQL root password"
|
||||||
|
if sudo mysql -e "CREATE DATABASE IF NOT EXISTS trasabilitate; CREATE USER IF NOT EXISTS 'trasabilitate'@'localhost' IDENTIFIED BY 'Initial01!'; GRANT ALL PRIVILEGES ON trasabilitate.* TO 'trasabilitate'@'localhost'; FLUSH PRIVILEGES;" 2>/dev/null; then
|
||||||
|
print_success "Database 'trasabilitate' and user created successfully"
|
||||||
|
else
|
||||||
|
print_error "Failed to create database or user. Please check MySQL root access"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_step 3 "Activating Virtual Environment and Installing Dependencies"
|
||||||
|
|
||||||
|
# Activate virtual environment and install dependencies
|
||||||
|
source ../recticel/bin/activate
|
||||||
|
|
||||||
|
if pip install -r requirements.txt > /dev/null 2>&1; then
|
||||||
|
print_success "Python dependencies installed/verified"
|
||||||
|
else
|
||||||
|
print_error "Failed to install Python dependencies"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_step 4 "Running Complete Database Setup"
|
||||||
|
|
||||||
|
# Run the comprehensive database setup script
|
||||||
|
if python3 app/db_create_scripts/setup_complete_database.py; then
|
||||||
|
print_success "Database setup completed successfully"
|
||||||
|
else
|
||||||
|
print_error "Database setup failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_step 5 "Testing Application Startup"
|
||||||
|
|
||||||
|
# Test if the application can start (run for 3 seconds then kill)
|
||||||
|
print_warning "Testing application startup (will stop after 3 seconds)..."
|
||||||
|
|
||||||
|
timeout 3s python3 run.py > /dev/null 2>&1 || true
|
||||||
|
print_success "Application startup test completed"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=============================================="
|
||||||
|
echo -e "${GREEN}🎉 DEPLOYMENT COMPLETED SUCCESSFULLY!${NC}"
|
||||||
|
echo "=============================================="
|
||||||
|
echo ""
|
||||||
|
echo "📋 Deployment Summary:"
|
||||||
|
echo " • MariaDB database and user configured"
|
||||||
|
echo " • All database tables and triggers created"
|
||||||
|
echo " • Permissions system initialized"
|
||||||
|
echo " • Default superadmin user ready"
|
||||||
|
echo ""
|
||||||
|
echo "🚀 To start the application:"
|
||||||
|
echo " cd /srv/quality_recticel/py_app"
|
||||||
|
echo " source ../recticel/bin/activate"
|
||||||
|
echo " python3 run.py"
|
||||||
|
echo ""
|
||||||
|
echo "🌐 Application URLs:"
|
||||||
|
echo " • Local: http://127.0.0.1:8781"
|
||||||
|
echo " • Network: http://$(hostname -I | awk '{print $1}'):8781"
|
||||||
|
echo ""
|
||||||
|
echo "👤 Default Login:"
|
||||||
|
echo " • Username: superadmin"
|
||||||
|
echo " • Password: superadmin123"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}⚠️ Remember to change the default password after first login!${NC}"
|
||||||
|
echo ""
|
||||||
@@ -15,7 +15,12 @@
|
|||||||
sudo apt install -y mariadb-server libmariadb-dev
|
sudo apt install -y mariadb-server libmariadb-dev
|
||||||
|
|
||||||
5. Create MariaDB database and user:
|
5. Create MariaDB database and user:
|
||||||
sudo mysql -e "CREATE DATABASE recticel; CREATE USER 'sa'@'localhost' IDENTIFIED BY '12345678'; GRANT ALL PRIVILEGES ON recticel.* TO 'sa'@'localhost'; FLUSH PRIVILEGES;"
|
sudo mysql -e "CREATE DATABASE trasabilitate; CREATE USER 'sa'@'localhost' IDENTIFIED BY 'qasdewrftgbcgfdsrytkmbf\"b'; GRANT ALL PRIVILEGES ON quality.* TO 'sa'@'localhost'; FLUSH PRIVILEGES;"
|
||||||
|
sa
|
||||||
|
qasdewrftgbcgfdsrytkmbf\"b
|
||||||
|
|
||||||
|
trasabilitate
|
||||||
|
Initial01!
|
||||||
|
|
||||||
6. Install build tools (for compiling Python packages):
|
6. Install build tools (for compiling Python packages):
|
||||||
sudo apt install -y build-essential
|
sudo apt install -y build-essential
|
||||||
|
|||||||
Reference in New Issue
Block a user