h-1.flutter.0/README.md
2026-03-05 09:33:38 +09:00

237 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 販売アシスト1号 / 母艦「お局様」プロジェクト概要
販売アシスト1号は **オフライン単体で見積・納品・請求・レジ業務まで完結できる販売アシスタント** であり、オプション機能として **オンライン接続時に母艦「お局様」とデータ同期・バックアップ・監視を行う二層構造** を目指しています。
---
## コアコンセプト
| モード | 目的 | 主な特徴 |
| --- | --- | --- |
| オフライン・スタンドアロン | 端末単体で全業務を完結 | SQLite に全データ保存、印影以外は非暗号化、AI などによる再利用も想定 |
| オンライン(システムオプション) | 母艦と接続しデータ交換・監視 | SSH/クラウドトンネル経由で同期、APK寿命チェックやバックアップを遠隔制御 |
母艦「お局様」はブリッジモニタリングバックアップに専念し、実務機能は販売アシスト1号側に集約する方針です。TV BOX を母艦に据える運用や、単一端末で両役割を兼務するシナリオも想定しています。
---
## 現状の実装状況
- Flutter ベースの販売アシスト1号アプリ
- 会社・担当者・銀行口座を統合管理する事業プロフィール画面
- Phone ブック取り込みを共通化した `ContactPickerSheet`
- 税率・税表示・印影の追加設定
- 90 日寿命チェック(`BuildExpiryInfo`)と期限切れ画面
- モジュール指向ダッシュボード
- `FeatureModule` / `ModuleRegistry` により各機能を独立カードとして登録
- A2伝票履歴/A1伝票入力モジュールと売上管理モジュールを実装済み
- 伝票ロックバーやカード表示は AppConfig の feature flag で制御
- ダッシュボードと売上モジュールの最新強化
- `AppConfig.enabledRoutes` にダッシュボード・売上伝票U1/U2経路を含め、S1 設定画面で登録したカードが D1 に確実に現れるよう調整
- ダッシュボードカード登録時に有効モジュールを自動注入し、売上伝票入力(`sales_entries`)カードを SalesManagementModule から直接表示
- A2(AppBar) の左上ボタンがホームモード設定に追従し、戻る/メニューボタンを正しく出し分け
- U2:売上伝票編集では保存アイコン・テキストボタンを AppBar に追加し、明細フォームのキーボード余白を整理して「せり上がり」を軽減
- U2 の顧客選択モーダルを `Scaffold + AppBar` 構成に刷新し、タイトル/閉じる操作を統一
- ビルド用スクリプト `scripts/build_with_expiry.sh`
- `--dart-define=APP_BUILD_TIMESTAMP` を自動付与し APK を生成
- analyze 実行APK ビルドのワンステップ化
- 連絡帳・顧客モジュールの共通化BusinessProfileScreen / CustomerPickerModal / CustomerMasterScreen
---
## 将来像・ロードマップ
1. **母艦お局様Web UI 100%**
- 各クライアントのハッシュチェーン監視
- SSH/Cloudflare/自社 DDNS いずれにも対応するブリッジ機能
- Google Drive への自動バックアップ、容量推定
2. **販売アシスト1号の拡張モジュール化**
- 売上POS、仕入、在庫、チャット、通知をモジュールとして追加
- ダッシュボードにモジュールカードを組み込む方式へ刷新(初期実装済)
3. **チャット&サポート**
- 「順次対応である」旨を明記した問い合わせチャットをローカル実装
- 母艦側で受信・返信・履歴管理ができる仕組みを構築
4. **寿命延命と母艦同期**
- 母艦と定期同期できた端末は自動で寿命を延長(例: 半年)
---
## リポジトリ構成(抜粋)
```
/home/user/dev/h-1.flutter.0
├── README.md … 本ファイル
├── analysis_options.yaml … Lint 設定
├── lib/ … Flutter アプリ本体
│ ├── screens/ … 各種画面business_profile_screen 等)
│ ├── widgets/ … 共通ウィジェットcontact_picker_sheet 等)
│ ├── services/ … 永続化・ユーティリティcompany_profile_service 等)
│ └── utils/build_expiry_info.dart … ビルド寿命ユーティリティ
├── scripts/build_with_expiry.sh … dart-define 付きビルドスクリプト
├── android/, ios/, macos/, windows/, linux/ … 各プラットフォームテンプレート
├── assets/ … 画像・リソース
├── test/ … テストコード
└── 目標.md / 目的.md … 設計メモ
```
※ フルツリーが必要になった場合は `tree``list_dir` の出力を README 末尾に追加して更新していきます。
---
## セットアップ & ビルド
1. Flutter 3.x 環境を用意し、依存パッケージを取得
```bash
flutter pub get
```
2. 90 日寿命 APK の生成(`scripts/build_with_expiry.sh [mode] [flavor]`
```bash
chmod +x scripts/build_with_expiry.sh
# 例: debug×client フレーバー販売アシスト1号
./scripts/build_with_expiry.sh debug client
# 例: release×mothership フレーバー(お局様)
./scripts/build_with_expiry.sh release mothership
```
- `APP_BUILD_TIMESTAMP` を UTC で自動付与し、`ENABLE_DEBUG_FEATURES=true` で全機能を有効化
- `flutter analyze``flutter build apk --flavor ... --dart-define=...` を連続実行
3. 実機/エミュレータで起動すると、寿命切れ時には `ExpiredApp` が自動表示されます。
### 機能フラグ(モジュール)
アプリは `AppConfig` の dart-define を通じてモジュール単位で有効化できます。
| Flag | 既定値 | 説明 |
| --- | --- | --- |
| `ENABLE_DEBUG_FEATURES` | `false` | **マスター・スイッチ**。`true` にすると以下の各機能フラグ/Debug Webhook がすべて強制的に ON になる |
| `ENABLE_BILLING_DOCS` | `true` | 伝票作成/履歴モジュールA1/A2の表示を制御 |
| `ENABLE_SALES_MANAGEMENT` | `false` | 売上管理モジュール(年間カード・トップ顧客・月次サマリー)を有効化 |
| `ENABLE_SALES_OPERATIONS` | `false` | 受注/出荷/在庫など販売オペレーションモジュールを有効化 |
| `ENABLE_PURCHASE_MANAGEMENT` | `false` | 仕入伝票・支払管理P1〜P4モジュールを有効化 |
| `ENABLE_DEBUG_WEBHOOK` | `false` | MatterMost Webhook へノード情報/日時の debug log を送信 |
| `DEBUG_WEBHOOK_URL` | `https://mm.ka.sugeee.com/hooks/x6nxx8q35jdkuetbmh89ogt5ze` | debug 送信先を上書きしたい場合に指定 |
例1: 売上管理と debug ログ送信を同時に試す場合
```bash
flutter run \
--dart-define=ENABLE_SALES_MANAGEMENT=true \
--dart-define=ENABLE_DEBUG_WEBHOOK=true \
--dart-define=DEBUG_WEBHOOK_URL=https://mm.ka.sugeee.com/hooks/x6nxx8q35jdkuetbmh89ogt5ze
```
`ENABLE_DEBUG_WEBHOOK=false`(既定値)に戻すと MatterMost への送信は行われません。フラグが有効なモジュールは `ModuleRegistry` 経由でダッシュボードカードに自動注入され、debug フラグはアプリ起動時の ping 送信のみを制御します。
例2: すべての機能を一括で有効化したい場合(`ENABLE_DEBUG_FEATURES=true` を指定するだけで OK
```bash
flutter run \
--dart-define=ENABLE_DEBUG_FEATURES=true
```
個別フラグを false のまま渡しても、このマスター・スイッチが ON の間は `AppConfig` 内で強制的に true として扱われます。
### Android ビルドフレーバーPlay 想定の二本立て)
`android/app/build.gradle.kts``client` / `mothership` の 2 フレーバーを定義しました。これにより将来 Google Play で「販売アシスト1号」と「お局様」を別 APK として配布しやすくなります。
| Flavor | applicationId | `appName` (manifest placeholder) | 用途 |
| --- | --- | --- | --- |
| `client` | `com.example.assist1` | `販売アシスト1号` | 現場端末向けクライアントアプリ(既存機能) |
| `mothership` | `com.example.mothership` | `お局様` | 将来の母艦/監視用アプリ(まだ UI 未実装) |
起動コマンド例:
```bash
# 販売アシスト1号既定
flutter run --flavor client
# お局様フレーバーを実行(まだ UI は同じだがパッケージ名とラベルが分離)
flutter run --flavor mothership
```
> メモ: 実機配布前に `applicationId` を本番用ドメインへ変更し、Play Console の keystore / サイン設定に合わせて `release` ビルドタイプの signingConfig を更新してください。
この構成により、販売アシスト1号・お局様それぞれを別ストアリスティングで公開、あるいはお局様だけ別配布チャネルで提供するといった運用が可能になります。
### 画面IDとナビゲーション指針
最新の UI アップデートにより、画面遷移ルールと画面タイトルの表記を統一しました。
- **すべての AppBar タイトルは 2 文字の画面ID + コロン + タイトル** で表示します(例: `S1:設定`, `U4:入金編集`)。
- **ホーム以外の画面は明示的な戻るボタンを左上に表示** します。`Scaffold` の `leading``BackButton` を指定し、ユーザーが階層を把握しやすいようにします。
- **伝票一覧A2:履歴リスト)がホームモードの場合のみ三本線のメニューボタン** を表示し、ドロワーから各種マスターや設定へ遷移できます。ホームモードでなければ通常通り戻るボタンを表示します。
- 新規に追加する画面もこの規約に従って ID を採番し、Dashboard 側のカードやメニュー表示名もあわせて更新してください。
- 売上伝票・入金管理U1〜U4など財務関連の新画面にもすでに適用済みです。各機能の AppBar を流用する際は ID だけ差し替えられるようコンポーネント化を検討しています。
---
## 粗利計算と卸値管理
- 商品マスタP1**仕入値(wholesale_price)** を保持するフィールドを追加しました。新規/既存商品の編集ダイアログで販売単価と合わせて卸値を登録できます。
- U2/A1 での売上明細は商品選択時に `ProductPickerModal` から卸値を受け取り、明細内部にコストを保持します。仕入値が未登録の明細は **暫定粗利=0** として扱い、仕入確定後に再計算する前提です。
- S1:設定 に「粗利表示 / 暫定粗利」セクションを追加し、次のスイッチで運用を制御できます。
1. **U2/A1で粗利を表示** … 単価−仕入値を計算して行ごとに表示。
2. **営業端末に粗利表示スイッチを表示** … 現場ユーザーが粗利の表示/非表示を切り替えられるようにする。
3. **暫定粗利(仕入未確定)を合計に含める** … 未入荷・未知商品の粗利=0 を合計に含めるかどうかを制御。
- これらの設定値は `app_settings` テーブルに保存され、端末再起動後も保持されます。アプリの将来バージョンではロット別の仕入れ管理と合わせて粗利再計算ジョブを提供する予定です。
---
## Googleカレンダー連携
営業オペレーションの ToDo を Google カレンダーへ自動配信します。設定画面S1:設定 → 「Googleカレンダー連携」セクションから次の手順で利用できます。
1. 「Googleカレンダーと連携する」を ON に切り替え、Google アカウントへサインインします。
2. 「カレンダー一覧を取得」で同期可能なカレンダーを読み込み、プライマリまたは任意の書き込み権限付きカレンダーを選択します。
3. 以降、出荷 (`ShipmentService`) と債権 (`ReceivableService`) のイベントが `BusinessCalendarMapper` を経由して自動同期され、Google 側では `shipment-<id>` / `receivable-<invoiceId>` という extendedProperties 付きで登録されます。
4. 必要に応じて「今すぐカレンダー同期を実行」ボタンを押すと、全件再同期+結果サマリ(件数・エラー詳細)が表示されます。手動同期は `CalendarSyncDiagnostics` により実装されています。
### 同期対象イベント
| 区分 | 連携トリガー | 内容 |
| --- | --- | --- |
| 出荷 | 新規作成・更新・ステータス遷移 | 出荷予定/実績日を 9:00〜2h イベントとして登録。顧客名、受注番号、追跡情報などを本文に記載。 |
| 債権 | サマリ取得・入金追加/削除 | 期日を 10:00〜1h イベントとして登録。請求額や残高を本文に記載。 |
Google 側でカレンダーを変更したい場合は、再度一覧取得→選択を行ってください。サインイン状態が切れた場合は「Googleを再認証」でリフレッシュできます。
---
## 母艦「お局様」LAN サーバの起動
1. Dart/Flutter SDK が入った Linux / AndroidTermux 等)端末でリポジトリを取得
2. 監視サーバを起動
```bash
dart run bin/mothership_server.dart
```
- 環境変数 `MOTHERSHIP_HOST`, `MOTHERSHIP_PORT`, `MOTHERSHIP_API_KEY`, `MOTHERSHIP_DATA_DIR` で上書き可能
- 既定値: `0.0.0.0:8787`, API キー `TEST_MOTHERSHIP_KEY`, 保存先 `data/mothership`
- `data/mothership/status.json` に各クライアントの心拍/ハッシュを保存
3. ブラウザで `http://<host>:<port>/` を開くとステータス一覧を閲覧できますCUI 常駐で OK
### クライアント販売アシスト1号からの接続設定
1. アプリの `S1:設定` → 「外部同期(母艦システム『お局様』連携)」で以下を入力
- ホストドメイン: `http://192.168.0.10:8787` のようにプロトコル付きで指定
- パスワード: サーバ側 API キー(例: `TEST_MOTHERSHIP_KEY`
2. 保存するとアプリ起動時に `POST /sync/heartbeat` が自動送信され、寿命残時間が母艦に表示されます。
3. 同じ設定でチャット送受信・ハッシュ送信が有効になります(下記参照)。
### チャット同期(最小構成)
- Flutter アプリ側では 10 秒間隔の軽量ポーリングをバックグラウンドで実行し、`/chat/send` / `/chat/pending` / `/chat/ack` とローカル SQLite を同期します。
- 設定画面からチャット画面を開かなくても新着が取り込まれ、開いた瞬間に最新ログが表示されます。
- 端末がスリープに入るとポーリングを停止し、アプリが前面に戻ったタイミングで即時同期→再開します。
---
## 更新ポリシー
- README は **機能追加・アーキテクチャ変更・モジュール構成の見直し時に必ず更新** します。
- 変更履歴とファイルツリーは必要に応じて追記し、最新状態を反映させます。
- 設計検討中の内容(母艦 Web UI、チャット、モジュール化などは本 README の「将来像」節で随時アップデートします。現在は売上モジュールが最初の実装例です。
---
ご要望・アイデアがあれば Issue/チャットで共有いただき、README に反映していきます。