openapi: 3.0.3
info:
  title: Walet Inventory API
  version: 1.0.0
  description: API untuk sistem inventory sarang burung walet berbasis lot, traceability, sorting, dan partial sales allocation.
servers:
  - url: /api/v1
paths:
  /auth/login:
    post:
      summary: Login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email, password]
              properties:
                email:
                  type: string
                password:
                  type: string
      responses:
        '200':
          description: Login success
  /suppliers:
    get:
      summary: List suppliers
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id: { type: integer }
                    code: { type: string }
                    name: { type: string }
                    phone: { type: string }
                    email: { type: string }
                    bank_name: { type: string }
                    bank_account_number: { type: string }
                    address: { type: string }
    post:
      summary: Create supplier
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                name:
                  type: string
                phone:
                  type: string
                email:
                  type: string
                bank_name:
                  type: string
                bank_account_number:
                  type: string
                address:
                  type: string
      responses:
        '201':
          description: Created
  /customers:
    get:
      summary: List customers
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id: { type: integer }
                    code: { type: string }
                    name: { type: string }
                    phone: { type: string }
                    email: { type: string }
                    bank_name: { type: string }
                    bank_account_number: { type: string }
                    address: { type: string }
    post:
      summary: Create customer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                name:
                  type: string
                phone:
                  type: string
                email:
                  type: string
                bank_name:
                  type: string
                bank_account_number:
                  type: string
                address:
                  type: string
      responses:
        '201':
          description: Created
  /item-types:
    get:
      summary: List item types
      responses:
        '200':
          description: OK
  /item-grades:
    get:
      summary: List item grades
      responses:
        '200':
          description: OK
  /warehouses:
    get:
      summary: List warehouses
      responses:
        '200':
          description: OK
  /purchases:
    get:
      summary: List purchases
      parameters:
        - in: query
          name: supplier_id
          schema: { type: integer }
        - in: query
          name: status
          schema: { type: string }
      responses:
        '200':
          description: OK
    post:
      summary: Create purchase
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePurchaseRequest'
      responses:
        '201':
          description: Created
  /purchases/{id}:
    get:
      summary: Purchase detail
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: OK
  /receipts:
    get:
      summary: List receipts
      responses:
        '200':
          description: OK
    post:
      summary: Create receipt
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateReceiptRequest'
      responses:
        '201':
          description: Created
  /receipts/{id}/generate-lots:
    post:
      summary: Generate lots from receipt
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Lots generated
  /lots:
    get:
      summary: List inventory lots
      parameters:
        - in: query
          name: supplier_id
          schema: { type: integer }
        - in: query
          name: item_type_id
          schema: { type: integer }
        - in: query
          name: item_grade_id
          schema: { type: integer }
        - in: query
          name: warehouse_id
          schema: { type: integer }
        - in: query
          name: status
          schema: { type: string }
      responses:
        '200':
          description: OK
  /lots/{id}:
    get:
      summary: Lot detail
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: OK
  /lots/{id}/movements:
    get:
      summary: Lot movement history
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: OK
  /lots/{id}/trace:
    get:
      summary: Lot traceability detail
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: OK
  /lots/{id}/hold:
    post:
      summary: Hold lot
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Lot held
  /lots/{id}/release:
    post:
      summary: Release lot
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: Lot released
  /lots/{id}/transfer:
    post:
      summary: Transfer lot
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                to_warehouse_id:
                  type: integer
                to_location_id:
                  type: integer
                qty:
                  type: number
                notes:
                  type: string
      responses:
        '200':
          description: Transfer success
  /sorting-sessions:
    get:
      summary: List sorting sessions
      responses:
        '200':
          description: OK
    post:
      summary: Create sorting session
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSortingSessionRequest'
      responses:
        '201':
          description: Created
  /lots/{id}/regrade:
    post:
      summary: Regrade lot
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [target_grade_id, qty, reason_id]
              properties:
                target_grade_id:
                  type: integer
                qty:
                  type: number
                reason_id:
                  type: integer
                notes:
                  type: string
      responses:
        '200':
          description: Regrade success
  /sales:
    get:
      summary: List sales
      responses:
        '200':
          description: OK
    post:
      summary: Create sales order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSalesRequest'
      responses:
        '201':
          description: Created
  /sales/{id}:
    get:
      summary: Sales detail
      parameters:
        - $ref: '#/components/parameters/IdParam'
      responses:
        '200':
          description: OK
  /sales/{id}/allocate:
    post:
      summary: Manual allocate lots to sales lines
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AllocateSalesRequest'
      responses:
        '200':
          description: Allocation success
  /sales/{id}/auto-allocate:
    post:
      summary: Auto allocate by policy
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                policy:
                  type: string
                  example: FIFO
      responses:
        '200':
          description: Auto allocation success
  /sales/{id}/confirm-picking:
    post:
      summary: Confirm picked quantities
      parameters:
        - $ref: '#/components/parameters/IdParam'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfirmPickingRequest'
      responses:
        '200':
          description: Picking confirmed
  /stock-adjustments:
    get:
      summary: List stock adjustments
      responses:
        '200':
          description: OK
    post:
      summary: Create stock adjustment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateStockAdjustmentRequest'
      responses:
        '201':
          description: Created
  /sales-returns:
    post:
      summary: Create sales return
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSalesReturnRequest'
      responses:
        '201':
          description: Created
  /purchase-returns:
    post:
      summary: Create purchase return
      responses:
        '201':
          description: Created
  /barcode/lookup/{value}:
    get:
      summary: Lookup lot by barcode or QR value
      parameters:
        - in: path
          name: value
          required: true
          schema:
            type: string
      responses:
        '200':
          description: OK
  /reports/stock-summary:
    get:
      summary: Stock summary report
      responses:
        '200':
          description: OK
  /reports/stock-lots:
    get:
      summary: Stock lots report
      responses:
        '200':
          description: OK
  /reports/purchases:
    get:
      summary: Purchases report
      responses:
        '200':
          description: OK
  /reports/sales:
    get:
      summary: Sales report
      responses:
        '200':
          description: OK
  /reports/margins:
    get:
      summary: Margin report
      responses:
        '200':
          description: OK
  /reports/shrinkage:
    get:
      summary: Shrinkage report
      responses:
        '200':
          description: OK
  /reports/supplier-quality:
    get:
      summary: Supplier quality report
      responses:
        '200':
          description: OK
  /reports/traceability:
    get:
      summary: Traceability report
      parameters:
        - in: query
          name: sales_id
          schema: { type: integer }
        - in: query
          name: lot_id
          schema: { type: integer }
      responses:
        '200':
          description: OK
components:
  parameters:
    IdParam:
      in: path
      name: id
      required: true
      schema:
        type: integer
  schemas:
    CreatePurchaseRequest:
      type: object
      required: [supplier_id, purchase_date, lines]
      properties:
        supplier_id:
          type: integer
        purchase_date:
          type: string
          format: date
        supplier_invoice_no:
          type: string
        notes:
          type: string
        lines:
          type: array
          items:
            type: object
            required: [item_type_id, qty_ordered, unit_id, unit_price, classification_status]
            properties:
              item_type_id:
                type: integer
              item_grade_id:
                type: integer
                nullable: true
              qty_ordered:
                type: number
              unit_id:
                type: integer
              unit_price:
                type: number
              classification_status:
                type: string
    CreateReceiptRequest:
      type: object
      required: [purchase_id, supplier_id, receipt_date, lines]
      properties:
        purchase_id:
          type: integer
        supplier_id:
          type: integer
        receipt_date:
          type: string
          format: date
        notes:
          type: string
        lines:
          type: array
          items:
            type: object
            required: [purchase_line_id, item_type_id, qty_received, qty_accepted, unit_id, unit_cost, warehouse_id]
            properties:
              purchase_line_id:
                type: integer
              item_type_id:
                type: integer
              item_grade_id:
                type: integer
                nullable: true
              qty_received:
                type: number
              qty_accepted:
                type: number
              qty_rejected:
                type: number
              unit_id:
                type: integer
              unit_cost:
                type: number
              warehouse_id:
                type: integer
              warehouse_location_id:
                type: integer
    CreateSortingSessionRequest:
      type: object
      required: [source_lot_id, sorting_date, input_qty, results]
      properties:
        source_lot_id:
          type: integer
        sorting_date:
          type: string
          format: date-time
        input_qty:
          type: number
        shrinkage_qty:
          type: number
        notes:
          type: string
        results:
          type: array
          items:
            type: object
            required: [item_type_id, item_grade_id, qty_result, unit_cost]
            properties:
              item_type_id:
                type: integer
              item_grade_id:
                type: integer
              qty_result:
                type: number
              unit_cost:
                type: number
    CreateSalesRequest:
      type: object
      required: [customer_id, sales_date, lines]
      properties:
        customer_id:
          type: integer
        sales_date:
          type: string
          format: date
        notes:
          type: string
        lines:
          type: array
          items:
            type: object
            required: [item_type_id, item_grade_id, qty_sold, unit_id, selling_price]
            properties:
              item_type_id:
                type: integer
              item_grade_id:
                type: integer
              qty_sold:
                type: number
              unit_id:
                type: integer
              selling_price:
                type: number
    AllocateSalesRequest:
      type: object
      required: [lines]
      properties:
        lines:
          type: array
          items:
            type: object
            required: [sales_line_id, allocations]
            properties:
              sales_line_id:
                type: integer
              allocations:
                type: array
                items:
                  type: object
                  required: [inventory_lot_id, qty_allocated]
                  properties:
                    inventory_lot_id:
                      type: integer
                    qty_allocated:
                      type: number
    ConfirmPickingRequest:
      type: object
      required: [lines]
      properties:
        lines:
          type: array
          items:
            type: object
            required: [sales_line_id, picked_allocations]
            properties:
              sales_line_id:
                type: integer
              picked_allocations:
                type: array
                items:
                  type: object
                  required: [inventory_lot_id, qty_picked]
                  properties:
                    inventory_lot_id:
                      type: integer
                    qty_picked:
                      type: number
    CreateStockAdjustmentRequest:
      type: object
      required: [inventory_lot_id, adjustment_type, reason_id, qty_change]
      properties:
        inventory_lot_id:
          type: integer
        adjustment_type:
          type: string
        reason_id:
          type: integer
        qty_change:
          type: number
        notes:
          type: string
    CreateSalesReturnRequest:
      type: object
      required: [sales_id, customer_id, return_date, lines]
      properties:
        sales_id:
          type: integer
        customer_id:
          type: integer
        return_date:
          type: string
          format: date
        lines:
          type: array
          items:
            type: object
            required: [sales_line_id, item_type_id, item_grade_id, qty_returned]
            properties:
              sales_line_id:
                type: integer
              inventory_lot_id:
                type: integer
              item_type_id:
                type: integer
              item_grade_id:
                type: integer
              qty_returned:
                type: number
              return_condition:
                type: string
              resolution:
                type: string
              notes:
                type: string
