{
  "info": {
    "name": "SumiMega · API de pedidos",
    "description": "Colección para probar la API pública de SumiMega.\n\n**Antes de empezar:** abre la colección → pestaña *Variables* y pega tu clave (`smg_test_…`, la que creaste en sumimega.com → Mi Panel → Desarrollador) en **api_key**, columna *Current value*. Guarda (Ctrl+S).\n\n**Orden sugerido:** 0 Estado → 1 Mi cuenta → Mis sedes → 2 Buscar productos → 3 Simular → Crear pedido → Reenviar el mismo (idempotencia) → Mis pedidos. Cada paso guarda lo que necesita el siguiente (SKU, sede, método de pago, total, referencia). Mira los detalles en la *Console* de Postman.\n\nGuía: https://api.sandbox.sumimega.com/publica/v1/guia",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.sandbox.sumimega.com/publica/v1"
    },
    {
      "key": "api_key",
      "value": "",
      "type": "secret",
      "description": "Tu clave smg_test_… (NO la compartas ni la subas a ningún repositorio)."
    },
    {
      "key": "q",
      "value": "resma",
      "description": "Palabra para buscar productos."
    },
    {
      "key": "sku",
      "value": "",
      "description": "Lo llena «Buscar productos»; puedes escribir uno."
    },
    {
      "key": "cantidad",
      "value": "1"
    },
    {
      "key": "metodo_pago",
      "value": "",
      "description": "credito o transferencia_bancaria. Lo llena «Mi cuenta»."
    },
    {
      "key": "punto_envio",
      "value": "",
      "description": "Código de sede como TEXTO. Lo llena «Mis sedes»."
    },
    {
      "key": "orden_compra",
      "value": ""
    },
    {
      "key": "total_esperado",
      "value": ""
    },
    {
      "key": "idempotency_key",
      "value": ""
    },
    {
      "key": "referencia",
      "value": ""
    },
    {
      "key": "cursor",
      "value": ""
    },
    {
      "key": "actualizado_desde",
      "value": "2026-10-01T00:00:00-05:00"
    },
    {
      "key": "cuerpo_pedido",
      "value": ""
    }
  ],
  "item": [
    {
      "name": "0. Estado",
      "item": [
        {
          "name": "Estado de la API (sin clave)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/estado",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "estado"
              ]
            },
            "description": "Responde sin clave. `estado` = ok cuando la API está encendida.",
            "auth": {
              "type": "noauth"
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('La API responde', () => pm.response.to.have.status(200));",
                  "pm.test('Está encendida', () => pm.expect(pm.response.json().estado).to.eql('ok'));"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "1. Mi cuenta",
      "item": [
        {
          "name": "Mi cuenta, reglas y límites",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/cuenta",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "cuenta"
              ]
            },
            "description": "Tu empresa, métodos de pago permitidos, compra mínima, límites y la IP con la que te ve el servidor (`ip_vista`)."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Clave válida', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}",
                  "if (pm.response.code === 200) {",
                  "  const c = pm.response.json();",
                  "  // Método de pago: el primero permitido, si la variable está vacía.",
                  "  const metodos = (c.metodos_pago || []).map(m => typeof m === 'string' ? m : (m.codigo || m.metodo || m.id)).filter(Boolean);",
                  "  if (!pm.collectionVariables.get('metodo_pago') && metodos.length) pm.collectionVariables.set('metodo_pago', metodos[0]);",
                  "  console.log('Empresa:', c.empresa, '| métodos:', metodos, '| lista para pedidos:', c.lista_para_pedidos, '| IP vista:', c.ip_vista);",
                  "  pm.test('Lista para pedidos', () => pm.expect(c.lista_para_pedidos).to.eql(true));",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Mis sedes (puntos de envío)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/puntos-envio",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "puntos-envio"
              ]
            },
            "description": "Guarda en `punto_envio` la sede por defecto (o la primera activa)."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}",
                  "if (pm.response.code === 200) {",
                  "  const r = pm.response.json();",
                  "  const activa = (r.datos || []).find(p => p.activo !== false);",
                  "  const codigo = r.codigo_por_defecto || (activa && activa.codigo) || '';",
                  "  if (codigo) pm.collectionVariables.set('punto_envio', String(codigo));",
                  "  console.log('Sedes:', (r.datos || []).map(p => p.codigo + ' · ' + p.nombre), '| usaré:', codigo || '(ninguna)');",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "2. Catálogo",
      "item": [
        {
          "name": "Buscar productos (palabra clave)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/productos?q={{q}}&con_stock=true&limite=20",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "productos"
              ],
              "query": [
                {
                  "key": "q",
                  "value": "{{q}}"
                },
                {
                  "key": "con_stock",
                  "value": "true"
                },
                {
                  "key": "limite",
                  "value": "20"
                }
              ]
            },
            "description": "Busca por nombre o SKU con TU precio. Guarda en `sku` el primer resultado para los pasos de pedido."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}",
                  "if (pm.response.code === 200) {",
                  "  const r = pm.response.json();",
                  "  const p = (r.datos || [])[0];",
                  "  if (p) { pm.collectionVariables.set('sku', p.sku); }",
                  "  pm.collectionVariables.set('cursor', r.siguiente_cursor || '');",
                  "  console.log('Encontrados:', (r.datos || []).length);",
                  "  (r.datos || []).slice(0, 10).forEach(x => console.log(x.sku, '|', x.nombre, '| $', x.precio_unitario, '| disp:', x.disponible));",
                  "  pm.test('Hay al menos un producto con stock', () => pm.expect(p).to.be.ok);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Consultar SKU exactos",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/productos?sku={{sku}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "productos"
              ],
              "query": [
                {
                  "key": "sku",
                  "value": "{{sku}}"
                },
                {
                  "key": "sku",
                  "value": "OTRO_SKU",
                  "disabled": true,
                  "description": "Para varios SKU, repite el parámetro (no separes por comas)."
                }
              ]
            },
            "description": "Uno o varios SKU exactos: repite `?sku=A&sku=B`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Siguiente página",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/productos?q={{q}}&con_stock=true&limite=20&cursor={{cursor}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "productos"
              ],
              "query": [
                {
                  "key": "q",
                  "value": "{{q}}"
                },
                {
                  "key": "con_stock",
                  "value": "true"
                },
                {
                  "key": "limite",
                  "value": "20"
                },
                {
                  "key": "cursor",
                  "value": "{{cursor}}"
                }
              ]
            },
            "description": "Usa el `siguiente_cursor` que guardó la búsqueda."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "if (pm.response.code === 200) pm.collectionVariables.set('cursor', pm.response.json().siguiente_cursor || '');"
                ]
              }
            }
          ]
        },
        {
          "name": "Cambiados desde una fecha",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/productos?actualizado_desde={{actualizado_desde}}&limite=100",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "productos"
              ],
              "query": [
                {
                  "key": "actualizado_desde",
                  "value": "{{actualizado_desde}}"
                },
                {
                  "key": "limite",
                  "value": "100"
                }
              ]
            },
            "description": "Sincronización incremental: solo lo que cambió (precio, stock, datos) desde la fecha ISO 8601."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "3. Pedidos",
      "item": [
        {
          "name": "Simular pedido (no crea nada)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/pedidos/simular",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "pedidos",
                "simular"
              ]
            },
            "description": "Valida y calcula con tus precios SIN crear el pedido. Guarda el total en `total_esperado`.",
            "body": {
              "mode": "raw",
              "raw": "{{cuerpo_pedido}}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Arma el cuerpo con lo que ya guardaron los pasos anteriores (sku, punto, método).",
                  "// Los campos vacíos NO se envían: el esquema rechaza campos de más o vacíos.",
                  "const v = (k) => (pm.collectionVariables.get(k) || '').toString().trim();",
                  "if (!v('sku')) { throw new Error('Primero ejecuta «Buscar productos» (guarda un SKU) o escribe uno en la variable sku.'); }",
                  "const cuerpo = {",
                  "  lineas: [{ sku: v('sku'), cantidad: parseInt(v('cantidad') || '1', 10) }],",
                  "  metodo_pago: v('metodo_pago') || 'credito',",
                  "};",
                  "if (v('punto_envio')) cuerpo.punto_envio = v('punto_envio');",
                  "if (!v('orden_compra')) pm.collectionVariables.set('orden_compra', 'POSTMAN-' + Date.now());",
                  "cuerpo.orden_compra = v('orden_compra');",
                  "cuerpo.notas = 'Pedido de PRUEBA desde Postman';",
                  "if (pm.info.requestName.indexOf('Crear') !== -1 && v('total_esperado')) cuerpo.total_esperado = v('total_esperado');",
                  "pm.collectionVariables.set('cuerpo_pedido', JSON.stringify(cuerpo, null, 2));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}",
                  "if (pm.response.code === 200) {",
                  "  const s = pm.response.json();",
                  "  pm.test('Pedido válido', () => pm.expect(s.valido, JSON.stringify(s.problemas)).to.eql(true));",
                  "  const total = s.pedido && s.pedido.totales && s.pedido.totales.total;",
                  "  if (total !== undefined && total !== null) pm.collectionVariables.set('total_esperado', String(total));",
                  "  console.log('Válido:', s.valido, '| total:', total, '| problemas:', s.problemas, '| avisos:', s.avisos);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Crear pedido (REAL)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/pedidos",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "pedidos"
              ]
            },
            "description": "⚠ Crea un pedido REAL que entra a revisión de SumiMega y aparta inventario. Usa `total_esperado` de la simulación: si el precio cambió, responde 409 `precio_cambio` y no crea nada.",
            "body": {
              "mode": "raw",
              "raw": "{{cuerpo_pedido}}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Una Idempotency-Key por pedido. Se genera si está vacía y se CONSERVA:",
                  "// reenviar con la misma no duplica el pedido.",
                  "if (!pm.collectionVariables.get('idempotency_key')) pm.collectionVariables.set('idempotency_key', pm.variables.replaceIn('{{$guid}}'));",
                  "// Arma el cuerpo con lo que ya guardaron los pasos anteriores (sku, punto, método).",
                  "// Los campos vacíos NO se envían: el esquema rechaza campos de más o vacíos.",
                  "const v = (k) => (pm.collectionVariables.get(k) || '').toString().trim();",
                  "if (!v('sku')) { throw new Error('Primero ejecuta «Buscar productos» (guarda un SKU) o escribe uno en la variable sku.'); }",
                  "const cuerpo = {",
                  "  lineas: [{ sku: v('sku'), cantidad: parseInt(v('cantidad') || '1', 10) }],",
                  "  metodo_pago: v('metodo_pago') || 'credito',",
                  "};",
                  "if (v('punto_envio')) cuerpo.punto_envio = v('punto_envio');",
                  "if (!v('orden_compra')) pm.collectionVariables.set('orden_compra', 'POSTMAN-' + Date.now());",
                  "cuerpo.orden_compra = v('orden_compra');",
                  "cuerpo.notas = 'Pedido de PRUEBA desde Postman';",
                  "if (pm.info.requestName.indexOf('Crear') !== -1 && v('total_esperado')) cuerpo.total_esperado = v('total_esperado');",
                  "pm.collectionVariables.set('cuerpo_pedido', JSON.stringify(cuerpo, null, 2));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Creado (201) o repetido (200)', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}",
                  "if (pm.response.code === 200 || pm.response.code === 201) {",
                  "  const r = pm.response.json();",
                  "  pm.collectionVariables.set('referencia', r.pedido.referencia || String(r.pedido.numero));",
                  "  console.log('Pedido', r.pedido.referencia, '| estado:', r.pedido.estado, '| total:', r.pedido.totales && r.pedido.totales.total,",
                  "              '| repetido:', pm.response.headers.get('Idempotent-Replayed') || 'no', '| avisos:', r.avisos);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Reenviar el MISMO pedido (prueba de idempotencia)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Idempotency-Key",
                "value": "{{idempotency_key}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/pedidos",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "pedidos"
              ]
            },
            "description": "Misma Idempotency-Key que el paso anterior: NO crea otro pedido; devuelve el mismo con `Idempotent-Replayed: true`.",
            "body": {
              "mode": "raw",
              "raw": "{{cuerpo_pedido}}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "// Arma el cuerpo con lo que ya guardaron los pasos anteriores (sku, punto, método).",
                  "// Los campos vacíos NO se envían: el esquema rechaza campos de más o vacíos.",
                  "const v = (k) => (pm.collectionVariables.get(k) || '').toString().trim();",
                  "if (!v('sku')) { throw new Error('Primero ejecuta «Buscar productos» (guarda un SKU) o escribe uno en la variable sku.'); }",
                  "const cuerpo = {",
                  "  lineas: [{ sku: v('sku'), cantidad: parseInt(v('cantidad') || '1', 10) }],",
                  "  metodo_pago: v('metodo_pago') || 'credito',",
                  "};",
                  "if (v('punto_envio')) cuerpo.punto_envio = v('punto_envio');",
                  "if (!v('orden_compra')) pm.collectionVariables.set('orden_compra', 'POSTMAN-' + Date.now());",
                  "cuerpo.orden_compra = v('orden_compra');",
                  "cuerpo.notas = 'Pedido de PRUEBA desde Postman';",
                  "if (pm.info.requestName.indexOf('Crear') !== -1 && v('total_esperado')) cuerpo.total_esperado = v('total_esperado');",
                  "pm.collectionVariables.set('cuerpo_pedido', JSON.stringify(cuerpo, null, 2));"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Devuelve el mismo pedido, sin duplicar', () => {",
                  "  pm.expect(pm.response.headers.get('Idempotent-Replayed')).to.eql('true');",
                  "  pm.expect(pm.response.json().pedido.referencia).to.eql(pm.collectionVariables.get('referencia'));",
                  "});",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Preparar un pedido NUEVO (limpia la clave)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/estado",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "estado"
              ]
            },
            "description": "Borra `idempotency_key`, `orden_compra` y `total_esperado` para que el siguiente «Crear pedido» sea uno distinto. Después: Simular → Crear.",
            "auth": {
              "type": "noauth"
            }
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "['idempotency_key', 'orden_compra', 'total_esperado'].forEach(k => pm.collectionVariables.set(k, ''));",
                  "console.log('Listo: el próximo «Crear pedido» será un pedido nuevo.');"
                ]
              }
            }
          ]
        },
        {
          "name": "Mis pedidos",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/pedidos?limite=20",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "pedidos"
              ],
              "query": [
                {
                  "key": "limite",
                  "value": "20"
                },
                {
                  "key": "desde",
                  "value": "",
                  "disabled": true,
                  "description": "AAAA-MM-DD"
                },
                {
                  "key": "estado",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "orden_compra",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "Tus pedidos (de la web y de la API), del más reciente al más antiguo."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}",
                  "if (pm.response.code === 200) (pm.response.json().datos || []).slice(0, 10).forEach(p => console.log(p.referencia, '|', p.origen, '|', p.estado, '| $', p.total, '| OC', p.orden_compra));"
                ]
              }
            }
          ]
        },
        {
          "name": "Ver un pedido",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/pedidos/{{referencia}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "pedidos",
                "{{referencia}}"
              ]
            },
            "description": "Por número o `WEB-n`. Usa la `referencia` que guardó «Crear pedido»."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "9. Peligro",
      "item": [
        {
          "name": "⚠ Revocar ESTA clave",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/credencial/revocar",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "credencial",
                "revocar"
              ]
            },
            "description": "Revoca para siempre la clave con la que llamas (por ejemplo, si se filtró). Después tendrás que crear otra en Desarrollador."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Revocada', () => pm.response.to.have.status(200));",
                  "// Si algo falla, el error de la API viene con code y message en español.",
                  "if (pm.response.code >= 400) {",
                  "  try { const e = pm.response.json(); console.log('Error', pm.response.code, e); } catch (_) {}",
                  "}"
                ]
              }
            }
          ]
        }
      ]
    }
  ]
}
