{
  "info": {
    "name": "Privadas",
    "description": "Consultas de RUC, DNI, establecimientos y tipo de cambio.\n\nExigen la CREDENCIAL DE LA CUENTA (client_id / client_secret), que se administra en el panel: Integraciones API. Es una sola por Cuenta y sirve para todas las consultas, ademas de listar/crear empresas (ver Empresas -> icono del escudo -> Crear credencial para la credencial de cada Empresa: con ELLA se cargan sus credenciales SOL y su certificado digital, en la coleccion \"Facturacion\").\n\nOJO: la credencial de una Empresa (la de la coleccion \"Facturacion\") NO puede consultar: responde 403. Y esta credencial de Cuenta no puede facturar ni administrar la ficha/aprovisionamiento SUNAT de una empresa ya creada: tambien 403.\n\nEjecuta primero \"token (cuenta)\" para obtener el access_token.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [{ "key": "token", "value": "{{access_token}}", "type": "string" }]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Sin token todo responde 401 y es facil confundirlo con una caida.",
          "if (!pm.environment.get('access_token')) {",
          "    console.warn('No hay access_token. Ejecuta primero \"token (cuenta)\".');",
          "}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "token (cuenta)",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Responde 200', () => pm.response.to.have.status(200));",
              "",
              "const body = pm.response.json();",
              "",
              "if (body.data && body.data.access_token) {",
              "    pm.environment.set('access_token', body.data.access_token);",
              "    pm.environment.set('token_expires_at', body.data.expires_at);",
              "    console.log('Token de cuenta guardado. Vence: ' + body.data.expires_at);",
              "}"
            ]
          }
        }
      ],
      "request": {
        "auth": { "type": "noauth" },
        "method": "POST",
        "header": [
          { "key": "Accept", "value": "application/json" },
          { "key": "Content-Type", "value": "application/json" }
        ],
        "body": {
          "mode": "raw",
          "raw": "{\n    \"client_id\": \"{{client_id}}\",\n    \"client_secret\": \"{{client_secret}}\"\n}"
        },
        "url": {
          "raw": "{{base_url}}/api/v1/auth/token",
          "host": ["{{base_url}}"],
          "path": ["api", "v1", "auth", "token"]
        },
        "description": "Token de la credencial de la Cuenta, la que habilita las consultas."
      }
    },
    {
      "name": "Consulta RUC",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Token valido (no 401)', () => {",
              "    pm.expect(pm.response.code).to.not.eql(401);",
              "});",
              "",
              "// Un 404 aqui casi siempre es padron sin sincronizar en el",
              "// servidor, no un fallo del endpoint.",
              "if (pm.response.code === 404) {",
              "    console.warn('RUC no encontrado. Revisa si ref_rucs tiene datos en el servidor (php artisan sunat:padron-ruc:sync).');",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "GET",
        "header": [{ "key": "Accept", "value": "application/json" }],
        "url": {
          "raw": "{{base_url}}/api/consultas/v1/ruc/{{ruc}}",
          "host": ["{{base_url}}"],
          "path": ["api", "consultas", "v1", "ruc", "{{ruc}}"]
        },
        "description": "Lee unicamente la tabla local ref_rucs via DocumentLookupService: no hay proveedor externo de respaldo.\n\nLimitado por throttle:consultas (config/api_auth.php). Es un limite de rafaga por integracion, no una cuota comercial.\n\nCambia el numero en la variable {{ruc}} del environment."
      }
    },
    {
      "name": "Consulta DNI",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Token valido (no 401)', () => {",
              "    pm.expect(pm.response.code).to.not.eql(401);",
              "});",
              "",
              "if (pm.response.code === 404) {",
              "    console.warn('DNI no encontrado. El padron de DNI se carga manualmente: revisa si ref_dnis tiene datos en el servidor.');",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "GET",
        "header": [{ "key": "Accept", "value": "application/json" }],
        "url": {
          "raw": "{{base_url}}/api/consultas/v1/dni/{{dni}}",
          "host": ["{{base_url}}"],
          "path": ["api", "consultas", "v1", "dni", "{{dni}}"]
        },
        "description": "Mismo criterio que la consulta de RUC, sobre ref_dnis. Ese padron no tiene fuente publica descargable: se carga manualmente."
      }
    },
    {
      "name": "Consulta Establecimientos",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Token valido (no 401)', () => {",
              "    pm.expect(pm.response.code).to.not.eql(401);",
              "});",
              "",
              "// 200 con total 0 = el RUC existe pero no declaro anexos.",
              "// 404 = el RUC no esta en el padron.",
              "if (pm.response.code === 200) {",
              "    console.log('Anexos encontrados: ' + pm.response.json().total);",
              "}",
              "",
              "if (pm.response.code === 404) {",
              "    console.warn('RUC no encontrado en ref_rucs.');",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "GET",
        "header": [{ "key": "Accept", "value": "application/json" }],
        "url": {
          "raw": "{{base_url}}/api/consultas/v1/establecimientos/{{ruc}}",
          "host": ["{{base_url}}"],
          "path": ["api", "consultas", "v1", "establecimientos", "{{ruc}}"]
        },
        "description": "Locales anexos declarados ante SUNAT para un RUC (tabla ref_locales_anexos). Devuelve una LISTA: un mismo RUC puede tener varios anexos.\n\nOJO: el padron de SUNAT no publica el codigo de establecimiento (0000, 0001...), solo las direcciones. Ese codigo es el que el emisor declara en cada comprobante -sunat_establishment_code- y no sale de aqui.\n\n- 200 con total > 0: anexos encontrados.\n- 200 con total 0: el RUC existe pero no declaro anexos (es lo normal).\n- 404: el RUC no esta en el padron."
      }
    },
    {
      "name": "Tipo de cambio",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Token valido (no 401)', () => {",
              "    pm.expect(pm.response.code).to.not.eql(401);",
              "});",
              "",
              "if (pm.response.code === 200) {",
              "    const d = pm.response.json().data;",
              "    console.log('Compra ' + d.compra + ' / Venta ' + d.venta",
              "        + ' (publicado ' + d.fecha_publicacion + ')');",
              "",
              "    if (d.arrastrado) {",
              "        console.log('Arrastrado ' + d.dias_arrastre + ' dia(s): no hubo publicacion nueva.');",
              "    }",
              "}",
              "",
              "if (pm.response.code === 404) {",
              "    console.warn('No hay publicacion anterior a esa fecha.');",
              "}"
            ]
          }
        }
      ],
      "request": {
        "method": "GET",
        "header": [{ "key": "Accept", "value": "application/json" }],
        "url": {
          "raw": "{{base_url}}/api/consultas/v1/tipo-cambio?fecha={{fecha_tc}}",
          "host": ["{{base_url}}"],
          "path": ["api", "consultas", "v1", "tipo-cambio"],
          "query": [
            {
              "key": "fecha",
              "value": "{{fecha_tc}}",
              "description": "Opcional (AAAA-MM-DD). Sin ella responde el de hoy."
            }
          ]
        },
        "description": "Tipo de cambio del dolar publicado por la SBS, que es el que aplica SUNAT.\n\n## La regla\n\nLo que la SBS publica un dia -hacia las 18:00- rige al dia SIGUIENTE, y sigue rigiendo mientras no haya otra publicacion. Por eso sabado, domingo y lunes suelen compartir el valor del viernes: la publicacion del lunes recien rige el martes.\n\nEl endpoint resuelve eso solo. No hay que calcular dias habiles ni feriados: se pregunta por una fecha y responde el que rige.\n\n## Respuesta\n\n    {\n        \"success\": true,\n        \"data\": {\n            \"moneda\": \"USD\",\n            \"fecha\": \"2026-08-16\",\n            \"compra\": \"3.358\",\n            \"venta\": \"3.368\",\n            \"fecha_publicacion\": \"2026-08-14\",\n            \"vigente_desde\": \"2026-08-15\",\n            \"arrastrado\": true,\n            \"dias_arrastre\": 1,\n            \"fuente\": \"bcrp\"\n        }\n    }\n\n\"arrastrado\" avisa de que el valor viene de una publicacion anterior y no del dia consultado. Con \"dias_arrastre\" sabes cuanto: un domingo son 1, un lunes 2.\n\n## Origen\n\nSe toma de la API del BCRP, que republica la serie del sistema bancario de la SBS. Ni la SBS ni el calendario de SUNAT son automatizables -la primera esta detras de un anti-bot y el segundo exige reCAPTCHA-, asi que el BCRP es la unica fuente estable. Puede tardar en cargar el dato del dia; cuando pasa, el valor anterior sigue rigiendo, que es exactamente lo que hace SUNAT.\n\n## Alcance\n\nInformativo: NO interviene en la emision. El emisor sigue enviando su propio tipo de cambio en el comprobante.\n\n- 200: hay valor vigente.\n- 404: no hay ninguna publicacion anterior a esa fecha.\n- 422: la fecha no tiene el formato AAAA-MM-DD."
      }
    },
    {
      "name": "403 - Credencial de Empresa intentando consultar",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "pm.test('Responde 403', () => pm.response.to.have.status(403));",
              "",
              "console.log('Correcto: la credencial de una Empresa no puede consultar.');"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "bearer",
          "bearer": [{ "key": "token", "value": "{{company_access_token}}", "type": "string" }]
        },
        "method": "GET",
        "header": [{ "key": "Accept", "value": "application/json" }],
        "url": {
          "raw": "{{base_url}}/api/consultas/v1/ruc/{{ruc}}",
          "host": ["{{base_url}}"],
          "path": ["api", "consultas", "v1", "ruc", "{{ruc}}"]
        },
        "description": "Este request usa a proposito el company_access_token (el de la coleccion \"Facturacion\") contra una ruta de consulta.\n\nEsperado: 403. La separacion es estricta en los dos sentidos, y este es el reverso del caso \"403 - Credencial de Cuenta intentando facturar\" de la otra coleccion.\n\nEjecuta antes \"token (empresa)\" en Facturacion para tener cargado el company_access_token."
      }
    }
  ]
}
