街の本屋さんのアナログ伝票管理をデジタル化した話

この記事は データラーニングギルドアドベントカレンダー 4日目の記事です。

データ利活用をテーマに一つ四方山話を書こうと思います。

本記事の雑な3行まとめ

  • 街の本屋さんのアナログ伝票管理をデジタイズするお手伝いをしました
  • 顧客の課題はなんだろうって話
  • GASでシステム開発する時のノウハウの話

背景

ある時、とある街の本屋さんでIT周りのお手伝いをさせてもらう機会をいただいたのですが、その一環として、全店舗の売上管理デジタル化に着手することになりました。


それまでその書店では、全て店舗ごとの売上は紙媒体で管理しておりました。その紙ベースの売上伝票を見ながら、経理部や社長が集計用のエクセルに手書きで打ち込んでいくという気の遠くなるようなオペレーションをしていたのです。


さらに、そのエクセルファイルはローカルファイルです。誰かに受け渡す際、コピーが発生してしまい、(重要な経営資料が)多重管理に陥るという状況でした。マスターデータの多重管理はあまり良い結果を生みません。

プロジェクトのはじまりと課題設定

まず何が問題なのかを明確にしました。上記のような背景情報をいただいた段階では、「自動で集計結果を出してほしい」という要望があるだけです。顧客から上がるのはソリューション案なので、それをそのまま実現するのではなく、そのソリューションによって解決しようとしている問題を明らかにすることが重要です。そして、それがそもそも問題であるかどうかも見極めて行く必要があります。


この段階では、何が問題なのかを明らかにするために話を聞き、議論を進めました。結果として、以下のような課題が浮き彫りになりました。


売上データを入力する行為が2重に行われている

  • 工程として無駄である
  • ヒューマンエラーが発生する量も2倍になる


離れた場所にいる者同士で同じデータを共有、編集できる仕組みがない

  • 正確性を担保すべきデータが多重管理されてしまい、エラーを発生させる要因になっている
  • 各店舗でレジ締め(夜10時以降)作業後、紙の伝票を経理部社員の元に物理的に集めて、はじめて集計作業にとりかかれるため、リードタイムが無駄に長い。売上管理のズレに気づくタイミングが遅くなり、諸々の対応が遅れてしまう。

顧客の事情に寄り添いながらソリューション提案

さて、上記の課題をみると、私のようなエンジニア風情の者からすると、「クラウド売上管理サービス導入すれば終わりじゃね?」って考えてしまうのですが、そういう訳にも行かない事情もあるようです。


例えば以下のような事情がありました。

  • 本屋特有の収入パターンがある(地域の教職員互助会の助成券など)ため、一般的な会計ソフトで賄えない領域がある
  • ITに詳しい社員が少なく、新たな枠組みを導入したときの教育コストが高くつくし、教育できる人材もいない
  • そもそも新しいシステム導入ができるほど潤沢な投資予算がない
  • など…


お金がないからシステム開発を委託することも当然難しいです。低コストでなんとか実現する必要がありました。


そんな中、どうやら「エクセルなら使える」ということもわかりました。

Google Spreadsheet + GAS で売上管理システムを作る

そこで、私が提案したのが Google Spreadsheet + GAS による売上管理システムの構築です。データインフラやプログラムのランタイム環境を完全にGoogleさんに頼った構成です。各店舗用のGoogleアカウントはすでに作っているということだったので、その点は幸いだったかなと思います。


この構成だと以下のメリットがあります。

  • Google Workspaceのインフラを存分に活用することで、フルスクラッチシステム開発に比べて開発コストが抑えられる
  • 運用コストも安い
  • 店舗の社員は、Google Driveでのファイル共有などは少し利用しているとのことなので、新システムに対する心理的障壁が低い(と期待できる)
  • 売上伝票の入力インターフェイスとして(エクセルライクに扱える)Spreadsheetを利用できるため、新システムに対する教育コストが抑えられる
  • 私の以前のGAS開発経験(※素人に毛が生えた程度)を活かせる


まぁ、こんなとこでしょうか。


あとは個人的なものとして、 GAS 開発でいろんなことの自動化は前々から興味があったので、「自分が楽しいと思える」というものもあります。 楽しめることは重要です。


GAS開発環境の整備

さて、ここからは少しだけ技術寄りの話をしたいと思います。


この章で言いたいことは一つだけです。「数年前に比べてGAS開発環境は圧倒的に良くなった」ということです。

GAS の基本

基本的なことは以下の参考サイトを読めばわかるでしょうが、簡単に説明しますと、

GASは V8エンジンをサポートする JavaScript ランタイム環境です。最もシンプルなコーディング方法は、 Google Apps Script のプロジェクト画面のWebエディタを使う、というものです。

workspace.google.co.jp

developers.google.com

script.google.com

clasp + TypeScript こそ至高

さて、本格的にコーディングする場合は、好みのエディタを使いたいものですし、バージョン管理もしたい、Git Repository ホスティングサービスも使いたいです。そんな要望に応えるのがGoogle 社謹製 GAS 開発支援CLIツール clasp です。コマンド1発でGASプロジェクト上にコードをデプロイすることができます。

github.com

(本格的に開発したい場合は)clasp のない GAS 開発なんて有り得ないと、そう思います。そして、この clasp ちゃんですが、実は TypeScript のトランスパイル機能を内包しています。

github.com


使い方はこちら

developers.google.com

DOAシステム開発では TypeScript が有り難い

DOA という言葉をご存知でしょうか?

Data Oriented Approach の略です。日本語で言えば データ指向なアプローチ という意味になるでしょう。

私の尊敬するミック大先生の言葉を引用しておきましょう。

近年のソフトウェア開発では、データ中心アプローチ(Data Oriented Approach : DOA)という考え方が主流です。これは、文字通りシステムを作る際に、プログラムよりも前にデータの設計から始める方法論です。スローガン的に言えば「最初にデータありき」です。

『達人に学ぶDB設計 徹底指南書』(ミック)

ということで、システム開発の重要なポイントの一つはなんと言っても、 データ設計 です。


データのモデリングをしっかり実装しようとすると、クラスの継承、インターフェイスや型というものが欲しくなります。そういった事由から、今回 clasp + TypeScript という選択をしました。


開発の中で学んだTIPSとか

さて、最後は本開発で自分なりに学んだことを整理する場所とさせてください。ただの備忘録です。今回の案件でワークした、というだけで他のシーンでは適用できないノウハウという可能性もありますが、そこは良しとしましょう。


スプシでは人のためのUIとプログラムのためのInterfaceを定義する

今回、売上伝票の入力UIとしてSpreadsheetを利用した訳ですが、少し工夫することでより安全に開発を進められるなと感じました。それが、UIとデータの分離です。


具体的には、売上伝票で店員の方が実際に入力するセル群とDatabaseに取り込む(すなわち、Scriptで読み取る)セル群を明確に分けるということです。

UIのみ変更する場合
UIのみの変更

※もちろん、データベースの読み取る領域には保護をかけて、通常のユーザーが入力できないようにしています。


こうすることで、 UIの変更とデータ仕様の変更を分離する ことができました。


通常のWebアプリケーションと違って、SpreadsheetをUIとして使う場合は、セルの位置がそのままオブジェクトのIDになります。したがって、Scriptから読み取るセルはあまり頻繁に変更してほしくありません。一方で、ユーザーからのUI変更の要望は頻繁に上がります。UIの変更とデータ仕様の変更を分離する ことにより、これらの要件を満たせる様になった訳です。


まぁ、ソフトウェア開発では間に緩衝材を入れることでシステムの柔軟性を高める手法はあるあるですよね。例えば、BFF ( Backend for frontends ) は、リソース指向な世界とUIコンポーネント指向な世界を橋渡しするための緩衝材みたいな役割を果たしています。


スタンドアロンGAS + テンプレートスプシ + 定まったフォルダ構造」 が保守性高く開発しやすい

Spreadsheetに紐付いたスクリプトは辛い事が多い

GASにおけるスクリプトファイルの作り方はいくつかあります。


一つはSpreadsheet、FormやDocumentといった Google形式のファイルに紐づくマクロとして作成するパターン です。VBAみたいな雰囲気ですね。


もう一つは、Google Apps Script のダッシュボードから単独のスクリプトファイルを作成するパターンです。後者は通常 スタンドアロンGoogle Apps Script」 と呼ばれるようです。


ちゃんとしたシステムを作るなら、 スタンドアロンGoogle Apps Scriptを利用することを推奨します。 ファイル(今回はSpreadsheet)に紐付いたスクリプトを使うことは、以下の理由によりアンチパターンになると感じました。


  • スプシをコピーするとスクリプトもコピーされる
  • その結果、バックアップなどでSpreadsheetをコピーするたびに Google Apps Script ダッシュボード上に同じ中身のスクリプトが無限増殖する
  • 生データを保管するSpreadsheetは基本的にバックアップを取るため、実行されるプログラムが一箇所にまとまらない。プログラムファイルの多重管理につながる。開発ノウハウとしては論外。


ということで、ファイルに紐付いたスクリプトを使うのは、アドホックユースケースに限ったものであるべきだと感じました。私見ですが。

テンプレートのスプシを用意しておくと楽

Google WorkspaceにおけるSpreadsheetは、プログラムの一部と考えるべきでしょう。したがって、管理すべきSpreadsheetは一つの目的に対して、1ファイルだけにしておきたいです。


Google Apps Scriptには Spreadsheet や Drive を操作するためのAPIラッパーライブラリが実装されています。このライブラリ群が超優秀でして、ファイルのコピー、移動や権限設定などが超簡単に実装できます。テンプレートを元にその日のファイルを新規作成する、といった処理も簡単に実現できます。


以上のことから、テンプレートのスプシを作っておき、日毎・ユーザー毎にファイル生成するアプローチが良い開発体験を実現してくれます。

フォルダ構造の例

さて、ファイル操作は以下のような形になるでしょう。

  • テンプレートファイル: File ID でアクセス
  • 都度作成するファイル:フォルダ情報(名前 or ID)とファイル名でアクセス


ちなみに、フォルダやファイルはIDが使える状況なら積極的にIDを使うべきでしょう。フォルダ名やファイル名の変更に頑健だからです。

今回は、こんな感じにしてみました。

- SERVICE_ROOT/  <-- IDでアクセス
  - template/  <-- IDでアクセス
    - XXX-template  <-- IDでアクセス
    - YYY-template  <-- IDでアクセス
  - backup/  <-- IDでアクセス
  - database/  <-- IDでアクセス
  - report/  <-- IDでアクセス
  - YYYY-MM-DD_1_AAA  <-- 名前でアクセス(都度生成されるファイル)
  - YYYY-MM-DD_2_AAA  <-- 名前でアクセス(都度生成されるファイル)
  - YYYY-MM-DD_3_AAA  <-- 名前でアクセス(都度生成されるファイル)

おわりに

明日は、バンコクで活躍中の @DaikichiDaze さんの『社内DXで苦しんでいる話』です。乞うご期待。