openapi: 3.1.2
info:
  title: API du parc de Sorbier Logistique
  version: 1.2.0
  summary: Inventaire des postes et tickets d'assistance du service informatique.
  description: |
    API interne du service informatique de Sorbier Logistique. Elle expose
    l'inventaire des postes de travail et permet d'ouvrir des tickets.

    - Les dates suivent la norme ISO 8601 (`2026-11-08`).
    - Les erreurs suivent le format de la RFC 9457 (`application/problem+json`).
    - L'API n'est joignable que depuis le réseau interne.
  contact:
    name: Service informatique de Sorbier Logistique
    email: informatique@sorbier.example
  license:
    name: Usage interne, propriété de Sorbier Logistique
    identifier: LicenseRef-Sorbier-interne
servers:
  - url: https://parc.ad.sorbier.example/api/v1
    description: Production, sur le réseau interne
  - url: http://localhost:3000/api/v1
    description: Poste du développeur
security:
  - jeton: []
tags:
  - name: Postes
    description: Inventaire des postes de travail
  - name: Tickets
    description: Demandes d'assistance
paths:
  /postes:
    get:
      tags: [Postes]
      summary: Lister les postes
      description: Renvoie les postes de l'inventaire, triés par numéro d'inventaire.
      operationId: listerPostes
      parameters:
        - name: type
          in: query
          description: Ne garder que les postes de ce type.
          schema:
            $ref: '#/components/schemas/TypePoste'
        - name: enStock
          in: query
          description: "`true` : seulement les postes affectés à personne."
          schema:
            type: boolean
        - name: stock
          in: query
          deprecated: true
          description: Remplacé par `enStock` ; sera supprimé dans la version 2.0.0.
          schema:
            type: boolean
      responses:
        '200':
          description: La liste des postes, éventuellement vide.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Poste'
              example:
                - numInventaire: PC-0013
                  type: fixe
                  marque: Dell
                  modele: OptiPlex 7010
                  dateAchat: '2019-02-14'
                  prixAchat: 689
                  affecteA: null
        '401':
          $ref: '#/components/responses/NonAuthentifie'
  /postes/{numInventaire}:
    get:
      tags: [Postes]
      summary: Lire un poste
      description: Renvoie un poste à partir du numéro collé sur l'unité centrale.
      operationId: lirePoste
      parameters:
        - name: numInventaire
          in: path
          required: true
          description: Numéro d'inventaire du poste.
          schema:
            $ref: '#/components/schemas/NumInventaire'
          example: PC-0008
      responses:
        '200':
          description: Le poste demandé.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Poste'
        '401':
          $ref: '#/components/responses/NonAuthentifie'
        '404':
          $ref: '#/components/responses/Introuvable'
  /tickets:
    post:
      tags: [Tickets]
      summary: Ouvrir un ticket
      description: |
        Ouvre un ticket au nom de l'utilisateur authentifié. Le ticket est créé
        avec le statut `ouvert` ; sans priorité, il reçoit la priorité `normale`.
      operationId: ouvrirTicket
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NouveauTicket'
            example:
              titre: Ventilateur bruyant
              priorite: basse
              numInventaire: PC-0008
      responses:
        '201':
          description: Ticket créé. L'en-tête `Location` donne son adresse.
          headers:
            Location:
              description: URI du nouveau ticket.
              schema:
                type: string
              example: /api/v1/tickets/15
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
        '401':
          $ref: '#/components/responses/NonAuthentifie'
        '422':
          $ref: '#/components/responses/Invalide'
components:
  securitySchemes:
    jeton:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Jeton délivré par le service d'authentification de l'intranet.
  schemas:
    NumInventaire:
      type: string
      pattern: '^PC-[0-9]{4}$'
      examples: [PC-0008]
    TypePoste:
      type: string
      enum: [fixe, portable]
    Priorite:
      type: string
      enum: [basse, normale, haute, critique]
    Poste:
      type: object
      required: [numInventaire, type, marque, modele, dateAchat, prixAchat, affecteA]
      properties:
        numInventaire:
          $ref: '#/components/schemas/NumInventaire'
        type:
          $ref: '#/components/schemas/TypePoste'
        marque:
          type: string
          examples: [HP]
        modele:
          type: string
          examples: [ProDesk 400]
        dateAchat:
          type: string
          format: date
        prixAchat:
          type: number
          minimum: 0
          description: Prix d'achat en euros.
        affecteA:
          type: [string, 'null']
          format: email
          description: Adresse du salarié qui utilise le poste ; `null` pour un poste en stock.
    NouveauTicket:
      type: object
      required: [titre]
      additionalProperties: false
      properties:
        titre:
          type: string
          minLength: 5
          maxLength: 100
        priorite:
          $ref: '#/components/schemas/Priorite'
        numInventaire:
          $ref: '#/components/schemas/NumInventaire'
    Ticket:
      type: object
      required: [id, titre, priorite, statut, dateOuverture]
      properties:
        id:
          type: integer
          readOnly: true
          examples: [15]
        titre:
          type: string
        priorite:
          $ref: '#/components/schemas/Priorite'
        statut:
          type: string
          enum: [ouvert, en_cours, resolu]
        dateOuverture:
          type: string
          format: date-time
        numInventaire:
          oneOf:
            - $ref: '#/components/schemas/NumInventaire'
            - type: 'null'
    Probleme:
      type: object
      description: Erreur au format de la RFC 9457.
      required: [title, status]
      properties:
        type:
          type: string
          format: uri-reference
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
  responses:
    NonAuthentifie:
      description: Jeton absent, invalide ou expiré.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Probleme'
          example:
            title: Authentification requise
            status: 401
    Introuvable:
      description: Aucun poste ne porte ce numéro d'inventaire.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Probleme'
          example:
            title: Poste introuvable
            status: 404
            detail: Aucun poste ne porte le numéro PC-0099.
    Invalide:
      description: Le corps de la requête ne respecte pas le schéma.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Probleme'
          example:
            title: Données invalides
            status: 422
            detail: Le titre doit contenir au moins 5 caractères.
