openapi: 3.0.3
info:
  title: Gestion Moda API v1
  version: 1.1.0
  description: >
    Contrato tecnico base de la API v1. Prioriza autenticacion, scopes y el modulo
    de ventas alineado a la primera etapa de paridad funcional con LaPyme.
servers:
  - url: https://app.gestion.moda/api
    description: Produccion
  - url: http://localhost/api
    description: Desarrollo local
tags:
  - name: Auth
    description: Emision y validacion de tokens personales
  - name: Sales
    description: Consulta, bootstrap y creacion de ventas
components:
  securitySchemes:
    sanctumBearer:
      type: http
      scheme: bearer
      bearerFormat: Sanctum
  parameters:
    SaleId:
      name: id
      in: path
      required: true
      schema:
        type: integer
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
    PerPage:
      name: per_page
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
    Search:
      name: search
      in: query
      required: false
      schema:
        type: string
    From:
      name: from
      in: query
      required: false
      schema:
        type: string
        format: date
    To:
      name: to
      in: query
      required: false
      schema:
        type: string
        format: date
    DateFrom:
      name: dateFrom
      in: query
      required: false
      schema:
        type: string
        format: date
    DateTo:
      name: dateTo
      in: query
      required: false
      schema:
        type: string
        format: date
    IncludeDetails:
      name: include_details
      in: query
      required: false
      schema:
        type: boolean
        default: false
    IncludePayments:
      name: include_payments
      in: query
      required: false
      schema:
        type: boolean
        default: false
    ClientId:
      name: client_id
      in: query
      required: false
      schema:
        type: integer
    ChannelId:
      name: channel_id
      in: query
      required: false
      schema:
        type: integer
    StoreId:
      name: store_id
      in: query
      required: false
      schema:
        type: integer
    SaleStateId:
      name: sale_state_id
      in: query
      required: false
      schema:
        type: integer
  schemas:
    TokenRequest:
      type: object
      required:
        - email
        - password
        - device_name
      properties:
        email:
          type: string
          format: email
        password:
          type: string
        device_name:
          type: string
        abilities:
          type: array
          items:
            type: string
            enum:
              - prices:read
              - prices:write
              - products:read
              - products:write
              - inventory:read
              - inventory:write
              - sales:read
              - sales:write
              - costs:read
              - costs:write
              - clients:read
              - clients:write
              - categories:read
              - categories:write
              - accounts:read
              - accounts:write
              - stores:read
              - stores:write
    ApiMessage:
      type: object
      properties:
        message:
          type: string
    ValidationError:
      allOf:
        - $ref: '#/components/schemas/ApiMessage'
        - type: object
          properties:
            errors:
              type: object
              additionalProperties:
                type: array
                items:
                  type: string
    PaginationMeta:
      type: object
      properties:
        current_page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        last_page:
          type: integer
        from:
          type: integer
          nullable: true
        to:
          type: integer
          nullable: true
        has_more_pages:
          type: boolean
    PaginationLinks:
      type: object
      properties:
        first:
          type: string
          nullable: true
        last:
          type: string
          nullable: true
        prev:
          type: string
          nullable: true
        next:
          type: string
          nullable: true
    SaleClient:
      type: object
      properties:
        id:
          type: integer
          nullable: true
        name:
          type: string
        email:
          type: string
        phone_number:
          type: string
        address:
          type: string
        city:
          type: string
        province:
          type: string
        postal_code:
          type: string
    SaleItem:
      type: object
      properties:
        id:
          type: integer
        product_id:
          type: integer
        product_code:
          type: string
        product_name:
          type: string
        size_id:
          type: integer
          nullable: true
        size:
          type: string
        store_id:
          type: integer
          nullable: true
        quantity:
          type: number
          format: float
        unit_price:
          type: number
          format: float
        subtotal:
          type: number
          format: float
        discount:
          type: number
          format: float
        total:
          type: number
          format: float
        vat_rate:
          type: number
          format: float
        vat_amount:
          type: number
          format: float
    SalePayment:
      type: object
      properties:
        id:
          type: integer
        date_payment:
          type: string
          format: date
        amount:
          type: number
          format: float
        description:
          type: string
        account_id:
          type: integer
          nullable: true
        account_name:
          type: string
        linked_payment_client_id:
          type: integer
          nullable: true
        account:
          $ref: '#/components/schemas/SaleReferenceItem'
    SaleCreditPayment:
      type: object
      properties:
        id:
          type: integer
        date_payment:
          type: string
          format: date
        amount:
          type: number
          format: float
        description:
          type: string
        account_id:
          type: integer
          nullable: true
        account_name:
          type: string
        linked_payment_sale_id:
          type: integer
          nullable: true
        account:
          $ref: '#/components/schemas/SaleReferenceItem'
    SaleUser:
      type: object
      properties:
        id:
          type: integer
          nullable: true
        name:
          type: string
    SaleInvoice:
      type: object
      properties:
        id:
          type: integer
          nullable: true
        cae:
          type: string
          nullable: true
        status:
          type: string
          nullable: true
        type:
          type: string
          nullable: true
        sale_point:
          type: integer
          nullable: true
        voucher:
          type: integer
          nullable: true
        invoice_number:
          type: string
          nullable: true
    SaleTotals:
      type: object
      properties:
        gross:
          type: number
          format: float
        net:
          type: number
          format: float
        vat_amount:
          type: number
          format: float
        discount:
          type: number
          format: float
        shipping_cost:
          type: number
          format: float
        payments:
          type: number
          format: float
        client_credit_generated:
          type: number
          format: float
        due:
          type: number
          format: float
        cost:
          type: number
          format: float
        profit:
          type: number
          format: float
    SaleResource:
      type: object
      properties:
        id:
          type: integer
        number:
          type: integer
        date_sale:
          type: string
          format: date
        delivery_date:
          type: string
          format: date
          nullable: true
        client_id:
          type: integer
          nullable: true
        channel_id:
          type: integer
          nullable: true
        currency_id:
          type: integer
          nullable: true
        sale_type_id:
          type: integer
          nullable: true
        sale_state_id:
          type: integer
          nullable: true
        store_id:
          type: integer
          nullable: true
        payment_method_id:
          type: integer
          nullable: true
        shipping_method_id:
          type: integer
          nullable: true
        exchange_rate:
          type: number
          format: float
        total_price:
          type: number
          format: float
        net_price:
          type: number
          format: float
        vat_amount:
          type: number
          format: float
        total_cost:
          type: number
          format: float
        profit:
          type: number
          format: float
        total_payment:
          type: number
          format: float
        total_due:
          type: number
          format: float
        discount:
          type: number
          format: float
        shipping_cost:
          type: number
          format: float
        items_sold:
          type: number
          format: float
        items_lines:
          type: integer
        comments:
          type: string
          nullable: true
        bill_number:
          type: string
          nullable: true
        invoice_number:
          type: string
          nullable: true
        active:
          type: boolean
        budget:
          type: boolean
        archived:
          type: boolean
        direct_sale:
          type: boolean
        direct_sale_no_inventory:
          type: boolean
        import:
          type: boolean
        produced:
          type: boolean
        is_exchange:
          type: boolean
        user_id:
          type: integer
          nullable: true
        user_name:
          type: string
        integration_source:
          type: string
          nullable: true
        integration_id:
          type: string
          nullable: true
        invoice_id:
          type: integer
          nullable: true
        bill_cae:
          type: string
          nullable: true
        bill_status:
          type: string
          nullable: true
        invoice_type:
          type: string
          nullable: true
        sale_point:
          type: integer
          nullable: true
        voucher:
          type: integer
          nullable: true
        tn_order:
          type: string
          nullable: true
        tn_note:
          type: string
          nullable: true
        account_display:
          type: string
        client_credit_generated:
          type: number
          format: float
        client_name:
          type: string
        client_email:
          type: string
        client_phone:
          type: string
        client_address:
          type: string
        client_city:
          type: string
        client_province:
          type: string
        client_postal_code:
          type: string
        channel:
          type: string
        currency:
          type: string
        sale_type:
          type: string
        sale_state:
          type: string
        store:
          type: string
        payment_method:
          type: string
        shipping_method:
          type: string
        user:
          $ref: '#/components/schemas/SaleUser'
        invoice:
          $ref: '#/components/schemas/SaleInvoice'
        client:
          $ref: '#/components/schemas/SaleClient'
        totals:
          $ref: '#/components/schemas/SaleTotals'
        items:
          type: array
          items:
            $ref: '#/components/schemas/SaleItem'
        detalles:
          type: array
          items:
            $ref: '#/components/schemas/SaleItem'
        payments:
          type: array
          items:
            $ref: '#/components/schemas/SalePayment'
        credit_payments:
          type: array
          items:
            $ref: '#/components/schemas/SaleCreditPayment'
    SaleListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/SaleResource'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
        links:
          $ref: '#/components/schemas/PaginationLinks'
    SaleReferenceItem:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
    SaleReferenceAccount:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        description:
          type: string
          nullable: true
        balance:
          type: number
          format: float
        payment_method_id:
          type: integer
          nullable: true
        payment_method:
          type: string
        commission_percentage:
          type: number
          format: float
        is_default:
          type: boolean
    SaleReferencePriceList:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        currency_id:
          type: integer
          nullable: true
        related_price_list_id:
          type: integer
          nullable: true
        is_base:
          type: boolean
        variance:
          type: number
          format: float
          nullable: true
        use_retailer_price:
          type: boolean
        use_wholesaler_price:
          type: boolean
        use_promotional_price:
          type: boolean
        use_unit_cost:
          type: boolean
    SaleReferenceSaleMode:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        comments:
          type: string
          nullable: true
        discount_percent:
          type: number
          format: float
        inventory_discount:
          type: boolean
        show_stock:
          type: boolean
        store_id:
          type: integer
          nullable: true
        channel_id:
          type: integer
          nullable: true
        price_list_id:
          type: integer
          nullable: true
        sale_state_id:
          type: integer
          nullable: true
        sale_type_id:
          type: integer
          nullable: true
        payment_method_id:
          type: integer
          nullable: true
        salesman_id:
          type: integer
          nullable: true
        account_id:
          type: integer
          nullable: true
        allow_client_search:
          type: boolean
        commission:
          type: number
          format: float
        commission_payment:
          type: number
          format: float
        retained_tax:
          type: number
          format: float
        auto_create_client:
          type: boolean
    SaleReferenceConfiguration:
      type: object
      properties:
        default_store_id:
          type: integer
          nullable: true
        default_account_id:
          type: integer
          nullable: true
        inventory_discount:
          type: boolean
        allow_negative_stock:
          type: boolean
        show_produce:
          type: boolean
        edit_sale_prices:
          type: boolean
        decimals:
          type: integer
        price_decimals:
          type: integer
    SaleReferencesResponse:
      type: object
      properties:
        canales:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        estados_venta:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        tipos_venta:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        monedas:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        depositos:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        cuentas:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceAccount'
        metodos_envio:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        listas_precio:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferencePriceList'
        modos_venta:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceSaleMode'
        asociados:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        proveedores:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        vendedores:
          type: array
          items:
            $ref: '#/components/schemas/SaleReferenceItem'
        configuracion:
          $ref: '#/components/schemas/SaleReferenceConfiguration'
    SaleCreateItemInput:
      type: object
      required:
        - product_id
        - size_id
        - quantity
        - unit_price
      properties:
        product_id:
          type: integer
        size_id:
          type: integer
        quantity:
          type: number
          format: float
        unit_price:
          type: number
          format: float
        discount:
          type: number
          format: float
    SaleCreatePaymentInput:
      type: object
      required:
        - amount
      properties:
        amount:
          type: number
          format: float
        description:
          type: string
        date_payment:
          type: string
          format: date
        account_id:
          type: integer
    SaleCreateRequest:
      type: object
      required:
        - client_id
        - channel_id
        - sale_type_id
        - currency_id
        - items
      properties:
        client_id:
          type: integer
        channel_id:
          type: integer
        sale_type_id:
          type: integer
        currency_id:
          type: integer
        date_sale:
          type: string
          format: date
        delivery_date:
          type: string
          format: date
        store_id:
          type: integer
        sale_state_id:
          type: integer
        payment_method_id:
          type: integer
        shipping_method_id:
          type: integer
        exchange_rate:
          type: number
          format: float
        comments:
          type: string
        discount_amount:
          type: number
          format: float
        shipping_cost_amount:
          type: number
          format: float
        discount_inventory:
          type: boolean
        integration_source:
          type: string
          maxLength: 100
        integration_id:
          type: string
          maxLength: 191
        items:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/SaleCreateItemInput'
        payments:
          type: array
          items:
            $ref: '#/components/schemas/SaleCreatePaymentInput'
    SaleCreateResponse:
      allOf:
        - $ref: '#/components/schemas/ApiMessage'
        - type: object
          properties:
            data:
              $ref: '#/components/schemas/SaleResource'
paths:
  /sanctum/token:
    post:
      tags: [Auth]
      summary: Emitir un token personal
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: Token emitido en texto plano
          content:
            text/plain:
              schema:
                type: string
        '422':
          description: Error de validacion o credenciales invalidas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
  /v1/usuario:
    get:
      tags: [Auth]
      summary: Obtener usuario autenticado
      security:
        - sanctumBearer: []
      responses:
        '200':
          description: Usuario autenticado
          content:
            application/json:
              schema:
                type: object
        '401':
          description: No autenticado
  /v1/ventas:
    get:
      tags: [Sales]
      summary: Listar ventas
      description: >
        Ruta REST recomendada para consulta de ventas. Devuelve solo ventas activas,
        no archivadas y no presupuesto, respetando los permisos del usuario del token
        sobre depositos y visibilidad de ventas de otros usuarios.
      security:
        - sanctumBearer: [sales:read]
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Search'
        - $ref: '#/components/parameters/From'
        - $ref: '#/components/parameters/To'
        - $ref: '#/components/parameters/DateFrom'
        - $ref: '#/components/parameters/DateTo'
        - $ref: '#/components/parameters/IncludeDetails'
        - $ref: '#/components/parameters/IncludePayments'
        - $ref: '#/components/parameters/ClientId'
        - $ref: '#/components/parameters/ChannelId'
        - $ref: '#/components/parameters/StoreId'
        - $ref: '#/components/parameters/SaleStateId'
      responses:
        '200':
          description: Listado paginado de ventas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleListResponse'
        '401':
          description: No autenticado
        '403':
          description: Falta sales:read
        '422':
          description: Fecha invalida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiMessage'
    post:
      tags: [Sales]
      summary: Crear una venta
      description: >
        Alta simplificada de venta. El servidor calcula importes, IVA, costo, profit y saldo.
        Soporta descuento por linea en items[].discount. Si recibe pagos opcionales, registra
        el importe aplicable en payment_sales y cualquier excedente como saldo a favor del
        cliente en payment_clients. Si recibe integration_source + integration_id, aplica
        deduplicacion por company_id usando la tabla auxiliar sale_integration_references.
      security:
        - sanctumBearer: [sales:write]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SaleCreateRequest'
      responses:
        '201':
          description: Venta creada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleCreateResponse'
        '404':
          description: Cliente, producto, cuenta o referencia inexistente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiMessage'
        '409':
          description: Duplicado por referencia de integracion
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiMessage'
        '422':
          description: Validacion o stock insuficiente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationError'
  /v1/ventas/referencias:
    get:
      tags: [Sales]
      summary: Bootstrap para ventas
      description: >
        Devuelve canales, estados, tipos, monedas, depositos, cuentas, metodos de envio,
        listas de precios, modos de venta, asociados, proveedores, vendedores y
        configuracion por defecto para bootstrap del alta simplificada.
      security:
        - sanctumBearer: [sales:read]
        - sanctumBearer: [sales:write]
      responses:
        '200':
          description: Referencias disponibles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleReferencesResponse'
  /v1/ventas/{id}:
    get:
      tags: [Sales]
      summary: Ver una venta
      description: Ruta REST recomendada para detalle unitario.
      security:
        - sanctumBearer: [sales:read]
      parameters:
        - $ref: '#/components/parameters/SaleId'
      responses:
        '200':
          description: Venta expandida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleResource'
        '404':
          description: Venta no encontrada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiMessage'
  /v1/ventas/obtener:
    get:
      tags: [Sales]
      summary: Listar ventas (legacy)
      description: Alias legacy soportado de /v1/ventas.
      security:
        - sanctumBearer: [sales:read]
      parameters:
        - $ref: '#/components/parameters/PerPage'
        - $ref: '#/components/parameters/Search'
        - $ref: '#/components/parameters/From'
        - $ref: '#/components/parameters/To'
        - $ref: '#/components/parameters/IncludeDetails'
        - $ref: '#/components/parameters/IncludePayments'
        - $ref: '#/components/parameters/ClientId'
        - $ref: '#/components/parameters/ChannelId'
        - $ref: '#/components/parameters/StoreId'
        - $ref: '#/components/parameters/SaleStateId'
      responses:
        '200':
          description: Listado paginado de ventas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleListResponse'
  /v1/ventas/ver/{id}:
    get:
      tags: [Sales]
      summary: Ver una venta (legacy)
      description: Alias legacy soportado de /v1/ventas/{id}.
      security:
        - sanctumBearer: [sales:read]
      parameters:
        - $ref: '#/components/parameters/SaleId'
      responses:
        '200':
          description: Venta expandida
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SaleResource'
        '404':
          description: Venta no encontrada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiMessage'
