アシアル情報教育研究所・所長の岡本です

みなさんんは高校や大学の文化祭・学園祭で模擬店を出されたことはありますでしょうか?

私はありません!

ちょっと憧れます。

さて、今回のアプリは模擬店で物販などを行う時に使われることを想定したレジアプリです。

商品のバーコード(やQRコード)を読み取ると、その番号に紐付いた商品の名前と金額が表示されます。また、預かったお金の金額を打ち込むことで、お釣りの計算も行えます。

模擬レジのサンプルアプリ入手方法

Monaca用のプロジェクトとして公開されています
模擬レジアプリ(ダイレクトインポート)

動作デモ

アプリの動作デモです。Web版のため、バーコードリーダー機能は動きません。

模擬レジの機能解説

フローチャートにすると、以下のようなイメージです。

基本的には、一本道のプログラムです。

『お会計』ボタンが押されるまで、ずっと、バーコードの入力を受け付けています。
バーコードの値が入力されたら、読み取った値を、配列に追記します。
そして、画面に配列の内容を反映させます。

コード読み取りの動作

アプリ右下のコードっぽいボタンを押すと、関数scan()が呼び出され、コード読み取りが実行されます。コード読み取りは、関数scan()で行っています。

scan()関数の中では、まず、実行環境が『アプリ』かそれ以外かを確認しています。コードを読み取るためには、アプリとして動作させる必要があるため、アプリの場合のみ、コードを読み取る『cordova.plugins.barcodeScanner.scan()』命令を実行しています。

※ cordovaというのは、アプリでOSのカメラやコンパスなどの機能を呼び出すための仕組みです。

プレビューでの動作

バーコードリーダー機能はアプリ限定機能のため、プレビューで動作差せるときには、コードを手入力するプログラムになっています。具体的には、promptInt()という関数で数値の入力欄をポップアップ表示しています。

同じ商品を2回以上、読み込んだ場合

このアプリでは個数の概念がないため、同じ商品を2回以上取り込んでも、それぞれ別の商品として扱われます。

もし、個数の概念を持たせたい場合は、配列に商品番号と個数をペアで記録できるように改造する方法などが考えられます。

バーコードリーダー機能を試すためのサンプルQRコード(抜粋)

今回のサンプルアプリでは、動作確認用に幾つかの商品が設定されています。

以下に、『商品番号1001:リンゴ』と『商品番号1002:サクランボ』のQRコードを例示します。Monaca for Studyアプリ(デバッガーアプリ)で模擬レジアプリを動かして、試しに、読み取ってみましょう。

商品番号:1001
商品名:リンゴ

商品番号:1002
商品名:サクランボ

カートの中の商品一覧の表示

商品をカートに入る際、プログラムの内部では変数cartの配列の中に商品情報を追加しています。
しかし、配列の中に商品情報を追加するだけではアプリの画面は変わりません。

そこで、配列の内容を元に商品一覧表示を書き換えるためにestimate()関数を呼び出します。

estimate()関数は、forの繰り返し文を使って変数cartの商品を画面に書き出しています。

table要素をDOMで変更する

商品一覧は『table要素』で表示しているため、for文の中では表のレコードを表す『tr要素』を作成し、追記を行っています。

なお、コードを読み取る度に追記してしまうと、実際のcart変数の商品数よりも多く、商品が表示されてしまうため、estimate()関数では、forの繰り返しを行う前に、table要素の中のレコードを初期化してから追記するようにしています。

お会計

お会計ボタンをが押されると関数pay()が呼び出されます。
プロンプトで金額の入力を求め、合計金額との差額を計算した上でお釣りの表示を行います。

HTML側のソースコード解説

HTML側は大きく3つの要素に分かれています。

  • header要素:お会計ボタンを表示
  • footer要素:合計金額を表示
  • table要素:商品一覧を表示
  • コード読み取りボタン:押されたらコードを読み取る

<body>
    <header>
        <h1 onclick="reset()">模擬レジ</h1>
        <button type="button" onclick="pay()">お会計</button>
    </header>
    <table id="itemList">
        <thead>
            <tr>
                <th>商品</th>
                <th>金額</th>
            </tr>
        </thead>
        <tbody id="itemListBody"></tbody>
    </table>
    <div id="barcode" onclick="scan()">
        <img src="img/iconCode.svg" alt="コード読み取りボタン">
    </div>
    <footer>
        <div id="title">合計金額</div>
        <div id="totalPrice">0</div>
    </footer>
</body>

header要素

まず、アプリの名前として『模擬レジ』の名前をh1要素で定義しています。また、クリックされたらreset()関数を呼ぶようになっています。
そして、右上にボタン要素として『お会計』を表示しています。押されたらpay()関数が実行されます。

footer要素

div要素で『合計金額』という文字列と合計金額『0』を定義しています。0の要素はid名を定義しているため(totalPrice)、JavaScriptで計算した金額を反映できます。

JavaScript側のソースコード解説

全体でグローバルに共有している変数として『cart』と『totalPrice』変数があります。

cart変数にはかごに入れた商品の番号が配列形式で格納されます。また、totalPrice変数は、かごに入った商品の合計金額が反映される変数です。

また、両変数に格納された値を画面に反映する関数があり、それによって、画面に最新のカートの情報や合計金額を表示しています。


/**
 * スクリプト全体で共有する変数を定義
 */
let cart = [];
let totalPrice = 0

/**
 * 購入商品の配列を元に画面を描画する関数
 */
function estimate() {
    totalPrice = 0;
    let table = document.getElementById("itemListBody");
    table.innerHTML = "";
    for (let i = 0; i < cart.length; i++) {
        // 商品マスターの情報を元に合計金額を計算
        let item = itemMaster[cart[i]];
        totalPrice += item.price;
        // 商品情報を表に反映
        let tr = document.createElement("tr");
        tr.innerHTML = "<td>"
                     + item.name 
                     + '<span class="delete"  onclick="cartDelete(' 
                     +  i + ')"></span>' 
                     + "</td>" 
                     + "<td>" + item.price + "</td>";
        table.appendChild(tr);
    }
    document.getElementById("totalPrice").innerText = totalPrice.toLocaleString();
    console.log(cart);
}
/**
 * カートから指定された商品を削除して画面を描画する関数を呼ぶ関数
 */
function cartDelete(key) {
    cart.splice(key,1);
    estimate();
}

/**
 * お会計を実行する関数
 */
function pay() {
    payment = promptInt("お支払い金額を入力してください");
    let change = payment - totalPrice;

    if (change == 0) {
        alert("お釣りはありません");
    } else if (change > 0) {
        alert("お釣りは" + change + "円です");
    } else {
        alert("お預かり金額が" + Math.abs(change) + "円不足です");
    } 
}

/**
 * カートを空にして画面を書き換える関数
 */
function reset() {
    if(confirm("リセットしますか?")){
        cart = [];
        totalPrice = 0;
        estimate();
    }
}

/**
 * コードを読み取るための関数の定義
 */
function onSuccess (result) {
    let str = result.text;
    let int = parseInt(str);
    cart.push(int);
    estimate();
}
function onError (error) {
    alert("スキャン失敗: " + error);
}
function scan() {
    if (typeof cordova === "undefined") {
        let int = promptInt("数値を入力してください。");
        cart.push(int);
        estimate();
        return false;
    }
    cordova.plugins.barcodeScanner.scan(
        onSuccess,
        onError,
        options
    );
}

estimate()関数

画面に商品一覧や合計金額を表示する関数です。

cartDelete()関数

指定されたキーの商品をカートから取り除きます。
具体的には、JavaScriptの配列操作関数であるsplice()命令で要素を一つ取り除いているだけです。

pay()関数

アプリの利用者に、支払金額の入力を促し、現在の合計金額との差額を表示する関数です。

reset()関数

変数cartを空にして画面を再描画する関数です。
画面の再描画といっても、実際にはestimate()関数を呼び出しているだけです。

onSuccess()関数 ※スキャン用

コードの読み取りに成功したら値をcart配列に追加してestimate()関数で画面を再描画します。

onError()関数 ※スキャン用

コードの読み取りに失敗したときに呼び出される関数です。

scan()関数

バーコードをスキャンする関数です。

cordovaが呼び出せればスキャナーによるスキャンを実施、呼び出せなければ代替処理としてプロンプトを表示し、コードを手入力させます。

※cordovaの機能を呼び出すためにはアプリである必要があり餡巣。なお、バーコードスキャナーの機能は、Monaca for Study版のデバッガーには標準で搭載済みです。

おわりに

商品一覧をページの末尾に掲載しておきます。

バーコードリーダー機能を試すためのサンプルQRコード(一覧)

商品番号:1001
商品名:リンゴ

商品番号:1002
商品名:サクランボ

商品番号:1003
商品名:梨


商品番号:1004
商品名:イチゴ


商品番号:1005
商品名:ブルーベリー


商品番号:1006
商品名:ぶどう


商品番号:1007
商品名:もも


商品番号:1008
商品名:みかん


商品番号:1009
商品名:すいか


商品番号:1010
商品名:マンゴー


商品番号:1011
商品名:メロン