ChatGPTとCodePenで送料パズルを解く！EC事業者のためのアプリ制作記

# ノーコーダーの強い味方、ChatGPT！！
今回は普段から仕事で課題に感じている件を、ChatGPTの力を借りながら身近に解決できそうなアプリを作ってみました。

https://twitter.com/tnsato2000/status/1705730900347240451

:::note info
今回使用したツール
1.CodePen
2.ChatGPT
:::
# 目次
[身近な業務課題](#身近な業務課題)
[CodePenで解決へ](#codepenで解決へ)
[ChatGPTと一から真摯に向き合う！](#chatgptと一から真摯に向き合う)
[お試しの感想](#お試しの感想)
[プロトタイプからのヒントと学び](#プロトタイプからのヒントと学び)
# 身近な業務課題
私はEC事業の商品を中心とした企画・販売・サイトメンテ・顧客対応などの管理業務に携わっています。
業務・作業は非常に多岐にわたるのですが、

代表的な重要指標数値は以下の二つがあります。
**「商品売上」**
**「商品荒利額」（商品販売売価から商品仕入れ原価を差し引いた利益額）**
当然ビジネスですので商品荒利額から経費各種を指し引いた後の、営業利益や経常利益の最大化も重要です。

そんな中、EC事業では大きな経費として発生するのが送料であり、避けて通ることもできません。

ところが私のチームでは、この送料が各商品ごとにいくら発生するのか簡易に確認する環境が整っておらず、各運送会社の送料テーブルも「発送元」「発送先」「サイズ」「重量」など4象限が絡む複雑な料金体系のためとても分かりにくいです。
<font color="Red">**なんと送料パターンは、1859パターン！！**　</summary>
あまりの複雑さに、実際のところ社内ではだれも送料を確認や計画したり、進捗管理すら行えていないのが実情です。今回は、ここにメスを入れるべくきっかけになればと思い取り組みます。

<details>
<summary>【参考】当社主要配送業社が開示している送料テーブルを加工してまとめたものはこちら　<font color="Red">折りたたんでます。</summary>

　そりゃわからんわ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/a2fa09e7-887a-1d53-0dff-f8bd2cdbae75.png)


</details>

結果、我がチームでは送料を経費としてコントロールしようという意識が薄れがちになり、「全体の売上」に対する「全体の送料」を管理するレベルに留まり、商品単位レベルで利益をコントロールできていないことを課題と感じています。

商品の売価決定時に商品荒利額だけを確認するのではなく、送料がその商品にどのくらい発生するのかを手軽にチェックできるツールを提供することで、

:::note info
**・適正売価の決定
・送料も差引いた利益管理への意識付け
・日々の業務を経営としてとらえるマネジメント力向上**
:::
メンバーと事業の成長を思い描きながらツールを作ってみました。

# CodePenで解決へ

今回は、利用するメンバーが手軽に使えるツールであることを前提としたいため、モバイルで実装できるツールとして、CodePenを選択しました。
Codepenは無料で使用でき、コードを書きながらリアルタイムで表示が確認できるので非常に使い勝手が良いです。　HTML　CSS　JS　の外部ライブラリも使用できるため超便利。
わかりやすく解説されていた記事を共有しておきます。

https://junpei-sugiyama.com/codepen/

### そんな私はノーコーダー
前述の記載では、あたかも私がコードが書けるような表現で、偉そうにCodePenをご紹介しましたが、プログラミング言語を全く書くことも読むこともできないノーコーダーです！
しかし、**「苦手は人に頼れ！」** が私のモットーで、**ChatGPT** にコードを書いてもらうことで、今回も乗り切ります！

### まずはCodePenの設計イメージ

CodePenで作りたいアプリのイメージは、
**「送料算出のための条件を入力or選択」**
**「送料の計算及びその送料経費を差し引いた商品の送料後利益を手軽に確認」**　です

そこで下記のような項目を設定とアウトプットと決めました。

:::note info
**送料算出のための設定項目**

| |設定項目| 方式 | 
|:-:|:-:|:-:|
|1|商品販売売価 | 数値入力　例：4500| 
|2|商品原価 | 数値入力　例：3000| 
|3|商品発送元| 選択式　　例：関東|
|4|商品発送先 | 選択式　　例：関西|
|5|発送サイズ・重量 | 選択式　　例：60㎝ (2kg) |
|6|お客さま送料負担の有無 | 自動計算 例：売価5000円以上は0円,未満は500円 |
:::
これらの項目を入力後に「計算ボタン」を押すと、
以下の情報が返ってくるアプリをイメージしました。

:::note info
**アウトプットする情報項目**

| |項目| 算出ロジック | 
|:-:|:-:|:-:|
|1|商品荒利額（円）| 商品販売売価　ー　商品原価| 
|2|商品荒利率（％）| 商品荒利額　÷　商品販売売価| 
|3|送料（円）| 発送先・発送元・サイズ・重量から算出|
|4|送料後利益額（円）| 商品荒利額　ー　送料　＋　お客さま送料負担|
|5|送料後利益率（％）| 送料後利益額　÷　商品販売売価 |
:::

### いよいよノーコーダー勝負の時です！

これをCodepenで実装するために、HTMLコードを書かねばなりません。
ChatGPTと向かい合います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/5a2b5cf3-9d3b-8e79-d07d-0e801c1594d8.png)

こんな無謀なやり取りから開始です。基礎知識の無さに落ち込みます。

その後、数時間やり取りを行い修正・改善を繰り返し、新しい修正を出すとそれまでのコードが先祖返りしたり繰り返し、途中は喧嘩状態です（笑）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/8ee83ed1-c0fe-2277-db99-4c0138153d19.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/5c6bd4fb-e466-78a7-3f7e-5ba4ba7d42aa.png)


四苦八苦の末、ようやく下記のように一旦機能するアウトプットにはたどり着きました。
（項目名称などが違うのは後に修正します。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/1fb9ee59-3f22-ac4f-29e4-ca29263c388a.png)


<details>
<summary>コードはこちら。　<font color="Red">折りたたんでます。</summary>

```js
<!DOCTYPE html>
<html>
<head>
    <title>送料計算アプリ</title>
    <style>
        /* CSSスタイルを追加 */
    </style>
</head>
<body>
    <h1>送料計算アプリ</h1>
    <form id="calculator-form">
        <label for="product-price">商品売価:</label>
        <input type="number" id="product-price" required>

        <label for="product-cost">商品原価:</label>
        <input type="number" id="product-cost" required>

        <label for="package-size">サイズ名（重量）:</label>
        <select id="package-size" required>
            <option value="60cm（2kg）">60cm（2kg）</option>
            <option value="80cm（5kg）">80cm（5kg）</option>
            <option value="100cm（10kg）">100cm（10kg）</option>
            <option value="140cm（20kg）">140cm（20kg）</option>
            <option value="160cm（30kg）">160cm（30kg）</option>
            <option value="170cm（※）">170cm（※）</option>
            <option value="180cm（※）">180cm（※）</option>
            <option value="200cm（※）">200cm（※）</option>
            <option value="220cm（※）">220cm（※）</option>
            <option value="240cm（※）">240cm（※）</option>
            <option value="260cm（※）">260cm（※）</option>
        </select>

        <label for="shipping-cost">送料:</label>
        <input type="text" id="shipping-cost" readonly>

        <button type="button" id="calculate-button">計算</button>
    </form>

    <div id="results">
        <h2>計算結果</h2>
        <p id="shipping-refund"></p>
        <p>商品利益額: <span id="profit-amount"></span></p>
        <p>利益率: <span id="profit-margin"></span>%</p>
        <p>送料支払後の一次利益額: <span id="net-profit-amount"></span></p>
        <p>一次利益率: <span id="net-profit-margin"></span>%</p>
    </div>

    <script>
        // JavaScriptコードを追加
        document.getElementById('calculate-button').addEventListener('click', function () {
            // フォームの値を取得
            const productPrice = parseFloat(document.getElementById('product-price').value);
            const productCost = parseFloat(document.getElementById('product-cost').value);
            const packageSize = document.getElementById('package-size').value;

            // 送料データをオブジェクトで定義
            const shippingRates = {
                '60cm（2kg）': 970,
                '80cm（5kg）': 1280,
                '100cm（10kg）': 1610,
                '140cm（20kg）': 2100,
                '160cm（30kg）': 2340,
                '170cm（※）': 3360,
                '180cm（※）': 3660,
                '200cm（※）': 4480,
                '220cm（※）': 5240,
                '240cm（※）': 6830,
                '260cm（※）': 8420
            };

            // 選択されたサイズに対応する送料を取得
            const selectedShippingCost = shippingRates[packageSize];

            // 商品売価が5000円未満の場合
            if (productPrice < 5000) {
                // 送料▲500を表示
                document.getElementById('shipping-refund').textContent = '送料▲500';
                // 商品利益額に送料▲500を加算
                const profitAmount = productPrice - productCost + 500;
                // 利益率を計算
                const profitMargin = (profitAmount / productPrice) * 100;
                // 一次利益額を計算
                const netProfitAmount = profitAmount - selectedShippingCost;
                // 一次利益率を計算
                const netProfitMargin = (netProfitAmount / productPrice) * 100;

                // 計算結果を表示
                document.getElementById('profit-amount').textContent = profitAmount.toFixed(2);
                document.getElementById('profit-margin').textContent = profitMargin.toFixed(2);
                document.getElementById('net-profit-amount').textContent = netProfitAmount.toFixed(2);
                document.getElementById('net-profit-margin').textContent = netProfitMargin.toFixed(2);

                // 送料を表示
                document.getElementById('shipping-cost').value = selectedShippingCost + '円';
            } else {
                // 商品売価が5000円以上の場合、送料▲500は表示しない
                document.getElementById('shipping-refund').textContent = '';

                // 商品利益額を計算
                const profitAmount = productPrice - productCost;
                // 利益率を計算
                const profitMargin = (profitAmount / productPrice) * 100;
                // 一次利益額を計算
                const netProfitAmount = profitAmount - selectedShippingCost;
                // 一次利益率を計算
                const netProfitMargin = (netProfitAmount / productPrice) * 100;

                // 計算結果を表示
                document.getElementById('profit-amount').textContent = profitAmount.toFixed(2);
                document.getElementById('profit-margin').textContent = profitMargin.toFixed(2);
                document.getElementById('net-profit-amount').textContent = netProfitAmount.toFixed(2);
                document.getElementById('net-profit-margin').textContent = netProfitMargin.toFixed(2);

                // 送料を表示
                document.getElementById('shipping-cost').value = selectedShippingCost + '円';
            }
        });
    </script>
</body>
</html>
```
</details>

ここまでのChatGPTとのやり取りやプロンプトがあまりにも乱雑で、五月雨式に多く右往左往したため、共有できる状態になくすいません。
**そして、この現状と経緯が十分なアウトプットを満たせない要因として、気づき反省です。**

# ChatGPTと一から真摯に向き合う！
さきほどのアウトプットでは、正しく送料テーブルを読み込めていません。
あれこれChatGPTと格闘するも、送料テーブルは完全には読み込むことはできず、送料算出条件となる「発送元」「発送先」のコードが書けず、項目もありません。

:::note info
**反映できない理由**
**開示されている送料テーブルをそのままメッセージに貼っても、ChatGPTは理解できない**
**送料パターン1859ある情報量は、ChatGPTでは文字数オーバーのためメッセージに入りきらない。**
:::

よって、下記の対策を行うことにしました。

:::note info
**対策1.発送元を「関東」のみに設定**
当社の配送実績では約9割が関東から発送しているため精度に影響は少と判断しました。こうすることで、送料パターンは1859から143となり情報量を削減しました。
:::

:::note info
**対策2.シンプルで簡素化したプロンプトを作成し、事前準備して最初からやり直す**
ここまでは、無謀なリクエストから始まり何度も修正・追加を行い原型がわからないやり取りを繰り返したため、もうよくわかりません。
ここまでのやり取りを整理して、設定条件・アウトプットイメージをより明確な「プロンプト」をcsvファイルやメモ帳を活用し準備して、ChatGPTに指示することにしました。
:::

この気づきと対策をもとに作成した新たなプロンプトはこちら。

```js
codepenで送料計算アプリを作成します。
仕様は以下の通りでHTMLコードを書いてください。

①商品売価　"数値入力"
②商品原価　"数値入力"
③発送元　　"プルダウン"
④発送先　　"プルダウン"　
⑤サイズ名（重量）"プルダウン"
⑥お客さま送料負担　"商品売価が5000円未満の場合は500円　5000円以上は0円　を自動表示"
⑦計算ボタンの設置

①～⑥入力orプルダウン後、計算ボタンクリックで以下の計算を返す。・

・商品荒利額(円)　①-②
・商品荒利率(％)　(①-②)÷①
・お客さま送料負担　
・送料　③,④,⑤の条件より下記のテーブルから計算
,,発送先,発送先,発送先,発送先,発送先,発送先,発送先,発送先,発送先,発送先,発送先,発送先,発送先
発送元,サイズ名（重量）,北海道,北東北,南東北,関東,信越,東海,北陸,関西,中国,四国,北九州,南九州,沖縄
関東,60cm（2kg）,"1,340円",970円,850円,850円,850円,850円,850円,970円,"1,100円","1,220円","1,340円","1,340円","1,914円"
関東,80cm（5kg）,"1,650円","1,280円","1,160円","1,160円","1,160円","1,160円","1,160円","1,280円","1,400円","1,520円","1,650円","1,650円","3,520円"
関東,100cm（10kg）,"1,960円","1,610円","1,490円","1,490円","1,490円","1,490円","1,490円","1,610円","1,720円","1,840円","1,960円","1,960円","4,686円"
関東,140cm（20kg）,"2,460円","2,100円","1,980円","1,980円","1,980円","1,980円","1,980円","2,100円","2,220円","2,340円","2,460円","2,460円","7,579円"
関東,160cm（30kg）,"2,690円","2,340円","2,220円","2,220円","2,220円","2,220円","2,220円","2,340円","2,450円","2,570円","2,690円","2,690円","10,560円"
関東,170cm（50kg）,"4,240円","3,660円","3,420円","2,600円","3,360円","3,360円","3,360円","3,360円","3,660円","3,660円","3,890円","4,240円","15,400円"
関東,180cm（50kg）,"4,780円","4,010円","3,770円","2,890円","3,660円","3,660円","3,660円","3,660円","4,010円","4,010円","4,300円","4,730円","17,820円"
関東,200cm（50kg）,"5,890円","4,890円","4,600円","3,480円","4,480円","4,480円","4,480円","4,480円","4,890円","4,890円","5,360円","5,890円","22,660円"
関東,220cm（50kg）,"7,480円","5,770円","5,420円","4,070円","5,240円","5,240円","5,240円","5,240円","5,830円","5,830円","6,360円","7,010円","27,500円"
関東,240cm（50kg）,"9,360円","7,600円","7,070円","5,240円","6,830円","6,830円","6,830円","6,830円","7,600円","7,600円","8,360円","9,300円","37,180円"
関東,260cm（50kg）,"11,720円","9,420円","8,710円","6,420円","8,420円","8,420円","8,420円","8,420円","9,420円","9,420円","10,360円","11,600円","46,860円"

・送料後利益額　①-②-送料+お客さま送料負担
・送料後利益率　送料後利益額÷①
```

### ようやく送料テーブルを読み込めた
4象限のテーブルをChatGPTに理解させるにはかなり工夫が必要ですね。
Webサイトに貼ってある料金テーブルをテキストとしてエクセルに貼付け、CSVへ変換しメモ帳でプロンプトに、という工程が必要でした。小さな学びの連続です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/d29c73f3-adf5-9079-d753-15dc1a500849.png)
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="bGOaqGq" data-user="uetyutnr-the-lessful" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/uetyutnr-the-lessful/pen/bGOaqGq">
  送料計算アプリ</a> by 佐藤ダミー (<a href="https://codepen.io/uetyutnr-the-lessful">@uetyutnr-the-lessful</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

**いきなり不明確なイメージをもったまま、ChatGPTとやり取りを開始すると沼化していきます。明確なイメージとChatGPTの特性を理解したプロンプトを作成することが近道であること学びました。**

# お試しの感想
さっそく商品担当2名に「今後、売価決定時に送料を確認し収支管理精度を上げるため、新作できたの使ってみて」と、紹介し使ってもらいました。

:::note info
感想は
・おお！すげぇ！　（こんなことできるの、の定番のコメントです）
・送料が1859パターンもあったことも知らなかったけど、簡単にチェックできそう
・発送元　関東しか機能してませんよね？　（するどい。。。）
・でも管理商品数は3万くらいあるので、手打ちですべて確認するこのツールは実用的ではない
:::


核心ついたご意見いただきました。やっぱり？って感じです。
特定の商品をスポット商品（セール企画や在庫処分で見切り販売したいなど）には、ふさわしいツールですが、数は捌ききれないお手軽ツールどまりです。

# プロトタイプからのヒントと学び
今回の取組はお試しと手軽さを前提とした、まさにプロトタイプでした。
しかし、このプロトタイプの作成は、真の課題解決となる「送料の見える化」と「経費管理意識の向上」を、より効果的に実務で成果に繋げていくためには、どうしたらよいのかについてメンバーと話す良いきっかけにもなりました。

そのためには、商品登録マスターのロジックにメスを入れるべきとの建設的な意見もメンバーから出ました。

:::note info
1.商品マスターに配送梱包時のサイズ・重量をすべて登録
2.発送元・発送先　別の送料テーブルに受注管理システムに登録
3.上記登録に基づき、オンタイムで商品ごとの商品荒利額・送料後利益額が自動計算
4.異常値や警告レベルの価格設定商品はアラート発報
:::

システム改修が必要なレベルとなりますが、事業における課題と捉え関連部署も巻き込みながら、実現模索していきます。

### ChatGPTとの戯れから得られた学び

**「自身が実現したい姿」「論理的思考と整理されたアウトプット」「聞き手の立場に立って考える」**　
リーダーが、これらのことを踏まえ行動に移さねば、ChatGPTも組織も路頭に迷わせるということ。

それは、相手が機械であっても人であっても原理原則・根底にあるものに相違なく、自分自身が謙虚に変革することが先であるということを学びを得ました。

### おまけ

より多くの方々に親しみもってご利用いただけるよう、ChatGPTのおかげで浮いた時間を活用しCodePenのUIを彩ってみました。
下記の記事参考に着飾ってみましたが、出来が悪いため工程の詳細は割愛し最後のおまけとしました。

https://qiita.com/dx_kumagai/items/3348b27ea4c4f47ddfc3

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3519202/0929fd4a-1fb9-4b16-ea73-67e9baae2bde.png)