# モバイルPOS・見積/納品/請求/領収書システム ## 概要 Proxmox CT上で動作するオフラインファーストのスマートフォンアプリケーション。 営業現場で完全スタンドアロンで見積・納品・請求・領収書を作成・管理し、 ネットワーク接続時にOdooと同期します。 ## アーキテクチャ ``` ┌─────────────────┐ │ Android App │ ← スマホ(完全オフライン対応) │ SQLite DB │ │ PDF生成 │ └────────┬────────┘ │ (ネットワーク接続時) │ ┌────▼─────────────────────────┐ │ REST API コンテナ │ │ (Python FastAPI) │ │ - 同期エンドポイント │ │ - Odoo連携 │ └────┬────────────────┬─────────┘ │ │ ┌────▼──────┐ ┌─────▼─────┐ │ PostgreSQL│ │ Odoo │ │ DB │ │ (Sales) │ └───────────┘ └───────────┘ ``` ## ディレクトリ構成 ``` project_root/ ├── docker-compose.yml # Odoo + API + DB ├── api/ │ ├── main.py # FastAPI メイン │ ├── requirements.txt │ └── Dockerfile ├── addons/ # Odooカスタムモジュール ├── scheduler/ # 定期同期スクリプト └── android/ ├── Models.kt # データモデル ├── DAO.kt # Room DAO ├── Database.kt # Room DB ├── ApiService.kt # Retrofit API └── PdfGenerator.kt # PDF生成 ``` ## セットアップ手順 ### 1. Docker環境構築(Proxmox CT) ```bash # リポジトリクローン git clone cd project_root # 環境変数設定 cp .env.example .env # .envを編集(API_SECRET_KEY等) # コンテナ起動 docker-compose up -d # ログ確認 docker-compose logs -f api docker-compose logs -f odoo ``` ### 2. Odooの初期セットアップ ```bash # Odooにアクセス # http://localhost:8069 # 以下のモジュールを有効化 # - Sales (見積・受注管理) # - Invoicing (請求・領収書) # - Accounting (会計・売掛金) # API認証設定 # Admin > 設定 > API キーを生成 ``` ### 3. REST APIの初期化 ```bash # DB テーブル作成 docker-compose exec api python -c "from main import Base, engine; Base.metadata.create_all(bind=engine)" # テスト curl -X GET http://localhost:8000/api/v1/health \ -H "X-API-Key: your_secret_key" ``` ### 4. Androidアプリ開発 #### 依存パッケージ (build.gradle) ```gradle dependencies { // Room implementation "androidx.room:room-runtime:2.6.0" implementation "androidx.room:room-ktx:2.6.0" kapt "androidx.room:room-compiler:2.6.0" // Retrofit implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0" implementation "com.squareup.okhttp3:logging-interceptor:4.11.0" // Coroutines implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" // Jetpack Compose implementation "androidx.compose.ui:ui:1.6.0" implementation "androidx.compose.material3:material3:1.1.0" // PDF (iText or Apache POI) implementation "com.itextpdf:itext-core:8.0.0" // Gson implementation "com.google.code.gson:gson:2.10.1" } ``` #### AndroidManifest.xml の設定 ```xml ``` #### build.gradle 設定 ```gradle android { compileSdk 34 defaultConfig { applicationId "com.example.mobilepos" minSdk 26 targetSdk 34 } buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.0" } } ``` ## API エンドポイント ### 同期 **POST** `/api/v1/sync` ```bash curl -X POST http://localhost:8000/api/v1/sync \ -H "X-API-Key: secret_key" \ -H "Content-Type: application/json" \ -d '{ "device_id": "device_001", "last_sync_timestamp": null, "documents": [ { "doc_type": "quotation", "customer_id": 1, "document_date": "2026-01-31T10:00:00", "items": [ { "product_name": "商品A", "quantity": 10, "unit_price": 1000, "subtotal": 10000 } ], "subtotal": 10000, "tax": 1000, "total": 11000, "payment_terms": { "billing_date": "2026-01-31", "payment_due_date": "2026-02-28", "payment_method": "bank_transfer" } } ] }' ``` ### 顧客一覧 **GET** `/api/v1/customers` ```bash curl http://localhost:8000/api/v1/customers \ -H "X-API-Key: secret_key" ``` ### ドキュメント取得 **GET** `/api/v1/documents/{id}` ### 領収書自動生成 **POST** `/api/v1/receipts/{invoice_id}` ```bash # 入金から1週間以内の請求書から領収書を生成 curl -X POST http://localhost:8000/api/v1/receipts/1 \ -H "X-API-Key: secret_key" ``` ## Android実装ガイド ### 1. データベース初期化 ```kotlin val db = AppDatabase.getInstance(context) val documentDao = db.documentDao() val customerDao = db.customerDao() ``` ### 2. ドキュメント作成・保存 ```kotlin val document = DocumentEntity( docType = "quotation", customerId = 1, documentDate = System.currentTimeMillis(), items = Gson().toJson(listOf( DocumentItemDto("商品A", 10.0, 1000.0, 10000.0) )), subtotal = 10000.0, tax = 1000.0, total = 11000.0, paymentDueDate = calculateDueDate(PaymentPattern.END_OF_MONTH), synced = false ) documentDao.insertDocument(document) ``` ### 3. PDF生成 ```kotlin val file = PdfGenerator.generateQuotationPdf( context = context, document = document, customer = customer, fileName = "quotation_${document.id}.pdf" ) // ファイル共有 val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) val shareIntent = Intent(Intent.ACTION_SEND).apply { type = "application/pdf" putExtra(Intent.EXTRA_STREAM, uri) } startActivity(Intent.createChooser(shareIntent, "PDFを共有")) ``` ### 4. 同期処理 ```kotlin suspend fun syncDocuments(context: Context) { val apiKey = "your_secret_key" val baseUrl = "http://your_api_server:8000" val apiService = ApiClient.getSyncApiService(baseUrl) val db = AppDatabase.getInstance(context) val unsyncedDocs = db.documentDao().getUnsyncedDocuments() val request = SyncRequestDto( deviceId = getDeviceId(), documents = unsyncedDocs.map { convertToDto(it) } ) try { val response = apiService.syncDocuments(apiKey, request) if (response.isSuccessful && response.body()?.status == "success") { response.body()?.newDocuments?.forEach { doc -> db.documentDao().markDocumentAsSynced(doc["local_id"] as Int, System.currentTimeMillis()) } } } catch (e: Exception) { Log.e("Sync", "Error: ${e.message}") } } ``` ### 5. 支払期限の計算 ```kotlin fun calculatePaymentDueDate(billingDate: Long, pattern: PaymentPattern): Long { val calendar = Calendar.getInstance().apply { timeInMillis = billingDate } return when (pattern) { PaymentPattern.IMMEDIATE -> calendar.timeInMillis PaymentPattern.END_OF_MONTH -> { calendar.set(Calendar.DAY_OF_MONTH, 1) calendar.add(Calendar.MONTH, 1) calendar.add(Calendar.DAY_OF_MONTH, -1) calendar.timeInMillis } PaymentPattern.THIRTY_DAYS -> { calendar.add(Calendar.DAY_OF_MONTH, 30) calendar.timeInMillis } PaymentPattern.SIXTY_DAYS -> { calendar.add(Calendar.DAY_OF_MONTH, 60) calendar.timeInMillis } else -> calendar.timeInMillis } } ``` ## 同期フロー ### オフライン時 1. スマホアプリで見積/納品/請求/領収書を作成 2. SQLiteに自動保存 3. PDF生成・送信(メール等) ### ネットワーク接続時 1. 未同期ドキュメントを検出 2. REST APIに送信 3. API が Odoo に登録 4. 同期完了後、ローカルの synced フラグを更新 5. Odoo で売掛金管理・レポート生成 ## 支払条件パターン | パターン | 説明 | 計算方式 | |---------|------|--------| | 即支払い | 当日支払い | 請求日 | | 末締め翌月末 | 末締めで翌月末払い | 翌月末日 | | 30日後 | 請求日から30日後 | 請求日 + 30日 | | 60日後 | 請求日から60日後 | 請求日 + 60日 | | カスタム | 任意設定 | ユーザーが指定 | ## セキュリティ - API キーは環境変数で管理 - HTTPS通信を推奨(本番環境) - トークン認証で API アクセス制限 - Odoo への認証も環境変数化 ## トラブルシューティング ### API接続エラー ```bash # ヘルスチェック curl http://localhost:8000/api/v1/health -H "X-API-Key: your_key" # ログ確認 docker-compose logs api ``` ### Odoo連携エラー ```bash # Odooログ docker-compose logs odoo # DBテーブル確認 docker-compose exec postgres psql -U odoo -d odoo -c "\dt" ``` ### Android PDF生成エラー - ストレージパーミッション確認 - 外部ストレージ空き容量確認 - iText/Apache POI の対応バージョン確認 ## 今後の実装予定 - [ ] Odoo REST API 直接連携 - [ ] Nextcloud WebDAV バックアップ統合 - [ ] 複数ユーザー・デバイス同期 - [ ] オフライン時の競合解決 - [ ] 売掛金ダッシュボード - [ ] Web UI(PCから Odoo 管理用) - [ ] カスタム領収書テンプレート - [ ] 電子署名対応 ## 開発者向け情報 ### REST API テスト ```bash # FastAPI ドキュメント http://localhost:8000/docs ``` ### DB マイグレーション ```bash # 新しいテーブル追加時 # Android: Migrations クラスを追加 # API: SQLAlchemy モデルを追加 → DB再作成 ``` ## ライセンス MIT License ## サポート 問題報告は Issues で。