背景
EC やら POS やらの検証で、商品マスタのダミーデータが要ることがあります。
商品コード、商品名、価格、在庫、そして JAN コード。
私はこれを毎回その場で雑に作っていました(効率は悪いです…)。
さすがに面倒になったのでツールにしたのですが、
JAN コードのところで一度手が止まったので、そこだけ書きます。
JAN コードというのは、日本の商品によく付いている 13 桁のバーコード番号です。
国際的には EAN-13 と呼ばれていて、構造は同じもの。
4901777018686 みたいなやつですね。
やりがちな作り方と、その問題
最初に考えたのは「日本っぽく 49 で始めて、残りをランダムにする」です。
// これはやめたほうがいい
var d12 = '49' + Array.from({length: 10}, () => Math.floor(Math.random() * 10)).join('');
これ、動くには動きます。
チェックディジットさえ合わせればバーコードリーダーも通ります。
問題は実在の商品と番号がぶつかりうることです。
JAN コードの先頭は GS1 プレフィックスといって、国・地域ごとに割り当てられています。
日本は 45 と 49。
そして GS1 Japan は、この帯の中の番号を実在の事業者に払い出しています。
つまり 49 始まりの 13 桁をランダムに作るというのは、
すでに誰かの商品に付いている番号を引く可能性があるということです。
テストデータのつもりで作った番号が、そのまま自社マスタに残る。
在庫連携や外部サービスとの突き合わせを始めた段階で、他社商品として引っかかる。
すぐには壊れないぶん、あとで気づくやつです。
20-29 を使う
GS1 は 20〜29 で始まる帯を社内・店内での利用向けに留保しています。
事業者に払い出されることがない帯です。
いわゆるインストアコードで、スーパーの量り売り商品に貼ってあるラベルなどがこれ。
ここから作れば、実在商品と衝突しません。
なので、こう変えました。
// 先頭2桁を 20-29 に固定する。rnd() は 0以上1未満を返す関数(シード固定のPRNG)
var head = '2' + String(Math.floor(rnd() * 10));
var body = '';
for (var k = 0; k < 10; k++) body += String(Math.floor(rnd() * 10));
var d12 = head + body; // ここまでで12桁
var jan = d12 + ean13CheckDigit(d12); // 13桁目を計算して足す
チェックディジットの計算
13 桁目は、前の 12 桁から機械的に決まる検査用の数字です。
左から数えて奇数桁を 1 倍、偶数桁を 3 倍して合計する。
その合計を 10 の倍数に切り上げるのに足りない数が、チェックディジット。
function ean13CheckDigit(d12) {
var sum = 0;
for (var i = 0; i < 12; i++) {
sum += Number(d12[i]) * (i % 2 === 0 ? 1 : 3);
}
return String((10 - sum % 10) % 10);
}
最後の % 10 を忘れると、合計がちょうど 10 の倍数だったときに 10 という 2 桁が返ります。
私は一度これを踏みました…。
検算しておきます。
> ean13CheckDigit('490177701868')
'6'
4901777018686 になって、実在の商品の番号と一致します。
計算式が合っていることはこれで確認できました(この番号自体は検算用に使っただけで、生成には 49 帯を使いません)。
発売日を未来にしない
もう1つ、地味にハマった所。
最初は発売日を「2024〜2026 年のランダムな日」にしていました。
そうしたら 2026-09-22 みたいな、今日より後の日付が混ざりました。
商品マスタとして眺めるぶんには気づきにくいのですが、
「発売済みの商品を一覧する」みたいなクエリのテストに使うと、件数が合わなくなります。
なので基準日から過去 1000 日の範囲で引くようにしました。
// opts.today を渡さなければ実行時の今日が基準になる
var base = opts.today ? new Date(opts.today + 'T00:00:00Z') : new Date();
var d = new Date(base.getTime() - Math.floor(rnd() * 1000) * 86400000);
出力の例
シードを固定してあるので、同じシードなら何度実行しても同じ行が出ます。
sku,name,category,jan,price,tax_rate,price_incl_tax,stock,released_on
AP-4869-F,サンプル USB充電器 20W,家電,2809184568300,3160,0.1,3476,195,2024-10-18
ST-3105-O,ハシト ノート A5 40枚,文具,2679405083819,900,0.1,990,374,2025-01-24
HH-4055-U,テスト ボックスティッシュ 5箱,日用品,2531480240610,290,0.1,319,361,2024-08-09
JAN が全部 2 始まりになっているのが分かると思います。
テストの期待値をコードに書きたいときに、毎回データが変わると比較にならないので、
シード固定は最初から入れておくのがよさそうでした。
置いてあるのはここです。
テスト用の JAN が要るときは 49 ではなく 2 から作る。
これだけ覚えて帰ってもらえれば十分です。
本記事はAI補助で執筆した、個人開発の紹介記事です。