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",
|
||||
"password": "Initial01!",
|
||||
"host": "localhost",
|
||||
"database": "trasabilitate_database"
|
||||
"database": "trasabilitate"
|
||||
}
|
||||
|
||||
# Connect to the database
|
||||
@@ -6,7 +6,7 @@ db_config = {
|
||||
"user": "trasabilitate",
|
||||
"password": "Initial01!",
|
||||
"host": "localhost",
|
||||
"database": "trasabilitate_database"
|
||||
"database": "trasabilitate"
|
||||
}
|
||||
|
||||
try:
|
||||
@@ -5,7 +5,7 @@ db_config = {
|
||||
"user": "trasabilitate",
|
||||
"password": "Initial01!",
|
||||
"host": "localhost",
|
||||
"database": "trasabilitate_database"
|
||||
"database": "trasabilitate"
|
||||
}
|
||||
|
||||
# Connect to the database
|
||||
@@ -5,7 +5,7 @@ db_config = {
|
||||
"user": "trasabilitate",
|
||||
"password": "Initial01!",
|
||||
"host": "localhost",
|
||||
"database": "trasabilitate_database"
|
||||
"database": "trasabilitate"
|
||||
}
|
||||
|
||||
# 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
|
||||
port=3602
|
||||
database_name=trasabilitate_database
|
||||
port=3306
|
||||
database_name=trasabilitate
|
||||
username=trasabilitate
|
||||
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
|
||||
|
||||
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):
|
||||
sudo apt install -y build-essential
|
||||
|
||||
Reference in New Issue
Block a user