在庫同期システム 仕様書 (README)

本システムは、楽天市場(RMS)および Shopifyストア(artgraph、PHOTOPRI、Qoo)の注文情報を自動検知し、実在庫(Googleスプレッドシート)と各販売チャネル間の在庫数を同期・調整するシステムです。


🖥️ Web管理画面 (Flask)

システムの実行状況の確認、手動実行、ロールバック、ログやデータベースの監視をブラウザ上で行える管理画面です。

1. 起動方法

起動ポートは開発フォルダ共通のポート台帳(997_開発ナレッジ/04_PORT_MANAGEMENT.md)に従い 3142024_在庫同期 に割り当てられた 3140-3149 ブロックの Web Frontend 用)を使用します。main/config/.envPORT で指定し、本番サーバー(PM2 + nginx)も同じポートです。

# 1. 仮想環境のアクティベート
source .venv/bin/activate

# 2. カレントディレクトリをmainへ移動
cd main

# 3. アプリケーションの起動
python3 app.py

2. 管理画面の主な機能

  • ダッシュボード: 同期処理の実行(非同期)、直前処理のロールバック、最新結果のサマリー表示
  • ログ表示: システム実行ログ(logs/inventory_sync.log)のリアルタイム表示・ダウンロード
  • データベース: SQLiteデータベース(inventory_sync.db)の各テーブルデータの閲覧
  • 仕様書: 本READMEのHTML表示

🚀 実行方法

1. 依存関係のセットアップ

システムには Python 3.13 互換の仮想環境とパッケージが必要です。

# 1. 仮想環境の作成
python3 -m venv .venv

# 2. pandas等のインストール
.venv/bin/pip install pandas
.venv/bin/pip install -r main/requirements.txt flask markdown pytz

2. 同期処理の実行コマンド

通常実行 (1回実行)

# 本番環境で実行
ENVIRONMENT=production .venv/bin/python main/inventory_sync_allinone.py

# テスト環境で実行
ENVIRONMENT=test .venv/bin/python main/inventory_sync_allinone.py

個別モジュールの呼び出し (Pythonスクリプト内)

from main.inventory_sync_allinone import InventorySyncAllInOne

sync = InventorySyncAllInOne()

# A. 注文処理のみを実行
all_orders = sync.process_orders()

# B. 在庫調整処理を実行
sync.process_inventory_adjustment_v2(all_orders)

# C. 楽天在庫の反映のみを実行
sync.sync_sheet_to_rakuten_inventory()

# D. 楽天とスプレッドシートの在庫突合チェック
sync.compare_rakuten_and_sheet_inventory()

⚙️ 環境設定

1. 環境変数 (.env)

main/config/.env に各ストアのAPI接続情報を設定します。

# 実行環境設定 ("production" または "test")
ENVIRONMENT=production

# Shopify API 設定
ARTGRAPH_SHOP=your-shop.myshopify.com
ARTGRAPH_TOKEN=shpat_xxx
PHOTOPRI_SHOP=your-shop.myshopify.com
PHOTOPRI_TOKEN=shpat_xxx
QOO_SHOP=your-shop.myshopify.com
QOO_TOKEN=shpat_xxx

# 楽天市場 API 設定
RAKUTEN_SERVICE_SECRET=ESA xxx
RAKUTEN_LICENSE_KEY=SLxxx

# 通知設定 (Lark Webhook URL)
LARK_WEBHOOK_URL=https://open.larksuite.com/open-apis/bot/v2/hook/xxx

2. Google Sheets API 認証

Googleスプレッドシートへのアクセスには、サービスアカウントキーファイルが必要です。 - 配置パス: main/config/credentials.json - スコープ: https://www.googleapis.com/auth/spreadsheets


📊 同期対象商品と判定ロジック

システムは、注文に特定商品が含まれているかを以下の優先順位とルールで判定し、スプレッドシート上の対応する在庫セルを減算します。

1. 同期対象商品の定義

商品タイプ (部材) 対象サイズ 対象カラー スプレッドシート該当行範囲 楽天管理番号
チャイナフレーム (ウッド額縁) A5, A4, A3, A2, A1, B5, B4, B3, B2, B1 ビルマチーク, チェリーウッド, ブラックウォルナット 3行目 〜 29行目 p00002 (outletは除外)
キャンバス木枠 A5〜B1, 300300, 600600, F0〜F20 (なし) 51行目 〜 70行目 (なし)
L字スタンド 2L (ハガキサイズは除外) ブラウン, ナチュラル, ダークブラウン 1行目 〜 100行目の動的検索 photo_stand
カレンダー&木製スタンド (なし) (なし) 1行目 〜 100行目の動的検索 (なし)

2. ストアごとの判定優先順位

Shopify (artgraph, PHOTOPRI, Qoo)

  1. チャイナフレーム: variant_title にカラーおよびサイズが含まれる場合 (最優先)
  2. キャンバス木枠: product_title に「キャンバス木枠張り」「Canvas.」「似顔絵キャンバス」等を含む場合
  3. L字スタンド: product_title に「フォトスタンド」「Postcard.」「L字スタンド」等を含む場合
  4. カレンダー&木製スタンド: product_title に「卓上フォトカレンダー」等を含み、variant_title が「カレンダー&木製スタンド」の場合

楽天市場

  1. SKU情報抽出: skuInfo (または variant_title) から「サイズ:XX」「カラー:XX」を直接抽出 (最優先)
  2. SKUパターン: r-sku00000001 等のSKUマッピングテーブルに基づく判定
  3. 商品名パターン: 商品名の中のサイズ・カラー名による判定
  4. 商品管理番号: p00002 などの管理番号の一致

🗄️ データベース構造

SQLite3データベース main/data/inventory_sync.db にて動作データとマッピング情報を管理しています。

  • processed_orders: 重複集計防止のため、処理済み注文番号・品目IDを管理します。
  • inventory_adjustments: 在庫調整が行われた際の「変更前・変更後在庫数」「調整理由」等の履歴を保存します。
  • material_conditions: 各ストア・商品ごとの「スプレッドシートの行・列位置」や「判定条件」をマッピングします。
  • execution_logs: システムの起動、完了、エラー等の実行ステータスをログとして記録します。

[!NOTE] データベースのDDLスキーマやメンテナンスコマンドの詳細は、別途 データベース構成引き継ぎ書.md を参照してください。


🔧 トラブルシューティング

1. Google Sheets API 認証エラー (invalid_grant: account not found)

本番サーバーおよびローカルでスプレッドシートアクセス時に以下が発生する場合、接続用サービスアカウントが無効化されています。

google.auth.exceptions.RefreshError: ('invalid_grant: Invalid grant: account not found', ...)

【対処手順】

  1. Google Cloud Console の「IAMと管理」>「サービスアカウント」にアクセス。
  2. 削除されている、あるいは無効化されているサービスアカウント(id-425@shopify-orderlist.iam.gserviceaccount.com)の代わりに新しいサービスアカウントを作成、または既存アカウントで新しい JSON キー を作成。
  3. 新しい鍵ファイルを main/config/credentials.json に上書き配置(本番サーバー側も同様)。
  4. 新しいサービスアカウントのメールアドレスを、対象のスプレッドシート(共有設定)に「閲覧者/編集者」として追加・共有します。

2. ログ確認用コマンド

# エラーログの抽出
grep "ERROR" main/logs/inventory_sync.log

# 実行状態・消費ログの確認
grep "在庫調整" main/logs/inventory_sync.log
grep "部材消費" main/logs/inventory_sync.log

在庫同期システム データベース構成引き継ぎ書

本ドキュメントは、在庫同期システムが使用している SQLite3 データベースのテーブル設計、初期化方法、運用メンテナンス、および最新のシステム不具合改修内容について引き継ぎ用にまとめたものです。


📂 データベース基本情報

  • ファイル配置: main/data/inventory_sync.db
    (※ 過去の改修により、カレントディレクトリに依存せず常にこの絶対パスを参照するようプログラムが最適化されています)
  • エンジンタイプ: SQLite3
  • エンコーディング: UTF-8
  • 主要なバックアップ先:
  • 本番サーバー: /var/www/inventory-sync/data/inventory_sync.db

🗄️ テーブル設計 (スキーマ定義)

1. processed_orders (処理済み注文テーブル)

  • 用途: 注文データの重複取り込み・在庫の二重減算を防止するため、処理完了した各ストアの注文・商品品目レベルのキーを記録します。
  • スキーマ: sql CREATE TABLE processed_orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id TEXT NOT NULL, -- 注文商品の一意なID (Shopifyは "注文ID_品目index", 楽天は "rakuten_注文ID_品目index") store_name TEXT NOT NULL, -- ストア識別名 (artgraph, PHOTOPRI, Qoo, rakuten) order_number TEXT, -- 注文番号 (表示用) processed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 処理日時 shipping_date TEXT, -- 出荷予定日 order_type TEXT, -- 注文タイプ (大物印刷 / 小物印刷) order_size TEXT, -- 商品サイズ (A4, A3, 2L等) order_quantity INTEGER, -- 注文数量 order_color TEXT, -- カラー (チェリーウッド, ナチュラル等) UNIQUE(order_id, store_name) -- 重複登録防止のユニーク制約 );

2. inventory_adjustments (在庫調整履歴テーブル)

  • 用途: システムが実行した在庫の減算・調整結果の全履歴を記録します。
  • スキーマ: sql CREATE TABLE inventory_adjustments ( id INTEGER PRIMARY KEY AUTOINCREMENT, store_name TEXT NOT NULL, -- 調整対象ストア product_id TEXT NOT NULL, -- 商品ID variant_id TEXT, -- バリアントID / SKU old_stock INTEGER, -- 調整前在庫数 new_stock INTEGER, -- 調整後在庫数 adjustment_reason TEXT, -- 調整理由 (例: "部材消費(注文処理による)") adjusted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 調整日時 );

3. execution_logs (システム実行ログテーブル)

  • 用途: システムのバッチ処理実行開始、正常完了、異常終了時のステータスを監視用に記録します。
  • スキーマ: sql CREATE TABLE execution_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, execution_type TEXT NOT NULL, -- 実行タイプ ("inventory_sync" 等) status TEXT NOT NULL, -- 実行結果ステータス (started / completed / error) message TEXT, -- 補足メッセージ・エラー詳細 executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

4. material_conditions (部材条件マッピングテーブル)

  • 用途: 各ストアの商品タイトルや管理番号、バリアント情報をスプレッドシート(在庫シート)の「どの行・列」と紐付けて在庫調整するかを動的に定義します。
  • スキーマ: sql CREATE TABLE material_conditions ( id INTEGER PRIMARY KEY AUTOINCREMENT, material_name TEXT NOT NULL, -- 部材名 (例: "キャンバス木枠", "L字スタンド") store_name TEXT NOT NULL, -- ストア識別名 store_type TEXT NOT NULL, -- ストアタイプ (shopify / rakuten) condition_type TEXT NOT NULL, -- 条件判定タイプ (product_type / product_name_pattern / product_id_and_variant等) condition_value TEXT, -- 条件値 (例: "Canvas.", "L字スタンド") condition_pattern TEXT, -- (予備) 判定パターン sheet_name TEXT NOT NULL, -- スプレッドシートのシート名 sheet_row_start INTEGER, -- 対象セルの開始行 sheet_row_end INTEGER, -- 対象セルの終了行 sheet_col_type TEXT, -- タイプ(名称)列 (A列 等) sheet_col_size TEXT, -- サイズ列 (C列 等) sheet_col_stock TEXT, -- 在庫数列 (G列 等) rakuten_manage_number TEXT, -- 楽天側の管理番号 rakuten_sku_pattern TEXT, -- 楽天側のSKUパターン created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

🔧 直近の不具合改修内容

本番環境で発生していたデータの不整合・バグに対して以下の修正を行いました。

1. パスの絶対パス化によるデータベースの集約

  • 問題: カレントディレクトリによってローカル内の別階層に inventory_sync.db が自動生成され、過去データと最新データが分断される。
  • 修正: 実行ファイルの場所を基準(BASE_DIR)とし、data/inventory_sync.db へのフルパスで固定接続するよう変更。

2. Shopify重複チェックキーの「商品品目レベル」への統一

  • 問題: 1つの注文に複数商品が含まれる場合、注文単位の order_id で重複判定していたため、同一注文内の別商品が再処理時にスキップされるか、二重引き下げになる現象が発生。
  • 修正: ユニークキーを商品品目(Line Item)レベルの f"{order['id']}_{item_index}" に完全統一。

3. スプレッドシート在庫減算時の下限値 (0) 制限

  • 問題: 在庫数が0の状態で注文が入った際、スプレッドシートが -1-2 などのマイナス値を記録し、楽天(0以上の整数のみ対応)との同期で値の乖離が発生。
  • 修正: 在庫引き下げ時に max(0, current_stock - qty) とし、負の値にならないよう制限。

🛠️ データベース運用とメンテナンス

1. 基本操作コマンド (SQLite3)

本番サーバーにログイン後、以下のコマンドで手動メンテナンスが可能です。

# データベースへ接続
sqlite3 /var/www/inventory-sync/data/inventory_sync.db

# 接続後の便利なメタコマンド
.tables       -- テーブル一覧
.schema       -- テーブルの作成スキーマ
.header on    -- 出力時にヘッダー(列名)を表示
.mode column  -- 出力を見やすいテーブル形式にする

2. データメンテナンス SQL の例

定期的な古いログ・古い注文の削除

長期間の運用でデータサイズが肥大化するのを防ぐため、30日以上前の実行ログ、および出荷後7日以上経過した重複チェック用データをクリーンアップします。

-- 30日以上前のログをクリーンアップ
DELETE FROM execution_logs WHERE executed_at < datetime('now', '-30 days');

-- 出荷予定日から7日以上経過した重複チェック用注文データの削除
DELETE FROM processed_orders WHERE shipping_date < DATE('now', '-7 days');

⚠️ トラブルシューティング

1. Google Sheets API 認証エラー (invalid_grant: account not found)

本番サーバーのログにおいて、以下のようにスプレッドシート接続処理時に invalid_grant が発生した場合、Google Cloud Console 側で接続用サービスアカウントが無効化されています。

ERROR - 在庫情報取得エラー: ('invalid_grant: Invalid grant: account not found', ...)
  • 原因: config/credentials.json 内に記載されている client_email (サービスアカウント) が無効、もしくは削除されています。
  • 対策:
    1. GCP管理画面よりサービスアカウントを再作成、または有効なJSONキーを発行します。
    2. 発行したJSONキーファイルで /var/www/inventory-sync/config/credentials.json を上書き差し替えします。
    3. 新しいサービスアカウントのメールアドレスに対し、対象スプレッドシートの「共有」設定でアクセス権限(閲覧・編集)を再度付与します。