factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
Barefoot Inc Malaysia の販売実績ダッシュボード(店舗/オンライン別・週次/月次・未入金台帳)
Translation pending
DataHTML · PythonGitHub @KeisukeKirbyOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Barefoot Inc Malaysia(Johor Bahru 店舗 / Shopee / Lazada / 自社直販)の販売実績を 1枚の静的 HTML にまとめたダッシュボード。ビルド成果物は index.html 単体で、 外部ライブラリ・CDN・API に一切依存しない。 チャートは手書きの inline SVG。ビルドに Node も npm も不要で、必要なのは Python 3 だけ。 管理画面から Orders をエクスポートした xlsx を用意して、上から順に実行する。 エクスポートは差分で出てくることがある。 2026-08-28 のエクスポートは INV-71 以降だけを 含んでいた。過去分のファイルもあわせて渡すこと。aggregate.py が order_id で統合し、 同じ注文が複数ファイルにあれば後ろのファイルを採用する。 元の xlsx は移動・削除されることがある(9/14 分は Downloads から消え、ごみ箱から取り出した)。 aggregate.py は実行のたびに統合済みの生データを raw_orders.json に保存するので、次回からは 過去分の xlsx の代わりにこれを先頭に渡せばよい。Read the full README ↗
Barefoot Inc Malaysia(Johor Bahru 店舗 / Shopee / Lazada / 自社直販)の販売実績を
1枚の静的 HTML にまとめたダッシュボード。ビルド成果物は index.html 単体で、
外部ライブラリ・CDN・API に一切依存しない。
初回データ: 2026-07-17(開業日)〜 2026-08-21
| ファイル | 役割 |
|---|---|
index.html | ビルド成果物。これ単体で動く。Vercel が配信するのもこれ |
dashboard.html | テンプレート(HTML / CSS / チャート描画)。__PAYLOAD__ にデータが差し込まれる |
payload.json | 集計済みデータ。テンプレートに埋め込まれる |
xlsxread.py | xlsx リーダー(zipfile + xml の標準ライブラリのみ。pandas 不要) |
aggregate.py | 明細行の読み込み・商品分類・注文単位への集約 → agg.json |
build_payload.py | 期間定義に沿った集計 → payload.json |
audit.py | 各集計が全注文を拾えているかの検算 |
parse_sheet.py / reconcile_regions.py / build_regions.py | 販売記録シートとの突合 → regions.json |
build.py | テンプレート + payload → index.html |
チャートは手書きの inline SVG。ビルドに Node も npm も不要で、必要なのは Python 3 だけ。
管理画面から Orders をエクスポートした xlsx を用意して、上から順に実行する。
実績を更新するときは、購入者の地域も毎回やる。 2026-10-19 に Keisuke が指示。エクスポートを取り込んだら、そのまま 販売記録シートを読み直して
regions.jsonまで更新すること。 忘れてもbuild_payload.pyが「地域がありません」と警告を出す。
python aggregate.py "<Orders.xlsx>" ["<追加のOrders.xlsx>" ...] && python build_payload.py && python build.py
エクスポートは差分で出てくることがある。 2026-08-28 のエクスポートは INV-71 以降だけを
含んでいた。過去分のファイルもあわせて渡すこと。aggregate.py が order_id で統合し、
同じ注文が複数ファイルにあれば後ろのファイルを採用する。
元の xlsx は移動・削除されることがある(9/14 分は Downloads から消え、ごみ箱から取り出した)。
aggregate.py は実行のたびに統合済みの生データを raw_orders.json に保存するので、次回からは
過去分の xlsx の代わりにこれを先頭に渡せばよい。
python aggregate.py raw_orders.json "<新しいOrders.xlsx>" && python build_payload.py && python build.py
raw_orders.json は顧客名・電話・住所を含む。.gitignore 済みで、リポジトリは public なので
絶対にコミットしないこと。
書式が外れたセルは日付が Excel のシリアル値(46261.5090 など)で降ってくるため、
fix_date() で文字列に戻している。起点は Excel 標準の 1899-12-30。
order_id 103-110 の10行を実データと突き合わせて日時とも一致を確認済み(2026-08-31)。
起点を1日ずらすと日付が全て翌日になるので、変えないこと。
同じ注文が複数ファイルに入っていても order_id で1件に寄せるため、同じファイルを
何度渡しても二重計上にはならない。
環境変数 ORDERS_XLSX でも指定できる(複数は ; 区切り)。
build_payload.py は卸売を含む payload.json と、卸売を除いた payload_retail.json の2つを
書き出す(後者は同じスクリプトを EXCLUDE_WH=1 で自動的に再実行して作る)。画面の各セクションの
見出し下にある「卸売を除く」チェックは、そのセクションだけ後者のデータで描き直す。チェック状態は
ブラウザ(localStorage)に保存される。客単価はチェックに関係なく常に卸売を除いた値。
index.html を commit して push すれば Vercel が自動で再デプロイする。
各集計が全注文を拾えているかは audit.py で検算する。日次・週別・月別・チャネル・曜日・
決済・地域・州・モデル・サイズ・カラー・明細の合計を、売上の 純売上/注文数/点数 と突き合わせ、
ズレがあれば内容を出して exit 1 する。ビルドのたびに回す。
python build_payload.py && python audit.py && python build.py
過去に見つかった取りこぼし: モデル別に送料の行が1点として混ざっていた / OLN0001 が商品マスタに 無くカラーと数量が落ちていた / モデル別のグラフと表に卸売の列が無かった。
モデル・カラー・サイズ別の実績を Excel にまとめるときは build_sku_xlsx.py を回す。
ダッシュボードの生成には関わらない補助スクリプトで、ここだけ openpyxl を使う。
python build_sku_xlsx.py # 商品別実績.xlsx(*.xlsx は gitignore 済み)
シートは 概要(モデル別) / モデル×カラー / SKU明細 / サイズ別マトリクス の4枚。
売上の列が2本あるのは、SiteGiant のエクスポートが明細行に定価を入れ、値引きを注文合計側でしか 引いていないため。明細をそのまま足すと純売上より多くなる(2026-10-11 時点で +RM3,657.39)。 「売上(定価)」は明細どおりの額、「売上(実売)」は注文合計を明細金額の比で按分した額で、後者は 全体の純売上と一致する。ダッシュボードのモデル別売上は定価ベースのまま。
エクスポートは注文のあった日しか教えてくれないので、最終日の翻日以降が
「売上ゼロ」なのか「まだデータが無い」のか区別できない。build_payload.py の ASOF に
「この日まで確認済み」を入れると、その日までを対象期間にして日次グラフにゼロの印を出す。
データの最終日が ASOF より後ならデータ側を採るので、更新を忘れても期間が短くはならない。
環境変数 ASOF=2026-09-24 でも指定できる。
build_payload.py の WEEKS / MONTHS を編集する。開業週だけ 7/17(金)〜7/26(日) の
10日間で、以降は月曜起点の週次。週を足すときは WEEKS に1行追加するだけ。
WEEKS = [
('W1', '開業週', '2026-07-17', '2026-07-26'),
('W2', '第2週', '2026-07-27', '2026-08-02'),
...
]
order_id 134 / 161 のように、どのエクスポートにも現れない注文がある。管理画面の注文詳細から
aggregate.py の MANUAL_ORDERS に書き起こす。明細が複数ある注文は lines=[_ml(...), ...] で書く。
161 は画面に明細単価が出ていなかったため注文合計を点数で均等割りしており、モデル別の金額だけは推定値。後日エクスポートに現れたらそちらが優先
される(手動分は order_id が未登録のときだけ取り込む)ので、消さずに残しておいてよい。
元データの payment_method は表記ゆれを含めて 15 種類あるので、build_payload.py の PAYGRP で QR決済 / カード / 現金 / 銀行振込・ネットバンキング にまとめている。
画面では大分類を出し、行をクリックすると内訳が開く。未登録の決済方法は「その他・未記録」に
落ち、集計時に警告が出るので PAYGRP に足すこと。Apple Pay は店頭の非接触カード決済とみて
カードに入れている。
SiteGiant には購入者の地域が無いので、Google Drive の
Sales record for Malaysia.xlsx(店舗側が手でつけている台帳)の From 欄 を
日付・チャネル・金額・商品・時刻で突き合わせて注文に紐づけている。
実績を更新したら毎回これも回す(注文は増えるのに地域が古いままだと、
地域別から直近の売上が丸こと洩れる)。
Drive のファイルID: 1vnEY7dHckpuRsJ_5p30d-uc0HwDtHNHT
(月ごとにシートが分かれている。読むときは全シートが1本のテキストで返る)
# 1. シートの中身を sheet_raw.txt に落とす(Drive から取得)
# 2. 行に切り直す → sheet.json
SHEET_WORKDIR=<作業フォルダ> python parse_sheet.py
# 3. 取込データと突合 → region_map.json、未結合の一覧を標準出力へ
SHEET_WORKDIR=<作業フォルダ> python reconcile_regions.py
# 4. 公開用に order_id と地域だけ抜き出す → regions.json
SHEET_WORKDIR=<作業フォルダ> python build_regions.py
手順 1 の書き方は、既存の sheet_raw.txt に新しい行を差し込む。シートの末尾には
27,,,,,,,,,,,,,, のような空行が並んでいるので、そこを実データで置き換え、
最後の ,TOTAL,... 行も新しい値に直す。ファイル全体が改行なしの1行になっている
のが前提(parse_sheet.py が 15 セル単位で切り直す)。
シートの記述と SiteGiant が食い違うときは SiteGiant を正 として、シートからは From 欄(地域)だけを取る。シート側の金額はマーケットプレイスの手数料引き後だったり、 色・サイズの書き違いがあったりする。
自動で突合できない行は reconcile_regions.py の MANUAL に理由付きで書く。
「シート側の未結合 0 行」になるまでやる。
sheet_raw.txt / sheet.json / region_map.json / unmatched.json は顧客名・住所・電話を
含むので .gitignore 済み。リポジトリに入れるのは regions.json(order_id と地域のみ。
従業員・親族の名前は「関係者」にまとめて落とす)だけ。
突合は自動でつかないものだけ reconcile_regions.py の MANUAL に理由付きで書く。
マーケットプレイスはシートが手取り額、SiteGiant が注文合計なので金額が数%ずれる。
同額の注文が同じ日に並ぶと取り違えるため、モデルとサイズも点数に入れている。
地域は国籍ではない。 From 欄は店頭で聞き取った自己申告で、国籍の列はシートに無い。
オーナーが「これは実績に入れない」と判断した伝票は aggregate.py の VOID に理由付きで書く。
bucket が void になり、test と同じく全集計・未入金台帳・地域突合から外れる。
キャンセルバケツに入れないのは、失注として数えてしまうから。起こし直しやプレゼントは 「売れなかった」わけではないので、キャンセル率の分母からも外している。
現在の登録:
| order | 理由 |
|---|---|
| 205 | 10/2 の卸売 RM5,518.50。SiteGiant でキャンセル済みで、10/8 の 222 が正しい記録(2026-10-26 確認)。エクスポートではまだ Open のまま出てくる |
| 226 | RM0 の INV-195(BFJ Gray S 1足)。プレゼント(2026-10-26 確認) |
サイズ交換は「返品(数量 -1)+ 新しいサイズ(数量 +1)」の別伝票で起票される。そのままだと
注文が1件増えてしまうので、aggregate.py の FOLD_INTO に {交換伝票: 元の注文} を書いて
元の注文に明細を寄せる。金額と点数は元から差し引き後なので、寄せるとサイズも交換後が残る。
販売記録シートにあって SiteGiant に起票されていない販売は、オーナーの判断で計上することがある。
MANUAL_ORDERS に order_id='SH-<シートの出典>' で書く(SiteGiant に存在しない番号にしておくと、
あとでエクスポートに現れても衝突しない)。時刻がシートに無いものはコメントに「仮の時刻」と残す。
新しい SKU プレフィックスは aggregate.py の MODEL に、カラー略号は COLORNAME に追加する。
未登録の SKU は「その他」に落ちるだけで、集計自体は壊れない。
payment_status が Paid で、order_status が Completed / Shipped または出荷前
(Pending Process / Processed / Ready To Ship)の注文。
金額は注文合計(total = 商品合計 + 送料 − 値引き)marketplace = Barefoot Malaysia POSmarketplace 空欄)aggregate.py の WHOLESALE に注文IDを列挙する。
法人名(billing_company)が入っていて未指定の注文があると集計時に警告が出るので、
卸売ならIDを追加する。1件で通常の15倍の金額が動くため、混ぜると客単価も点数も歪むPending Process / Processed / Ready To Ship。入金済み(Paid)なら売上に数える
(2026-09-14 オーナー判断)。未入金のものだけが「処理中」に残り、注文明細の「処理中のみ」で確認する。
以前は Completed/Shipped/Returned 以外を全部キャンセルと判定していて、order 133 がキャンセル扱い
になっていた。知らないステータスが来たら集計時に警告が出るpayment_status = Unpaid。売上にもキャッシュにも計上しないproduct_total)ベース。注文単位の値引き・送料を含まないため純売上とは一致しないtest3 / test4)は全集計から除外product_cost が元データでほぼ未入力のため、粗利は算出していない。
xlsx の手動エクスポートをやめて注文を直接取得するため、SiteGiant Open API を調査した。 Administrator 権限の承認が必要なため 2026-08-24 時点では保留。 再開時のために調査結果を残す。
https://opensgapi.sitegiant.co/api/v1Access-Token ヘッダGET /orders、GET /orders/list、POST /order、PUT /order/list認証情報は2系統ある。混同しないこと。
| 入口 | 認証情報 | 用途 |
|---|---|---|
| 店舗管理画面 Settings → API | Secret Key + Store Email | AutoCount / Biztory など会計ソフト連携 |
| opensgapi.sitegiant.co(開発者アカウント) | Access-Token | Open API |
店舗側の Secret Key で Open API を叩くと全ルートが 403 "Access denied due to invalid token" を返す。
開発者アカウントの登録(/register)と、店舗の Administrator による承認が要る。
再開するときは .env に SITEGIANT_TOKEN=<Access-Token> を入れて疎通確認する。
python fetch_orders.py --probe
GET /orders が 200 を返したら、レスポンス形状に合わせて aggregate.py を繋ぎ込む。
fetch_orders.py はページングとパラメータ名の差し替えに対応済み。
このダッシュボードは注文単位の売上明細を含む。リポジトリとデプロイ先の公開設定は 社内で共有できる範囲に合わせて設定すること。
.env は .gitignore 済みでコミットされない。トークン類をコードに直書きしないこと。