File size: 5,538 Bytes
b79b0dc
 
 
 
 
8730c05
b79b0dc
 
 
 
 
 
 
 
21ab478
b79b0dc
 
 
 
 
 
 
 
 
 
21ab478
b79b0dc
 
 
 
 
5fa5dc7
b79b0dc
 
b40f73e
b79b0dc
 
 
 
 
5fa5dc7
8730c05
b79b0dc
8730c05
 
 
 
5fa5dc7
02a6b87
 
5fa5dc7
1326afb
4f4cfa4
 
 
 
 
1326afb
 
 
9d20b05
 
1326afb
 
 
 
 
 
 
 
 
9d20b05
1326afb
 
4f4cfa4
 
 
 
 
1326afb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5fa5dc7
 
4bd3006
 
 
 
 
 
 
 
 
5fa5dc7
4bd3006
5fa5dc7
b79b0dc
 
ea1b45f
 
 
 
 
 
 
 
 
5fa5dc7
ea1b45f
5fa5dc7
b79b0dc
 
 
5fa5dc7
b79b0dc
5fa5dc7
f8eac62
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from fastapi import APIRouter, Depends, status, Query
from sqlalchemy.orm import Session
from app.db.session import get_db
from app.services.project_service import ProjectService
from app.schemas.project import ProjectCreate, ProjectOut
from app.schemas.project_detail import ProjectDetailOut, ProjectCustomerOut, ProjectDetailNoCustomersOut
from app.schemas.paginated_response import PaginatedResponse
from typing import List, Optional

router = APIRouter(prefix="/api/v1/projects", tags=["projects"])

@router.get("/", response_model=PaginatedResponse[ProjectOut])
def list_projects(
    customer_type: Optional[int] = Query(0, description="Customer type filter (0 for all types)", ge=0),
    status: Optional[int] = Query(None, description="Status filter (None excludes status=3, pass specific value to include all)"),
    order_by: Optional[str] = Query("project_no", description="Field to order by"),
    order_direction: Optional[str] = Query("asc", description="Order direction: asc or desc", regex="^(asc|desc)$"),
    page: Optional[int] = Query(1, description="Page number (1-indexed)", ge=1),
    page_size: Optional[int] = Query(10, description="Number of records per page", ge=1, le=100),
    db: Session = Depends(get_db)
):
    """
    Get paginated list of projects with filtering and sorting.
    
    - **customer_type**: Filter by customer type (0 = all types)
    - **status**: Filter by status (None = exclude status 3, pass specific value to include all)
    - **order_by**: Field name to sort by (project_no, project_name, etc.)
    - **order_direction**: Sort direction (asc or desc)
    - **page**: Page number starting from 1
    - **page_size**: Number of records per page (max 100)
    """
    service = ProjectService(db)
    return service.list_projects(
        customer_type=customer_type,
        status=status,
        order_by=order_by,
        order_direction=order_direction,
        page=page,
        page_size=page_size
    )

@router.get("/{project_no}", response_model=ProjectDetailNoCustomersOut)
def get_project(project_no: int, db: Session = Depends(get_db)):
    """Get a specific project by ProjectNo without customers collection

    For customer data, use `/api/v1/projects/{project_no}/customers` instead.
    """
    service = ProjectService(db)
    # Use lean method that does not fetch customers or notes for performance
    return service.get_detail_no_customers(project_no)


@router.get(
    "/{project_no}/customers",
    response_model=List[ProjectCustomerOut],
    response_model_exclude={"barrier_sizes", "contacts", "bidder_notes"}
)
def get_project_customers(
    project_no: int,
    page: Optional[int] = Query(1, description="Page number (1-indexed)", ge=1),
    page_size: Optional[int] = Query(25, description="Number of records per page", ge=1, le=1000),
    last_id: Optional[int] = Query(None, description="Keyset pagination anchor: last seen bidder Id"),
    db: Session = Depends(get_db)
):
    """Get customers associated with a specific project (by ProjectNo)

    Returns a paginated list of customers for the project. Pagination defaults
    to page=1 and page_size=100 to avoid very large responses.
    """
    service = ProjectService(db)
    # Use the dedicated service method to fetch only customers for the project
    return service.get_customers(project_no, page=page, page_size=page_size, last_id=last_id)


@router.get(
    "/{project_no}/customers/{customer_id}",
    response_model=ProjectCustomerOut,
    response_model_exclude={"barrier_sizes", "contacts", "bidder_notes"}
)
def get_project_customer_detail(
    project_no: int,
    customer_id: str,
    db: Session = Depends(get_db)
):
    """Get detailed bidder information for a specific customer on a project
    
    Returns the complete bidder details including barrier sizes, contacts, and notes
    for the specified project number and customer ID combination.
    
    - **project_no**: The project number
    - **customer_id**: The customer ID (CustId from Bidders table)
    """
    service = ProjectService(db)
    return service.get_project_customer_detail(project_no, customer_id)

@router.post("/", response_model=ProjectOut, status_code=status.HTTP_201_CREATED)
def create_project(project_in: ProjectCreate, db: Session = Depends(get_db)):
    """
    Create a new project using stored procedure
    
    Creates a new project record using the spProjectsInsert stored procedure.
    All project fields including billing, shipping, payment, and project details
    can be provided in the request body.
    
    Returns the created project with the generated ProjectNo.
    """
    service = ProjectService(db)
    return service.create(project_in.model_dump())

@router.put("/{project_no}", response_model=ProjectOut)
def update_project(project_no: int, project_in: ProjectCreate, db: Session = Depends(get_db)):
    """
    Update an existing project using stored procedure
    
    Updates an existing project record using the spProjectsUpdate stored procedure.
    All project fields including billing, shipping, payment, and project details
    can be updated in the request body.
    
    Returns the updated project data.
    """
    service = ProjectService(db)
    return service.update(project_no, project_in.model_dump())

@router.delete("/{project_no}", status_code=status.HTTP_204_NO_CONTENT)
def delete_project(project_no: int, db: Session = Depends(get_db)):
    """Delete a project"""
    service = ProjectService(db)
    service.delete(project_no)
    return None