journal-quest / src /store /statementStore.ts
MATSUMURA Taishi
Add 第2問(勘定記入)・第3問(財務諸表)
ebe620b
Raw
History Blame Contribute Delete
5.48 kB
import { create } from 'zustand'
import type { StatementProblem, StatementResult } from '../types'
import { judgeStatement } from '../lib/judge'
import { fetchStatementProblems } from '../lib/supabase'
import { useAuth } from './authStore'
export type StatementPhase = 'select' | 'loading' | 'playing' | 'result'
// ゲストモード用サンプル問題(貸借対照表の作成)
const SAMPLE_STATEMENT: StatementProblem = {
id: 'sample-statement-1',
section: 'statement',
topic: 'fixed_asset',
event_text:
'下記の決算整理前残高試算表にもとづき、決算整理を行って貸借対照表を完成させなさい。①貸倒引当金:売掛金の2%設定(差額補充法)。②減価償却:建物30年・備品8年(定額法・残存価額¥0)。③未払利息:借入金¥1,000,000の2か月分(年利3.6%・月割)。④消費税精算:仮受消費税-仮払消費税=未払消費税。⑤法人税等:¥100,000。',
documents: [
{
type: 'table',
title: '決算整理前残高試算表(関連部分)',
headers: ['勘定科目(借方)', '金額', '勘定科目(貸方)', '金額'],
rows: [
['売掛金', '450,000', '貸倒引当金', '4,000'],
['建物', '2,400,000', '建物減価償却累計額', '1,040,000'],
['備品', '700,000', '備品減価償却累計額', '262,500'],
['仮払消費税', '88,000', '借入金(年利3.6%・残2か月)', '1,000,000'],
['', '', '買掛金', '330,000'],
['', '', '仮受消費税', '160,000'],
],
},
],
bs_rows: [
{
left: { label: { type: 'given_label', value: '売掛金' },
amount: { type: 'blank_amount', answer: 450000 } },
right: { label: { type: 'given_label', value: '買掛金' },
amount: { type: 'given_amount', value: 330000 } },
},
{
left: { label: { type: 'given_label', value: '貸倒引当金' },
amount: { type: 'blank_contra', answer: 9000, answer2: 441000 } },
right: { label: { type: 'given_label', value: '未払費用' },
amount: { type: 'blank_amount', answer: 6000 } },
},
{
left: { label: { type: 'given_label', value: '建物' },
amount: { type: 'blank_amount', answer: 2400000 } },
right: { label: { type: 'blank_account', answer: '未払', suffix: '消費税' },
amount: { type: 'blank_amount', answer: 72000 } },
},
{
left: { label: { type: 'given_label', value: '建物減価償却累計額' },
amount: { type: 'blank_contra', answer: 1120000, answer2: 1280000 } },
right: { label: { type: 'given_label', value: '未払法人税等' },
amount: { type: 'blank_amount', answer: 100000 } },
},
{
left: { label: { type: 'given_label', value: '備品' },
amount: { type: 'blank_amount', answer: 700000 } },
right: { label: { type: 'given_label', value: null },
amount: { type: 'given_amount', value: null } },
},
{
left: { label: { type: 'given_label', value: '備品減価償却累計額' },
amount: { type: 'blank_contra', answer: 350000, answer2: 350000 } },
right: { label: { type: 'given_label', value: null },
amount: { type: 'given_amount', value: null } },
},
],
hint: '①9,000(450,000×2%)②建物80,000(÷30)・備品87,500(÷8)③6,000(×3.6%×2/12)④72,000(160,000-88,000)',
explanation:
'貸倒引当金9,000(追加5,000)。建物累計1,120,000(+80,000)、備品累計350,000(+87,500)。未払費用6,000。未払消費税72,000。未払法人税等100,000。',
}
interface StatementState {
phase: StatementPhase
problem: StatementProblem | null
inputs: Record<string, string | number>
result: StatementResult | null
error: string | null
start: () => Promise<void>
setInput: (key: string, value: string | number) => void
submit: () => void
retry: () => void
backToSelect: () => void
}
export const useStatement = create<StatementState>((set, get) => ({
phase: 'select',
problem: null,
inputs: {},
result: null,
error: null,
start: async () => {
set({ phase: 'loading', error: null })
const isGuest = useAuth.getState().authStatus === 'guest'
if (isGuest) {
set({ phase: 'playing', problem: SAMPLE_STATEMENT, inputs: {}, result: null })
return
}
try {
const problems = await fetchStatementProblems()
if (problems.length === 0) {
set({ phase: 'select', error: '財務諸表問題がまだありません。管理者に問い合わせください。' })
return
}
set({ phase: 'playing', problem: problems[0], inputs: {}, result: null })
} catch (e) {
set({
phase: 'select',
error: e instanceof Error ? e.message : '問題の取得に失敗しました。',
})
}
},
setInput: (key, value) => {
set((s) => ({ inputs: { ...s.inputs, [key]: value } }))
},
submit: () => {
const { problem, inputs } = get()
if (!problem) return
const result = judgeStatement(problem, inputs)
set({ phase: 'result', result })
},
retry: () => {
set({ inputs: {}, result: null, phase: 'playing' })
},
backToSelect: () => {
set({ phase: 'select', problem: null, inputs: {}, result: null, error: null })
},
}))