お局様 Web UI 実装(事務担当者向け) #29
Labels
No labels
h1-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
joe/o2#29
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
概要
o2 に「事務おばちゃん向け」Web UI を実装する。
h1-c2(スマホアプリ)が入力、o2 のブラウザ画面で帳票閲覧・経営分析を行う分担。
ターゲットユーザー
設計方針
やること
画面優先順位
技術制約
static/ベース)で簡潔に役割分担
実装範囲
設計ドキュメント
詳細は
.planning/specs/2026-09-16-web-ui-design.mdを参照test
対応完了(Phase 1: clerk auth)
実施内容
1. Userモデル修正
is_clerkフィールド追加(src/o2/models/user.py)2. clerk-login エンドポイント
POST /api/auth/clerk/loginを追加(MFAなし、パスワードのみ)is_clerk=TrueのUserのみログイン可能3. テンプレート・JS追加
templates/clerk_login.html新規作成(既存login.htmlをベースにMFA除去)static/js/main.jsにhandleClerkLogin関数追加clerk_tokenクッキー認証、ログアウト時にCookie削除4. ルーティング追加
GET /clerk/loginで clerk_login.html を返すように追加変更ファイル(10ファイル)
models/user.pyis_clerkフィールド追加api/auth.py/clerk/loginエンドポイント追加api/schemas.pyClerkLoginRequestスキーマ追加templates/clerk_login.htmlstatic/js/main.jsmigrations/versions/is_clerk列追加テスト結果
次のステップ
進捗: 弥生風 Web UI(基盤+P1 5画面)実装完了
nazca(弥生再現)のデザインを o2 の Python スタックへ移植し、基盤と P1 5画面を実装しました。main
4782bb2にマージ・push 済み。方針
dashboard.htmlタブ版 /main.js/main.css)は廃止実装内容
static/css/yayoi.css(弥生ブルー#1361b2ほかトークン、.yayoi-*部品、menubar/toolbar/sidebar/statusbar)、templates/base.html(5ブロック)、static/js/app.js(認証/api/金額フォーマット/Fキー骨格)、static/fonts/ipagp.woff2(2.9MB)+ライセンス/dashboard(KPI+クイックナビ)//trial-balance//balance-sheet//income-statement//journals(期間フィルタ+ページネーション)/login(MFA対応)・/clerk/loginを弥生風に改修GET /api/journalsにdate_from/date_to/page/page_size/with_linesを追加(後方互換、with_linesは N+1 回避+account_name付与)セキュリティ
/static/%2e%2e/...でo2.db・/etc/passwd到達)を検出。Path.resolve()+is_relative_toガードで遮断し、回帰テストを追加(既存実装由来の脆弱性)テスト結果
変更規模
残り(P2以降・本Issue継続)
お局様 Web UI 実装(事務おばちゃん向け)to お局様 Web UI 実装(事務担当者向け)進捗: P2 マスタ4画面 実装完了
Web UI の P2(マスタ)を実装し、main
f3b1162にマージ・push しました。追加画面(一覧+新規+編集+無効化)
/clients/api/customers(GET/POST/PUT/deactivate)/products/api/products(GET/POST/PUT/deactivate)/accounts/api/accounts(GET/POST)+期首残高PUT(無効化APIは無し)/suppliers/api/suppliers(GET/POST/PUT/deactivate).yayoi-table+モーダルフォーム、検索(クライアント側)、状態バッジ、無効化確認、二重送信防止x-textで挿入(XSS対策)エラー処理の統一(レビュー指摘の修正)
API エラーは
error_handlerの統一形式{code,message,details}を返すが、各ページがdetailを読んでいたためメッセージが出ない不具合を修正。o2.fetchJson/o2.errorMessageを共通化し、P1/P2 全9ページを統一(409/422 の日本語メッセージを表示)。テスト結果
成果物
3f03b73(P2)、c32bb37(エラー修正)、02988f3(P1統一)→ mainf3b1162セキュリティ課題(別Issue化)
書き込み系 API が
require_readのみでVIEWERでも変更可能な件を #38 として起票しました(本変更起因ではない既存事項)。残り
進捗: P3(経費・分析・給与・消費税)+ダッシュボードチャート 完了
Web UI の P3 を実装し、main
132c57aにマージ・push しました。追加画面(4)
/expensesGET/POST /api/expenses(一覧+登録、/api/accountsで科目選択)/analysisfinancial-ratios/{year}・yoy-comparison/income-statement・monthly-trend/{year}・break-even/{year}。月次推移を Chart.js 棒グラフ/payrollGET /api/payroll/payslips、POST /run、POST /payslips/{id}/confirm/taxGET /api/tax/monthly-sales/{year}、GET /api/tax/tax-returns(e-Tax送信UIは置かない)ダッシュボード
/api/analysis/monthly-trend/{当年}の売上/仕入を折れ線チャートで表示品質
Promise.allSettledで部分失敗を分離表示x-textのみ)、テナントは認証コンテキスト、Chart はdestroy()で再生成null.changes防御、日付整形テスト結果
成果物
b585138(P3)、bd6ea8c(補正)→ main132c57a残り(本Issueは継続)
/api/expenses等の変更系require_write未付与(#39 に含めて監査)進捗: P4(販売帳票)+ billing セキュリティ修正 完了
販売帳票の3画面を実装し、main
354c49eにマージ・push しました。追加画面(3)
/quotationssend/accept/reject/expire/convert/sales-ordersconfirm/deliver/invoice/pay/close/billingissue・入金記録・期限超過GET /api/sales-orders(一覧。statusフィルタ、詳細と同形の配列)— 既存の詳細レスポンスは_order_to_dict共通化で不変セキュリティ修正(レビュー検出・本変更で露出)
billing_api.pyでservice.get(id)をテナント検証なしに使用していた IDOR を修正:GET /{id}/POST /receipts/DELETE /{id}/issue/cancel/close/lines/DELETE /lines/{id}//summary/{id}/DELETE /groups/{id}にテナント検証(他テナントは 404)delete/delete_lineもテナント対応その他の修正
BillingStatus.PARTIALを設定(従来は到達不能)/api/billing/overdue(動的判定)に整合テスト結果
test_billing_tenant_isolation.py(7)・test_billing_partial.py(1)・test_sales_orders_list_api.py(7)成果物
0b7986b(P4販売)、9605592(billing修正)→ main354c49e残り
/purchase-orders/purchase-invoices)・在庫(/inventory/stocktaking/lots/serials)・設定(AI設定/倉庫/部門/プロジェクト)進捗: P4b(購買・在庫)完了
購買・在庫の6画面を実装し、main
b994f1eにマージ・push しました。追加画面(6)
/purchase-orders/purchase-invoices/inventory/stocktaking/lots/serialsstatic_routerのページルート追加のみ(APIロジック変更なし)レビュー指摘の修正
partially_paidから残金支払できるようcanPayを修正generate_adjustment_entryを冪等化(journal_entry_id設定済みなら既存を返す)+ UI ボタンを未計上時のみ表示。テスト追加テスト結果
成果物
58c19eb(P4b)、4dca0a9(修正)→ mainb994f1e残り
/ai-settings、倉庫/部門/プロジェクト等のマスタ追加)、ダッシュボード拡充進捗: P5(設定画面+ロール別UI)完了
設定の5画面とロール別UIガードを実装し、main
f682202にマージ・push しました。追加画面(5)
/warehouses/departments/projects/bank-accounts/ai-settingsdeactivateはAPIが無かったため追加)ロール別UIガード
o2.getRole()(JWTペイロードをデコード、署名検証なし・失敗時 null)とo2.canWrite()(platform_admin/admin/accountant)を追加x-show="canWrite"で出し分け。ツールバーの「新規」ボタンも JS で VIEWER 時に非表示化require_writeで 403)レビュー指摘の修正
exclude_noneにより親/部門の「(なし)」が保存できなかった問題をexclude_unsetに変更して修正(counterparty も同様)テスト結果
test_masters_api.py含め 251 passed、コア 5468 passed, 0 failed成果物
ca33036(P5)、d180dfc(修正)→ mainf682202残り
進捗: P6(全画面ロールガード・ログアウト・レスポンシブ)完了
main
58fca94にマージ・push しました。A. ロール別ガードの全画面横展開
data-write-actionを付与applyRoleClass()でbody.role-viewer/role-writerを付与し、CSSbody.role-viewer [data-write-action]{display:none!important}で Alpine が後から描画する行アクションにも適用if (!window.o2.canWrite()) return;(UI+JSの二重防御。APIは従来どおり403)B. サーバ側ログアウト
POST /api/auth/logoutを追加(clerk_tokenCookie を削除、公開エンドポイント)handleLogout()が logout API → localStorage 破棄 →/login。API失敗時も遷移C. レスポンシブ最小対応
@media (max-width:900px): サイドバーを固定・折りたたみ、ハンバーガーで開閉、実体バックドロップのクリックで閉じる、メニューバーを前面化.content{overflow-x:auto}で横スクロールテスト
成果物
f730d38(P6)、d95dbeb(修正)→ main58fca94残り(本Issueは継続)
進捗: P7(ダークモード・モバイル改善)完了
main
a4b565fにマージ・push しました。ダークモード
[data-theme="dark"]のトークン一式(サーフェス/文字/罫線/テーブル/入力/サイドバー/ステータスバー/モーダル/トースト/バッジ/影/スクロールバー/グラデ)+prefers-color-scheme: dark既定(明示テーマが優先)<head>の同期スクリプトで FOUC 防止。メニューバーにテーマ切替(localStorage.o2_theme+OS追従)o2.chartColors()連動でテーマに追随(切替時に再描画)モバイル改善(900px以下)
.nav-link含む)、テーブル横スクロール維持テスト
成果物
4af962a(P7)、0d8a75b(修正)→ maina4b565f残り(本Issueは継続)
進捗: P7b(ロット一覧のN+1改善)完了 / Web UI 実装 完了
main
3bced45にマージ・push しました。P7b
LotService.balances_for_lots(lot_ids):StockMovementを1クエリで集計(sum+group_by)し、lot_id → 残高を返すGET /api/lotsが各ロットにbalanceを同梱(既存キー維持)list_expiringも同方式に変更(N+1解消、結果不変)lots.jsの per-lot 並列取得を削除しlot.balanceを使用Web UI(#29)実装サマリ
base.html(メニュー/ツールバー/サイドバー/ステータスバー)、IPA Pゴシック woff2、Alpine/Chart.js 同梱、旧タブUI廃止exclude_noneによる参照解除不可、棚卸仕訳の重複計上 など残り(軽微な磨き込みのみ)
/api/inventory/balancesの評価額同梱(N+1回避。現状は行選択時に平均原価API)主要機能は完成しています。上記の軽微項目を続けるか、本Issueをクローズして別タスクに移るか、ご判断ください。
完了: 最終磨き込み(P8)/Web UI 実装 完了
main
9fc7728にマージ・push しました。P8
matchMedia('(prefers-color-scheme: dark)')の change で、ユーザーが明示テーマを保存していない場合のみテーマとチャートを更新(明示保存時は無視、OS追従は保存しない)--accent-green/amber/red等を追加し、.status-dot・トーストを変数参照に(ライトの見た目は不変、ダーク値も定義)#29 全体(P1〜P8)
基盤(弥生トークン・IPA Pゴシック woff2・Alpine/Chart.js 同梱)+ 会計5/マスタ4/経費・分析・給与・税/販売・購買・在庫13/設定5 の各画面、ロール別ガード、サーバ側ログアウト、レスポンシブ、ダークモード、ロットN+1改善。
exclude_noneの参照解除不可、棚卸仕訳の重複計上残(軽微・別タスク)
/api/inventory/balancesの評価額同梱(現状は行選択時に平均原価を取得)主要機能の実装が完了したため、本Issueをクローズします。追加要望は新規Issueで受け付けます。