291 lines
9.4 KiB
Python
291 lines
9.4 KiB
Python
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
|
from sqlalchemy.orm import Session
|
|
from starlette.requests import Request
|
|
|
|
from app.core.rbac import require_permission
|
|
from app.db.database import get_db
|
|
from app.models.customer import Customer, CustomerContact
|
|
from app.models.user import User
|
|
from app.repositories.customer_repository import CustomerRepository
|
|
from app.schemas.customer import (
|
|
CustomerContactCreate,
|
|
CustomerContactResponse,
|
|
CustomerContactUpdate,
|
|
CustomerCreate,
|
|
CustomerResponse,
|
|
CustomerUpdate,
|
|
)
|
|
from app.services.audit_service import sanitize, write_audit_log
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(
|
|
prefix="/customers",
|
|
tags=["Customers"],
|
|
)
|
|
|
|
|
|
def get_customer_or_404(db: Session, customer_id: int) -> Customer:
|
|
customer = CustomerRepository.get_by_id(db, customer_id)
|
|
if customer is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Kunde nicht gefunden",
|
|
)
|
|
return customer
|
|
|
|
|
|
def get_contact_or_404(db: Session, customer_id: int, contact_id: int) -> CustomerContact:
|
|
contact = CustomerRepository.get_contact_by_id(db, customer_id, contact_id)
|
|
if contact is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Ansprechpartner nicht gefunden",
|
|
)
|
|
return contact
|
|
|
|
|
|
def ensure_unique_customer_number(
|
|
db: Session,
|
|
customer_number: str,
|
|
customer_id: int | None = None,
|
|
) -> None:
|
|
conflict = CustomerRepository.find_number_conflict(
|
|
db,
|
|
customer_number=customer_number,
|
|
exclude_customer_id=customer_id,
|
|
)
|
|
if conflict is not None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_409_CONFLICT,
|
|
detail="Kundennummer ist bereits vergeben",
|
|
)
|
|
|
|
|
|
@router.get("", response_model=list[CustomerResponse])
|
|
@router.get("/", response_model=list[CustomerResponse], include_in_schema=False)
|
|
def get_customers(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.read")),
|
|
):
|
|
logger.info("customers.list", extra={"actor_user_id": current_user.id})
|
|
return CustomerRepository.get_all(db)
|
|
|
|
|
|
@router.get("/{customer_id}", response_model=CustomerResponse)
|
|
def get_customer(
|
|
customer_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.read")),
|
|
):
|
|
logger.info(
|
|
"customers.detail",
|
|
extra={"actor_user_id": current_user.id, "target_customer_id": customer_id},
|
|
)
|
|
return get_customer_or_404(db, customer_id)
|
|
|
|
|
|
@router.post("", response_model=CustomerResponse, status_code=status.HTTP_201_CREATED)
|
|
@router.post(
|
|
"/",
|
|
response_model=CustomerResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
include_in_schema=False,
|
|
)
|
|
def create_customer(
|
|
customer: CustomerCreate,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.create")),
|
|
):
|
|
ensure_unique_customer_number(db, customer.customer_number)
|
|
logger.info("customers.create", extra={"actor_user_id": current_user.id})
|
|
created_customer = CustomerRepository.create(db, customer)
|
|
write_audit_log(
|
|
db,
|
|
action="customers.create",
|
|
entity_type="customers",
|
|
entity_id=created_customer.id,
|
|
entity_label=created_customer.company_name,
|
|
actor=current_user,
|
|
request=request,
|
|
after_data=created_customer,
|
|
)
|
|
return created_customer
|
|
|
|
|
|
@router.put("/{customer_id}", response_model=CustomerResponse)
|
|
def update_customer(
|
|
customer_id: int,
|
|
customer: CustomerUpdate,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.update")),
|
|
):
|
|
db_customer = get_customer_or_404(db, customer_id)
|
|
before_data = sanitize(db_customer)
|
|
ensure_unique_customer_number(db, customer.customer_number, customer_id)
|
|
logger.info(
|
|
"customers.update",
|
|
extra={"actor_user_id": current_user.id, "target_customer_id": customer_id},
|
|
)
|
|
updated_customer = CustomerRepository.update(db, db_customer, customer)
|
|
write_audit_log(
|
|
db,
|
|
action="customers.update",
|
|
entity_type="customers",
|
|
entity_id=updated_customer.id,
|
|
entity_label=updated_customer.company_name,
|
|
actor=current_user,
|
|
request=request,
|
|
before_data=before_data,
|
|
after_data=updated_customer,
|
|
)
|
|
return updated_customer
|
|
|
|
|
|
@router.delete("/{customer_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_customer(
|
|
customer_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.delete")),
|
|
):
|
|
db_customer = get_customer_or_404(db, customer_id)
|
|
before_data = sanitize(db_customer)
|
|
entity_label = db_customer.company_name
|
|
logger.info(
|
|
"customers.delete",
|
|
extra={"actor_user_id": current_user.id, "target_customer_id": customer_id},
|
|
)
|
|
CustomerRepository.delete(db, db_customer)
|
|
write_audit_log(
|
|
db,
|
|
action="customers.delete",
|
|
entity_type="customers",
|
|
entity_id=customer_id,
|
|
entity_label=entity_label,
|
|
actor=current_user,
|
|
request=request,
|
|
before_data=before_data,
|
|
)
|
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
|
|
|
|
@router.get("/{customer_id}/contacts", response_model=list[CustomerContactResponse])
|
|
def get_customer_contacts(
|
|
customer_id: int,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.read")),
|
|
):
|
|
get_customer_or_404(db, customer_id)
|
|
logger.info(
|
|
"customers.contacts.list",
|
|
extra={"actor_user_id": current_user.id, "target_customer_id": customer_id},
|
|
)
|
|
return CustomerRepository.get_contacts(db, customer_id)
|
|
|
|
|
|
@router.post(
|
|
"/{customer_id}/contacts",
|
|
response_model=CustomerContactResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
)
|
|
def create_customer_contact(
|
|
customer_id: int,
|
|
contact: CustomerContactCreate,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.update")),
|
|
):
|
|
customer = get_customer_or_404(db, customer_id)
|
|
logger.info(
|
|
"customers.contacts.create",
|
|
extra={"actor_user_id": current_user.id, "target_customer_id": customer_id},
|
|
)
|
|
created_contact = CustomerRepository.create_contact(db, customer_id, contact)
|
|
write_audit_log(
|
|
db,
|
|
action="customer_contacts.create",
|
|
entity_type="customer_contacts",
|
|
entity_id=created_contact.id,
|
|
entity_label=f"{created_contact.first_name} {created_contact.last_name}".strip(),
|
|
actor=current_user,
|
|
request=request,
|
|
after_data=created_contact,
|
|
metadata={"customer_id": customer_id, "customer_label": customer.company_name},
|
|
)
|
|
return created_contact
|
|
|
|
|
|
@router.put("/{customer_id}/contacts/{contact_id}", response_model=CustomerContactResponse)
|
|
def update_customer_contact(
|
|
customer_id: int,
|
|
contact_id: int,
|
|
contact: CustomerContactUpdate,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.update")),
|
|
):
|
|
customer = get_customer_or_404(db, customer_id)
|
|
db_contact = get_contact_or_404(db, customer_id, contact_id)
|
|
before_data = sanitize(db_contact)
|
|
logger.info(
|
|
"customers.contacts.update",
|
|
extra={
|
|
"actor_user_id": current_user.id,
|
|
"target_customer_id": customer_id,
|
|
"target_contact_id": contact_id,
|
|
},
|
|
)
|
|
updated_contact = CustomerRepository.update_contact(db, db_contact, contact)
|
|
write_audit_log(
|
|
db,
|
|
action="customer_contacts.update",
|
|
entity_type="customer_contacts",
|
|
entity_id=updated_contact.id,
|
|
entity_label=f"{updated_contact.first_name} {updated_contact.last_name}".strip(),
|
|
actor=current_user,
|
|
request=request,
|
|
before_data=before_data,
|
|
after_data=updated_contact,
|
|
metadata={"customer_id": customer_id, "customer_label": customer.company_name},
|
|
)
|
|
return updated_contact
|
|
|
|
|
|
@router.delete("/{customer_id}/contacts/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_customer_contact(
|
|
customer_id: int,
|
|
contact_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(require_permission("customers.update")),
|
|
):
|
|
customer = get_customer_or_404(db, customer_id)
|
|
db_contact = get_contact_or_404(db, customer_id, contact_id)
|
|
before_data = sanitize(db_contact)
|
|
entity_label = f"{db_contact.first_name} {db_contact.last_name}".strip()
|
|
logger.info(
|
|
"customers.contacts.delete",
|
|
extra={
|
|
"actor_user_id": current_user.id,
|
|
"target_customer_id": customer_id,
|
|
"target_contact_id": contact_id,
|
|
},
|
|
)
|
|
CustomerRepository.delete_contact(db, db_contact)
|
|
write_audit_log(
|
|
db,
|
|
action="customer_contacts.delete",
|
|
entity_type="customer_contacts",
|
|
entity_id=contact_id,
|
|
entity_label=entity_label,
|
|
actor=current_user,
|
|
request=request,
|
|
before_data=before_data,
|
|
metadata={"customer_id": customer_id, "customer_label": customer.company_name},
|
|
)
|
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|