> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.vpms.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Folio 관리

> 숙박 시설의 Folio(포리오) 관리 API

## 개요

Folio 도메인은 숙박 시설에서 고객의 계정 및 그룹 단위 청구를 관리하는 시스템입니다. Folio는 예약(Reservation)과 비즈니스 블록(BusinessBlock)을 묶어 통합 청구를 제공하며, 요금제(RatePlan) 및 포함 항목(Inclusion) 스냅샷을 관리합니다.

## 주요 기능

* **Folio 생성 및 관리**: 고객 계정 또는 그룹 단위 Folio 생성
* **예약 연결**: 기존 Folio에 예약 추가
* **비즈니스 블록 연결**: 기존 Folio에 비즈니스 블록 추가
* **요금 스냅샷 관리**: 적용된 요금제 및 포함 항목 추적
* **청구서 연동**: Folio 내 모든 청구서(Bill) 통합 관리

## Queries

### getFolio

특정 Folio의 상세 정보를 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetFolio($id: ID!) {
  getFolio(id: $id) {
    id
    folioCode
    name
    status
    accommodationId
    accountProfileId
    groupProfileId
    accountProfile {
      id
      name
    }
    groupProfile {
      id
      name
    }
    bills {
      id
      billCode
      totalAmount
      balanceAmount
    }
    appliedFolioRates {
      id
      totalAmount
      balanceAmount
    }
    appliedFolioInclusions {
      id
      totalAmount
      balanceAmount
    }
    reservations {
      id
      reservationCode
    }
    businessBlocks {
      id
      blockCode
    }
    createdAt
    updatedAt
  }
}
```

#### 파라미터

<ParamField path="id" type="ID!" required>
  조회할 Folio의 고유 식별자
</ParamField>

#### 응답

<ResponseField name="id" type="ID!">
  Folio의 고유 식별자 (ULID 형식)
</ResponseField>

<ResponseField name="folioCode" type="String!">
  Folio의 고유 코드 (자동 생성)
</ResponseField>

<ResponseField name="name" type="String">
  Folio 이름 (선택사항)
</ResponseField>

<ResponseField name="status" type="FolioStatus!">
  Folio 상태:

  * `ACTIVE`: 활성
  * `CLOSED`: 종료
</ResponseField>

<ResponseField name="accommodationId" type="ID!">
  숙박 시설 ID
</ResponseField>

<ResponseField name="accountProfile" type="Profile">
  계정 프로필 정보
</ResponseField>

<ResponseField name="groupProfile" type="Profile">
  그룹 프로필 정보
</ResponseField>

<ResponseField name="bills" type="[Bill!]!">
  Folio에 속한 청구서 목록
</ResponseField>

<ResponseField name="appliedFolioRates" type="[FolioRateSnapshot!]!">
  적용된 요금제 스냅샷 목록
</ResponseField>

<ResponseField name="appliedFolioInclusions" type="[FolioInclusionSnapshot!]!">
  적용된 포함 항목 스냅샷 목록
</ResponseField>

<ResponseField name="reservations" type="[Reservation!]!">
  연결된 예약 목록
</ResponseField>

<ResponseField name="businessBlocks" type="[BusinessBlock!]!">
  연결된 비즈니스 블록 목록
</ResponseField>

<ResponseField name="transferredFolioRates" type="[TransferredRate!]!">
  다른 Folio로 이전된 요금 스냅샷
</ResponseField>

<ResponseField name="transferredFolioInclusions" type="[TransferredInclusion!]!">
  다른 Folio로 이전된 포함 항목 스냅샷
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getFolio(id: "01HQKS9V8X2N3P4Q5R6S7T8U9V") {
      id
      folioCode
      name
      status
      accountProfile {
        name
      }
      bills {
        billCode
        totalAmount
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getFolio": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "folioCode": "F-ABC123",
        "name": "김철수 님 단체 예약",
        "status": "ACTIVE",
        "accountProfile": {
          "name": "김철수"
        },
        "bills": [
          {
            "billCode": "B-XYZ789",
            "totalAmount": 150000
          }
        ]
      }
    }
  }
  ```
</CodeGroup>

***

### getFolios

숙박 시설의 Folio 목록을 필터링하여 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetFolios(
  $accommodationId: ID!
  $filter: FolioFilterInput
  $orders: [OrderInput!]
  $first: Int
  $after: String
) {
  getFolios(
    accommodationId: $accommodationId
    filter: $filter
    orders: $orders
    first: $first
    after: $after
  ) {
    edges {
      node {
        id
        folioCode
        name
        status
        createdAt
      }
      cursor
    }
    pageInfo {
      hasNextPage
      endCursor
    }
    totalCount
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ID!" required>
  숙박 시설 ID
</ParamField>

<ParamField path="filter" type="FolioFilterInput">
  필터 조건:

  * `name`: Folio 이름 검색 (부분 일치)
  * `startDate`: 생성일 시작 범위
  * `endDate`: 생성일 종료 범위
  * `status`: Folio 상태 필터
  * `accountProfileIds`: 계정 프로필 ID 목록
  * `groupProfileIds`: 그룹 프로필 ID 목록
</ParamField>

<ParamField path="orders" type="[OrderInput!]">
  정렬 조건 (예: `[{ field: "createdAt", direction: DESC }]`)
</ParamField>

<ParamField path="first" type="Int">
  페이지당 항목 수 (기본값: 10)
</ParamField>

<ParamField path="after" type="String">
  페이지네이션 커서
</ParamField>

#### 응답

페이지네이션된 Folio 목록을 반환합니다 (Relay-style Cursor Connection).

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getFolios(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      filter: {
        status: ACTIVE
        startDate: "2025-01-01T00:00:00Z"
      }
      orders: [{ field: "createdAt", direction: DESC }]
      first: 20
    ) {
      edges {
        node {
          id
          folioCode
          name
          status
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
      totalCount
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getFolios": {
        "edges": [
          {
            "node": {
              "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
              "folioCode": "F-ABC123",
              "name": "김철수 님 단체 예약",
              "status": "ACTIVE"
            }
          }
        ],
        "pageInfo": {
          "hasNextPage": false,
          "endCursor": "YXJyYXljb25uZWN0aW9uOjE5"
        },
        "totalCount": 1
      }
    }
  }
  ```
</CodeGroup>

***

## Mutations

### createFolio

새로운 Folio를 생성합니다. Folio는 독립적으로 생성되며, 예약 및 비즈니스 블록은 별도로 추가해야 합니다.

#### GraphQL Signature

```graphql theme={null}
mutation CreateFolio($input: CreateFolioInput!) {
  createFolio(input: $input) {
    id
    folioCode
    name
    status
    accommodationId
    accountProfileId
    groupProfileId
    createdAt
  }
}
```

#### 파라미터

<ParamField path="input.accommodationId" type="ID!" required>
  숙박 시설 ID
</ParamField>

<ParamField path="input.accountProfileId" type="ID">
  계정 프로필 ID (선택사항)
</ParamField>

<ParamField path="input.groupProfileId" type="ID">
  그룹 프로필 ID (선택사항)
</ParamField>

<ParamField path="input.name" type="String">
  Folio 이름 (선택사항)
</ParamField>

<ParamField path="input.folioRatePlans" type="[FolioRatePlanInput!]">
  적용할 요금제 목록:

  * `ratePlanId`: 요금제 ID
  * `rateId`: 요금 ID
  * `quantity`: 수량
  * `startDate`: 시작일
  * `endDate`: 종료일
</ParamField>

<ParamField path="input.folioInclusions" type="[FolioInclusionInput!]">
  적용할 포함 항목 목록:

  * `inclusionId`: 포함 항목 ID
  * `quantity`: 수량
  * `usageDate`: 사용 날짜
</ParamField>

#### 응답

생성된 Folio 객체를 반환합니다.

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    createFolio(
      input: {
        accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
        name: "김철수 님 단체 예약"
        accountProfileId: "01HQKS9V8X2N3P4Q5R6S7T8U9W"
        folioRatePlans: [
          {
            ratePlanId: "01HQKS9V8X2N3P4Q5R6S7T8U9X"
            rateId: "01HQKS9V8X2N3P4Q5R6S7T8U9Y"
            quantity: 2
            startDate: "2025-12-23"
            endDate: "2025-12-25"
          }
        ]
      }
    ) {
      id
      folioCode
      name
      status
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "createFolio": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9Z",
        "folioCode": "F-DEF456",
        "name": "김철수 님 단체 예약",
        "status": "ACTIVE"
      }
    }
  }
  ```
</CodeGroup>

<Note>
  Folio 생성 시 요금제와 포함 항목을 지정하면 자동으로 청구서(Bill)가 생성됩니다.
</Note>

***

### updateFolio

기존 Folio의 정보를 수정합니다.

#### GraphQL Signature

```graphql theme={null}
mutation UpdateFolio($input: UpdateFolioInput!) {
  updateFolio(input: $input) {
    id
    folioCode
    name
    status
    updatedAt
  }
}
```

#### 파라미터

<ParamField path="input.id" type="ID!" required>
  수정할 Folio의 ID
</ParamField>

<ParamField path="input.name" type="String">
  변경할 Folio 이름
</ParamField>

<ParamField path="input.status" type="FolioStatus">
  변경할 Folio 상태 (`ACTIVE` | `CLOSED`)
</ParamField>

<ParamField path="input.accountProfileId" type="ID">
  변경할 계정 프로필 ID
</ParamField>

<ParamField path="input.groupProfileId" type="ID">
  변경할 그룹 프로필 ID
</ParamField>

#### 응답

수정된 Folio 객체를 반환합니다.

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    updateFolio(
      input: {
        id: "01HQKS9V8X2N3P4Q5R6S7T8U9Z"
        name: "김철수 님 VIP 단체 예약"
        status: CLOSED
      }
    ) {
      id
      folioCode
      name
      status
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "updateFolio": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9Z",
        "folioCode": "F-DEF456",
        "name": "김철수 님 VIP 단체 예약",
        "status": "CLOSED"
      }
    }
  }
  ```
</CodeGroup>

***

### deleteFolio

Folio를 삭제합니다.

#### GraphQL Signature

```graphql theme={null}
mutation DeleteFolio($id: ID!) {
  deleteFolio(id: $id)
}
```

#### 파라미터

<ParamField path="id" type="ID!" required>
  삭제할 Folio의 ID
</ParamField>

#### 응답

<ResponseField name="success" type="Boolean!">
  삭제 성공 여부 (true 반환)
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    deleteFolio(id: "01HQKS9V8X2N3P4Q5R6S7T8U9Z")
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "deleteFolio": true
    }
  }
  ```
</CodeGroup>

<Warning>
  Folio 삭제 시 연결된 예약, 청구서, 결제 정보도 함께 영향을 받을 수 있습니다. 삭제 전 데이터를 확인하세요.
</Warning>

***

### addReservationToFolio

기존 Folio에 새로운 예약을 추가합니다.

#### GraphQL Signature

```graphql theme={null}
mutation AddReservationToFolio($input: AddReservationToFolioInput!) {
  addReservationToFolio(input: $input) {
    id
    reservationCode
    folioId
    checkInDate
    checkOutDate
    status
  }
}
```

#### 파라미터

<ParamField path="input.folioId" type="ID!" required>
  예약을 추가할 Folio ID
</ParamField>

<ParamField path="input.businessBlockId" type="ID">
  연결할 비즈니스 블록 ID (선택사항)
</ParamField>

<ParamField path="input.profileIds" type="[ID!]">
  예약에 연결할 프로필 ID 목록
</ParamField>

<ParamField path="input.checkInDate" type="Date!" required>
  체크인 날짜
</ParamField>

<ParamField path="input.checkOutDate" type="Date!" required>
  체크아웃 날짜
</ParamField>

<ParamField path="input.roomTypeId" type="ID!" required>
  객실 타입 ID
</ParamField>

<ParamField path="input.ratePlans" type="[RatePlanInput!]">
  적용할 요금제 목록
</ParamField>

<ParamField path="input.inclusions" type="[InclusionInput!]">
  적용할 포함 항목 목록
</ParamField>

<ParamField path="input.needCheckIn" type="Boolean">
  즉시 체크인 처리 여부 (기본값: false)
</ParamField>

<ParamField path="input.sendSMS" type="Boolean">
  예약 확인 SMS 발송 여부 (기본값: false)
</ParamField>

#### 응답

생성된 Reservation 객체를 반환합니다.

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    addReservationToFolio(
      input: {
        folioId: "01HQKS9V8X2N3P4Q5R6S7T8U9Z"
        checkInDate: "2025-12-23"
        checkOutDate: "2025-12-25"
        roomTypeId: "01HQKS9V8X2N3P4Q5R6S7T8U9A"
        profileIds: ["01HQKS9V8X2N3P4Q5R6S7T8U9B"]
        needCheckIn: false
        sendSMS: true
      }
    ) {
      id
      reservationCode
      folioId
      checkInDate
      checkOutDate
      status
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "addReservationToFolio": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9C",
        "reservationCode": "R-GHI789",
        "folioId": "01HQKS9V8X2N3P4Q5R6S7T8U9Z",
        "checkInDate": "2025-12-23",
        "checkOutDate": "2025-12-25",
        "status": "CONFIRMED"
      }
    }
  }
  ```
</CodeGroup>

<Note>
  `needCheckIn`을 true로 설정하면 예약 생성과 동시에 체크인 처리됩니다.
</Note>

***

### addBusinessBlockToFolio

기존 Folio에 새로운 비즈니스 블록을 추가합니다.

#### GraphQL Signature

```graphql theme={null}
mutation AddBusinessBlockToFolio($input: AddBusinessBlockToFolioInput!) {
  addBusinessBlockToFolio(input: $input) {
    id
    blockCode
    folioId
    blockName
    startDate
    endDate
    status
  }
}
```

#### 파라미터

<ParamField path="input.folioId" type="ID!" required>
  비즈니스 블록을 추가할 Folio ID
</ParamField>

<ParamField path="input.blockName" type="String!" required>
  블록 이름
</ParamField>

<ParamField path="input.startDate" type="Date!" required>
  블록 시작 날짜
</ParamField>

<ParamField path="input.endDate" type="Date!" required>
  블록 종료 날짜
</ParamField>

<ParamField path="input.roomTypeId" type="ID!" required>
  객실 타입 ID
</ParamField>

<ParamField path="input.roomCount" type="Int!" required>
  예약할 객실 수
</ParamField>

#### 응답

생성된 BusinessBlock 객체를 반환합니다.

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    addBusinessBlockToFolio(
      input: {
        folioId: "01HQKS9V8X2N3P4Q5R6S7T8U9Z"
        blockName: "연말 세미나 블록"
        startDate: "2025-12-23"
        endDate: "2025-12-25"
        roomTypeId: "01HQKS9V8X2N3P4Q5R6S7T8U9A"
        roomCount: 10
      }
    ) {
      id
      blockCode
      folioId
      blockName
      startDate
      endDate
      status
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "addBusinessBlockToFolio": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9D",
        "blockCode": "BB-JKL012",
        "folioId": "01HQKS9V8X2N3P4Q5R6S7T8U9Z",
        "blockName": "연말 세미나 블록",
        "startDate": "2025-12-23",
        "endDate": "2025-12-25",
        "status": "ACTIVE"
      }
    }
  }
  ```
</CodeGroup>

***

## 데이터 모델

### FolioStatus

Folio의 상태를 나타냅니다.

* `ACTIVE`: 활성 상태 (청구 및 예약 가능)
* `CLOSED`: 종료 상태 (정산 완료)

### FolioRateSnapshot

Folio에 적용된 요금제의 스냅샷입니다. 요금제 변경 시에도 기존 스냅샷은 유지됩니다.

**주요 필드:**

* `appliedRatePlan`: 적용된 요금제 정보 (히스토리)
* `appliedRate`: 적용된 요금 정보 (히스토리)
* `totalAmount`: 총 요금
* `balanceAmount`: 잔액
* `businessBlock`: 연결된 비즈니스 블록

### FolioInclusionSnapshot

Folio에 적용된 포함 항목의 스냅샷입니다.

**주요 필드:**

* `appliedInclusion`: 적용된 포함 항목 정보 (히스토리)
* `totalAmount`: 총 금액
* `balanceAmount`: 잔액

***

## 에러 처리

<ResponseField name="DATA_NOT_FOUND_ON_ID">
  지정한 ID의 Folio를 찾을 수 없습니다.
</ResponseField>

<ResponseField name="BUSINESS_BLOCK_FOLIO_MISMATCH">
  비즈니스 블록이 지정한 Folio에 속하지 않습니다.
</ResponseField>

<ResponseField name="REQUIRED_PARAMETER">
  필수 파라미터가 누락되었습니다.
</ResponseField>

<ResponseField name="INVALID_PARAMETER">
  잘못된 파라미터가 전달되었습니다.
</ResponseField>

***

## 사용 흐름

1. **Folio 생성**: `createFolio`로 계정 또는 그룹 단위 Folio 생성
2. **예약 추가**: `addReservationToFolio`로 Folio에 예약 연결
3. **비즈니스 블록 추가**: `addBusinessBlockToFolio`로 단체 블록 연결
4. **청구서 확인**: Folio에 자동 생성된 청구서 조회
5. **Folio 종료**: 정산 완료 후 `updateFolio`로 상태를 `CLOSED`로 변경

***

## 관련 API

* [예약 관리 API](/api-reference/core-svc/reservation) - 예약 생성 및 관리
* [청구서 관리 API](/api-reference/core-svc/bill) - Folio 내 청구서 및 결제
* [프로필 관리 API](/api-reference/core-svc/profile) - 계정 및 그룹 프로필
* [비즈니스 블록 API](/api-reference/core-svc/business-block) - 단체 블록 관리
