Files
2026-06-07 00:33:58 +09:00

362 lines
17 KiB
PHP

<?php
require_once __DIR__ . '/../app/lib/auth.php';
require_once __DIR__ . '/../app/lib/db.php';
require_once __DIR__ . '/../app/lib/helpers.php';
require_once __DIR__ . '/../app/lib/account_service.php';
check_auth();
$pdo = db();
$uid = user_id();
$id = (int)($_GET['id'] ?? 0);
$error = '';
$stmt = $pdo->prepare("SELECT * FROM accounts WHERE id = ? AND user_id = ?");
$stmt->execute([$id, $uid]);
$account = $stmt->fetch();
if (!$account) {
exit('계좌를 찾을 수 없습니다.');
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
$accountType = $_POST['account_type'] ?? '';
$institutionName = trim($_POST['institution_name'] ?? '');
$accountName = trim($_POST['account_name'] ?? '');
$openingBalance = (float)str_replace(',', '', (string)($_POST['opening_balance'] ?? 0));
$isActive = isset($_POST['is_active']) ? 1 : 0;
$cardKind = $_POST['card_kind'] ?? null;
$billingDay = !empty($_POST['billing_day']) ? (int)$_POST['billing_day'] : null;
$paymentDay = !empty($_POST['payment_day']) ? (int)$_POST['payment_day'] : null;
$useCreditGracePeriod = !empty($_POST['use_credit_grace_period']) ? 1 : 0;
$billingCycleMemo = trim($_POST['billing_cycle_memo'] ?? '');
$statementStartMonthOffset = isset($_POST['statement_start_month_offset']) && $_POST['statement_start_month_offset'] !== ''
? (int)$_POST['statement_start_month_offset']
: null;
$statementStartDay = !empty($_POST['statement_start_day']) ? (int)$_POST['statement_start_day'] : null;
$statementEndMonthOffset = isset($_POST['statement_end_month_offset']) && $_POST['statement_end_month_offset'] !== ''
? (int)$_POST['statement_end_month_offset']
: null;
$statementEndDay = !empty($_POST['statement_end_day']) ? (int)$_POST['statement_end_day'] : null;
if (!in_array($accountType, ['bank', 'card', 'cash', 'other'], true)) {
throw new RuntimeException('계정 유형이 올바르지 않습니다.');
}
if ($institutionName === '' || $accountName === '') {
throw new RuntimeException('기관명과 계좌명을 입력하세요.');
}
if ($accountType !== 'card') {
$cardKind = null;
$billingDay = null;
$paymentDay = null;
$useCreditGracePeriod = 0;
$billingCycleMemo = null;
$statementStartMonthOffset = null;
$statementStartDay = null;
$statementEndMonthOffset = null;
$statementEndDay = null;
} else {
if (!in_array($cardKind, ['credit', 'check'], true)) {
throw new RuntimeException('카드 종류를 선택하세요.');
}
if ($cardKind === 'check') {
$billingDay = null;
$paymentDay = null;
$useCreditGracePeriod = 0;
$billingCycleMemo = '즉시출금';
$statementStartMonthOffset = null;
$statementStartDay = null;
$statementEndMonthOffset = null;
$statementEndDay = null;
}
if ($cardKind === 'credit') {
if ($paymentDay === null || $paymentDay < 1 || $paymentDay > 31) {
throw new RuntimeException('납부일은 1~31 사이여야 합니다.');
}
if ($useCreditGracePeriod) {
if ($statementStartMonthOffset === null || $statementEndMonthOffset === null) {
throw new RuntimeException('사용기간의 시작/종료 월 기준을 선택하세요.');
}
if ($statementStartDay === null || $statementStartDay < 1 || $statementStartDay > 31) {
throw new RuntimeException('사용기간 시작일은 1~31 사이여야 합니다.');
}
if ($statementEndDay === null || $statementEndDay < 1 || $statementEndDay > 31) {
throw new RuntimeException('사용기간 종료일은 1~31 사이여야 합니다.');
}
} else {
$statementStartMonthOffset = null;
$statementStartDay = null;
$statementEndMonthOffset = null;
$statementEndDay = null;
}
if ($billingCycleMemo === '') {
$billingCycleMemo = null;
}
if ($billingDay !== null && ($billingDay < 1 || $billingDay > 31)) {
throw new RuntimeException('구형 결제기준일은 1~31 사이여야 합니다.');
}
}
}
$stmt = $pdo->prepare("
UPDATE accounts
SET
account_type = ?,
institution_name = ?,
account_name = ?,
opening_balance = ?,
is_active = ?,
card_kind = ?,
billing_day = ?,
payment_day = ?,
use_credit_grace_period = ?,
statement_start_month_offset = ?,
statement_start_day = ?,
statement_end_month_offset = ?,
statement_end_day = ?,
billing_cycle_memo = ?
WHERE id = ?
AND user_id = ?
");
$stmt->execute([
$accountType,
$institutionName,
$accountName,
$openingBalance,
$isActive,
$cardKind,
$billingDay,
$paymentDay,
$useCreditGracePeriod,
$statementStartMonthOffset,
$statementStartDay,
$statementEndMonthOffset,
$statementEndDay,
$billingCycleMemo,
$id,
$uid
]);
recalculate_account_balance($id);
redirect('/accounts.php');
} catch (Throwable $e) {
$error = $e->getMessage();
}
}
require __DIR__ . '/../app/views/header.php';
?>
<div class="page-head">
<h2>계좌 / 카드 수정</h2>
<a href="/accounts.php" class="btn btn-outline-secondary">목록</a>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?= h($error) ?></div>
<?php endif; ?>
<div class="card finance-card">
<div class="card-body">
<form method="post" class="row g-3" id="accountEditForm">
<div class="col-12 col-md-4">
<label class="form-label">유형</label>
<select name="account_type" id="account_type" class="form-select" required>
<?php foreach (['bank'=>'은행계좌','card'=>'카드','cash'=>'현금','other'=>'기타'] as $k => $v): ?>
<option value="<?= $k ?>" <?= $account['account_type'] === $k ? 'selected' : '' ?>><?= $v ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-md-4">
<label class="form-label">기관명</label>
<input type="text" name="institution_name" class="form-control" value="<?= h($account['institution_name']) ?>" required>
</div>
<div class="col-12 col-md-4">
<label class="form-label">계좌명</label>
<input type="text" name="account_name" class="form-control" value="<?= h($account['account_name']) ?>" required>
</div>
<div class="col-12 col-md-4">
<label class="form-label">시작 잔액</label>
<input type="text" name="opening_balance" id="opening_balance" class="form-control" inputmode="numeric" value="<?= h(money_plain($account['opening_balance'])) ?>" required>
</div>
<div class="col-12 col-md-4 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" <?= $account['is_active'] ? 'checked' : '' ?>>
<label class="form-check-label" for="is_active">활성</label>
</div>
</div>
<div class="col-12" id="card_setting_wrap" style="display:none;">
<div class="loan-create-highlight">
<div class="title">카드 설정</div>
<div class="desc">
카드사/결제일마다 신용공여기간이 다르므로 사용기간을 직접 설정합니다.
</div>
<div class="row g-3 mt-1">
<div class="col-12 col-md-3">
<label class="form-label">카드 종류</label>
<select name="card_kind" id="card_kind" class="form-select">
<option value="">선택하세요</option>
<option value="credit" <?= ($account['card_kind'] ?? '') === 'credit' ? 'selected' : '' ?>>신용카드</option>
<option value="check" <?= ($account['card_kind'] ?? '') === 'check' ? 'selected' : '' ?>>체크카드</option>
</select>
</div>
<div class="col-12 col-md-3 credit-only">
<label class="form-label">납부일</label>
<input type="number" name="payment_day" id="payment_day" class="form-control" min="1" max="31" value="<?= h((string)($account['payment_day'] ?? '')) ?>" placeholder="예: 25">
</div>
<div class="col-12 col-md-3 credit-only d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="use_credit_grace_period" id="use_credit_grace_period" value="1" <?= !empty($account['use_credit_grace_period']) ? 'checked' : '' ?>>
<label class="form-check-label" for="use_credit_grace_period">신용공여기간 계산 사용</label>
</div>
</div>
<div class="col-12 credit-only">
<hr>
<div class="fw-bold mb-2">청구월 기준 사용기간</div>
<div class="form-text mb-3">
예: 결제일 25일 기준 IBK는 “전월 11일 ~ 당월 10일 사용분” → 시작 월 기준 전월 / 시작일 11 / 종료 월 기준 당월 / 종료일 10
</div>
</div>
<div class="col-12 col-md-3 credit-period-only">
<label class="form-label">시작 월 기준</label>
<select name="statement_start_month_offset" class="form-select">
<?php foreach ([-2=>'전전월', -1=>'전월', 0=>'당월', 1=>'익월'] as $k => $v): ?>
<option value="<?= $k ?>" <?= (string)($account['statement_start_month_offset'] ?? '') === (string)$k ? 'selected' : '' ?>><?= $v ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-md-3 credit-period-only">
<label class="form-label">시작일</label>
<input type="number" name="statement_start_day" class="form-control" min="1" max="31" value="<?= h((string)($account['statement_start_day'] ?? '')) ?>" placeholder="예: 11">
</div>
<div class="col-12 col-md-3 credit-period-only">
<label class="form-label">종료 월 기준</label>
<select name="statement_end_month_offset" class="form-select">
<?php foreach ([-2=>'전전월', -1=>'전월', 0=>'당월', 1=>'익월'] as $k => $v): ?>
<option value="<?= $k ?>" <?= (string)($account['statement_end_month_offset'] ?? '') === (string)$k ? 'selected' : '' ?>><?= $v ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-md-3 credit-period-only">
<label class="form-label">종료일</label>
<input type="number" name="statement_end_day" class="form-control" min="1" max="31" value="<?= h((string)($account['statement_end_day'] ?? '')) ?>" placeholder="예: 10">
</div>
<div class="col-12 credit-only">
<label class="form-label">신용공여기간 메모</label>
<input type="text" name="billing_cycle_memo" class="form-control" value="<?= h($account['billing_cycle_memo'] ?? '') ?>" placeholder="예: 전월 11일 ~ 당월 10일 사용분">
</div>
<div class="col-12 credit-only">
<div class="alert alert-info mb-0">
결제일 25일 기준 예시:
IBK 신용카드 = 전월 11일 ~ 당월 10일,
우리카드 = 전월 12일 ~ 당월 11일.
신규 카드사는 카드사 안내표를 보고 직접 입력하면 됩니다.
</div>
</div>
<div class="col-12 check-only" style="display:none;">
<div class="alert alert-secondary mb-0">
체크카드는 신용공여기간 없이 거래월 기준으로 반영됩니다.
</div>
</div>
</div>
</div>
</div>
<div class="col-12 d-flex flex-wrap gap-2">
<button class="btn btn-primary">저장</button>
<a href="/accounts.php" class="btn btn-outline-secondary">목록</a>
</div>
</form>
</div>
</div>
<script>
(function () {
const accountTypeEl = document.getElementById('account_type');
const cardWrapEl = document.getElementById('card_setting_wrap');
const cardKindEl = document.getElementById('card_kind');
const creditOnlyEls = document.querySelectorAll('.credit-only');
const creditPeriodOnlyEls = document.querySelectorAll('.credit-period-only');
const checkOnlyEls = document.querySelectorAll('.check-only');
const useGraceEl = document.getElementById('use_credit_grace_period');
const openingBalanceEl = document.getElementById('opening_balance');
function formatWithComma(value) {
const normalized = String(value || '').replace(/,/g, '').replace(/[^\d.-]/g, '');
if (normalized === '' || normalized === '-') return normalized;
const negative = normalized.startsWith('-');
const raw = negative ? normalized.slice(1) : normalized;
const parts = raw.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return (negative ? '-' : '') + parts.join('.');
}
function setDisplay(nodes, visible) {
nodes.forEach(el => el.style.display = visible ? '' : 'none');
}
function toggleCardSettings() {
const isCard = accountTypeEl.value === 'card';
const isCredit = cardKindEl.value === 'credit';
const isCheck = cardKindEl.value === 'check';
const useGrace = useGraceEl && useGraceEl.checked;
cardWrapEl.style.display = isCard ? '' : 'none';
if (!isCard) {
return;
}
setDisplay(creditOnlyEls, isCredit);
setDisplay(checkOnlyEls, isCheck);
setDisplay(creditPeriodOnlyEls, isCredit && useGrace);
}
openingBalanceEl.addEventListener('input', function () {
this.value = formatWithComma(this.value);
});
document.getElementById('accountEditForm').addEventListener('submit', function () {
openingBalanceEl.value = openingBalanceEl.value.replace(/,/g, '');
});
accountTypeEl.addEventListener('change', toggleCardSettings);
cardKindEl.addEventListener('change', toggleCardSettings);
if (useGraceEl) {
useGraceEl.addEventListener('change', toggleCardSettings);
}
toggleCardSettings();
})();
</script>
<?php require __DIR__ . '/../app/views/footer.php'; ?>