improved changes
This commit is contained in:
@ -53,6 +53,7 @@ def create_app():
|
||||
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['SQLALCHEMY_ECHO'] = os.getenv('SQLALCHEMY_ECHO', 'False').lower() == 'true'
|
||||
|
||||
|
||||
# Initialize extensions
|
||||
db.init_app(app)
|
||||
|
||||
BIN
Machine-Backend/app/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Machine-Backend/app/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Machine-Backend/app/models/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Machine-Backend/app/models/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
Machine-Backend/app/models/__pycache__/models.cpython-314.pyc
Normal file
BIN
Machine-Backend/app/models/__pycache__/models.cpython-314.pyc
Normal file
Binary file not shown.
@ -4,7 +4,35 @@ import time
|
||||
import json
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
# Machine Model
|
||||
class RefillerMachine(db.Model):
|
||||
__tablename__ = 'refiller_machines'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
refiller_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
|
||||
machine_id = db.Column(db.String(10), db.ForeignKey('machines.machine_id', ondelete='CASCADE'), nullable=False)
|
||||
assigned_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
assigned_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
|
||||
# Relationships
|
||||
assigner = db.relationship('User', foreign_keys=[assigned_by], backref='machine_assignments_made')
|
||||
|
||||
# Unique constraint: one refiller can't be assigned to same machine twice
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint('refiller_id', 'machine_id', name='unique_refiller_machine'),
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'refiller_id': self.refiller_id,
|
||||
'machine_id': self.machine_id,
|
||||
'assigned_at': self.assigned_at.strftime("%Y-%m-%d %H:%M:%S") if self.assigned_at else None,
|
||||
'assigned_by': self.assigned_by,
|
||||
'assigned_by_username': self.assigner.username if self.assigner else None
|
||||
}
|
||||
|
||||
|
||||
# Machine Model - UPDATED
|
||||
class Machine(db.Model):
|
||||
__tablename__ = 'machines'
|
||||
|
||||
@ -20,10 +48,37 @@ class Machine(db.Model):
|
||||
connection_status = db.Column(db.String(50), nullable=False)
|
||||
created_on = db.Column(db.String(20), nullable=False)
|
||||
password = db.Column(db.String(128), nullable=False)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
|
||||
# Relationships
|
||||
slots = db.relationship('VendingSlot', backref='machine', lazy=True)
|
||||
client = db.relationship('User', backref='machines')
|
||||
client = db.relationship('User', foreign_keys=[client_id], backref='client_machines')
|
||||
creator = db.relationship('User', foreign_keys=[created_by], backref='created_machines')
|
||||
|
||||
# ⭐ NEW: Many-to-many relationship with Refillers through RefillerMachine
|
||||
assigned_refillers = db.relationship(
|
||||
'User',
|
||||
secondary='refiller_machines',
|
||||
primaryjoin='Machine.machine_id == RefillerMachine.machine_id',
|
||||
secondaryjoin='and_(User.id == RefillerMachine.refiller_id, User.roles == "Refiller")',
|
||||
backref='assigned_machines_rel',
|
||||
viewonly=True
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
# Get assigned refillers
|
||||
refiller_assignments = RefillerMachine.query.filter_by(machine_id=self.machine_id).all()
|
||||
assigned_refillers = []
|
||||
for assignment in refiller_assignments:
|
||||
refiller = User.query.get(assignment.refiller_id)
|
||||
if refiller:
|
||||
assigned_refillers.append({
|
||||
'id': refiller.id,
|
||||
'username': refiller.username,
|
||||
'email': refiller.email,
|
||||
'assigned_at': assignment.assigned_at.strftime("%Y-%m-%d %H:%M:%S") if assignment.assigned_at else None
|
||||
})
|
||||
|
||||
return {
|
||||
'id': self.id,
|
||||
'machine_id': self.machine_id,
|
||||
@ -36,7 +91,10 @@ class Machine(db.Model):
|
||||
'operation_status': self.operation_status,
|
||||
'connection_status': self.connection_status,
|
||||
'created_on': self.created_on,
|
||||
'password': self.password
|
||||
'password': self.password,
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'assigned_refillers': assigned_refillers # ⭐ NEW
|
||||
}
|
||||
|
||||
def set_password(self, password):
|
||||
@ -46,7 +104,7 @@ class Machine(db.Model):
|
||||
return check_password_hash(self.password, password)
|
||||
|
||||
|
||||
# User Model - UPDATED with proper password hashing
|
||||
# User Model - UPDATED WITH BOTH CLIENT AND MACHINE ASSIGNMENTS
|
||||
class User(db.Model):
|
||||
__tablename__ = 'users'
|
||||
|
||||
@ -57,7 +115,9 @@ class User(db.Model):
|
||||
contact = db.Column(db.String(20), nullable=False)
|
||||
roles = db.Column(db.String(50), nullable=False)
|
||||
user_status = db.Column(db.String(50), nullable=False)
|
||||
password = db.Column(db.String(255), nullable=False) # Increased length for hash
|
||||
password = db.Column(db.String(255), nullable=False)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
assigned_to = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) # Assigned to Client
|
||||
|
||||
# File storage fields
|
||||
photo = db.Column(db.String(255), nullable=True)
|
||||
@ -67,9 +127,27 @@ class User(db.Model):
|
||||
# Timestamps
|
||||
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
creator = db.relationship('User', remote_side=[id], backref='created_users', foreign_keys=[created_by])
|
||||
assigned_client = db.relationship('User', remote_side=[id], backref='assigned_refillers', foreign_keys=[assigned_to])
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert user object to dictionary"""
|
||||
# Get assigned machines for Refillers
|
||||
assigned_machines = []
|
||||
if self.roles == 'Refiller':
|
||||
machine_assignments = RefillerMachine.query.filter_by(refiller_id=self.id).all()
|
||||
for assignment in machine_assignments:
|
||||
machine = Machine.query.filter_by(machine_id=assignment.machine_id).first()
|
||||
if machine:
|
||||
assigned_machines.append({
|
||||
'machine_id': machine.machine_id,
|
||||
'machine_model': machine.machine_model,
|
||||
'branch_name': machine.branch_name,
|
||||
'assigned_at': assignment.assigned_at.strftime("%Y-%m-%d %H:%M:%S") if assignment.assigned_at else None
|
||||
})
|
||||
|
||||
return {
|
||||
'id': self.id,
|
||||
'user_id': self.user_id,
|
||||
@ -83,7 +161,12 @@ class User(db.Model):
|
||||
'documents': json.loads(self.documents) if self.documents else [],
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None,
|
||||
'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None,
|
||||
'machines': [m.to_dict() for m in self.machines] if hasattr(self, 'machines') else []
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'assigned_to': self.assigned_to,
|
||||
'assigned_to_username': self.assigned_client.username if self.assigned_client else None,
|
||||
'assigned_machines': assigned_machines, # ⭐ NEW
|
||||
'assigned_machines_count': len(assigned_machines) # ⭐ NEW
|
||||
}
|
||||
|
||||
def set_password(self, password):
|
||||
@ -125,6 +208,13 @@ class Product(db.Model):
|
||||
price = db.Column(db.Float, nullable=False)
|
||||
product_image = db.Column(db.String(255), nullable=False)
|
||||
created_date = db.Column(db.String(20), nullable=False)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
# NEW: Billing and expiration dates
|
||||
billing_date = db.Column(db.DateTime, nullable=True)
|
||||
expiration_date = db.Column(db.DateTime, nullable=True)
|
||||
|
||||
# Relationship
|
||||
creator = db.relationship('User', backref='created_products')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
@ -133,7 +223,11 @@ class Product(db.Model):
|
||||
'product_name': self.product_name,
|
||||
'price': str(self.price),
|
||||
'product_image': self.product_image,
|
||||
'created_date': self.created_date
|
||||
'created_date': self.created_date,
|
||||
'billing_date': self.billing_date.strftime("%Y-%m-%d") if self.billing_date else None,
|
||||
'expiration_date': self.expiration_date.strftime("%Y-%m-%d") if self.expiration_date else None,
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None
|
||||
}
|
||||
|
||||
|
||||
@ -191,7 +285,9 @@ class Transaction(db.Model):
|
||||
'return_amount': self.return_amount,
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None
|
||||
}
|
||||
# Add to your models.py
|
||||
|
||||
|
||||
# Role Model
|
||||
class Role(db.Model):
|
||||
__tablename__ = 'roles'
|
||||
|
||||
@ -199,15 +295,260 @@ class Role(db.Model):
|
||||
name = db.Column(db.String(50), unique=True, nullable=False)
|
||||
description = db.Column(db.String(255))
|
||||
permissions = db.Column(db.Text) # JSON string of permission IDs
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
# Relationship
|
||||
creator = db.relationship('User', backref='created_roles')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'description': self.description,
|
||||
'permissions': json.loads(self.permissions) if self.permissions else [],
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None,
|
||||
'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Branch Model
|
||||
class Branch(db.Model):
|
||||
__tablename__ = 'branches'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
branch_id = db.Column(db.String(50), unique=True, nullable=False)
|
||||
code = db.Column(db.String(20), unique=True, nullable=False)
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
location = db.Column(db.String(100), nullable=False)
|
||||
address = db.Column(db.Text, nullable=False)
|
||||
contact = db.Column(db.String(20), nullable=False)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
# Relationship
|
||||
creator = db.relationship('User', backref='created_branches')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'branch_id': self.branch_id,
|
||||
'code': self.code,
|
||||
'name': self.name,
|
||||
'location': self.location,
|
||||
'address': self.address,
|
||||
'contact': self.contact,
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None,
|
||||
'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_branch_id():
|
||||
"""Generate unique branch ID with BR prefix"""
|
||||
import uuid
|
||||
while True:
|
||||
branch_id = f"BR{uuid.uuid4().hex[:8].upper()}"
|
||||
if not Branch.query.filter_by(branch_id=branch_id).first():
|
||||
return branch_id
|
||||
|
||||
# Brand Model
|
||||
class Brand(db.Model):
|
||||
__tablename__ = 'brands'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
brand_id = db.Column(db.String(50), unique=True, nullable=False)
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
branch_id = db.Column(db.String(50), db.ForeignKey('branches.branch_id'), nullable=False)
|
||||
branch_name = db.Column(db.String(100), nullable=False)
|
||||
image = db.Column(db.String(255), nullable=True)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
branch = db.relationship('Branch', backref='brands', foreign_keys=[branch_id])
|
||||
creator = db.relationship('User', backref='created_brands')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'brand_id': self.brand_id,
|
||||
'name': self.name,
|
||||
'branch_id': self.branch_id,
|
||||
'branch_name': self.branch_name,
|
||||
'image': self.image,
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None,
|
||||
'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_brand_id(name):
|
||||
"""
|
||||
Generate brand ID from first 3 letters of name + 4-digit sequence
|
||||
Example: CocaCola -> COC0001, COC0002, etc.
|
||||
"""
|
||||
import re
|
||||
|
||||
# Extract only letters from name and get first 3
|
||||
letters_only = ''.join(filter(str.isalpha, name)).upper()
|
||||
prefix = letters_only[:3].ljust(3, 'X') # Pad with X if less than 3 letters
|
||||
|
||||
# Find the highest existing sequence number for this prefix
|
||||
existing_brands = Brand.query.filter(
|
||||
Brand.brand_id.like(f"{prefix}%")
|
||||
).all()
|
||||
|
||||
if not existing_brands:
|
||||
sequence = 1
|
||||
else:
|
||||
# Extract sequence numbers and find max
|
||||
sequences = []
|
||||
for brand in existing_brands:
|
||||
try:
|
||||
seq_part = brand.brand_id[3:] # Get part after prefix
|
||||
if seq_part.isdigit():
|
||||
sequences.append(int(seq_part))
|
||||
except:
|
||||
continue
|
||||
|
||||
sequence = max(sequences) + 1 if sequences else 1
|
||||
|
||||
# Format: PREFIX + 4-digit sequence
|
||||
brand_id = f"{prefix}{sequence:04d}"
|
||||
|
||||
return brand_id
|
||||
|
||||
# Category Model
|
||||
class Category(db.Model):
|
||||
__tablename__ = 'categories'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
category_id = db.Column(db.String(50), unique=True, nullable=False)
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
image = db.Column(db.String(255), nullable=True)
|
||||
brand_id = db.Column(db.String(50), db.ForeignKey('brands.brand_id'), nullable=False)
|
||||
brand_name = db.Column(db.String(100), nullable=False)
|
||||
branch_id = db.Column(db.String(50), db.ForeignKey('branches.branch_id'), nullable=False)
|
||||
branch_name = db.Column(db.String(100), nullable=False)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
brand = db.relationship('Brand', backref='categories', foreign_keys=[brand_id])
|
||||
branch = db.relationship('Branch', backref='categories', foreign_keys=[branch_id])
|
||||
creator = db.relationship('User', backref='created_categories')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'category_id': self.category_id,
|
||||
'name': self.name,
|
||||
'image': self.image,
|
||||
'brand_id': self.brand_id,
|
||||
'brand_name': self.brand_name,
|
||||
'branch_id': self.branch_id,
|
||||
'branch_name': self.branch_name,
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None,
|
||||
'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_category_id(name):
|
||||
"""
|
||||
Generate category ID from first 3 letters of name + 4-digit sequence
|
||||
Example: Beverages -> BEV0001, BEV0002, etc.
|
||||
"""
|
||||
import re
|
||||
|
||||
# Extract only letters from name and get first 3
|
||||
letters_only = ''.join(filter(str.isalpha, name)).upper()
|
||||
prefix = letters_only[:3].ljust(3, 'X') # Pad with X if less than 3 letters
|
||||
|
||||
# Find the highest existing sequence number for this prefix
|
||||
existing_categories = Category.query.filter(
|
||||
Category.category_id.like(f"{prefix}%")
|
||||
).all()
|
||||
|
||||
if not existing_categories:
|
||||
sequence = 1
|
||||
else:
|
||||
# Extract sequence numbers and find max
|
||||
sequences = []
|
||||
for category in existing_categories:
|
||||
try:
|
||||
seq_part = category.category_id[3:] # Get part after prefix
|
||||
if seq_part.isdigit():
|
||||
sequences.append(int(seq_part))
|
||||
except:
|
||||
continue
|
||||
|
||||
sequence = max(sequences) + 1 if sequences else 1
|
||||
|
||||
# Format: PREFIX + 4-digit sequence
|
||||
category_id = f"{prefix}{sequence:04d}"
|
||||
|
||||
return category_id
|
||||
|
||||
class SubCategory(db.Model):
|
||||
__tablename__ = 'sub_categories'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
sub_category_id = db.Column(db.String(50), unique=True, nullable=False)
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
image = db.Column(db.String(255), nullable=True)
|
||||
category_id = db.Column(db.String(50), db.ForeignKey('categories.category_id'), nullable=False)
|
||||
category_name = db.Column(db.String(100), nullable=False)
|
||||
brand_id = db.Column(db.String(50), db.ForeignKey('brands.brand_id'), nullable=False)
|
||||
brand_name = db.Column(db.String(100), nullable=False)
|
||||
branch_id = db.Column(db.String(50), db.ForeignKey('branches.branch_id'), nullable=False)
|
||||
branch_name = db.Column(db.String(100), nullable=False)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||
|
||||
# Relationships
|
||||
category = db.relationship('Category', backref='sub_categories', foreign_keys=[category_id])
|
||||
brand = db.relationship('Brand', backref='sub_categories', foreign_keys=[brand_id])
|
||||
branch = db.relationship('Branch', backref='sub_categories', foreign_keys=[branch_id])
|
||||
creator = db.relationship('User', backref='created_subcategories')
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'sub_category_id': self.sub_category_id,
|
||||
'name': self.name,
|
||||
'image': self.image,
|
||||
'category_id': self.category_id,
|
||||
'category_name': self.category_name,
|
||||
'brand_id': self.brand_id,
|
||||
'brand_name': self.brand_name,
|
||||
'branch_id': self.branch_id,
|
||||
'branch_name': self.branch_name,
|
||||
'created_by': self.created_by,
|
||||
'created_by_username': self.creator.username if self.creator else None,
|
||||
'created_at': self.created_at.strftime("%Y-%m-%d %H:%M:%S") if self.created_at else None,
|
||||
'updated_at': self.updated_at.strftime("%Y-%m-%d %H:%M:%S") if self.updated_at else None
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_sub_category_id(name):
|
||||
letters_only = ''.join(filter(str.isalpha, name)).upper()
|
||||
prefix = letters_only[:3].ljust(3, 'X')
|
||||
existing = SubCategory.query.filter(SubCategory.sub_category_id.like(f"{prefix}%")).all()
|
||||
if not existing:
|
||||
sequence = 1
|
||||
else:
|
||||
sequences = [int(s.sub_category_id[3:]) for s in existing if s.sub_category_id[3:].isdigit()]
|
||||
sequence = max(sequences) + 1 if sequences else 1
|
||||
return f"{prefix}{sequence:04d}"
|
||||
BIN
Machine-Backend/app/routes/__pycache__/__init__.cpython-314.pyc
Normal file
BIN
Machine-Backend/app/routes/__pycache__/__init__.cpython-314.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
Machine-Backend/app/routes/__pycache__/routes.cpython-314.pyc
Normal file
BIN
Machine-Backend/app/routes/__pycache__/routes.cpython-314.pyc
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
Binary file not shown.
Reference in New Issue
Block a user