2340 lines
90 KiB
PHP
2340 lines
90 KiB
PHP
|
|
<?php
|
|||
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/load_GoogleDrive.php'; // 세션 등 여러가지 포함됨 파일 포함
|
|||
|
|
|
|||
|
|
if (!isset($_SESSION["level"]) || $_SESSION["level"] > 5) {
|
|||
|
|
sleep(1);
|
|||
|
|
header("Location:" . $WebSite . "login/login_form.php");
|
|||
|
|
exit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 에러 표시 설정
|
|||
|
|
ini_set('display_errors', 1);
|
|||
|
|
ini_set('display_startup_errors', 1);
|
|||
|
|
error_reporting(E_ALL);
|
|||
|
|
|
|||
|
|
// **대량 등록용 임시 key 발급**
|
|||
|
|
$bulkTimeKey = bin2hex(random_bytes(16));
|
|||
|
|
|
|||
|
|
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
|
|||
|
|
$title_message = '금전 출납부';
|
|||
|
|
$tablename = 'account';
|
|||
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/load_GoogleDriveSecond.php'; // attached, image에 대한 정보 불러오기
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
<link href="css/style.css" rel="stylesheet">
|
|||
|
|
<title> <?=$title_message?> </title>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
/* 테이블에 테두리 추가 */
|
|||
|
|
#myTable, #myTable th, #myTable td {
|
|||
|
|
border: 1px solid black;
|
|||
|
|
border-collapse: collapse;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 테이블 셀 패딩 조정 */
|
|||
|
|
#myTable th, #myTable td {
|
|||
|
|
padding: 8px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/* 전체 모달 폰트 기본값 12px 유지 */
|
|||
|
|
#bulkEntryModal * {
|
|||
|
|
font-size: 12px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 모달 배경 오버레이 */
|
|||
|
|
#bulkEntryModal {
|
|||
|
|
display: none;
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
background: rgba(0, 0, 0, 0.6);
|
|||
|
|
z-index: 1050;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 모달 내용 영역 (가운데 정렬) */
|
|||
|
|
#bulkEntryModal > div {
|
|||
|
|
background: white;
|
|||
|
|
width: 1920px;
|
|||
|
|
height: 950px;
|
|||
|
|
margin: 40px auto;
|
|||
|
|
padding: 20px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
position: relative;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 버튼 스타일 */
|
|||
|
|
#bulkEntryModal .btn {
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 테이블 관련 */
|
|||
|
|
#bulkEntryModal table {
|
|||
|
|
width: 100%;
|
|||
|
|
border-collapse: collapse;
|
|||
|
|
}
|
|||
|
|
#bulkEntryModal table th,
|
|||
|
|
#bulkEntryModal table td {
|
|||
|
|
padding: 4px;
|
|||
|
|
vertical-align: middle;
|
|||
|
|
font-size: 12px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
}
|
|||
|
|
#bulkEntryModal table thead {
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 모달 바디 스크롤 설정 */
|
|||
|
|
#bulkEntryModal .custom-modal-body {
|
|||
|
|
flex: 1;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
overflow-x: hidden;
|
|||
|
|
margin: 10px 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 테이블 헤더 고정 */
|
|||
|
|
#bulkEntryModal .table-responsive {
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#bulkEntryModal table thead {
|
|||
|
|
position: sticky;
|
|||
|
|
top: 0;
|
|||
|
|
z-index: 10;
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 모달 열릴 때 바디 스크롤 방지 */
|
|||
|
|
body.modal-open {
|
|||
|
|
overflow: hidden;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<!-- 로딩 오버레이 -->
|
|||
|
|
<div id="loadingOverlay" style="
|
|||
|
|
display: none;
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0; left: 0;
|
|||
|
|
width: 100%; height: 100%;
|
|||
|
|
background: rgba(0,0,0,0.6);
|
|||
|
|
z-index: 2000;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: #fff;
|
|||
|
|
font-size: 1.5em;
|
|||
|
|
display: none;
|
|||
|
|
">
|
|||
|
|
<div>
|
|||
|
|
<i class="bi bi-arrow-clockwise rotate" style="font-size:2em; animation: spin 1s linear infinite;"></i>
|
|||
|
|
<div>잠시만 기다려주세요…</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
@keyframes spin {
|
|||
|
|
from { transform: rotate(0deg); }
|
|||
|
|
to { transform: rotate(360deg); }
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
<?php
|
|||
|
|
// 세무사아이디면 다른 메뉴 연결
|
|||
|
|
if($_SESSION["userid"] == '0266771300') {
|
|||
|
|
include $_SERVER['DOCUMENT_ROOT'] . '/myheader_accountant.php';
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
include $_SERVER['DOCUMENT_ROOT'] . '/myheader.php';
|
|||
|
|
}
|
|||
|
|
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
|||
|
|
$fromdate = isset($_REQUEST['fromdate']) ? $_REQUEST['fromdate'] : '';
|
|||
|
|
$todate = isset($_REQUEST['todate']) ? $_REQUEST['todate'] : '';
|
|||
|
|
$mode = isset($_REQUEST['mode']) ? $_REQUEST['mode'] : '';
|
|||
|
|
|
|||
|
|
// 현재 날짜
|
|||
|
|
$currentDate = date("Y-m-d");
|
|||
|
|
|
|||
|
|
// fromdate 또는 todate가 빈 문자열이거나 null인 경우
|
|||
|
|
if ($fromdate === "" || $fromdate === null || $todate === "" || $todate === null) {
|
|||
|
|
// 현재 월의 1일을 fromdate로 설정
|
|||
|
|
// $fromdate = date("Y-m-01");
|
|||
|
|
// 전달(이전 달)의 1일을 fromdate로 설정
|
|||
|
|
$fromdate = date("Y-m-01", strtotime("-1 month"));
|
|||
|
|
$todate = $currentDate;
|
|||
|
|
$Transtodate = $todate;
|
|||
|
|
} else {
|
|||
|
|
$Transtodate = $todate;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function checkNull($strtmp) {
|
|||
|
|
return $strtmp !== null && trim($strtmp) !== '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
|
|||
|
|
$pdo = db_connect();
|
|||
|
|
|
|||
|
|
$inoutsep_select = isset($_REQUEST['inoutsep_select']) ? $_REQUEST['inoutsep_select'] : '';
|
|||
|
|
$content_select = isset($_REQUEST['content_select']) ? $_REQUEST['content_select'] : '';
|
|||
|
|
|
|||
|
|
$order = " ORDER BY registDate ASC, num ASC ";
|
|||
|
|
|
|||
|
|
$sql_conditions = [];
|
|||
|
|
$sql_params = [];
|
|||
|
|
|
|||
|
|
if (checkNull($search)) {
|
|||
|
|
$sql_conditions[] = "searchtag LIKE :search";
|
|||
|
|
$sql_params[':search'] = "%$search%";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$sql_conditions[] = "registDate BETWEEN :fromdate AND :todate";
|
|||
|
|
$sql_params[':fromdate'] = $fromdate;
|
|||
|
|
$sql_params[':todate'] = $todate;
|
|||
|
|
|
|||
|
|
$sql_conditions[] = "is_deleted = 0";
|
|||
|
|
|
|||
|
|
if (checkNull($inoutsep_select)) {
|
|||
|
|
$sql_conditions[] = "inoutsep = :inoutsep";
|
|||
|
|
$sql_params[':inoutsep'] = $inoutsep_select;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (checkNull($content_select)) {
|
|||
|
|
$sql_conditions[] = "content = :content";
|
|||
|
|
$sql_params[':content'] = $content_select;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$sql = "SELECT * FROM " . $tablename . " WHERE " . implode(' AND ', $sql_conditions) . $order;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$stmh = $pdo->prepare($sql);
|
|||
|
|
foreach ($sql_params as $param => $value) {
|
|||
|
|
$stmh->bindValue($param, $value);
|
|||
|
|
}
|
|||
|
|
$stmh->execute();
|
|||
|
|
$total_row = $stmh->rowCount();
|
|||
|
|
|
|||
|
|
// 수입, 지출을 기반으로 초기 잔액 계산
|
|||
|
|
$initialBalanceSql = "SELECT
|
|||
|
|
SUM(CASE WHEN inoutsep = '수입' THEN REPLACE(amount, ',', '') ELSE 0 END) +
|
|||
|
|
SUM(CASE WHEN inoutsep = '최초전월이월' THEN REPLACE(amount, ',', '') ELSE 0 END) -
|
|||
|
|
SUM(CASE WHEN inoutsep = '지출' THEN REPLACE(amount, ',', '') ELSE 0 END) AS balance
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND registDate < :fromdate
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$initialBalanceStmh = $pdo->prepare($initialBalanceSql);
|
|||
|
|
$initialBalanceStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$initialBalanceStmh->execute();
|
|||
|
|
$initialBalance = $initialBalanceStmh->fetch(PDO::FETCH_ASSOC)['balance'];
|
|||
|
|
|
|||
|
|
$totalIncomeSql = "SELECT SUM(REPLACE(amount, ',', '')) AS totalIncome
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND (inoutsep = '수입' OR inoutsep = '최초전월이월')
|
|||
|
|
AND registDate BETWEEN :fromdate AND :todate
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$totalIncomeStmh = $pdo->prepare($totalIncomeSql);
|
|||
|
|
$totalIncomeStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$totalIncomeStmh->bindParam(':todate', $todate);
|
|||
|
|
$totalIncomeStmh->execute();
|
|||
|
|
$totalIncome = $totalIncomeStmh->fetch(PDO::FETCH_ASSOC)['totalIncome'];
|
|||
|
|
|
|||
|
|
$totalExpenseSql = "SELECT SUM(REPLACE(amount, ',', '')) AS totalExpense
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND inoutsep = '지출'
|
|||
|
|
AND registDate BETWEEN :fromdate AND :todate
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$totalExpenseStmh = $pdo->prepare($totalExpenseSql);
|
|||
|
|
$totalExpenseStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$totalExpenseStmh->bindParam(':todate', $todate);
|
|||
|
|
$totalExpenseStmh->execute();
|
|||
|
|
$totalExpense = $totalExpenseStmh->fetch(PDO::FETCH_ASSOC)['totalExpense'];
|
|||
|
|
|
|||
|
|
$finalBalance = $initialBalance + $totalIncome - $totalExpense;
|
|||
|
|
|
|||
|
|
// Bankbook options
|
|||
|
|
$bankbookOptions = [];
|
|||
|
|
$jsonFile = $_SERVER['DOCUMENT_ROOT'] . "/account/accoutlist.json";
|
|||
|
|
$accounts = [];
|
|||
|
|
$selectedAccount = null;
|
|||
|
|
$accountBalances = [];
|
|||
|
|
|
|||
|
|
if (file_exists($jsonFile)) {
|
|||
|
|
$jsonContent = file_get_contents($jsonFile);
|
|||
|
|
$accounts = json_decode($jsonContent, true);
|
|||
|
|
if (is_array($accounts) && !empty($accounts)) {
|
|||
|
|
// 선택된 계좌 또는 기본 계좌(첫 번째) 설정
|
|||
|
|
$selectedAccountIndex = isset($_REQUEST['selected_account']) ? intval($_REQUEST['selected_account']) : 0;
|
|||
|
|
$selectedAccount = $accounts[$selectedAccountIndex] ?? $accounts[0];
|
|||
|
|
|
|||
|
|
// 각 계좌별 잔액 계산
|
|||
|
|
foreach ($accounts as $index => $account) {
|
|||
|
|
$accountDisplay = $account['company'] . ' ' . $account['number'];
|
|||
|
|
if (!empty($account['memo'])) {
|
|||
|
|
$accountDisplay .= ' (' . $account['memo'] . ')';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 해당 계좌의 잔액 계산
|
|||
|
|
$accountBalanceSql = "SELECT
|
|||
|
|
SUM(CASE WHEN inoutsep = '수입' AND bankbook = ? THEN REPLACE(amount, ',', '') ELSE 0 END) +
|
|||
|
|
SUM(CASE WHEN inoutsep = '최초전월이월' AND bankbook = ? THEN REPLACE(amount, ',', '') ELSE 0 END) -
|
|||
|
|
SUM(CASE WHEN inoutsep = '지출' AND bankbook = ? THEN REPLACE(amount, ',', '') ELSE 0 END) AS balance
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0'
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$accountBalanceStmh = $pdo->prepare($accountBalanceSql);
|
|||
|
|
$accountBalanceStmh->bindValue(1, $accountDisplay, PDO::PARAM_STR);
|
|||
|
|
$accountBalanceStmh->bindValue(2, $accountDisplay, PDO::PARAM_STR);
|
|||
|
|
$accountBalanceStmh->bindValue(3, $accountDisplay, PDO::PARAM_STR);
|
|||
|
|
$accountBalanceStmh->execute();
|
|||
|
|
$accountBalances[$index] = $accountBalanceStmh->fetch(PDO::FETCH_ASSOC)['balance'] ?? 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 각 계좌별 요약 정보를 담을 배열 초기화
|
|||
|
|
$accountSummaries = [];
|
|||
|
|
if (is_array($accounts)) {
|
|||
|
|
foreach ($accounts as $index => $account) {
|
|||
|
|
$accountDisplay = $account['company'] . ' ' . $account['number'];
|
|||
|
|
if (!empty($account['memo'])) {
|
|||
|
|
$accountDisplay .= ' (' . $account['memo'] . ')';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 계좌별 기초 잔액 (기간 이전)
|
|||
|
|
$initialSql = "SELECT
|
|||
|
|
(SUM(CASE WHEN inoutsep = '수입' OR inoutsep = '최초전월이월' THEN REPLACE(amount, ',', '') ELSE 0 END) -
|
|||
|
|
SUM(CASE WHEN inoutsep = '지출' THEN REPLACE(amount, ',', '') ELSE 0 END)) AS balance
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND registDate < :fromdate AND bankbook = :bankbook
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$initialStmh = $pdo->prepare($initialSql);
|
|||
|
|
$initialStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$initialStmh->bindParam(':bankbook', $accountDisplay);
|
|||
|
|
$initialStmh->execute();
|
|||
|
|
$initialBalanceAccount = $initialStmh->fetch(PDO::FETCH_ASSOC)['balance'] ?? 0;
|
|||
|
|
|
|||
|
|
// 2. 기간 내 수입
|
|||
|
|
$incomeSql = "SELECT SUM(REPLACE(amount, ',', '')) AS totalIncome
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND (inoutsep = '수입' OR inoutsep = '최초전월이월')
|
|||
|
|
AND registDate BETWEEN :fromdate AND :todate AND bankbook = :bankbook
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$incomeStmh = $pdo->prepare($incomeSql);
|
|||
|
|
$incomeStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$incomeStmh->bindParam(':todate', $todate);
|
|||
|
|
$incomeStmh->bindParam(':bankbook', $accountDisplay);
|
|||
|
|
$incomeStmh->execute();
|
|||
|
|
$totalIncomeAccount = $incomeStmh->fetch(PDO::FETCH_ASSOC)['totalIncome'] ?? 0;
|
|||
|
|
|
|||
|
|
// 3. 기간 내 지출
|
|||
|
|
$expenseSql = "SELECT SUM(REPLACE(amount, ',', '')) AS totalExpense
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND inoutsep = '지출'
|
|||
|
|
AND registDate BETWEEN :fromdate AND :todate AND bankbook = :bankbook
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$expenseStmh = $pdo->prepare($expenseSql);
|
|||
|
|
$expenseStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$expenseStmh->bindParam(':todate', $todate);
|
|||
|
|
$expenseStmh->bindParam(':bankbook', $accountDisplay);
|
|||
|
|
$expenseStmh->execute();
|
|||
|
|
$totalExpenseAccount = $expenseStmh->fetch(PDO::FETCH_ASSOC)['totalExpense'] ?? 0;
|
|||
|
|
|
|||
|
|
// 4. 최종 잔액
|
|||
|
|
$finalBalanceAccount = $initialBalanceAccount + $totalIncomeAccount - $totalExpenseAccount;
|
|||
|
|
|
|||
|
|
// 계산된 정보를 배열에 저장
|
|||
|
|
$accountSummaries[$index] = [
|
|||
|
|
'name' => $accountDisplay,
|
|||
|
|
'income' => $totalIncomeAccount,
|
|||
|
|
'expense' => $totalExpenseAccount,
|
|||
|
|
'balance' => $finalBalanceAccount
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 각 계좌별 최종 잔액 정보를 담을 배열 초기화
|
|||
|
|
$accountFinalBalances = [];
|
|||
|
|
if (is_array($accounts)) {
|
|||
|
|
foreach ($accounts as $index => $account) {
|
|||
|
|
$accountDisplay = $account['company'] . ' ' . $account['number'];
|
|||
|
|
if (!empty($account['memo'])) {
|
|||
|
|
$accountDisplay .= ' (' . $account['memo'] . ')';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 계좌별 전체 기간의 최종 잔액 계산
|
|||
|
|
$balanceSql = "SELECT
|
|||
|
|
(SUM(CASE WHEN inoutsep = '수입' OR inoutsep = '최초전월이월' THEN REPLACE(amount, ',', '') ELSE 0 END) -
|
|||
|
|
SUM(CASE WHEN inoutsep = '지출' THEN REPLACE(amount, ',', '') ELSE 0 END)) AS balance
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND bankbook = :bankbook
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
|
|||
|
|
$balanceStmh = $pdo->prepare($balanceSql);
|
|||
|
|
$balanceStmh->bindParam(':bankbook', $accountDisplay);
|
|||
|
|
$balanceStmh->execute();
|
|||
|
|
$balanceResult = $balanceStmh->fetch(PDO::FETCH_ASSOC);
|
|||
|
|
|
|||
|
|
// 계산된 정보를 배열에 저장
|
|||
|
|
$accountFinalBalances[$index] = [
|
|||
|
|
'name' => $accountDisplay,
|
|||
|
|
'balance' => $balanceResult['balance'] ?? 0
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// print $sql;
|
|||
|
|
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
|
|||
|
|
|
|||
|
|
<input type="hidden" id="mode" name="mode" value="<?= isset($mode) ? $mode : '' ?>">
|
|||
|
|
<input type="hidden" id="num" name="num" value="<?= isset($num) ? $num : '' ?>">
|
|||
|
|
<input type="hidden" id="tablename" name="tablename" value="<?= isset($tablename) ? $tablename : '' ?>">
|
|||
|
|
<input type="hidden" id="savetitle" name="savetitle" value="<?=isset($savetitle) ? $savetitle : '' ?>" >
|
|||
|
|
<input type="hidden" id="pInput" name="pInput" value="<?=isset($pInput) ? $pInput : '' ?>" >
|
|||
|
|
|
|||
|
|
<div id="bulkEntryModal" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.6); z-index:1050;">
|
|||
|
|
<div style="background:white; width:1880px; height:950px; margin:40px auto; padding:20px; border-radius:8px; position:relative;">
|
|||
|
|
<div class="d-flex justify-content-between bg-dark text-white" >
|
|||
|
|
<h3 class="p-2">금전출납부 대량 등록</h3>
|
|||
|
|
<button type="button" id="closeAccountModal" style="background:none; border:none; color:white; font-size:24px;">×</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="custom-modal-body">
|
|||
|
|
<div class="table-responsive">
|
|||
|
|
<table class="table table-bordered" id="bulkEntryTable">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th style="width:6%;">+/-/Copy</th>
|
|||
|
|
<th style="width:6%;">등록일자</th>
|
|||
|
|
<th style="width:7%;">구분</th>
|
|||
|
|
<th style="width:15%;">계좌</th>
|
|||
|
|
<th style="width:8%;">항목</th>
|
|||
|
|
<th style="width:8%;">세부항목</th>
|
|||
|
|
<th style="width:5%;">거래처 <i class="bi bi-search"></i></th>
|
|||
|
|
<th style="width:5%;">거래처코드</th>
|
|||
|
|
<th style="width:6%;">만기일자</th>
|
|||
|
|
<th style="width:6%;">배서일자</th>
|
|||
|
|
<th style="width:14%;">상세내용</th>
|
|||
|
|
<th style="width:6%;">금액</th>
|
|||
|
|
<th style="width:8%;">이미지첨부</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex justify-content-end">
|
|||
|
|
<button type="button" id="saveBulkBtn" class="btn btn-dark btn-sm me-2">
|
|||
|
|
<i class="bi bi-floppy-fill"></i> 전체 저장
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-2" id="closeAccountModal2">
|
|||
|
|
× 닫기
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 전자어음 검색 모달 -->
|
|||
|
|
<div class="modal fade" id="electronicBillModal" tabindex="-1" aria-labelledby="electronicBillModalLabel" aria-hidden="true" style="z-index:1060;">
|
|||
|
|
<div class="modal-dialog modal-xl">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h5 class="modal-title" id="electronicBillModalLabel">전자어음 검색</h5>
|
|||
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<div class="table-responsive">
|
|||
|
|
<table class="table table-hover">
|
|||
|
|
<thead class="table-secondary">
|
|||
|
|
<tr>
|
|||
|
|
<th>번호</th>
|
|||
|
|
<th>등록일자</th>
|
|||
|
|
<th>항목</th>
|
|||
|
|
<th>세부항목</th>
|
|||
|
|
<th>상세내용</th>
|
|||
|
|
<th>금액</th>
|
|||
|
|
<th>만기일자</th>
|
|||
|
|
<th>선택</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody id="electronicBillTableBody">
|
|||
|
|
<!-- 전자어음 데이터가 여기에 동적으로 추가됩니다 -->
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-footer">
|
|||
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="container-fluid">
|
|||
|
|
<!-- Modal -->
|
|||
|
|
<div id="myModal" class="modal">
|
|||
|
|
<div class="modal-content" style="width:1000px;">
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<span class="modal-title"> <?=$title_message?> </span>
|
|||
|
|
<span class="close">×</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<div class="custom-card"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="container-fluid">
|
|||
|
|
<div class="card justify-content-center text-center mt-5">
|
|||
|
|
<div class="card-header">
|
|||
|
|
<span class="text-center fs-5"> <?=$title_message?>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-1" onclick='location.reload()'>
|
|||
|
|
<i class="bi bi-arrow-clockwise" ></i> </button>
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="d-flex justify-content-center align-items-center mt-2">
|
|||
|
|
<span>
|
|||
|
|
▷ <?= $total_row ?>
|
|||
|
|
</span>
|
|||
|
|
|
|||
|
|
<!-- 기간부터 검색까지 연결 묶음 start -->
|
|||
|
|
<span id="showdate" class="btn btn-dark btn-sm">기간</span>
|
|||
|
|
|
|||
|
|
<div id="showframe" class="card" style="width:500px;">
|
|||
|
|
<div class="card-header" style="padding:2px;">
|
|||
|
|
<div class="d-flex justify-content-center align-items-center">
|
|||
|
|
기간 설정
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="d-flex justify-content-center align-items-center">
|
|||
|
|
<button type="button" class="btn btn-outline-success btn-sm me-1 change_dateRange" onclick='alldatesearch()'>전체</button>
|
|||
|
|
<button type="button" class="btn btn-outline-primary btn-sm me-1 change_dateRange" onclick='pre_year()'>전년도</button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-1 change_dateRange" onclick='pre_month()'>전월</button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-1 change_dateRange" onclick='dayBeforeYesterday()'>전전일</button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-1 change_dateRange" onclick='yesterday()'>전일</button>
|
|||
|
|
<button type="button" class="btn btn-outline-danger btn-sm me-1 change_dateRange" onclick='this_today()'>오늘</button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-1 change_dateRange" onclick='this_month()'>당월</button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-1 change_dateRange" onclick='this_year()'>당해년도</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<input type="date" id="fromdate" name="fromdate" class="form-control" style="width:110px;" value="<?=$fromdate?>"> ~
|
|||
|
|
<input type="date" id="todate" name="todate" class="form-control me-1" style="width:110px;" value="<?=$todate?>">
|
|||
|
|
|
|||
|
|
<!-- 첫 번째 select 문: 수입/지출 구분 -->
|
|||
|
|
<select id="inoutsep_select" name="inoutsep_select" class="form-select form-select-sm mx-1 d-block w-auto mx-1" >
|
|||
|
|
<option value="">구분</option>
|
|||
|
|
<option value="수입" <?= $inoutsep_select === '수입' ? 'selected' : '' ?>>수입</option>
|
|||
|
|
<option value="지출" <?= $inoutsep_select === '지출' ? 'selected' : '' ?>>지출</option>
|
|||
|
|
<option value="최초전월이월" <?= $inoutsep_select === '최초전월이월' ? 'selected' : '' ?>>최초전월이월</option>
|
|||
|
|
</select>
|
|||
|
|
|
|||
|
|
<!-- 두 번째 select 문: 항목 선택 -->
|
|||
|
|
<select id="content_select" name="content_select" class="form-select form-select-sm mx-1 d-block w-auto mx-1" >
|
|||
|
|
<option value="">전체항목</option>
|
|||
|
|
<?php
|
|||
|
|
include 'fetch_options.php';
|
|||
|
|
$options = array_merge(array_keys($incomeOptions), array_keys($expenseOptions));
|
|||
|
|
foreach ($options as $option) {
|
|||
|
|
$selected = ($content_select === $option) ? 'selected' : '';
|
|||
|
|
echo "<option value=\"$option\" $selected>$option</option>";
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
</select>
|
|||
|
|
|
|||
|
|
<div class="inputWrap30">
|
|||
|
|
<input type="text" id="search" class="form-control" style="width:150px;" name="search" value="<?=$search?>" onKeyPress="if (event.keyCode==13){ enter(); }">
|
|||
|
|
<button class="btnClear"></button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<button class="btn btn-outline-dark btn-sm" type="button" id="searchBtn"> <i class="bi bi-search"></i> </button>
|
|||
|
|
<button type="button" class="btn btn-secondary btn-sm ms-2 me-2" onclick="settings();" data-bs-toggle="tooltip" data-bs-placement="bottom" title="계정 관리(추가/수정/삭제)" > <i class="bi bi-gear-fill"></i> </button>
|
|||
|
|
<button id="newBtn" type="button" class="btn btn-dark btn-sm me-2"> <i class="bi bi-pencil-square"></i> 신규 </button>
|
|||
|
|
<button id="bulkNewBtn" type="button" class="btn btn-info btn-sm me-2"> <i class="bi bi-plus-square"></i> 대량등록 </button>
|
|||
|
|
<button type="button" class="btn btn-dark btn-sm me-2" onclick="generateExcel();" > <i class="bi bi-file-earmark-spreadsheet"></i> 엑셀다운로드 </button>
|
|||
|
|
<button type="button" class="btn btn-primary btn-sm me-2" onclick="detail();" > <i class="bi bi-ticket-detailed"></i> 상세내역 </button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex flex-wrap justify-content-start align-items-center mt-2 p-2 border rounded" style="gap: 10px;">
|
|||
|
|
<strong class="me-2">계좌 잔액:</strong>
|
|||
|
|
<?php foreach ($accountFinalBalances as $summary): ?>
|
|||
|
|
<?php if ($summary['balance'] > 0): ?>
|
|||
|
|
<?php
|
|||
|
|
// USD가 포함된 경우 금액 앞에 $ 추가
|
|||
|
|
$isUSD = stripos($summary['name'], 'USD') !== false;
|
|||
|
|
$formattedBalance = $isUSD ? '$' . number_format($summary['balance']) : number_format($summary['balance']);
|
|||
|
|
?>
|
|||
|
|
<div class="border rounded p-1" style="font-size: 0.8em;">
|
|||
|
|
<span class="text-secondary"><?= htmlspecialchars($summary['name']) ?>:</span>
|
|||
|
|
<span class="fw-bold ms-1"><?= $formattedBalance ?></span>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="table-responsive">
|
|||
|
|
<table class="table table-hover" id="myTable">
|
|||
|
|
<thead class="table-secondary">
|
|||
|
|
<tr>
|
|||
|
|
<th class="text-center" style="width:40px;">번호</th>
|
|||
|
|
<th class="text-center" style="width:70px;">등록일자</th>
|
|||
|
|
<th class="text-center" style="width:50px;">구분</th>
|
|||
|
|
<th class="text-center" style="width:80px;">항목</th>
|
|||
|
|
<th class="text-center" style="width:80px;">세부항목</th>
|
|||
|
|
<th class="text-center" style="width:250px;">상세내용</th>
|
|||
|
|
<th class="text-center" style="width:90px;">수입</th>
|
|||
|
|
<th class="text-center" style="width:90px;">지출</th>
|
|||
|
|
<th class="text-center" style="width:90px;">잔액</th>
|
|||
|
|
<th class="text-center" style="width:230px;">계좌</th>
|
|||
|
|
<th class="text-center" style="width:70px;">만기일자</th>
|
|||
|
|
<th class="text-center" style="width:70px;">배서일자</th>
|
|||
|
|
<th class="text-center" style="width:60px;">✅ 첨부서류</th>
|
|||
|
|
</tr>
|
|||
|
|
<tr style="background-color: #808080!important;">
|
|||
|
|
<th class="text-end" colspan="6"> 합계 </th>
|
|||
|
|
<th class="text-end" id="totalIncomeAmount"></th>
|
|||
|
|
<th class="text-end" id="totalExpenseAmount"></th>
|
|||
|
|
<th class="text-end" id="totalBalanceAmount"></th>
|
|||
|
|
<th class="text-end" colspan="4"></th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<?php
|
|||
|
|
// 1. 계좌별 기초 잔액을 계산하여 배열로 관리 (기간 시작일 이전)
|
|||
|
|
$runningBalances = [];
|
|||
|
|
if (is_array($accounts)) {
|
|||
|
|
foreach ($accounts as $account) {
|
|||
|
|
$accountDisplay = $account['company'] . ' ' . $account['number'];
|
|||
|
|
if (!empty($account['memo'])) {
|
|||
|
|
$accountDisplay .= ' (' . $account['memo'] . ')';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$initialBalanceSql = "SELECT
|
|||
|
|
(SUM(CASE WHEN inoutsep = '수입' OR inoutsep = '최초전월이월' THEN REPLACE(amount, ',', '') ELSE 0 END) -
|
|||
|
|
SUM(CASE WHEN inoutsep = '지출' THEN REPLACE(amount, ',', '') ELSE 0 END)) AS balance
|
|||
|
|
FROM $tablename
|
|||
|
|
WHERE is_deleted = '0' AND registDate < :fromdate AND bankbook = :bankbook
|
|||
|
|
AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')";
|
|||
|
|
$initialBalanceStmh = $pdo->prepare($initialBalanceSql);
|
|||
|
|
$initialBalanceStmh->bindParam(':fromdate', $fromdate);
|
|||
|
|
$initialBalanceStmh->bindParam(':bankbook', $accountDisplay);
|
|||
|
|
$initialBalanceStmh->execute();
|
|||
|
|
$result = $initialBalanceStmh->fetch(PDO::FETCH_ASSOC);
|
|||
|
|
$runningBalances[$accountDisplay] = $result['balance'] ?? 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$start_num = $total_row;
|
|||
|
|
$counter = 1;
|
|||
|
|
|
|||
|
|
// 2. 루프를 돌며 각 거래에 대한 계좌별 잔액을 계산하고 출력
|
|||
|
|
// 이 while 루프는 페이지 상단에서 시작된 try 블록 안에서 실행됩니다.
|
|||
|
|
while($row = $stmh->fetch(PDO::FETCH_ASSOC)) {
|
|||
|
|
|
|||
|
|
include '_row.php'; // $num, $registDate, $bankbook 등의 변수 할당
|
|||
|
|
|
|||
|
|
$amount = floatval(str_replace(',', '', $row['amount']));
|
|||
|
|
|
|||
|
|
// json에 없는 계좌의 거래가 있을 경우를 대비해 초기화
|
|||
|
|
if (!isset($runningBalances[$bankbook])) {
|
|||
|
|
$runningBalances[$bankbook] = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 해당 계좌의 잔액을 업데이트 (어음이 아닌 경우만)
|
|||
|
|
if ($dueDate === '0000-00-00' || !$dueDate || $dueDate === '' || $bankbook === '전자어음') {
|
|||
|
|
if ($row['inoutsep'] === '수입' || $row['inoutsep'] === '최초전월이월') {
|
|||
|
|
$runningBalances[$bankbook] += $amount;
|
|||
|
|
} else { // 지출
|
|||
|
|
$runningBalances[$bankbook] -= $amount;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
<tr>
|
|||
|
|
<td class="text-center" onclick="loadForm('update', '<?=$num?>');"><?= $counter ?></td>
|
|||
|
|
<td class="text-center" onclick="loadForm('update', '<?=$num?>');"><?= $registDate ?></td>
|
|||
|
|
<td class="text-center <?= ($inoutsep === '수입' || $inoutsep === '최초전월이월') ? 'text-primary' : 'text-danger' ?>" onclick="loadForm('update', '<?=$num?>');"><?= $inoutsep ?></td>
|
|||
|
|
<td class="text-center fw-bold <?= ($inoutsep === '수입' || $inoutsep === '최초전월이월') ? 'text-primary' : 'text-danger' ?>" onclick="loadForm('update', '<?=$num?>');"> <?= $content ?> </td>
|
|||
|
|
<td class="text-center fw-bold <?= ($inoutsep === '수입' || $inoutsep === '최초전월이월') ? 'text-primary' : 'text-danger' ?>" onclick="loadForm('update', '<?=$num?>');"> <?= $contentSub ?> </td>
|
|||
|
|
<td class="text-start <?= ($inoutsep === '수입' || $inoutsep === '최초전월이월') ? 'text-primary' : 'text-danger' ?>" onclick="loadForm('update', '<?=$num?>');"><?= $content_detail ?></td>
|
|||
|
|
<?php if ($inoutsep === '수입' || $inoutsep === '최초전월이월') : ?>
|
|||
|
|
<td class="text-end fw-bold text-primary" onclick="loadForm('update', '<?=$num?>');">
|
|||
|
|
<?= is_numeric($amount) ? number_format($amount) : htmlspecialchars($amount) ?>
|
|||
|
|
</td>
|
|||
|
|
<td class="text-end"></td>
|
|||
|
|
<?php else : ?>
|
|||
|
|
<td class="text-end"></td>
|
|||
|
|
<td class="text-end fw-bold text-danger" onclick="loadForm('update', '<?=$num?>');">
|
|||
|
|
<?= is_numeric($amount) ? number_format($amount) : htmlspecialchars($amount) ?>
|
|||
|
|
</td>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<td class="text-end fw-bold" onclick="loadForm('update', '<?=$num?>');"><?= number_format($runningBalances[$bankbook]) ?></td>
|
|||
|
|
<td class="text-start" onclick="loadForm('update', '<?=$num?>');"><?= $bankbook ?></td>
|
|||
|
|
<td class="text-start" onclick="loadForm('update', '<?=$num?>');">
|
|||
|
|
<?= ($dueDate === '0000-00-00' || !$dueDate) ? '' : '(어음)'. $dueDate ?>
|
|||
|
|
</td>
|
|||
|
|
<td class="text-start" onclick="loadForm('update', '<?=$num?>');">
|
|||
|
|
<?= ($bankbook === '전자어음' && $endorsementDate && $endorsementDate !== '0000-00-00') ? $endorsementDate : '' ?>
|
|||
|
|
</td>
|
|||
|
|
<td class="text-center">
|
|||
|
|
<?php
|
|||
|
|
// picuploads 테이블에서 이미지 검색
|
|||
|
|
$imageSql = "SELECT picname FROM {$DB}.picuploads WHERE tablename = 'account' AND parentnum = :num AND item = 'image' ";
|
|||
|
|
$imageStmh = $pdo->prepare($imageSql);
|
|||
|
|
$imageStmh->bindParam(':num', $num);
|
|||
|
|
$imageStmh->execute();
|
|||
|
|
$images = $imageStmh->fetchAll(PDO::FETCH_COLUMN); // 모든 결과를 배열로 가져옴
|
|||
|
|
|
|||
|
|
if (!empty($images)) {
|
|||
|
|
foreach ($images as $fileId) {
|
|||
|
|
if ($fileId) {
|
|||
|
|
$imageId = 'account_image_' . $num . '_' . array_search($fileId, $images); // 고유 ID 생성
|
|||
|
|
$link = "https://drive.google.com/file/d/{$fileId}/view?usp=drivesdk";
|
|||
|
|
?>
|
|||
|
|
<i class="bi bi-check2-square text-success" style="cursor: pointer;"
|
|||
|
|
onclick="openTmpImagePopup('<?= $link ?>', '<?= $imageId ?>');"></i>
|
|||
|
|
<?php
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
echo ' ';
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
<?php
|
|||
|
|
$start_num--;
|
|||
|
|
$counter++;
|
|||
|
|
} // while 루프의 닫는 괄호
|
|||
|
|
|
|||
|
|
// ▼▼▼ 페이지 상단의 try 구문을 닫는 필수 catch 구문 ▼▼▼
|
|||
|
|
} catch (PDOException $Exception) {
|
|||
|
|
print "오류: ".$Exception->getMessage();
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
|
|||
|
|
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<!-- 이미지 로딩 모달 -->
|
|||
|
|
<div class="modal fade" id="loadingImageModal" tabindex="-1" aria-labelledby="loadingImageModalLabel" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
|
|||
|
|
<div class="modal-dialog modal-dialog-centered modal-sm">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<div class="modal-body text-center">
|
|||
|
|
<div class="spinner-border text-primary" role="status">
|
|||
|
|
<span class="visually-hidden">Loading...</span>
|
|||
|
|
</div>
|
|||
|
|
<p class="mt-2 mb-0">이미지를 불러오는 중...</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// 페이지 로딩
|
|||
|
|
$(document).ready(function(){
|
|||
|
|
var loader = document.getElementById('loadingOverlay');
|
|||
|
|
if (loader) {
|
|||
|
|
loader.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
$("#loadingOverlay").hide();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function numberWithCommas(x) {
|
|||
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
$(document).ready(function() {
|
|||
|
|
|
|||
|
|
const initialBalance = <?= json_encode($initialBalance) ?>;
|
|||
|
|
const finalBalance = <?= json_encode($finalBalance) ?>;
|
|||
|
|
|
|||
|
|
dataTable = $('#myTable').DataTable({
|
|||
|
|
"paging": true,
|
|||
|
|
"ordering": true,
|
|||
|
|
"searching": true,
|
|||
|
|
"pageLength": 1000,
|
|||
|
|
"lengthMenu": [1000],
|
|||
|
|
"language": {
|
|||
|
|
"lengthMenu": "Show _MENU_ entries",
|
|||
|
|
"search": "Live Search:"
|
|||
|
|
},
|
|||
|
|
"order": [[0, 'desc']],
|
|||
|
|
"dom": 't<"bottom"ip>',
|
|||
|
|
"columnDefs": [
|
|||
|
|
{
|
|||
|
|
"orderable": true, // 정렬 활성화
|
|||
|
|
"targets": [0, 1, 2, 3, 4, 5, 6, 7, 8] // 정렬 가능하도록 설정할 열 인덱스
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"orderable": false, // 정렬 비활성화
|
|||
|
|
"targets": [9, 10, 11, 12] // 나머지 열 (계좌, 만기일자, 배서일자) 비활성화
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"footerCallback": function (row, data, start, end, display) {
|
|||
|
|
var api = this.api();
|
|||
|
|
|
|||
|
|
var intVal = function (i) {
|
|||
|
|
return typeof i === 'string' ?
|
|||
|
|
i.replace(/[\$,]/g, '')*1 :
|
|||
|
|
typeof i === 'number' ?
|
|||
|
|
i : 0;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Calculate the totals for Income, Expense, and Balance
|
|||
|
|
// 컬럼숫자 입력
|
|||
|
|
var totalIncomeAmount = api.column(6, { page: 'current' }).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0);
|
|||
|
|
var totalExpenseAmount = api.column(7, { page: 'current' }).data().reduce(function (a, b) { return intVal(a) + intVal(b); }, 0);
|
|||
|
|
|
|||
|
|
// Update the header with the calculated totals
|
|||
|
|
$('#totalIncomeAmount').html(numberWithCommas(totalIncomeAmount));
|
|||
|
|
$('#totalExpenseAmount').html(numberWithCommas(totalExpenseAmount));
|
|||
|
|
$('#totalBalanceAmount').html(numberWithCommas(finalBalance));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
|
|||
|
|
let isSaving = false;
|
|||
|
|
var ajaxRequest = null;
|
|||
|
|
var ajaxRequest_SubOption = null;
|
|||
|
|
|
|||
|
|
// PHP 변수를 JS 에 전달
|
|||
|
|
const bulkTimeKey = "<?= $bulkTimeKey ?>";
|
|||
|
|
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
|
|||
|
|
$("#newBtn").on("click", function() {
|
|||
|
|
loadForm('insert');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
$("#searchBtn").on("click", function() {
|
|||
|
|
$("#board_form").submit();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 복사 버튼 클릭 이벤트
|
|||
|
|
$(document).on("click", "#copyBtn", function() {
|
|||
|
|
document.querySelector(".modal-body .custom-card").innerHTML = '';
|
|||
|
|
$("#myModal").hide();
|
|||
|
|
var num = $("#num").val();
|
|||
|
|
loadForm('copy', num);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 전자어음 선택 버튼 클릭 이벤트
|
|||
|
|
$(document).on("click", "#selectElectronicBillBtn", function() {
|
|||
|
|
// 거래처가 먼저 선택되어 있는지 확인
|
|||
|
|
const secondordnum = document.getElementById('secondordnum').value;
|
|||
|
|
if (!secondordnum || secondordnum.trim() === '') {
|
|||
|
|
alert('전자어음을 선택하기 전에 먼저 거래처를 검색하여 선택해주세요.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
loadElectronicBills();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 모달창에 내용을 넣는 구조임 모달을 부르고 내용을 동적으로 넣는다.
|
|||
|
|
function loadForm(mode, num = null) {
|
|||
|
|
// 로딩 오버레이 보여주기
|
|||
|
|
$("#loadingOverlay").show();
|
|||
|
|
|
|||
|
|
if (num == null || mode == 'copy') {
|
|||
|
|
$("#mode").val('insert');
|
|||
|
|
} else {
|
|||
|
|
$("#mode").val('update');
|
|||
|
|
$("#num").val(num);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (ajaxRequest !== null) {
|
|||
|
|
ajaxRequest.abort();
|
|||
|
|
}
|
|||
|
|
ajaxRequest = $.ajax({
|
|||
|
|
type: "POST",
|
|||
|
|
url: "fetch_modal.php",
|
|||
|
|
data: { mode: mode, num: num },
|
|||
|
|
dataType: "html",
|
|||
|
|
success: function(response) {
|
|||
|
|
document.querySelector(".modal-body .custom-card").innerHTML = response;
|
|||
|
|
|
|||
|
|
$("#myModal").show();
|
|||
|
|
|
|||
|
|
// PHP 데이터를 JSON으로 인코딩
|
|||
|
|
const incomeOptions = <?php echo json_encode($incomeOptions, JSON_UNESCAPED_UNICODE); ?>;
|
|||
|
|
const expenseOptions = <?php echo json_encode($expenseOptions, JSON_UNESCAPED_UNICODE); ?>;
|
|||
|
|
|
|||
|
|
console.log('Income Options:', incomeOptions);
|
|||
|
|
console.log('Expense Options:', expenseOptions);
|
|||
|
|
|
|||
|
|
function updateDescription() {
|
|||
|
|
const contentSelect = document.getElementById('content');
|
|||
|
|
const descriptionDiv = document.getElementById('content_description');
|
|||
|
|
const selectedValue = contentSelect.value;
|
|||
|
|
|
|||
|
|
// 수입인지 지출인지에 따라 설명 변경
|
|||
|
|
const descriptions = document.querySelector('input[name="inoutsep"]:checked').value === '수입' ? incomeOptions : expenseOptions;
|
|||
|
|
descriptionDiv.innerText = descriptions[selectedValue] || '';
|
|||
|
|
|
|||
|
|
// '거래처 수금' 또는 계좌가 '전자어음'일 때 검색 버튼 추가
|
|||
|
|
const bankbookSelect = document.getElementById('bankbook');
|
|||
|
|
const isElectronicBill = bankbookSelect && bankbookSelect.value === '전자어음';
|
|||
|
|
if (selectedValue === '거래처 수금' || isElectronicBill) {
|
|||
|
|
// contentSub를 '외상매출금'으로 설정 (거래처 수금일 때만)
|
|||
|
|
if (selectedValue === '거래처 수금') {
|
|||
|
|
const contentSubSelect = document.getElementById('contentSub');
|
|||
|
|
if (contentSubSelect) {
|
|||
|
|
contentSubSelect.innerHTML = '<option value="외상매출금">외상매출금</option>';
|
|||
|
|
contentSubSelect.value = '외상매출금';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 검색 버튼이 이미 있는지 확인
|
|||
|
|
let searchBtn = document.getElementById('phonebookSearchBtn');
|
|||
|
|
if (!searchBtn) {
|
|||
|
|
searchBtn = document.createElement('button');
|
|||
|
|
searchBtn.id = 'phonebookSearchBtn';
|
|||
|
|
searchBtn.type = 'button';
|
|||
|
|
searchBtn.className = 'btn btn-primary btn-sm ms-2 w120px';
|
|||
|
|
searchBtn.innerHTML = '거래처 <i class="bi bi-search"></i> ';
|
|||
|
|
descriptionDiv.parentNode.insertBefore(searchBtn, descriptionDiv.nextSibling);
|
|||
|
|
// 검색 버튼 클릭 이벤트
|
|||
|
|
searchBtn.addEventListener('click', function() {
|
|||
|
|
phonebookBtn('');
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// '거래처 수금'이 아닌 경우 검색 버튼 제거
|
|||
|
|
const searchBtn = document.getElementById('phonebookSearchBtn');
|
|||
|
|
if (searchBtn) {
|
|||
|
|
searchBtn.remove();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateContentOptions() {
|
|||
|
|
console.log('updateContentOptions');
|
|||
|
|
const contentSelect = document.getElementById('content');
|
|||
|
|
if (contentSelect) {
|
|||
|
|
contentSelect.innerHTML = '';
|
|||
|
|
|
|||
|
|
const selectedType = document.querySelector('input[name="inoutsep"]:checked').value;
|
|||
|
|
let options;
|
|||
|
|
|
|||
|
|
if (selectedType === '최초전월이월') {
|
|||
|
|
options = {'최초전월이월': '최초전월이월'};
|
|||
|
|
} else {
|
|||
|
|
options = selectedType === '수입' ? incomeOptions : expenseOptions;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for (const [value, text] of Object.entries(options)) {
|
|||
|
|
const option = document.createElement('option');
|
|||
|
|
option.value = value;
|
|||
|
|
option.text = value;
|
|||
|
|
contentSelect.appendChild(option);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 수입 선택 시 자동으로 '거래처 수금' 선택
|
|||
|
|
if (selectedType === '수입') {
|
|||
|
|
contentSelect.value = '거래처 수금';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
updateDescription();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function phonebookBtn(search)
|
|||
|
|
{
|
|||
|
|
returnID = '수금등록';
|
|||
|
|
href = '/phonebook/list.php?search=' + search + '&returnID=' + returnID;
|
|||
|
|
popupCenter(href, '전화번호 검색', 1800, 800);
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
$(document).on("click", "#closeBtn", function() {
|
|||
|
|
$("#myModal").hide();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
// loadForm() 성공 콜백 안에서
|
|||
|
|
// 1) 기존에 붙어 있던 saveBtn 클릭 핸들러 제거
|
|||
|
|
$(document).off("click", "#saveBtn");
|
|||
|
|
// 2) 새로 핸들러 바인딩
|
|||
|
|
$(document).on("click", "#saveBtn", function() {
|
|||
|
|
// if (isSaving) return;
|
|||
|
|
// isSaving = true;
|
|||
|
|
|
|||
|
|
// AJAX 요청을 보냄
|
|||
|
|
if (ajaxRequest !== null) {
|
|||
|
|
ajaxRequest.abort();
|
|||
|
|
}
|
|||
|
|
ajaxRequest = $.ajax({
|
|||
|
|
url: "/account/insert.php",
|
|||
|
|
type: "post",
|
|||
|
|
data: {
|
|||
|
|
mode: $("#mode").val(),
|
|||
|
|
num: $("#num").val(),
|
|||
|
|
update_log: $("#update_log").val(),
|
|||
|
|
registDate: $("#registDate").val(),
|
|||
|
|
inoutsep: $("input[name='inoutsep']:checked").val(),
|
|||
|
|
content: $("#content").val(),
|
|||
|
|
amount: $("#amount").val(),
|
|||
|
|
dueDate: $("#dueDate").val(),
|
|||
|
|
first_writer: $("#first_writer").val(),
|
|||
|
|
content_detail: $("#content_detail").val(),
|
|||
|
|
contentSub: $("#contentSub").val(),
|
|||
|
|
bankbook: $("#bankbook").val(),
|
|||
|
|
secondordnum: $("#secondordnum").val(),
|
|||
|
|
endorsementDate: $("#endorsementDate").val(),
|
|||
|
|
parentEBNum: $("#parentEBNum").val()
|
|||
|
|
},
|
|||
|
|
dataType: "json",
|
|||
|
|
success: function(response) {
|
|||
|
|
Toastify({
|
|||
|
|
text: "저장 완료",
|
|||
|
|
duration: 3000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top",
|
|||
|
|
position: "center",
|
|||
|
|
backgroundColor: "#4fbe87",
|
|||
|
|
}).showToast();
|
|||
|
|
|
|||
|
|
setTimeout(function() {
|
|||
|
|
$("#myModal").hide();
|
|||
|
|
location.reload();
|
|||
|
|
}, 1500); // 1.5초 후 실행
|
|||
|
|
|
|||
|
|
},
|
|||
|
|
error: function(jqxhr, status, error) {
|
|||
|
|
console.log("AJAX Error: ", status, error);
|
|||
|
|
// isSaving = false;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
$(document).on("click", "#deleteBtn", function() {
|
|||
|
|
var level = '<?= $_SESSION["level"] ?>';
|
|||
|
|
|
|||
|
|
if (level !== '1') {
|
|||
|
|
Swal.fire({
|
|||
|
|
title: '삭제불가',
|
|||
|
|
text: "관리자만 삭제 가능합니다.",
|
|||
|
|
icon: 'error',
|
|||
|
|
confirmButtonText: '확인'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Swal.fire({
|
|||
|
|
title: '자료 삭제',
|
|||
|
|
text: "삭제는 신중! 정말 삭제하시겠습니까?",
|
|||
|
|
icon: 'warning',
|
|||
|
|
showCancelButton: true,
|
|||
|
|
confirmButtonColor: '#3085d6',
|
|||
|
|
cancelButtonColor: '#d33',
|
|||
|
|
confirmButtonText: '삭제',
|
|||
|
|
cancelButtonText: '취소'
|
|||
|
|
}).then((result) => {
|
|||
|
|
if (result.isConfirmed) {
|
|||
|
|
$("#mode").val('delete');
|
|||
|
|
var formData = $("#board_form").serialize();
|
|||
|
|
|
|||
|
|
$.ajax({
|
|||
|
|
url: "/account/insert.php",
|
|||
|
|
type: "post",
|
|||
|
|
data: formData,
|
|||
|
|
success: function(response) {
|
|||
|
|
Toastify({
|
|||
|
|
text: "파일 삭제완료",
|
|||
|
|
duration: 2000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top",
|
|||
|
|
position: "center",
|
|||
|
|
backgroundColor: "#4fbe87",
|
|||
|
|
}).showToast();
|
|||
|
|
|
|||
|
|
$("#myModal").hide();
|
|||
|
|
location.reload();
|
|||
|
|
},
|
|||
|
|
error: function(jqxhr, status, error) {
|
|||
|
|
console.log(jqxhr, status, error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
$(".close").on("click", function() {
|
|||
|
|
$("#myModal").hide();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 항목 선택 변경 시 설명 업데이트
|
|||
|
|
$(document).on("change", "#content", updateDescription);
|
|||
|
|
|
|||
|
|
$(document).on("change", "input[name='inoutsep']", updateContentOptions);
|
|||
|
|
|
|||
|
|
// 계좌 선택 변경 시 배서일자 필드 표시/숨김 처리
|
|||
|
|
$(document).on("change", "#bankbook", function() {
|
|||
|
|
const endorsementDateContainer = document.getElementById('endorsementDateContainer');
|
|||
|
|
const selectElectronicBillBtn = document.getElementById('selectElectronicBillBtn');
|
|||
|
|
|
|||
|
|
if (endorsementDateContainer) {
|
|||
|
|
endorsementDateContainer.style.display = this.value === '전자어음' ? 'inline-block' : 'none';
|
|||
|
|
}
|
|||
|
|
if (selectElectronicBillBtn) {
|
|||
|
|
selectElectronicBillBtn.style.display = this.value === '전자어음' ? 'inline-block' : 'none';
|
|||
|
|
}
|
|||
|
|
// 계좌가 바뀌면 거래처 검색 버튼 표시/숨김도 갱신
|
|||
|
|
if (typeof updateDescription === 'function') updateDescription();
|
|||
|
|
|
|||
|
|
// 전자어음 선택 시 그리기 버튼 표시
|
|||
|
|
const drawBtn = document.getElementById('drawBtn');
|
|||
|
|
if (drawBtn) {
|
|||
|
|
if (this.value === '전자어음') {
|
|||
|
|
drawBtn.style.display = 'inline-block';
|
|||
|
|
} else {
|
|||
|
|
drawBtn.style.display = 'none';
|
|||
|
|
// 그리기 모드가 활성화되어 있다면 비활성화
|
|||
|
|
if (document.body.classList.contains('drawing-mode')) {
|
|||
|
|
toggleDrawingMode();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 전자어음 선택 버튼 클릭 이벤트
|
|||
|
|
$(document).on("click", "#selectElectronicBillBtn", function() {
|
|||
|
|
loadElectronicBills();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 기존 이미지 불러오기 (Google Drive에서 가져오기)
|
|||
|
|
function displayImageLoad() {
|
|||
|
|
$('#displayImage').show();
|
|||
|
|
var data = <?php echo json_encode($saveimagename_arr); ?>;
|
|||
|
|
$("#displayImage").html('');
|
|||
|
|
if (Array.isArray(data) && data.length > 0) {
|
|||
|
|
data.forEach(function (fileData, i) {
|
|||
|
|
const realName = fileData.realname || '다운로드 파일';
|
|||
|
|
const thumbnail = fileData.thumbnail || '/assets/default-thumbnail.png';
|
|||
|
|
const link = fileData.link || '#';
|
|||
|
|
const fileId = fileData.fileId || null;
|
|||
|
|
const rotation = fileData.rotation || 0;
|
|||
|
|
const mode = '<?php echo $mode; ?>';
|
|||
|
|
|
|||
|
|
if (!fileId) {
|
|||
|
|
console.error("fileId가 누락되었습니다. index: " + i, fileData);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 구글 드라이브 이미지 팝업 링크 생성
|
|||
|
|
let deleteButton = '';
|
|||
|
|
if (mode !== 'view') {
|
|||
|
|
deleteButton = `<button type="button" class="btn btn-danger btn-sm mx-3" id="delImage${i}" onclick="delImageFn('${i}', '${fileId}')">
|
|||
|
|
<i class="bi bi-trash"></i>
|
|||
|
|
</button>`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$("#displayImage").append(
|
|||
|
|
"<div class='row mb-3'>" +
|
|||
|
|
"<div class='col d-flex align-items-center justify-content-center'>" +
|
|||
|
|
"<div class='position-relative'>" +
|
|||
|
|
"<a href='#' onclick=\"openTmpImagePopup('" + link + "', 'image" + i + "'); return false;\">" +
|
|||
|
|
"<img id='image" + i + "' src='" + thumbnail + "' style='width:100px; height:auto; transform: rotate(" + rotation + "deg);'>" +
|
|||
|
|
"</a>" +
|
|||
|
|
(mode !== 'view' ?
|
|||
|
|
"<div class='position-absolute top-0 end-0 mt-1 me-1'>" +
|
|||
|
|
"<button type='button' class='btn btn-primary btn-sm rotate-btn' onclick=\"rotateImage('" + fileId + "', 'image" + i + "')\">" +
|
|||
|
|
"<i class='bi bi-arrow-clockwise'></i>" +
|
|||
|
|
"</button>" +
|
|||
|
|
"</div>" : "") +
|
|||
|
|
"</div>" +
|
|||
|
|
deleteButton +
|
|||
|
|
"</div>" +
|
|||
|
|
"</div>"
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
$("#displayImage").append(
|
|||
|
|
"<div class='text-center text-muted'>No files</div>"
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
displayImageLoad(); // 기존이미지 업로드 보이기
|
|||
|
|
|
|||
|
|
// 모달 로드 완료 후 초기 상태 설정
|
|||
|
|
if (typeof updateDescription === 'function') {
|
|||
|
|
updateDescription();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function(jqxhr, status, error) {
|
|||
|
|
console.log("AJAX error in loadForm:", status, error);
|
|||
|
|
},
|
|||
|
|
complete: function() {
|
|||
|
|
// 성공이든 실패든 로딩 오버레이 숨기기
|
|||
|
|
$("#loadingOverlay").hide();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateSubOptions() {
|
|||
|
|
const contentSelect = document.getElementById('content');
|
|||
|
|
const contentSubSelect = document.getElementById('contentSub');
|
|||
|
|
const selectedValue = contentSelect.value;
|
|||
|
|
const inoutsepValue = document.querySelector('input[name="inoutsep"]:checked')?.value || '지출';
|
|||
|
|
|
|||
|
|
if (ajaxRequest !== null) {
|
|||
|
|
ajaxRequest.abort();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AJAX 요청으로 세부항목 가져오기
|
|||
|
|
ajaxRequest = $.ajax({
|
|||
|
|
url: 'fetch_modal.php',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: {
|
|||
|
|
action: 'getSubOptions',
|
|||
|
|
selectedKey: selectedValue,
|
|||
|
|
inoutsep: inoutsepValue // 수입/지출 구분 추가
|
|||
|
|
},
|
|||
|
|
dataType: 'json',
|
|||
|
|
success: function(response) {
|
|||
|
|
// 기존 옵션 초기화
|
|||
|
|
contentSubSelect.innerHTML = '';
|
|||
|
|
|
|||
|
|
if (response.subOptions && response.subOptions.length > 0) {
|
|||
|
|
// 새로운 옵션 추가
|
|||
|
|
response.subOptions.forEach(item => {
|
|||
|
|
for (const key in item) {
|
|||
|
|
const option = document.createElement('option');
|
|||
|
|
option.value = key;
|
|||
|
|
option.text = key;
|
|||
|
|
contentSubSelect.appendChild(option);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
// 세부항목이 없는 경우 기본값 처리
|
|||
|
|
const option = document.createElement('option');
|
|||
|
|
option.value = '';
|
|||
|
|
option.text = '세부항목 없음';
|
|||
|
|
contentSubSelect.appendChild(option);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function(jqxhr, status, error) {
|
|||
|
|
console.log("AJAX Error:", status, error);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// content 선택 변경 이벤트
|
|||
|
|
$(document).on('change', '#content', updateSubOptions);
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
function generateExcel() {
|
|||
|
|
var table = document.getElementById('myTable');
|
|||
|
|
var rows = table.getElementsByTagName('tr');
|
|||
|
|
var data = [];
|
|||
|
|
|
|||
|
|
// 각 행을 반복하여 데이터 수집
|
|||
|
|
for (var i = 1; i < rows.length; i++) { // 헤더 행을 건너뜀
|
|||
|
|
var cells = rows[i].getElementsByTagName('td');
|
|||
|
|
var rowData = {};
|
|||
|
|
rowData['number'] = cells[0]?.innerText || '';
|
|||
|
|
rowData['registDate'] = cells[1]?.innerText || '';
|
|||
|
|
rowData['content'] = cells[2]?.innerText || '';
|
|||
|
|
rowData['contentSub'] = cells[3]?.innerText || '';
|
|||
|
|
rowData['contentDetail'] = cells[4]?.innerText || '';
|
|||
|
|
rowData['income'] = cells[5]?.innerText || '';
|
|||
|
|
rowData['expense'] = cells[6]?.innerText || '';
|
|||
|
|
rowData['balance'] = cells[7]?.innerText || '';
|
|||
|
|
rowData['dueDate'] = cells[8]?.innerText || '';
|
|||
|
|
|
|||
|
|
data.push(rowData);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// saveExcel.php에 데이터 전송
|
|||
|
|
var xhr = new XMLHttpRequest();
|
|||
|
|
xhr.open("POST", "saveExcel.php", true);
|
|||
|
|
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|||
|
|
xhr.onreadystatechange = function () {
|
|||
|
|
if (xhr.readyState == 4) {
|
|||
|
|
if (xhr.status == 200) {
|
|||
|
|
try {
|
|||
|
|
var response = JSON.parse(xhr.responseText);
|
|||
|
|
if (response.success) {
|
|||
|
|
console.log('Excel file generated successfully.');
|
|||
|
|
// 다운로드 스크립트로 리디렉션
|
|||
|
|
window.location.href = 'saveExcel.php?download=' + encodeURIComponent(response.filename);
|
|||
|
|
} else {
|
|||
|
|
console.log('Failed to generate Excel file: ' + response.message);
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
console.log('Error parsing response: ' + e.message + '\nResponse text: ' + xhr.responseText);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.log('Failed to generate Excel file: Server returned status ' + xhr.status);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
xhr.send(JSON.stringify(data));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function settings() {
|
|||
|
|
// 계정설정
|
|||
|
|
const url = `settings.php`;
|
|||
|
|
customPopup(url, '계정 관리', 600, 850);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function detail() {
|
|||
|
|
// detail.php로 이동할 URL 생성
|
|||
|
|
const url = `detail.php`;
|
|||
|
|
|
|||
|
|
// customPopup을 사용하여 detail.php를 팝업으로 열기
|
|||
|
|
customPopup(url, '상세 내역', 800, 900);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
function openPopup(url, title, width, height) {
|
|||
|
|
// 화면 중앙에 팝업을 띄우도록 좌표 계산
|
|||
|
|
const left = (window.screen.width / 2) - (width / 2);
|
|||
|
|
const top = (window.screen.height / 2) - (height / 2);
|
|||
|
|
|
|||
|
|
// 팝업 창 생성
|
|||
|
|
const popupWindow = window.open(
|
|||
|
|
url,
|
|||
|
|
title,
|
|||
|
|
`width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 오버레이 생성
|
|||
|
|
const overlay = document.createElement('div');
|
|||
|
|
overlay.id = 'overlay';
|
|||
|
|
overlay.style.position = 'fixed';
|
|||
|
|
overlay.style.top = 0;
|
|||
|
|
overlay.style.left = 0;
|
|||
|
|
overlay.style.width = '100%';
|
|||
|
|
overlay.style.height = '100%';
|
|||
|
|
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; // 반투명 검은색
|
|||
|
|
overlay.style.zIndex = 10000; // 최상위 레이어
|
|||
|
|
overlay.style.cursor = 'not-allowed'; // 사용자가 클릭하지 못하도록 마우스 커서를 변경
|
|||
|
|
document.body.appendChild(overlay);
|
|||
|
|
|
|||
|
|
// 팝업이 닫히면 오버레이 제거
|
|||
|
|
const interval = setInterval(() => {
|
|||
|
|
if (popupWindow.closed) {
|
|||
|
|
clearInterval(interval);
|
|||
|
|
document.body.removeChild(overlay);
|
|||
|
|
}
|
|||
|
|
}, 500);
|
|||
|
|
|
|||
|
|
// 팝업 창에 포커스 이동
|
|||
|
|
if (window.focus) {
|
|||
|
|
popupWindow.focus();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
function enter() {
|
|||
|
|
$("#board_form").submit();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<!-- 부트스트랩 툴팁 -->
|
|||
|
|
<script>
|
|||
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|||
|
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
|||
|
|
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|||
|
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|||
|
|
});
|
|||
|
|
// $("#order_form_write").modal("show");
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
// PHP 데이터를 JavaScript 변수로 변환
|
|||
|
|
const incomeOptions = <?php echo json_encode($incomeOptions, JSON_UNESCAPED_UNICODE); ?>;
|
|||
|
|
const expenseOptions = <?php echo json_encode($expenseOptions, JSON_UNESCAPED_UNICODE); ?>;
|
|||
|
|
|
|||
|
|
// 수입/지출 선택에 따른 계정과목 업데이트
|
|||
|
|
document.getElementById('inoutsep_select').addEventListener('change', function() {
|
|||
|
|
const contentSelect = document.getElementById('content_select');
|
|||
|
|
const selectedType = this.value;
|
|||
|
|
|
|||
|
|
// 기존 옵션 제거 (전체항목 제외)
|
|||
|
|
while (contentSelect.options.length > 1) {
|
|||
|
|
contentSelect.remove(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 선택된 타입에 따라 옵션 추가
|
|||
|
|
const options = selectedType === '수입' ? incomeOptions : expenseOptions;
|
|||
|
|
|
|||
|
|
// 옵션 추가
|
|||
|
|
Object.keys(options).forEach(key => {
|
|||
|
|
const option = document.createElement('option');
|
|||
|
|
option.value = key;
|
|||
|
|
option.textContent = key;
|
|||
|
|
contentSelect.appendChild(option);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 전체항목으로 초기화
|
|||
|
|
contentSelect.value = '';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
$(document).ready(function(){
|
|||
|
|
// 방문기록 남김
|
|||
|
|
var title = '<?php echo $title_message; ?>';
|
|||
|
|
saveMenuLog(title);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// account/list.php 파일의 기존 <script> 태그 안에 추가
|
|||
|
|
|
|||
|
|
// --- 대량등록 관련 스크립트 시작 ---
|
|||
|
|
$(document).ready(function() {
|
|||
|
|
// 배경 스크롤 방지 함수
|
|||
|
|
function disableBodyScroll() {
|
|||
|
|
$('body').css('overflow', 'hidden');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 배경 스크롤 복원 함수
|
|||
|
|
function enableBodyScroll() {
|
|||
|
|
$('body').css('overflow', 'auto');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// '대량등록' 버튼 클릭 시 모달 열기
|
|||
|
|
$("#bulkNewBtn").on("click", function() {
|
|||
|
|
// 테이블 내용 초기화
|
|||
|
|
$("#bulkEntryTable tbody").empty();
|
|||
|
|
// 첫 행 추가
|
|||
|
|
addRow_BulkEntry();
|
|||
|
|
// 배경 스크롤 방지
|
|||
|
|
disableBodyScroll();
|
|||
|
|
// 모달 표시
|
|||
|
|
$("#bulkEntryModal").show();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 모달 닫기 버튼
|
|||
|
|
$("#bulkEntryModal .close").on("click", function() {
|
|||
|
|
$("#bulkEntryModal").hide();
|
|||
|
|
// 배경 스크롤 복원
|
|||
|
|
enableBodyScroll();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 행 추가 버튼 (+)
|
|||
|
|
$(document).on('click', '#bulkEntryTable .add-row', function() {
|
|||
|
|
addRow_BulkEntry();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 행 삭제 버튼 (-)
|
|||
|
|
$(document).on('click', '#bulkEntryTable .remove-row', function() {
|
|||
|
|
if ($("#bulkEntryTable tbody tr").length > 1) { // 최소 1개의 행은 유지
|
|||
|
|
$(this).closest('tr').remove();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// '전체 저장' 버튼 클릭 이벤트
|
|||
|
|
$("#saveBulkBtn").on("click", function() {
|
|||
|
|
saveBulkData();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// '구분' 변경 시 '항목' 드롭다운 업데이트
|
|||
|
|
$(document).on('change', '.bulk-inoutsep', function() {
|
|||
|
|
updateBulkContentOptions($(this));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// '항목' 변경 시 '세부항목' 드롭다운 업데이트
|
|||
|
|
$(document).on('change', '.bulk-content', function() {
|
|||
|
|
updateBulkSubOptions($(this));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// '계좌' 변경 시 배서일자 필드 표시/숨김
|
|||
|
|
$(document).on('change', '.bulk-bankbook', function() {
|
|||
|
|
const $row = $(this).closest('tr');
|
|||
|
|
const $endorsementDateField = $row.find('.bulk-endorsementDate');
|
|||
|
|
const $selectElectronicBillBtn = $row.find('.bulk-select-electronic-bill-btn');
|
|||
|
|
|
|||
|
|
if (this.value === '전자어음') {
|
|||
|
|
$endorsementDateField.show();
|
|||
|
|
$selectElectronicBillBtn.show();
|
|||
|
|
// 전자어음 선택 시 오늘날짜로 설정
|
|||
|
|
// 지출이 선택된 경우에만 오늘 날짜로 설정
|
|||
|
|
if (!$endorsementDateField.val() && $row.find('.bulk-inoutsep').val() === '지출') {
|
|||
|
|
$endorsementDateField.val(new Date().toISOString().slice(0, 10));
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$endorsementDateField.hide();
|
|||
|
|
$selectElectronicBillBtn.hide();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 대량등록에서 전자어음 선택 버튼 클릭 이벤트
|
|||
|
|
$(document).on('click', '.bulk-select-electronic-bill-btn', function() {
|
|||
|
|
const $row = $(this).closest('tr');
|
|||
|
|
const secondordnum = $row.find('.bulk-secondordnum').val();
|
|||
|
|
|
|||
|
|
// 거래처가 먼저 선택되어 있는지 확인
|
|||
|
|
if (!secondordnum || secondordnum.trim() === '') {
|
|||
|
|
alert('전자어음을 선택하기 전에 먼저 거래처를 검색하여 선택해주세요.');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
loadElectronicBillsForBulk($row);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 대량등록 모달 바깥 영역 클릭 시 닫기
|
|||
|
|
$("#bulkEntryModal").on('click', function(e) {
|
|||
|
|
if (e.target === this) {
|
|||
|
|
$("#bulkEntryModal").hide();
|
|||
|
|
// 배경 스크롤 복원
|
|||
|
|
enableBodyScroll();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
/**
|
|||
|
|
* 대량등록 테이블에 새로운 행을 추가하는 함수
|
|||
|
|
*/
|
|||
|
|
function addRow_BulkEntry() {
|
|||
|
|
// 현재 행의 등록일자 가져오기
|
|||
|
|
const currentRow = $(event.target).closest('tr');
|
|||
|
|
const currentDate = currentRow ? currentRow.find('.bulk-registDate').val() : new Date().toISOString().slice(0, 10);
|
|||
|
|
|
|||
|
|
const bankbookOptions = `<?php
|
|||
|
|
$optionsHtml = "";
|
|||
|
|
$jsonFile = $_SERVER['DOCUMENT_ROOT'] . "/account/accoutlist.json";
|
|||
|
|
if (file_exists($jsonFile)) {
|
|||
|
|
$accounts = json_decode(file_get_contents($jsonFile), true);
|
|||
|
|
if (is_array($accounts)) {
|
|||
|
|
foreach ($accounts as $account) {
|
|||
|
|
$displayText = htmlspecialchars($account['company'] . ' ' . $account['number'] . (!empty($account['memo']) ? ' (' . $account['memo'] . ')' : ''), ENT_QUOTES);
|
|||
|
|
$optionsHtml .= "<option value='" . $displayText . "'>" . $displayText . "</option>";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
echo $optionsHtml;
|
|||
|
|
?><option value="전자어음">전자어음</option>`;
|
|||
|
|
|
|||
|
|
const newRow = `
|
|||
|
|
<tr>
|
|||
|
|
<td class="text-center">
|
|||
|
|
<button type="button" class="btn btn-sm btn-outline-primary add-row" style="padding: 2px 5px;">+</button>
|
|||
|
|
<button type="button" class="btn btn-sm btn-outline-danger remove-row" style="padding: 2px 6px;">-</button>
|
|||
|
|
<button type="button" class="btn btn-sm btn-outline-secondary copy-row" style="padding: 2px 6px;">
|
|||
|
|
<i class="bi bi-files"></i>
|
|||
|
|
</button>
|
|||
|
|
</td>
|
|||
|
|
<td><input type="date" class="form-control form-control-sm bulk-registDate noborder-input" value="${currentDate}"></td>
|
|||
|
|
<td>
|
|||
|
|
<select class="form-select form-select-sm bulk-inoutsep">
|
|||
|
|
<option value="지출" selected>지출</option>
|
|||
|
|
<option value="수입">수입</option>
|
|||
|
|
<option value="최초전월이월">최초전월이월</option>
|
|||
|
|
</select>
|
|||
|
|
</td>
|
|||
|
|
<td><select class="form-select form-select-sm bulk-bankbook">${bankbookOptions}</select></td>
|
|||
|
|
<td><select class="form-select form-select-sm bulk-content"></select></td>
|
|||
|
|
<td><select class="form-select form-select-sm bulk-contentSub"></select></td>
|
|||
|
|
<td>
|
|||
|
|
<button type="button" class="btn btn-sm btn-outline-primary bulk-search-company">
|
|||
|
|
<i class="bi bi-search"></i>
|
|||
|
|
</button>
|
|||
|
|
</td>
|
|||
|
|
<td><input type="text" class="form-control form-control-sm bulk-secondordnum noborder-input" autocomplete="off"></td>
|
|||
|
|
<td><input type="date" class="form-control form-control-sm bulk-dueDate noborder-input"></td>
|
|||
|
|
<td>
|
|||
|
|
<input type="date" class="form-control form-control-sm bulk-endorsementDate noborder-input" value="" style="display: none;">
|
|||
|
|
<button type="button" class="btn btn-outline-primary btn-sm bulk-select-electronic-bill-btn" style="display: none;">
|
|||
|
|
<i class="bi bi-search"></i>
|
|||
|
|
</button>
|
|||
|
|
</td>
|
|||
|
|
<td><input type="text" class="form-control form-control-sm bulk-content_detail noborder-input text-start" autocomplete="off"></td>
|
|||
|
|
<td><input type="text" class="form-control form-control-sm bulk-amount text-end noborder-input" onkeyup="inputNumberFormat(this)" autocomplete="off"></td>
|
|||
|
|
<td>
|
|||
|
|
<input type="file"
|
|||
|
|
class="form-control form-control-sm bulk-image-input"
|
|||
|
|
multiple
|
|||
|
|
accept="image/*">
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
if (currentRow && currentRow.length) {
|
|||
|
|
// 현재 행이 있는 경우 해당 행 다음에 추가
|
|||
|
|
currentRow.after(newRow);
|
|||
|
|
currentRow.next().find('.bulk-inoutsep').trigger('change');
|
|||
|
|
} else {
|
|||
|
|
// 현재 행이 없는 경우(첫 행 추가) 테이블에 추가
|
|||
|
|
$('#bulkEntryTable tbody').append(newRow);
|
|||
|
|
$('#bulkEntryTable tbody tr:last .bulk-inoutsep').trigger('change');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* '구분' (수입/지출/최초전월이월) 선택에 따라 '항목' 드롭다운 메뉴를 업데이트하는 함수
|
|||
|
|
*/
|
|||
|
|
function updateBulkContentOptions($selectElement) {
|
|||
|
|
const selectedType = $selectElement.val();
|
|||
|
|
const contentSelect = $selectElement.closest('tr').find('.bulk-content');
|
|||
|
|
let options;
|
|||
|
|
|
|||
|
|
contentSelect.empty();
|
|||
|
|
if(selectedType == '최초전월이월') {
|
|||
|
|
contentSelect.append(`<option value="최초전월이월">최초전월이월</option>`);
|
|||
|
|
options = incomeOptions;
|
|||
|
|
} else {
|
|||
|
|
options = (selectedType === '수입') ? incomeOptions : expenseOptions;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 나머지 옵션들 추가
|
|||
|
|
for (const key in options) {
|
|||
|
|
if (key !== '전월이월') { // 전월이월은 이미 추가했으므로 건너뜀
|
|||
|
|
contentSelect.append(`<option value="${key}">${key}</option>`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
contentSelect.trigger('change'); // 항목이 변경되었으므로 세부항목도 업데이트
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* '항목' 선택에 따라 '세부항목' 드롭다운 메뉴를 업데이트하는 함수
|
|||
|
|
*/
|
|||
|
|
function updateBulkSubOptions($selectElement) {
|
|||
|
|
const selectedKey = $selectElement.val();
|
|||
|
|
const inoutsepValue = $selectElement.closest('tr').find('.bulk-inoutsep').val() || '지출';
|
|||
|
|
console.log('updateBulkSubOptions 함수 호출 후 selectedKey : ', selectedKey, ', inoutsep:', inoutsepValue);
|
|||
|
|
const contentSubSelect = $selectElement.closest('tr').find('.bulk-contentSub');
|
|||
|
|
|
|||
|
|
$.ajax({
|
|||
|
|
url: 'fetch_modal.php',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: {
|
|||
|
|
action: 'getSubOptions',
|
|||
|
|
selectedKey: selectedKey,
|
|||
|
|
inoutsep: inoutsepValue // 수입/지출 구분 추가
|
|||
|
|
},
|
|||
|
|
dataType: 'json',
|
|||
|
|
success: function(response) {
|
|||
|
|
contentSubSelect.empty();
|
|||
|
|
console.log(response);
|
|||
|
|
if (response.subOptions && response.subOptions.length > 0) {
|
|||
|
|
response.subOptions.forEach(item => {
|
|||
|
|
for (const key in item) {
|
|||
|
|
contentSubSelect.append(`<option value="${key}">${key}</option>`);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
contentSubSelect.append(`<option value="">없음</option>`);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function() {
|
|||
|
|
contentSubSelect.empty().append('<option value="">없음</option>');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 대량 등록 모달의 모든 데이터를 서버로 전송하여 저장하는 함수
|
|||
|
|
*/
|
|||
|
|
function saveBulkData() {
|
|||
|
|
// 1) 행 데이터 수집 (entries 배열)
|
|||
|
|
let entries = [];
|
|||
|
|
$("#bulkEntryTable tbody tr").each(function() {
|
|||
|
|
const $r = $(this);
|
|||
|
|
const amt = $r.find(".bulk-amount").val().replace(/,/g,'');
|
|||
|
|
const registDate = $r.find(".bulk-registDate").val();
|
|||
|
|
|
|||
|
|
if (!amt || !registDate || registDate === '0000-00-00') {
|
|||
|
|
Swal.fire({
|
|||
|
|
title: '등록일과 금액은 필수입니다.',
|
|||
|
|
icon: 'warning',
|
|||
|
|
confirmButtonText: '확인'
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (!amt) return; // 금액 없으면 건너뜀
|
|||
|
|
entries.push({
|
|||
|
|
registDate: $r.find(".bulk-registDate").val(),
|
|||
|
|
inoutsep: $r.find(".bulk-inoutsep").val(),
|
|||
|
|
bankbook: $r.find(".bulk-bankbook").val(),
|
|||
|
|
content: $r.find(".bulk-content").val() || '',
|
|||
|
|
contentSub: $r.find(".bulk-contentSub").val() || '',
|
|||
|
|
content_detail: $r.find(".bulk-content_detail").val() || '',
|
|||
|
|
amount: amt,
|
|||
|
|
secondordnum:$r.find(".bulk-secondordnum").val()|| '',
|
|||
|
|
dueDate: $r.find(".bulk-dueDate").val() || '',
|
|||
|
|
endorsementDate: $r.find(".bulk-endorsementDate").val() || '',
|
|||
|
|
|
|||
|
|
// **여기에 timekey 추가**
|
|||
|
|
timekey: bulkTimeKey
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (entries.length === 0) {
|
|||
|
|
return Swal.fire('입력 없음','저장할 데이터가 없습니다.','info');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2) 서버에 bulk insert
|
|||
|
|
$.ajax({
|
|||
|
|
url: "/account/insert_bulk.php",
|
|||
|
|
method: "POST",
|
|||
|
|
data: { entries: JSON.stringify(entries) },
|
|||
|
|
dataType: "json"
|
|||
|
|
}).done(function(resp) {
|
|||
|
|
if (resp.status !== 'success') {
|
|||
|
|
return Swal.fire('저장 실패', resp.message || 'Bulk insert 오류','error');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// resp.nums 는 [num1, num2, …] 형태로, entries 순서와 같은 인덱스 매칭
|
|||
|
|
const nums = resp.nums;
|
|||
|
|
let uploads = []; // Promise 배열
|
|||
|
|
|
|||
|
|
nums.forEach((num, idx) => {
|
|||
|
|
const input = $("#bulkEntryTable tbody tr").eq(idx).find(".bulk-image-input")[0];
|
|||
|
|
if (input && input.files.length > 0) {
|
|||
|
|
// FormData 로 이미지만 전송
|
|||
|
|
const fd = new FormData();
|
|||
|
|
fd.append("tablename", "account");
|
|||
|
|
fd.append("item", "image");
|
|||
|
|
fd.append("upfilename","bulkImage");
|
|||
|
|
fd.append("folderPath","경동기업/uploads");
|
|||
|
|
fd.append("DBtable", "picuploads");
|
|||
|
|
// **여기도 timekey 사용**
|
|||
|
|
fd.append("timekey", bulkTimeKey);
|
|||
|
|
fd.append("num", num); // 실제 num 업뎃 후에도 num 포함해 두면 안전합니다
|
|||
|
|
|
|||
|
|
Array.from(input.files).forEach(file => {
|
|||
|
|
fd.append("bulkImage[]", file);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
uploads.push(
|
|||
|
|
$.ajax({
|
|||
|
|
url: "/filedrive/fileprocess.php",
|
|||
|
|
method: "POST",
|
|||
|
|
data: fd,
|
|||
|
|
processData: false,
|
|||
|
|
contentType: false
|
|||
|
|
})
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 3) 모든 이미지 업로드가 끝나면 리로드
|
|||
|
|
Promise.all(uploads)
|
|||
|
|
.then(() => {
|
|||
|
|
Toastify({
|
|||
|
|
text: `${entries.length}건 저장 완료`,
|
|||
|
|
duration: 2000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top", position: "center",
|
|||
|
|
backgroundColor: "#4fbe87"
|
|||
|
|
}).showToast();
|
|||
|
|
})
|
|||
|
|
.catch(err => {
|
|||
|
|
console.error("이미지 업로드 중 오류:", err);
|
|||
|
|
Toastify({
|
|||
|
|
text: "일부 이미지 업로드에 실패했습니다.",
|
|||
|
|
duration: 3000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top", position: "center",
|
|||
|
|
backgroundColor: "#f44336"
|
|||
|
|
}).showToast();
|
|||
|
|
})
|
|||
|
|
.finally(() => {
|
|||
|
|
setTimeout(()=> {
|
|||
|
|
$("#bulkEntryModal").hide();
|
|||
|
|
// 배경 스크롤 복원
|
|||
|
|
enableBodyScroll();
|
|||
|
|
location.reload();
|
|||
|
|
}, 1500);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
}).fail((jq,status,err) => {
|
|||
|
|
Swal.fire('서버 오류','대량 저장 중 오류가 발생했습니다.','error');
|
|||
|
|
console.error(status, err);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
// --- 대량등록 관련 스크립트 끝 ---
|
|||
|
|
|
|||
|
|
$(function() {
|
|||
|
|
var $modal = $('#bulkEntryModal');
|
|||
|
|
var $openBtn = $('#openAccountModal');
|
|||
|
|
var $closeBtns = $('#closeAccountModal, #closeAccountModal2');
|
|||
|
|
var $dateInput = $('#accountDate');
|
|||
|
|
var $accountCompany = $('#accountCompany');
|
|||
|
|
var $secondord = $('#secondord');
|
|||
|
|
var $saveBtn = $('#saveAccountBtn');
|
|||
|
|
var $body = $('body');
|
|||
|
|
|
|||
|
|
// 모달 열기
|
|||
|
|
$openBtn.on('click', function() {
|
|||
|
|
$modal.show();
|
|||
|
|
|
|||
|
|
// 날짜 초기화
|
|||
|
|
if (!$dateInput.val()) {
|
|||
|
|
var today = new Date();
|
|||
|
|
var yyyy = today.getFullYear();
|
|||
|
|
var mm = String(today.getMonth() + 1).padStart(2, '0');
|
|||
|
|
var dd = String(today.getDate()).padStart(2, '0');
|
|||
|
|
$dateInput.val(yyyy + '-' + mm + '-' + dd);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 거래처 초기화
|
|||
|
|
if ($secondord.val()) {
|
|||
|
|
$accountCompany.val($secondord.val());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$body.addClass('modal-open'); // 스크롤 방지
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 모달 닫기 (× 버튼들)
|
|||
|
|
$closeBtns.on('click', function() {
|
|||
|
|
$modal.hide();
|
|||
|
|
$body.removeClass('modal-open');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 저장 버튼 클릭
|
|||
|
|
$saveBtn.on('click', function() {
|
|||
|
|
saveData_account(); // 사용자가 정의한 함수 호출
|
|||
|
|
$modal.hide();
|
|||
|
|
$body.removeClass('modal-open');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 바깥 영역 클릭 시 닫기
|
|||
|
|
$modal.on('click', function(e) {
|
|||
|
|
if (e.target === this) {
|
|||
|
|
$modal.hide();
|
|||
|
|
$body.removeClass('modal-open');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
|
|||
|
|
// 거래처 검색 버튼 클릭 이벤트
|
|||
|
|
$(document).on('click', '.bulk-search-company', function() {
|
|||
|
|
const $row = $(this).closest('tr');
|
|||
|
|
const $secondordnum = $row.find('.bulk-secondordnum');
|
|||
|
|
const $contentDetail = $row.find('.bulk-content_detail');
|
|||
|
|
|
|||
|
|
returnID = '수금등록';
|
|||
|
|
href = '/phonebook/list.php?search=&returnID=' + returnID;
|
|||
|
|
popupCenter(href, '전화번호 검색', 1800, 800);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 거래처 선택 시 처리
|
|||
|
|
function setCompanyInfo(companyCode, companyName) {
|
|||
|
|
const $activeRow = $('#bulkEntryTable tbody tr:last');
|
|||
|
|
$activeRow.find('.bulk-secondordnum').val(companyCode);
|
|||
|
|
$activeRow.find('.bulk-content_detail').val(companyName);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 팝업 중앙 정렬 함수
|
|||
|
|
function popupCenter(url, title, w, h) {
|
|||
|
|
var left = (screen.width/2)-(w/2);
|
|||
|
|
var top = (screen.height/2)-(h/2);
|
|||
|
|
return window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 행 복사 이벤트
|
|||
|
|
$(document).on('click', '.copy-row', function() {
|
|||
|
|
const $currentRow = $(this).closest('tr');
|
|||
|
|
const $newRow = $currentRow.clone();
|
|||
|
|
|
|||
|
|
// 현재 행의 값들 저장
|
|||
|
|
const currentInoutsep = $currentRow.find('.bulk-inoutsep').val();
|
|||
|
|
const currentBankbook = $currentRow.find('.bulk-bankbook').val();
|
|||
|
|
const currentContent = $currentRow.find('.bulk-content').val();
|
|||
|
|
const currentContentSub = $currentRow.find('.bulk-contentSub').val();
|
|||
|
|
const currentSecondordnum = $currentRow.find('.bulk-secondordnum').val();
|
|||
|
|
const currentContentDetail = $currentRow.find('.bulk-content_detail').val();
|
|||
|
|
const currentAmount = $currentRow.find('.bulk-amount').val();
|
|||
|
|
const currentDueDate = $currentRow.find('.bulk-dueDate').val();
|
|||
|
|
const currentEndorsementDate = $currentRow.find('.bulk-endorsementDate').val();
|
|||
|
|
const currentRegistDate = $currentRow.find('.bulk-registDate').val();
|
|||
|
|
|
|||
|
|
// 현재 행 바로 아래에 삽입
|
|||
|
|
$currentRow.after($newRow);
|
|||
|
|
|
|||
|
|
// 모든 값들을 새 행에 설정
|
|||
|
|
$newRow.find('.bulk-registDate').val(currentRegistDate);
|
|||
|
|
$newRow.find('.bulk-inoutsep').val(currentInoutsep);
|
|||
|
|
$newRow.find('.bulk-bankbook').val(currentBankbook);
|
|||
|
|
$newRow.find('.bulk-secondordnum').val(currentSecondordnum);
|
|||
|
|
$newRow.find('.bulk-content_detail').val(currentContentDetail);
|
|||
|
|
$newRow.find('.bulk-amount').val(currentAmount);
|
|||
|
|
$newRow.find('.bulk-dueDate').val(currentDueDate);
|
|||
|
|
$newRow.find('.bulk-endorsementDate').val(currentEndorsementDate);
|
|||
|
|
|
|||
|
|
// 구분 변경 이벤트 트리거
|
|||
|
|
$newRow.find('.bulk-inoutsep').trigger('change');
|
|||
|
|
|
|||
|
|
// 항목과 세부항목 설정을 위한 지연 처리
|
|||
|
|
setTimeout(function() {
|
|||
|
|
$newRow.find('.bulk-content').val(currentContent);
|
|||
|
|
$newRow.find('.bulk-content').trigger('change');
|
|||
|
|
|
|||
|
|
// 세부항목 설정을 위한 추가 지연
|
|||
|
|
setTimeout(function() {
|
|||
|
|
$newRow.find('.bulk-contentSub').val(currentContentSub);
|
|||
|
|
}, 100);
|
|||
|
|
}, 100);
|
|||
|
|
|
|||
|
|
// 토스트 메시지 표시
|
|||
|
|
Toastify({
|
|||
|
|
text: "행이 복사되었습니다",
|
|||
|
|
duration: 2000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top",
|
|||
|
|
position: "center",
|
|||
|
|
backgroundColor: "#4fbe87",
|
|||
|
|
}).showToast();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 항목 선택 변경 이벤트 핸들러 수정
|
|||
|
|
$(document).on('change', '.bulk-content', function() {
|
|||
|
|
const $row = $(this).closest('tr');
|
|||
|
|
const selectedKey = $(this).val();
|
|||
|
|
const inoutsepValue = $row.find('.bulk-inoutsep').val() || '지출';
|
|||
|
|
const $contentSubSelect = $row.find('.bulk-contentSub');
|
|||
|
|
|
|||
|
|
// 현재 선택된 세부항목 값 저장
|
|||
|
|
const currentSubValue = $contentSubSelect.val();
|
|||
|
|
|
|||
|
|
$.ajax({
|
|||
|
|
url: 'fetch_modal.php',
|
|||
|
|
type: 'POST',
|
|||
|
|
data: {
|
|||
|
|
action: 'getSubOptions',
|
|||
|
|
selectedKey: selectedKey,
|
|||
|
|
inoutsep: inoutsepValue // 수입/지출 구분 추가
|
|||
|
|
},
|
|||
|
|
dataType: 'json',
|
|||
|
|
success: function(response) {
|
|||
|
|
$contentSubSelect.empty();
|
|||
|
|
if (response.subOptions && response.subOptions.length > 0) {
|
|||
|
|
response.subOptions.forEach(item => {
|
|||
|
|
for (const key in item) {
|
|||
|
|
const option = document.createElement('option');
|
|||
|
|
option.value = key;
|
|||
|
|
option.text = key;
|
|||
|
|
$contentSubSelect.append(option);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
// 이전에 선택된 값이 있으면 다시 선택
|
|||
|
|
if (currentSubValue) {
|
|||
|
|
$contentSubSelect.val(currentSubValue);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$contentSubSelect.append(`<option value="">없음</option>`);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function() {
|
|||
|
|
$contentSubSelect.empty().append('<option value="">없음</option>');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
|
|||
|
|
function getGoogleDriveFileId(link) {
|
|||
|
|
// 구글드라이브 파일ID 추출
|
|||
|
|
var match = link.match(/\/d\/([a-zA-Z0-9_-]+)/);
|
|||
|
|
return match ? match[1] : '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- 여기부터 수정 및 추가된 함수 ---
|
|||
|
|
// 로딩 모달을 보여주는 함수 (새로 추가)
|
|||
|
|
function showImageLoadingModal() {
|
|||
|
|
$('#loadingImageModal').modal('show');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 로딩 모달을 숨기는 함수 (새로 추가)
|
|||
|
|
function hideImageLoadingModal() {
|
|||
|
|
$('#loadingImageModal').modal('hide');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 이미지 팝업을 여는 함수 (보완됨)
|
|||
|
|
function openTmpImagePopup(link, imageId) {
|
|||
|
|
showImageLoadingModal(); // 이미지 클릭 즉시 로딩 모달 표시
|
|||
|
|
|
|||
|
|
console.log('link : ', link);
|
|||
|
|
console.log('imageId : ', imageId);
|
|||
|
|
|
|||
|
|
var fileId = getGoogleDriveFileId(link);
|
|||
|
|
if (!fileId) {
|
|||
|
|
alert('구글드라이브 파일ID 추출 실패');
|
|||
|
|
hideImageLoadingModal(); // 오류 발생 시 모달 숨김
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const imgElement = document.getElementById(imageId);
|
|||
|
|
let currentRotation = 0;
|
|||
|
|
|
|||
|
|
if (imgElement) {
|
|||
|
|
const transformStyle = imgElement.style.transform;
|
|||
|
|
currentRotation = transformStyle
|
|||
|
|
? parseInt(transformStyle.replace('rotate(', '').replace('deg)', '')) || 0
|
|||
|
|
: 0;
|
|||
|
|
} else {
|
|||
|
|
console.warn('이미지 요소가 존재하지 않습니다:', imageId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$.post('/filedrive/download_and_rotate.php', { fileId: fileId, rotation: currentRotation }, function(res) {
|
|||
|
|
if (res.success) {
|
|||
|
|
// 팝업 창 열기. 팝업 창이 로딩 모달을 닫는 역할을 함.
|
|||
|
|
var popupWindow = popupCenter(
|
|||
|
|
'/filedrive/view_tmpimg.php?img=' + encodeURIComponent(res.imgUrl) + '&rotation=' + res.rotation,
|
|||
|
|
'imagePopup', 800, 600
|
|||
|
|
);
|
|||
|
|
if (!popupWindow) {
|
|||
|
|
alert('팝업 창을 여는 데 실패했습니다. 브라우저의 팝업 차단 설정을 확인해주세요.');
|
|||
|
|
hideImageLoadingModal();
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
alert(res.msg || '이미지 처리 실패');
|
|||
|
|
hideImageLoadingModal();
|
|||
|
|
}
|
|||
|
|
}, 'json').fail(function() {
|
|||
|
|
alert('서버와 통신 중 오류가 발생했습니다.');
|
|||
|
|
hideImageLoadingModal();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 전자어음 데이터 로드 함수
|
|||
|
|
function loadElectronicBills() {
|
|||
|
|
$.ajax({
|
|||
|
|
url: 'get_electronic_bills.php',
|
|||
|
|
type: 'GET',
|
|||
|
|
dataType: 'json',
|
|||
|
|
success: function(response) {
|
|||
|
|
if (response.success) {
|
|||
|
|
displayElectronicBills(response.data);
|
|||
|
|
$('#electronicBillModal').modal('show');
|
|||
|
|
} else {
|
|||
|
|
alert('전자어음 데이터를 불러오는데 실패했습니다.');
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function() {
|
|||
|
|
alert('서버와 통신 중 오류가 발생했습니다.');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 전자어음 목록 표시 함수
|
|||
|
|
function displayElectronicBills(data) {
|
|||
|
|
const tbody = $('#electronicBillTableBody');
|
|||
|
|
tbody.empty();
|
|||
|
|
|
|||
|
|
if (data.length === 0) {
|
|||
|
|
tbody.append('<tr><td colspan="8" class="text-center">배서일자가 없는 전자어음이 없습니다.</td></tr>');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data.forEach(function(item) {
|
|||
|
|
const row = `
|
|||
|
|
<tr>
|
|||
|
|
<td>${item.num}</td>
|
|||
|
|
<td>${item.registDate}</td>
|
|||
|
|
<td>${item.content || ''}</td>
|
|||
|
|
<td>${item.contentSub || ''}</td>
|
|||
|
|
<td>${item.content_detail || ''}</td>
|
|||
|
|
<td class="text-end">${numberWithCommas(item.amount)}</td>
|
|||
|
|
<td>${item.dueDate || ''}</td>
|
|||
|
|
<td>
|
|||
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="selectElectronicBill('${item.num}', '${item.content}', '${item.contentSub}', '${item.content_detail}', '${item.amount}', '${item.dueDate}')">
|
|||
|
|
선택
|
|||
|
|
</button>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
`;
|
|||
|
|
tbody.append(row);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 전자어음 선택 함수
|
|||
|
|
function selectElectronicBill(num, content, contentSub, contentDetail, amount, dueDate) {
|
|||
|
|
// 모달 닫기
|
|||
|
|
$('#electronicBillModal').modal('hide');
|
|||
|
|
|
|||
|
|
// 구분이 '지출'인 경우 항목을 '외상매출채권(전자어음)'으로 설정
|
|||
|
|
const inoutsep = document.getElementById('inoutsep') ? document.getElementById('inoutsep').value : '';
|
|||
|
|
if (inoutsep === '지출') {
|
|||
|
|
$('#content').val('외상매출채권(전자어음)');
|
|||
|
|
$('#contentSub').val('없음');
|
|||
|
|
} else {
|
|||
|
|
$('#content').val(content);
|
|||
|
|
$('#contentSub').val(contentSub);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$('#content_detail').val(contentDetail);
|
|||
|
|
$('#amount').val(numberWithCommas(amount));
|
|||
|
|
$('#dueDate').val(dueDate);
|
|||
|
|
$('#parentEBNum').val(num);
|
|||
|
|
|
|||
|
|
// 항목 변경 이벤트 트리거
|
|||
|
|
$('#content').trigger('change');
|
|||
|
|
|
|||
|
|
// 토스트 메시지 표시
|
|||
|
|
Toastify({
|
|||
|
|
text: "전자어음 데이터가 적용되었습니다",
|
|||
|
|
duration: 2000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top",
|
|||
|
|
position: "center",
|
|||
|
|
backgroundColor: "#4fbe87",
|
|||
|
|
}).showToast();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 대량등록용 전자어음 데이터 로드 함수
|
|||
|
|
function loadElectronicBillsForBulk($row) {
|
|||
|
|
$.ajax({
|
|||
|
|
url: 'get_electronic_bills.php',
|
|||
|
|
type: 'GET',
|
|||
|
|
dataType: 'json',
|
|||
|
|
success: function(response) {
|
|||
|
|
if (response.success) {
|
|||
|
|
displayElectronicBillsForBulk(response.data, $row);
|
|||
|
|
$('#electronicBillModal').modal('show');
|
|||
|
|
} else {
|
|||
|
|
alert('전자어음 데이터를 불러오는데 실패했습니다.');
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
error: function() {
|
|||
|
|
alert('서버와 통신 중 오류가 발생했습니다.');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 대량등록용 전자어음 목록 표시 함수
|
|||
|
|
function displayElectronicBillsForBulk(data, $row) {
|
|||
|
|
const tbody = $('#electronicBillTableBody');
|
|||
|
|
tbody.empty();
|
|||
|
|
|
|||
|
|
if (data.length === 0) {
|
|||
|
|
tbody.append('<tr><td colspan="8" class="text-center">배서일자가 없는 전자어음이 없습니다.</td></tr>');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data.forEach(function(item) {
|
|||
|
|
const rowIndex = $row.index();
|
|||
|
|
const row = `
|
|||
|
|
<tr>
|
|||
|
|
<td>${item.num}</td>
|
|||
|
|
<td>${item.registDate}</td>
|
|||
|
|
<td>${item.content || ''}</td>
|
|||
|
|
<td>${item.contentSub || ''}</td>
|
|||
|
|
<td>${item.content_detail || ''}</td>
|
|||
|
|
<td class="text-end">${numberWithCommas(item.amount)}</td>
|
|||
|
|
<td>${item.dueDate || ''}</td>
|
|||
|
|
<td>
|
|||
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="selectElectronicBillForBulk('${item.num}', '${item.content || ''}', '${item.contentSub || ''}', '${item.content_detail || ''}', '${item.amount}', '${item.dueDate || ''}', ${rowIndex})">
|
|||
|
|
선택
|
|||
|
|
</button>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
`;
|
|||
|
|
tbody.append(row);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 대량등록용 전자어음 선택 함수
|
|||
|
|
function selectElectronicBillForBulk(num, content, contentSub, contentDetail, amount, dueDate, rowIndex) {
|
|||
|
|
// 모달 닫기
|
|||
|
|
$('#electronicBillModal').modal('hide');
|
|||
|
|
|
|||
|
|
// 해당 행의 필드에 데이터 적용
|
|||
|
|
const $row = $('#bulkEntryTable tbody tr').eq(rowIndex);
|
|||
|
|
|
|||
|
|
// 구분이 '지출'인 경우 항목을 '외상매출채권(전자어음)'으로 설정
|
|||
|
|
const inoutsep = $row.find('.bulk-inoutsep').val();
|
|||
|
|
if (inoutsep === '지출') {
|
|||
|
|
$row.find('.bulk-content').val('외상매출채권(전자어음)');
|
|||
|
|
$row.find('.bulk-contentSub').val('없음');
|
|||
|
|
} else {
|
|||
|
|
$row.find('.bulk-content').val(content);
|
|||
|
|
$row.find('.bulk-contentSub').val(contentSub);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$row.find('.bulk-content_detail').val(contentDetail);
|
|||
|
|
$row.find('.bulk-amount').val(numberWithCommas(amount));
|
|||
|
|
$row.find('.bulk-dueDate').val(dueDate);
|
|||
|
|
|
|||
|
|
// 항목 변경 이벤트 트리거
|
|||
|
|
$row.find('.bulk-content').trigger('change');
|
|||
|
|
|
|||
|
|
// 토스트 메시지 표시
|
|||
|
|
Toastify({
|
|||
|
|
text: "전자어음 데이터가 적용되었습니다",
|
|||
|
|
duration: 2000,
|
|||
|
|
close: true,
|
|||
|
|
gravity: "top",
|
|||
|
|
position: "center",
|
|||
|
|
backgroundColor: "#4fbe87",
|
|||
|
|
}).showToast();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 전자어음 목록 불러오기
|
|||
|
|
|
|||
|
|
// 그리기 모드 토글 함수
|
|||
|
|
function toggleDrawingMode() {
|
|||
|
|
const body = document.body;
|
|||
|
|
const isDrawingMode = body.classList.contains('drawing-mode');
|
|||
|
|
|
|||
|
|
if (isDrawingMode) {
|
|||
|
|
// 그리기 모드 비활성화
|
|||
|
|
body.classList.remove('drawing-mode');
|
|||
|
|
// 그리기 관련 컨트롤들 숨기기
|
|||
|
|
const drawingControls = document.querySelectorAll('.drawing-controls .btn, .drawing-controls select, .drawing-controls input[type="color"], .drawing-controls input[type="range"], .drawing-controls span');
|
|||
|
|
drawingControls.forEach(control => {
|
|||
|
|
if (control.id !== 'drawBtn') {
|
|||
|
|
control.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
// 캔버스 제거
|
|||
|
|
const canvas = document.querySelector('.drawing-canvas');
|
|||
|
|
if (canvas) {
|
|||
|
|
canvas.remove();
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 그리기 모드 활성화
|
|||
|
|
body.classList.add('drawing-mode');
|
|||
|
|
// 그리기 관련 컨트롤들 표시
|
|||
|
|
const drawingControls = document.querySelectorAll('.drawing-controls .btn, .drawing-controls select, .drawing-controls input[type="color"], .drawing-controls input[type="range"], .drawing-controls span');
|
|||
|
|
drawingControls.forEach(control => {
|
|||
|
|
if (control.id !== 'drawBtn') {
|
|||
|
|
control.style.display = 'inline-block';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
// 캔버스 생성
|
|||
|
|
createDrawingCanvas();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 그리기 캔버스 생성 함수
|
|||
|
|
function createDrawingCanvas() {
|
|||
|
|
const previewContainer = document.getElementById('previewContainer');
|
|||
|
|
if (!previewContainer) return;
|
|||
|
|
|
|||
|
|
// 기존 캔버스 제거
|
|||
|
|
const existingCanvas = document.querySelector('.drawing-canvas');
|
|||
|
|
if (existingCanvas) {
|
|||
|
|
existingCanvas.remove();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 새 캔버스 생성
|
|||
|
|
const canvas = document.createElement('canvas');
|
|||
|
|
canvas.className = 'drawing-canvas';
|
|||
|
|
canvas.width = previewContainer.offsetWidth;
|
|||
|
|
canvas.height = previewContainer.offsetHeight;
|
|||
|
|
|
|||
|
|
previewContainer.appendChild(canvas);
|
|||
|
|
|
|||
|
|
// 그리기 기능 초기화
|
|||
|
|
initDrawingCanvas(canvas);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 그리기 기능 초기화
|
|||
|
|
function initDrawingCanvas(canvas) {
|
|||
|
|
const ctx = canvas.getContext('2d');
|
|||
|
|
let isDrawing = false;
|
|||
|
|
let lastX = 0;
|
|||
|
|
let lastY = 0;
|
|||
|
|
|
|||
|
|
// 마우스 이벤트 리스너
|
|||
|
|
canvas.addEventListener('mousedown', startDrawing);
|
|||
|
|
canvas.addEventListener('mousemove', draw);
|
|||
|
|
canvas.addEventListener('mouseup', stopDrawing);
|
|||
|
|
canvas.addEventListener('mouseout', stopDrawing);
|
|||
|
|
|
|||
|
|
function startDrawing(e) {
|
|||
|
|
isDrawing = true;
|
|||
|
|
[lastX, lastY] = [e.offsetX, e.offsetY];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function draw(e) {
|
|||
|
|
if (!isDrawing) return;
|
|||
|
|
|
|||
|
|
ctx.beginPath();
|
|||
|
|
ctx.moveTo(lastX, lastY);
|
|||
|
|
ctx.lineTo(e.offsetX, e.offsetY);
|
|||
|
|
ctx.strokeStyle = document.getElementById('drawColor').value;
|
|||
|
|
ctx.lineWidth = document.getElementById('eraserSize').value;
|
|||
|
|
ctx.lineCap = 'round';
|
|||
|
|
ctx.stroke();
|
|||
|
|
|
|||
|
|
[lastX, lastY] = [e.offsetX, e.offsetY];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopDrawing() {
|
|||
|
|
isDrawing = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 그리기 버튼 클릭 이벤트
|
|||
|
|
$(document).on("click", "#drawBtn", function() {
|
|||
|
|
toggleDrawingMode();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 지우개 크기 조절
|
|||
|
|
$(document).on("input", "#eraserSize", function() {
|
|||
|
|
document.getElementById('eraserSizeLabel').textContent = this.value + 'px';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 그리기 초기화 버튼
|
|||
|
|
$(document).on("click", "#clearDrawingBtn", function() {
|
|||
|
|
const canvas = document.querySelector('.drawing-canvas');
|
|||
|
|
if (canvas) {
|
|||
|
|
const ctx = canvas.getContext('2d');
|
|||
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 그리기 저장 버튼
|
|||
|
|
$(document).on("click", "#saveDrawingBtn", function() {
|
|||
|
|
const canvas = document.querySelector('.drawing-canvas');
|
|||
|
|
if (canvas) {
|
|||
|
|
const dataURL = canvas.toDataURL('image/png');
|
|||
|
|
// 여기에 저장 로직 추가
|
|||
|
|
console.log('그리기 저장:', dataURL);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
</body>
|
|||
|
|
</html>
|