diff --git a/backend/__pycache__/main.cpython-311.pyc b/backend/__pycache__/main.cpython-311.pyc
index 739167f..c495f2e 100644
Binary files a/backend/__pycache__/main.cpython-311.pyc and b/backend/__pycache__/main.cpython-311.pyc differ
diff --git a/backend/database/__pycache__/models.cpython-311.pyc b/backend/database/__pycache__/models.cpython-311.pyc
index d5d1a32..093e30d 100644
Binary files a/backend/database/__pycache__/models.cpython-311.pyc and b/backend/database/__pycache__/models.cpython-311.pyc differ
diff --git a/backend/database/__pycache__/schemas.cpython-311.pyc b/backend/database/__pycache__/schemas.cpython-311.pyc
index 807a84f..597b32c 100644
Binary files a/backend/database/__pycache__/schemas.cpython-311.pyc and b/backend/database/__pycache__/schemas.cpython-311.pyc differ
diff --git a/backend/database/models.py b/backend/database/models.py
index b4f24f6..77943b2 100644
--- a/backend/database/models.py
+++ b/backend/database/models.py
@@ -1,24 +1,32 @@
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean, Text, ForeignKey, Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
-from datetime import datetime
+from datetime import datetime, timezone, timedelta
import enum
+# 한국 시간대 설정
+KST = timezone(timedelta(hours=9))
+
+def get_kst_now():
+ """현재 한국 시간 반환"""
+ return datetime.now(KST)
+
Base = declarative_base()
class UserRole(str, enum.Enum):
- ADMIN = "admin"
- USER = "user"
+ admin = "admin"
+ user = "user"
class IssueStatus(str, enum.Enum):
- NEW = "new"
- PROGRESS = "progress"
- COMPLETE = "complete"
+ new = "new"
+ progress = "progress"
+ complete = "complete"
class IssueCategory(str, enum.Enum):
- MATERIAL_MISSING = "material_missing"
- DIMENSION_DEFECT = "dimension_defect"
- INCOMING_DEFECT = "incoming_defect"
+ material_missing = "material_missing"
+ design_error = "design_error" # 설계미스 (기존 dimension_defect 대체)
+ incoming_defect = "incoming_defect"
+ inspection_miss = "inspection_miss" # 검사미스 (신규 추가)
class User(Base):
__tablename__ = "users"
@@ -27,9 +35,9 @@ class User(Base):
username = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
full_name = Column(String)
- role = Column(Enum(UserRole), default=UserRole.USER)
+ role = Column(Enum(UserRole), default=UserRole.user)
is_active = Column(Boolean, default=True)
- created_at = Column(DateTime, default=datetime.utcnow)
+ created_at = Column(DateTime, default=get_kst_now)
# Relationships
issues = relationship("Issue", back_populates="reporter")
@@ -40,11 +48,12 @@ class Issue(Base):
id = Column(Integer, primary_key=True, index=True)
photo_path = Column(String)
+ photo_path2 = Column(String) # 두 번째 사진 경로
category = Column(Enum(IssueCategory), nullable=False)
description = Column(Text, nullable=False)
- status = Column(Enum(IssueStatus), default=IssueStatus.NEW)
+ status = Column(Enum(IssueStatus), default=IssueStatus.new)
reporter_id = Column(Integer, ForeignKey("users.id"))
- report_date = Column(DateTime, default=datetime.utcnow)
+ report_date = Column(DateTime, default=get_kst_now)
work_hours = Column(Float, default=0)
detail_notes = Column(Text)
@@ -63,7 +72,7 @@ class DailyWork(Base):
overtime_total = Column(Float, default=0)
total_hours = Column(Float, nullable=False)
created_by_id = Column(Integer, ForeignKey("users.id"))
- created_at = Column(DateTime, default=datetime.utcnow)
+ created_at = Column(DateTime, default=get_kst_now)
# Relationships
created_by = relationship("User", back_populates="daily_works")
diff --git a/backend/database/schemas.py b/backend/database/schemas.py
index 34e5893..d5d3014 100644
--- a/backend/database/schemas.py
+++ b/backend/database/schemas.py
@@ -14,8 +14,9 @@ class IssueStatus(str, Enum):
class IssueCategory(str, Enum):
material_missing = "material_missing"
- dimension_defect = "dimension_defect"
+ design_error = "design_error" # 설계미스 (기존 dimension_defect 대체)
incoming_defect = "incoming_defect"
+ inspection_miss = "inspection_miss" # 검사미스 (신규 추가)
# User schemas
class UserBase(BaseModel):
@@ -64,6 +65,7 @@ class IssueBase(BaseModel):
class IssueCreate(IssueBase):
photo: Optional[str] = None # Base64 encoded image
+ photo2: Optional[str] = None # Second Base64 encoded image
class IssueUpdate(BaseModel):
category: Optional[IssueCategory] = None
@@ -72,10 +74,12 @@ class IssueUpdate(BaseModel):
detail_notes: Optional[str] = None
status: Optional[IssueStatus] = None
photo: Optional[str] = None # Base64 encoded image for update
+ photo2: Optional[str] = None # Second Base64 encoded image for update
class Issue(IssueBase):
id: int
photo_path: Optional[str] = None
+ photo_path2: Optional[str] = None # 두 번째 사진 경로
status: IssueStatus
reporter_id: int
reporter: User
diff --git a/backend/main.py b/backend/main.py
index e333629..ba57295 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -9,6 +9,8 @@ from routers import auth, issues, daily_work, reports
from services.auth_service import create_admin_user
# 데이터베이스 테이블 생성
+# 메타데이터 캐시 클리어
+Base.metadata.clear()
Base.metadata.create_all(bind=engine)
# FastAPI 앱 생성
diff --git a/backend/migrations/001_init.sql b/backend/migrations/001_init.sql
index 9352b69..32e1fba 100644
--- a/backend/migrations/001_init.sql
+++ b/backend/migrations/001_init.sql
@@ -1,10 +1,22 @@
+-- 초기 데이터베이스 설정
+
+-- Enum 타입 생성 (4개 카테고리)
+CREATE TYPE userRole AS ENUM ('admin', 'user');
+CREATE TYPE issueStatus AS ENUM ('new', 'progress', 'complete');
+CREATE TYPE issueCategory AS ENUM (
+ 'material_missing', -- 자재누락
+ 'design_error', -- 설계미스
+ 'incoming_defect', -- 입고자재 불량
+ 'inspection_miss' -- 검사미스
+);
+
-- 사용자 테이블
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
hashed_password VARCHAR(255) NOT NULL,
full_name VARCHAR(100),
- role VARCHAR(20) DEFAULT 'user',
+ role userRole DEFAULT 'user',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
@@ -12,20 +24,21 @@ CREATE TABLE IF NOT EXISTS users (
-- 이슈 테이블
CREATE TABLE IF NOT EXISTS issues (
id SERIAL PRIMARY KEY,
- photo_path VARCHAR(255),
- category VARCHAR(50) NOT NULL,
+ photo_path VARCHAR(500),
+ photo_path2 VARCHAR(500), -- 두 번째 사진
+ category issueCategory NOT NULL,
description TEXT NOT NULL,
- status VARCHAR(20) DEFAULT 'new',
+ status issueStatus DEFAULT 'new',
reporter_id INTEGER REFERENCES users(id),
report_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
work_hours FLOAT DEFAULT 0,
detail_notes TEXT
);
--- 일일 공수 테이블
+-- 일일 작업 테이블
CREATE TABLE IF NOT EXISTS daily_works (
id SERIAL PRIMARY KEY,
- date DATE NOT NULL UNIQUE,
+ date DATE NOT NULL,
worker_count INTEGER NOT NULL,
regular_hours FLOAT NOT NULL,
overtime_workers INTEGER DEFAULT 0,
@@ -33,11 +46,13 @@ CREATE TABLE IF NOT EXISTS daily_works (
overtime_total FLOAT DEFAULT 0,
total_hours FLOAT NOT NULL,
created_by_id INTEGER REFERENCES users(id),
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UNIQUE(date)
);
-- 인덱스 생성
CREATE INDEX idx_issues_reporter_id ON issues(reporter_id);
CREATE INDEX idx_issues_status ON issues(status);
-CREATE INDEX idx_issues_report_date ON issues(report_date);
+CREATE INDEX idx_issues_category ON issues(category);
CREATE INDEX idx_daily_works_date ON daily_works(date);
+CREATE INDEX idx_daily_works_created_by_id ON daily_works(created_by_id);
\ No newline at end of file
diff --git a/backend/migrations/002_add_second_photo.sql b/backend/migrations/002_add_second_photo.sql
new file mode 100644
index 0000000..d459851
--- /dev/null
+++ b/backend/migrations/002_add_second_photo.sql
@@ -0,0 +1,5 @@
+-- 두 번째 사진 경로 추가
+ALTER TABLE issues ADD COLUMN IF NOT EXISTS photo_path2 VARCHAR(500);
+
+-- 인덱스 추가 (선택사항)
+CREATE INDEX IF NOT EXISTS idx_issues_photo_path2 ON issues(photo_path2);
diff --git a/backend/migrations/003_update_categories.sql b/backend/migrations/003_update_categories.sql
new file mode 100644
index 0000000..80f5948
--- /dev/null
+++ b/backend/migrations/003_update_categories.sql
@@ -0,0 +1,8 @@
+-- 카테고리 업데이트 마이그레이션
+-- dimension_defect를 design_error로 변경
+UPDATE issues
+SET category = 'design_error'
+WHERE category = 'dimension_defect';
+
+-- PostgreSQL enum 타입 업데이트 (필요한 경우)
+-- 기존 enum 타입 확인 후 필요시 재생성
diff --git a/backend/migrations/004_fix_category_values.sql b/backend/migrations/004_fix_category_values.sql
new file mode 100644
index 0000000..7e44509
--- /dev/null
+++ b/backend/migrations/004_fix_category_values.sql
@@ -0,0 +1,9 @@
+-- 카테고리 값 정규화 (대문자를 소문자로 변경)
+-- 기존 DIMENSION_DEFECT를 design_error로 변경
+UPDATE issues SET category = 'design_error' WHERE category IN ('DIMENSION_DEFECT', 'dimension_defect');
+UPDATE issues SET category = 'material_missing' WHERE category = 'MATERIAL_MISSING';
+UPDATE issues SET category = 'incoming_defect' WHERE category = 'INCOMING_DEFECT';
+UPDATE issues SET category = 'inspection_miss' WHERE category = 'INSPECTION_MISS';
+
+-- 카테고리 값 확인
+SELECT category, COUNT(*) FROM issues GROUP BY category;
diff --git a/backend/migrations/005_recreate_enum_type.sql b/backend/migrations/005_recreate_enum_type.sql
new file mode 100644
index 0000000..e90e22b
--- /dev/null
+++ b/backend/migrations/005_recreate_enum_type.sql
@@ -0,0 +1,20 @@
+-- PostgreSQL enum 타입 재생성
+-- 카테고리 컬럼을 임시로 텍스트로 변경
+ALTER TABLE issues ALTER COLUMN category TYPE VARCHAR(50);
+
+-- 기존 enum 타입 삭제
+DROP TYPE IF EXISTS issuecategory CASCADE;
+
+-- 새로운 enum 타입 생성 (4개 카테고리)
+CREATE TYPE issuecategory AS ENUM (
+ 'material_missing', -- 자재누락
+ 'design_error', -- 설계미스 (기존 dimension_defect 대체)
+ 'incoming_defect', -- 입고자재 불량
+ 'inspection_miss' -- 검사미스 (신규)
+);
+
+-- 카테고리 컬럼을 새 enum 타입으로 변경
+ALTER TABLE issues ALTER COLUMN category TYPE issuecategory USING category::issuecategory;
+
+-- 확인
+SELECT enumlabel FROM pg_enum WHERE enumtypid = (SELECT oid FROM pg_type WHERE typname = 'issuecategory');
diff --git a/backend/routers/__pycache__/auth.cpython-311.pyc b/backend/routers/__pycache__/auth.cpython-311.pyc
index e209c51..7cb5703 100644
Binary files a/backend/routers/__pycache__/auth.cpython-311.pyc and b/backend/routers/__pycache__/auth.cpython-311.pyc differ
diff --git a/backend/routers/__pycache__/daily_work.cpython-311.pyc b/backend/routers/__pycache__/daily_work.cpython-311.pyc
index 5375946..1963909 100644
Binary files a/backend/routers/__pycache__/daily_work.cpython-311.pyc and b/backend/routers/__pycache__/daily_work.cpython-311.pyc differ
diff --git a/backend/routers/__pycache__/issues.cpython-311.pyc b/backend/routers/__pycache__/issues.cpython-311.pyc
index 22476d1..5f7acc8 100644
Binary files a/backend/routers/__pycache__/issues.cpython-311.pyc and b/backend/routers/__pycache__/issues.cpython-311.pyc differ
diff --git a/backend/routers/__pycache__/reports.cpython-311.pyc b/backend/routers/__pycache__/reports.cpython-311.pyc
index c19d0a6..7f4fee9 100644
Binary files a/backend/routers/__pycache__/reports.cpython-311.pyc and b/backend/routers/__pycache__/reports.cpython-311.pyc differ
diff --git a/backend/routers/auth.py b/backend/routers/auth.py
index 1080ee7..a6a677e 100644
--- a/backend/routers/auth.py
+++ b/backend/routers/auth.py
@@ -29,7 +29,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = De
return user
async def get_current_admin(current_user: User = Depends(get_current_user)):
- if current_user.role != UserRole.ADMIN:
+ if current_user.role != UserRole.admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions"
@@ -37,8 +37,8 @@ async def get_current_admin(current_user: User = Depends(get_current_user)):
return current_user
@router.post("/login", response_model=schemas.Token)
-async def login(login_data: schemas.LoginRequest, db: Session = Depends(get_db)):
- user = authenticate_user(db, login_data.username, login_data.password)
+async def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
+ user = authenticate_user(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
diff --git a/backend/routers/daily_work.py b/backend/routers/daily_work.py
index fac3e59..fd9996f 100644
--- a/backend/routers/daily_work.py
+++ b/backend/routers/daily_work.py
@@ -1,10 +1,10 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List, Optional
-from datetime import datetime, date
+from datetime import datetime, date, timezone, timedelta
from database.database import get_db
-from database.models import DailyWork, User, UserRole
+from database.models import DailyWork, User, UserRole, KST
from database import schemas
from routers.auth import get_current_user
@@ -120,7 +120,7 @@ async def delete_daily_work(
raise HTTPException(status_code=404, detail="Daily work not found")
# 권한 확인 (관리자만 삭제 가능)
- if current_user.role != UserRole.ADMIN:
+ if current_user.role != UserRole.admin:
raise HTTPException(status_code=403, detail="Only admin can delete daily work")
db.delete(work)
diff --git a/backend/routers/issues.py b/backend/routers/issues.py
index a544da0..e3feaa6 100644
--- a/backend/routers/issues.py
+++ b/backend/routers/issues.py
@@ -19,16 +19,22 @@ async def create_issue(
):
# 이미지 저장
photo_path = None
+ photo_path2 = None
+
if issue.photo:
photo_path = save_base64_image(issue.photo)
+ if issue.photo2:
+ photo_path2 = save_base64_image(issue.photo2)
+
# Issue 생성
db_issue = Issue(
category=issue.category,
description=issue.description,
photo_path=photo_path,
+ photo_path2=photo_path2,
reporter_id=current_user.id,
- status=IssueStatus.NEW
+ status=IssueStatus.new
)
db.add(db_issue)
db.commit()
@@ -45,9 +51,8 @@ async def read_issues(
):
query = db.query(Issue)
- # 일반 사용자는 자신의 이슈만 조회
- if current_user.role == UserRole.USER:
- query = query.filter(Issue.reporter_id == current_user.id)
+ # 모든 사용자가 모든 이슈를 조회 가능
+ # (필터링 제거 - 협업을 위해 모두가 볼 수 있어야 함)
if status:
query = query.filter(Issue.status == status)
@@ -65,9 +70,7 @@ async def read_issue(
if not issue:
raise HTTPException(status_code=404, detail="Issue not found")
- # 권한 확인
- if current_user.role == UserRole.USER and issue.reporter_id != current_user.id:
- raise HTTPException(status_code=403, detail="Not authorized to view this issue")
+ # 모든 사용자가 모든 이슈를 조회 가능 (협업을 위해)
return issue
@@ -83,13 +86,13 @@ async def update_issue(
raise HTTPException(status_code=404, detail="Issue not found")
# 권한 확인
- if current_user.role == UserRole.USER and issue.reporter_id != current_user.id:
+ if current_user.role == UserRole.user and issue.reporter_id != current_user.id:
raise HTTPException(status_code=403, detail="Not authorized to update this issue")
# 업데이트
update_data = issue_update.dict(exclude_unset=True)
- # 사진이 업데이트되는 경우 처리
+ # 첫 번째 사진이 업데이트되는 경우 처리
if "photo" in update_data:
# 기존 사진 삭제
if issue.photo_path:
@@ -105,10 +108,26 @@ async def update_issue(
# photo 필드는 제거 (DB에는 photo_path만 저장)
del update_data["photo"]
+ # 두 번째 사진이 업데이트되는 경우 처리
+ if "photo2" in update_data:
+ # 기존 사진 삭제
+ if issue.photo_path2:
+ delete_file(issue.photo_path2)
+
+ # 새 사진 저장
+ if update_data["photo2"]:
+ photo_path2 = save_base64_image(update_data["photo2"])
+ update_data["photo_path2"] = photo_path2
+ else:
+ update_data["photo_path2"] = None
+
+ # photo2 필드는 제거 (DB에는 photo_path2만 저장)
+ del update_data["photo2"]
+
# work_hours가 입력되면 자동으로 상태를 complete로 변경
if "work_hours" in update_data and update_data["work_hours"] > 0:
- if issue.status == IssueStatus.NEW:
- update_data["status"] = IssueStatus.COMPLETE
+ if issue.status == IssueStatus.new:
+ update_data["status"] = IssueStatus.complete
for field, value in update_data.items():
setattr(issue, field, value)
@@ -128,7 +147,7 @@ async def delete_issue(
raise HTTPException(status_code=404, detail="Issue not found")
# 권한 확인 (관리자만 삭제 가능)
- if current_user.role != UserRole.ADMIN:
+ if current_user.role != UserRole.admin:
raise HTTPException(status_code=403, detail="Only admin can delete issues")
# 이미지 파일 삭제
@@ -148,7 +167,7 @@ async def get_issue_stats(
query = db.query(Issue)
# 일반 사용자는 자신의 이슈만
- if current_user.role == UserRole.USER:
+ if current_user.role == UserRole.user:
query = query.filter(Issue.reporter_id == current_user.id)
total = query.count()
diff --git a/backend/routers/reports.py b/backend/routers/reports.py
index 7e5d5f5..5bf90f9 100644
--- a/backend/routers/reports.py
+++ b/backend/routers/reports.py
@@ -35,7 +35,7 @@ async def generate_report_summary(
)
# 일반 사용자는 자신의 이슈만
- if current_user.role == UserRole.USER:
+ if current_user.role == UserRole.user:
issues_query = issues_query.filter(Issue.reporter_id == current_user.id)
issues = issues_query.all()
@@ -89,7 +89,7 @@ async def get_report_issues(
)
# 일반 사용자는 자신의 이슈만
- if current_user.role == UserRole.USER:
+ if current_user.role == UserRole.user:
query = query.filter(Issue.reporter_id == current_user.id)
issues = query.order_by(Issue.report_date).all()
diff --git a/backend/services/__pycache__/auth_service.cpython-311.pyc b/backend/services/__pycache__/auth_service.cpython-311.pyc
index 6121722..217c9f5 100644
Binary files a/backend/services/__pycache__/auth_service.cpython-311.pyc and b/backend/services/__pycache__/auth_service.cpython-311.pyc differ
diff --git a/backend/services/__pycache__/file_service.cpython-311.pyc b/backend/services/__pycache__/file_service.cpython-311.pyc
index 66d57e2..38557d6 100644
Binary files a/backend/services/__pycache__/file_service.cpython-311.pyc and b/backend/services/__pycache__/file_service.cpython-311.pyc differ
diff --git a/backend/services/auth_service.py b/backend/services/auth_service.py
index fb762ab..09fedc9 100644
--- a/backend/services/auth_service.py
+++ b/backend/services/auth_service.py
@@ -63,7 +63,7 @@ def create_admin_user(db: Session):
username=admin_username,
hashed_password=get_password_hash(admin_password),
full_name="관리자",
- role=UserRole.ADMIN,
+ role=UserRole.admin,
is_active=True
)
db.add(admin_user)
diff --git a/backend/services/file_service.py b/backend/services/file_service.py
index 06e4776..487adf9 100644
--- a/backend/services/file_service.py
+++ b/backend/services/file_service.py
@@ -27,20 +27,29 @@ def save_base64_image(base64_string: str) -> Optional[str]:
# 이미지 검증 및 형식 확인
image = Image.open(io.BytesIO(image_data))
- format = image.format.lower() if image.format else 'png'
- # 파일명 생성
- filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}.{format}"
+ # iPhone의 .mpo 파일이나 기타 형식을 JPEG로 강제 변환
+ # RGB 모드로 변환 (RGBA, P 모드 등을 처리)
+ if image.mode in ('RGBA', 'LA', 'P'):
+ # 투명도가 있는 이미지는 흰 배경과 합성
+ background = Image.new('RGB', image.size, (255, 255, 255))
+ if image.mode == 'P':
+ image = image.convert('RGBA')
+ background.paste(image, mask=image.split()[-1] if image.mode == 'RGBA' else None)
+ image = background
+ elif image.mode != 'RGB':
+ image = image.convert('RGB')
+
+ # 파일명 생성 (강제로 .jpg)
+ filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}.jpg"
filepath = os.path.join(UPLOAD_DIR, filename)
# 이미지 저장 (최대 크기 제한)
max_size = (1920, 1920)
image.thumbnail(max_size, Image.Resampling.LANCZOS)
- if format == 'png':
- image.save(filepath, 'PNG', optimize=True)
- else:
- image.save(filepath, 'JPEG', quality=85, optimize=True)
+ # 항상 JPEG로 저장
+ image.save(filepath, 'JPEG', quality=85, optimize=True)
# 웹 경로 반환
return f"/uploads/{filename}"
diff --git a/docker-compose.yml b/docker-compose.yml
index 9328391..ee6d9ee 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,6 +8,8 @@ services:
POSTGRES_USER: mproject
POSTGRES_PASSWORD: mproject2024
POSTGRES_DB: mproject
+ TZ: Asia/Seoul
+ PGTZ: Asia/Seoul
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backend/migrations:/docker-entrypoint-initdb.d
@@ -27,6 +29,7 @@ services:
ACCESS_TOKEN_EXPIRE_MINUTES: 10080 # 7 days
ADMIN_USERNAME: hyungi
ADMIN_PASSWORD: djg3-jj34-X3Q3
+ TZ: Asia/Seoul
volumes:
- ./backend:/app
- uploads:/app/uploads
diff --git a/frontend/admin.html b/frontend/admin.html
index 5a63a56..7c9e994 100644
--- a/frontend/admin.html
+++ b/frontend/admin.html
@@ -225,7 +225,8 @@
-
+
+
+
+
+
+
+
+
+