Square APIを使って在庫数リストを作成

Square APIを使って、在庫数の取得ができるようになったので、在庫数の一覧を作ってみます。

[Square APIを使ってみる（在庫数の取得） \- Qiita](https://qiita.com/seya128/items/c75ff173d1a1d353b419)

## 商品リストをシンプルにする

CatalogApiのlistCatalogで取得できるカタログオブジェクトから、在庫表を作るのに必要そうなデータだけを抜き出して、解析しやすくします。

```javascript
// カタログオブジェクトをシンプルにする
function simplifyCatalogObjects(catalogObjects) {
    let resultObjects = [];

    catalogObjects.forEach(obj => {
        const resultObj = {};
        if (!obj.is_deleted) {
            resultObj.id = obj.id;
            resultObj.item_data = {
                "name": obj.item_data.name,
            };
            //バリエーション
            if (obj.item_data.variations) {
                let variations = [];
                obj.item_data.variations.forEach(variation => {
                    if (!variation.is_deleted) {
                        variations.push({
                            "id": variation.id,
                            "item_variation_data": {
                                "name": variation.item_variation_data.name,
                                "sku": variation.item_variation_data.sku,
                            },
                        });
                    }
                });
                resultObj.item_data.variations = variations;
            }
            resultObjects.push(resultObj);
        }
    });
    return resultObjects;
}


// Square APIへアクセス
const SquareConnect = require('square-connect');
const defaultClient = SquareConnect.ApiClient.instance;
const oauth2 = defaultClient.authentications['oauth2'];
oauth2.accessToken = 'YOUR ACCESS TOKEN';

// カタログリスト取得
const apiInstance = new SquareConnect.CatalogApi();
const opts = {
    'types': 'ITEM' // String | An optional case-insensitive, comma-separated list of object types to retrieve, for example `ITEM,ITEM_VARIATION,CATEGORY,IMAGE`.  The legal values are taken from the CatalogObjectType enum: `ITEM`, `ITEM_VARIATION`, `CATEGORY`, `DISCOUNT`, `TAX`, `MODIFIER`, `MODIFIER_LIST`, or `IMAGE`.
};
apiInstance.listCatalog(opts).then(function (data) {
    console.log('API called successfully. Returned data: ');
    const listObj = simplifyCatalogObjects(data.objects);
    console.log(JSON.stringify(listObj, null, 2));
}, function (error) {
    console.error(error);
});

```

以下のようなオブジェクトになります。

```json
 {
    "id": "5PH23FJXUXRA762XBNMIWXOP",
    "item_data": {
      "name": "龍童子",
      "variations": [
        {
          "id": "YFUAKFPQFKZGLDVDAC7BAEZM",
          "item_variation_data": {
            "name": "亀",
            "sku": ""
          }
        },
        {
          "id": "GKG5AJSJGPGWBKKCPDXVTYYL",
          "item_variation_data": {
            "name": "龍",
            "sku": ""
          }
        }
      ]
    }
  },
  {
    "id": "GHUUZGRZR4RLKU5CORONQGCE",
    "item_data": {
      "name": "獅子毛毬",
      "variations": [
        {
          "id": "XLYCP3R5HKYGBZGXXYMR4FRG",
          "item_variation_data": {
            "name": "カラフル",
            "sku": ""
          }
        }
      ]
    }
  },
  {
    "id": "SGT6ELC5BRESKTIDJ4OV2AF5",
    "item_data": {
      "name": "たまねぎ屋",
      "variations": [
        {
          "id": "6I4VBILOEURJCZHDA5KXF3YS",
          "item_variation_data": {
            "name": "定価",
            "sku": ""
          }
        }
      ]
    }
  },

```

シンプルにしてみて気づいたのですが、Squareの商品管理画面では、バリエーションがないように見える商品も必ず「定価」とか「販売価格」といったバリエーションができるようです。


## 在庫数を問い合わせる

どの商品にも必ずバリエーションが１つはあることがわかったので、全てのバリエーションのidで在庫数を問い合わせれば良さそうです。

id一つ一つ問い合わせればいいかと思ったのですが、一度に取得出来そうです。

複数の商品の在庫数を問い合わせるには、InventoryApiのbatchRetrieveInventoryCountsを使います。

問い合わせるidを配列に入れて指定することもできるようですが、今回は全ての在庫数を取得してみます。

```javascript

var SquareConnect = require('square-connect');
var defaultClient = SquareConnect.ApiClient.instance;

// Configure OAuth2 access token for authorization: oauth2
var oauth2 = defaultClient.authentications['oauth2'];
oauth2.accessToken = 'YOUR ACCESS TOKEN';

var apiInstance = new SquareConnect.InventoryApi();

var body = new SquareConnect.BatchRetrieveInventoryCountsRequest(); // BatchRetrieveInventoryCountsRequest | An object containing the fields to POST for the request.  See the corresponding object definition for field details.

apiInstance.batchRetrieveInventoryCounts(body).then(function (data) {
    console.log('API called successfully. Returned data: ');
    console.log(JSON.stringify(data, null, 2));
}, function (error) {
    console.error(error);
});
```

在庫データが以下のような感じで取得できます。

```json
{
  "counts": [
    {
      "catalog_object_id": "CQZIEPDLFE64WVKMCQDLW7D3",
      "catalog_object_type": "ITEM_VARIATION",
      "state": "IN_STOCK",
      "location_id": "454ACGR0V7GPA",
      "quantity": "1",
      "calculated_at": "2020-01-25T03:15:12.1253Z"
    },
    {
      "catalog_object_id": "Q4QA5OQ6EY5ZQR6LC7K22RT6",
      "catalog_object_type": "ITEM_VARIATION",
      "state": "IN_STOCK",
      "location_id": "454ACGR0V7GPA",
      "quantity": "0",
      "calculated_at": "2020-02-03T04:27:25.234Z"
    },
    {
      "catalog_object_id": "52EALZY6VCSEZEHAJPGFJB5R",
      "catalog_object_type": "ITEM_VARIATION",
      "state": "IN_STOCK",
      "location_id": "454ACGR0V7GPA",
      "quantity": "0",
      "calculated_at": "2019-11-03T09:50:46.1139Z"
    },
    {
      "catalog_object_id": "LEGPUO52IVTP7RLT2A5NRD6T",
      "catalog_object_type": "ITEM_VARIATION",
      "state": "IN_STOCK",
      "location_id": "454ACGR0V7GPA",
      "quantity": "2",
      "calculated_at": "2019-11-03T09:50:16.1139Z"
    },
```

※後でわかったのですが、１度に取得できる件数が１００アイテム分のようで、返ってきたオブジェクトに`cursor`プロパティがあった場合は、このカーソルプロパティの値をパラメーターに使って、追加でbatchRetrieveInventoryCountsを呼び出す必要があります。


## 在庫データを連想配列にする

使いやすいように、idをキーにした連想配列に変換します。

```javascript
// 全在庫数データを連想配列にする
function arrayInventoryCounts(inventoryCounts) {
    let resultArray = [];

    inventoryCounts.forEach(obj => {
        resultArray[obj.catalog_object_id] = obj.quantity;
    });

    return resultArray;
}
```

以下のような感じの連想配列になります。

```javascript

[ CQZIEPDLFE64WVKMCQDLW7D3: '1',
  Q4QA5OQ6EY5ZQR6LC7K22RT6: '0',
  '52EALZY6VCSEZEHAJPGFJB5R': '0',
  LEGPUO52IVTP7RLT2A5NRD6T: '2',
  JE3HZYQEKDXDEPQJYYDGMWDV: '0',
  X7EGVDUYAVVWKWHHZPMDS53E: '0',
  THTWJQPEEAXU4S75DKUBSF6V: '0',
  Y7KFYSA33DQ22JY3QJVPBTRG: '0',
  K46BVOXXYN7IESVTNTKOQNLY: '0',
  ZZTLDYJAIXNCLWS3MV3NQTRX: '0',
  '3TUHUDMXKKS3RDLLGAJGTMPM': '0',
  F3Z6XGJPU7XKXCVLKQ5BBTDB: '0',
```

※こちらも後で分かったのですが、stateプロパティが`IN_STOCK`以外のものも入るようなので、stateプロパティが`IN_STOCK`のものだけを使うようにする必要があります。


## 在庫数の一覧を作る

batchRetrieveInventoryCountsのページネーションや、在庫データの種類の問題を解決して結局以下のようなプログラムで在庫一覧のデータを作成できました。

```javascript

// 在庫数リスト取得サンプル
const SquareConnect = require('square-connect');


// 全在庫数データを連想配列にする
function arrayInventoryCounts(inventoryCounts) {
    let resultArray = [];

    inventoryCounts.forEach(obj => {
        if (obj.state == "IN_STOCK") {
            if (resultArray[obj.catalog_object_id]) {
                // console.log(obj.catalog_object_id);
            }

            resultArray[obj.catalog_object_id] = obj.quantity;
        }
    });

    return resultArray;
}

// 全在庫数リストを作成
function listInventoryCount(catalogObjects, inventoryCounts) {
    let resultObjects = [];
    const arrayCounts = arrayInventoryCounts(inventoryCounts);

    catalogObjects.forEach(obj => {
        const resultObj = {};
        if (!obj.is_deleted) {
            resultObj.id = obj.id;
            resultObj.item_data = {
                "name": obj.item_data.name,
            };
            //バリエーション
            if (obj.item_data.variations) {
                let variations = [];
                obj.item_data.variations.forEach(variation => {
                    if (!variation.is_deleted) {
                        variations.push({
                            "id": variation.id,
                            "item_variation_data": {
                                "name": variation.item_variation_data.name,
                                "sku": variation.item_variation_data.sku,
                                "stock": arrayCounts[variation.id]
                            },
                        });
                    }
                });
                resultObj.item_data.variations = variations;
            }
            resultObjects.push(resultObj);
        }
    });
    return resultObjects;
}


//商品カタログ取得
async function listCatalog() {
    const apiInstance = new SquareConnect.CatalogApi();
    const opts = {
        'types': 'ITEM'
    };

    const data = await apiInstance.listCatalog(opts);
    return data;
}

// 全在庫数データ取得
async function batchRetrieveInventoryCounts() {
    const apiInstance = new SquareConnect.InventoryApi();
    let body = {
        "cursor": ""
    };
    let data = {
        "counts": []
    };

    do {
        const dt = await apiInstance.batchRetrieveInventoryCounts(body);
        data.counts = data.counts.concat(dt.counts);
        body.cursor = dt.cursor || null;
    }
    while (body.cursor);

    return data;
}


// Square APIへアクセス
const defaultClient = SquareConnect.ApiClient.instance;
const oauth2 = defaultClient.authentications['oauth2'];
oauth2.accessToken = 'YOUR ACCESS TOKEN';

(async () => {
    try {
        //カタログデータと在庫データを並列で問い合わせ
        const listCatalogPromise = listCatalog();
        console.log("listCatalog()");

        const inventoryCountsPromise = batchRetrieveInventoryCounts();
        console.log("batchRetrieveInventoryCounts()");

        //問い合わせ処理の終了待ち
        catalogObjs = await listCatalogPromise;
        inventoryCounts = await inventoryCountsPromise;
        console.log('問い合わせ終了');

        //在庫リスト作成
        const data = listInventoryCount(catalogObjs.objects, inventoryCounts.counts);
        console.log(JSON.stringify(data, null, 2));
    } catch (error) {
        console.error(error);
    }
})();
```

取得できるデータは以下のよう感じです。

```json
{
    "id": "KEGDS5AB55HZXNKDVU2KMR54",
    "item_data": {
      "name": "T ベージュ",
      "variations": [
        {
          "id": "L3AMBR4VKPEKACZM42W5XSSR",
          "item_variation_data": {
            "name": "XS",
            "sku": "",
            "stock": "1"
          }
        },
        {
          "id": "H6SZNXOHR5AODKVYXSF5LQUJ",
          "item_variation_data": {
            "name": "S",
            "sku": "",
            "stock": "0"
          }
        },
        {
          "id": "IEU5P2IX25NYGMTTNP6MJHZE",
          "item_variation_data": {
            "name": "M",
            "sku": "",
            "stock": "0"
          }
        },
        {
          "id": "OV2UHORNF7NMC6KJHUXQTOPR",
          "item_variation_data": {
            "name": "Ｌ",
            "sku": "",
            "stock": "2"
          }
        },
        {
          "id": "L22LMUV37MNPRZMA2SX3G3AI",
          "item_variation_data": {
            "name": "Kids",
            "sku": ""
          }
        }
      ]
    }
  },
  {
    "id": "YKWBCEXWXMQHJ3OJNSUTZ532",
    "item_data": {
      "name": "T オレンジ",
      "variations": [
        {
          "id": "ZXAY4RQ56TCXJQXWGJDKSBRP",
          "item_variation_data": {
            "name": "XS",
            "sku": "",
            "stock": "2"
          }
        },
        {
          "id": "MY7KQ7RUBDQG3LCWARXPZZCN",
          "item_variation_data": {
            "name": "S",
            "sku": "",
            "stock": "0"
          }
        },
        {
          "id": "RO4SSMLWPQ7IQ75G6HVFMXNH",
          "item_variation_data": {
            "name": "M",
            "sku": "",
            "stock": "0"
          }
        },
        {
          "id": "QLSRDXB52BKDFXLZ3XRAEQO4",
          "item_variation_data": {
            "name": "KIDS",
            "sku": "",
            "stock": "-1"
          }
        }
      ]
    }
  },
```

`stock`が在庫数になります。

`stock`プロパティがないものがありますが、これは在庫データを全く入力していない状況だとデータがないので、このようになります。  
在庫数=0とは別の状況です。

`stock`がマイナスのものは、在庫補充時に在庫データを変更しなかったので、売れた分マイナスになっていっている状況です。