퍼스트가든에서 사용하는 사용자 함수 및 관련파일 추가
This commit is contained in:
4
manager/bakery/_common.php
Normal file
4
manager/bakery/_common.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
include_once '../../common.php';
|
||||
include_once '../config.php';
|
||||
include_once '../lib/lib.php';
|
||||
65
manager/bakery/bakery_force_update.php
Normal file
65
manager/bakery/bakery_force_update.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
include_once "_common.php";
|
||||
|
||||
// 테이블 존재 유무 확인 쿼리
|
||||
$query_check_table = "SHOW TABLES LIKE '{$fg['bakery_product_table']}'";
|
||||
$result_check_table = sql_query($query_check_table);
|
||||
echo "테이블 존재 확인 쿼리 실행 결과...................." . json_encode($result_check_table) . "<br>";
|
||||
|
||||
if (sql_num_rows($result_check_table) == 0) {
|
||||
$query_create_table = "CREATE TABLE {$fg['bakery_product_table']} (
|
||||
idx INT AUTO_INCREMENT PRIMARY KEY,
|
||||
product_name VARCHAR(255) NOT NULL,
|
||||
barcode INT NOT NULL UNIQUE,
|
||||
used TINYINT(1) DEFAULT 1
|
||||
)";
|
||||
$result_create_table = sql_query($query_create_table);
|
||||
echo "테이블 생성 쿼리 실행 결과...................." . json_encode($result_create_table) . "<br>";
|
||||
} else {
|
||||
echo "테이블이 이미 존재합니다.<br>";
|
||||
}
|
||||
|
||||
// 중복을 제외하고 업데이트하는 쿼리 이전에 존재하는 데이터의 카운트를 가져옴
|
||||
$query_count_inventory = "SELECT COUNT(*) as total FROM {$fg['bakery_inventory_table']}";
|
||||
$result_count_inventory = sql_query($query_count_inventory);
|
||||
$row_count_inventory = sql_fetch_array($result_count_inventory);
|
||||
echo "{$fg['bakery_inventory_table']}에서 존재하는 데이터의 총 수...................." . $row_count_inventory['total'] . "<br>";
|
||||
|
||||
// 중복을 제외하고 업데이트하는 쿼리를 실행
|
||||
$query_update = "INSERT IGNORE INTO fg_manager_bakery_product_info (product_name, barcode, used)
|
||||
SELECT DISTINCT product_name, barcode, 1
|
||||
FROM fg_manager_bakery_inventory AS inv
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM fg_manager_bakery_product_info AS prod
|
||||
WHERE prod.barcode = inv.barcode
|
||||
)";
|
||||
|
||||
$result_update = sql_query($query_update);
|
||||
if ($result_update) {
|
||||
echo "중복을 제외하고 업데이트 쿼리 실행 성공" . "<br>";
|
||||
// 필드명을 변경하는 쿼리 추가
|
||||
$query_alter_table = "ALTER TABLE {$fg['bakery_inventory_table']} CHANGE product_name product_name_old VARCHAR(255) NOT NULL";
|
||||
$result_alter_table = sql_query($query_alter_table);
|
||||
|
||||
echo "'{$fg['bakery_inventory_table']}'의 'product_name' 필드명이 'product_name_old'로 변경되었습니다.<br>";
|
||||
|
||||
// INSERT된 데이터의 수를 가져옴
|
||||
$query_count_inserted = "SELECT COUNT(*) as total FROM {$fg['bakery_product_table']} WHERE used = 1";
|
||||
$result_count_inserted = sql_query($query_count_inserted);
|
||||
$row_count_inserted = sql_fetch_array($result_count_inserted);
|
||||
|
||||
echo "{$fg['bakery_product_table']}에 삽입된 데이터의 수...................." . $row_count_inserted['total'] . "<br>";
|
||||
|
||||
// 중복 데이터를 제거한 데이터의 수
|
||||
$inserted_rows = $row_count_inserted['total'] - $row_count_inventory['total'];
|
||||
echo "중복을 제거한 후 기록된 데이터의 수...................." . max($inserted_rows, 0) . "<br>";
|
||||
|
||||
} else {
|
||||
echo "업데이트에 실패했습니다. 종료합니다.<br>";
|
||||
// 업데이트가 실패했다면 디버깅을 위해 쿼리를 출력함함
|
||||
print_r($query_update);
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
|
||||
21
manager/bakery/bakery_product_list.ajax.add.php
Normal file
21
manager/bakery/bakery_product_list.ajax.add.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
include_once "_common.php";
|
||||
if(!$_SESSION['user_id']) exit; // 로그인되어있지 않으면 확인 불가
|
||||
|
||||
$product_name = isset($_POST['product_name']) ? trim($_POST['product_name']) : exit;
|
||||
$barcode = isset($_POST['barcode']) ? trim($_POST['barcode']) : exit;
|
||||
$used = isset($_POST['used']) ? trim($_POST['used']) : exit;
|
||||
|
||||
// 데이터베이스에 입력하는 쿼리
|
||||
$query = "INSERT INTO {$fg['bakery_product_table']} (product_name, barcode, used)
|
||||
VALUES ('{$product_name}', '{$barcode}', '{$used}')";
|
||||
|
||||
$result = sql_query($query);
|
||||
|
||||
header("Content-Type: application/json");
|
||||
if ($result) {
|
||||
echo json_encode(["isSuccess" => true]);
|
||||
} else {
|
||||
echo json_encode(["isSuccess" => false, "message" => "추가에 실패하였습니다."]);
|
||||
}
|
||||
?>
|
||||
18
manager/bakery/bakery_product_list.ajax.delete.php
Normal file
18
manager/bakery/bakery_product_list.ajax.delete.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
include_once "_common.php";
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
// 기본적인 보안 조치로 모든 데이터는 필터링하여 보관됩니다.
|
||||
$idx = isset($_POST['idx']) ? intval($_POST['idx']) : exit;
|
||||
|
||||
// 데이터 행 삭제 쿼리
|
||||
$query = "DELETE FROM {$fg['bakery_product_table']} WHERE idx = $idx";
|
||||
|
||||
// 쿼리 실행
|
||||
$result = sql_query($query);
|
||||
|
||||
header("Content-Type: application/json");
|
||||
if ($result) {
|
||||
echo json_encode(["isSuccess" => true]);
|
||||
} else {
|
||||
echo json_encode(["isSuccess" => false]);
|
||||
}
|
||||
34
manager/bakery/bakery_product_list.ajax.update.php
Normal file
34
manager/bakery/bakery_product_list.ajax.update.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
include_once "_common.php";
|
||||
if(!$_SESSION['user_id']) exit; // 로그인되어있지 않으면 확인 불가
|
||||
|
||||
|
||||
|
||||
$product_name = isset($_POST['product_name']) ? trim($_POST['product_name']) : exit;
|
||||
$barcode = isset($_POST['barcode']) ? trim($_POST['barcode']) : exit;
|
||||
$used = isset($_POST['used']) ? trim($_POST['used']) : exit;
|
||||
|
||||
// bakery_stock.php에서는 idx가 넘어오지 않음.
|
||||
if (isset($_POST['idx'])) {
|
||||
$idx = intval($_POST['idx']);
|
||||
} else {
|
||||
$searchQuery = "SELECT idx FROM {$fg['bakery_product_table']} WHERE barcode = '{$barcode}'";
|
||||
$searchResult = sql_fetch($searchQuery);
|
||||
|
||||
$idx = $searchResult['idx'];
|
||||
}
|
||||
|
||||
// 업데이트 쿼리
|
||||
$query = "UPDATE {$fg['bakery_product_table']}
|
||||
SET product_name = '$product_name', barcode = '$barcode', used = $used
|
||||
WHERE idx = $idx";
|
||||
|
||||
$result = sql_query($query);
|
||||
|
||||
header("Content-Type: application/json");
|
||||
if ($result) {
|
||||
echo json_encode(["isSuccess" => true]);
|
||||
} else {
|
||||
echo json_encode(["isSuccess" => false, "message" => "수정에 실패하였습니다."]);
|
||||
}
|
||||
?>
|
||||
155
manager/bakery/bakery_product_list.php
Normal file
155
manager/bakery/bakery_product_list.php
Normal file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
// 베이커리 제품목록록
|
||||
include_once "_common.php";
|
||||
if (!isset($_SESSION['user_id'])) header( 'Location: FG_MANAGER_URL' ); // 로그인 되어있지 않으면 로그인 페이지로 보냄
|
||||
include_once FG_MANAGER_PATH."/head.php";
|
||||
|
||||
// 검색 변수 초기화
|
||||
!isset($search) ?? "";
|
||||
!isset($where) ?? "";
|
||||
!isset($search_count) ?? "";
|
||||
|
||||
$is_debug = false; // 디버깅 시 true로 변경
|
||||
|
||||
// ord_by 로 넘어온 값이 있으면 반영
|
||||
$ord_by = isset($_REQUEST["ord_by"]) ? $_REQUEST["ord_by"] : "ASC";
|
||||
|
||||
// 제품 목록 불러오기
|
||||
$productList = getBakeryProductList($ord_by);
|
||||
|
||||
$totalCountQuery = "SELECT COUNT(*) as cnt FROM {$fg['bakery_product_table']}";
|
||||
$totalCountResult = sql_query($totalCountQuery);
|
||||
$totalCount = sql_fetch_array($totalCountResult)['cnt'];
|
||||
|
||||
$usedCountQuery = "SELECT COUNT(*) as cnt FROM {$fg['bakery_product_table']} WHERE used = 1";
|
||||
$usedCountResult = sql_query($usedCountQuery);
|
||||
$usedCount = sql_fetch_array($usedCountResult)['cnt'];
|
||||
?>
|
||||
|
||||
<style>
|
||||
.bakery tfoot {
|
||||
font-weight: 600;
|
||||
}
|
||||
.sort-icon {
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h2 class="mb-3">베이커리 제품목록</h2>
|
||||
<div class="d-flex justify-content-between my-1 mb-3">
|
||||
<div>
|
||||
<p>전체 품목 수 : <?=$totalCount?> | 활성 품목 수 : <?=$usedCount?></p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#add_modal">추가</button>
|
||||
<!-- 추가 버튼을 누르면 출력한다. -->
|
||||
<div id="add_modal" class="modal fade">
|
||||
<div class="modal-dialog modal-dialog-centered" style="width:800px;">
|
||||
<div class="modal-content">
|
||||
<?php //class="signup" ?>
|
||||
<form class="add" action="bakery_product_list.add.php" method="POST" enctype="multipart/form-data" > <!-- 폼 이름을 알려줌 -->
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">추가</h4>
|
||||
</div>
|
||||
<div class="modal-body text-start">
|
||||
<div class="input-group mb-1">
|
||||
<span class="input-group-text col-md-2" id="product_name">품명</span>
|
||||
<input type="text" class="form-control" id="product_name" name="product_name" value="">
|
||||
<input type="radio" class="btn-check" name="used" id="used_1" value="1" checked required>
|
||||
<label class="btn btn-outline-primary" for="used_1">사용</label>
|
||||
<input type="radio" class="btn-check" name="used" id="used_0" value="0" required>
|
||||
<label class="btn btn-outline-primary" for="used_0">미사용</label>
|
||||
</div>
|
||||
<div class="input-group mb-1">
|
||||
<span class="input-group-text col-md-2" id="barcode">바코드</span>
|
||||
<input type="text" class="form-control" id="barcode" name="barcode" value="" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" id="add_submit" class="btn btn-primary">추가</button>
|
||||
<button type="button" class="btn btn-default" data-bs-dismiss="modal">닫기</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped align-middle table-hover bakery" id="bakeryTable">
|
||||
<colgroup>
|
||||
<col width="30px">
|
||||
<col width="150px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="50px">
|
||||
</colgroup>
|
||||
<!-- 테이블 제목 -->
|
||||
<thead class="table-light">
|
||||
<tr class="align-middle">
|
||||
<th class="text-center no-click">No.</th>
|
||||
<th class="text-center" data-column="product_name" data-order="asc">품목 <span class="sort-icon" data-column="product_name"></span></th>
|
||||
<th class="text-center" data-column="barcode" data-order="asc">바코드 <span class="sort-icon" data-column="barcode"></span></th>
|
||||
<th class="text-center" data-column="current_stock" data-order="asc">사용 <span class="sort-icon" data-column="current_stock"></span></th>
|
||||
<th class="text-center no-click">관리</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- 테이블 데이터 -->
|
||||
<tbody class="table-group-divider" id="bakeryTableBody">
|
||||
<?php
|
||||
// 제품목록 반복문
|
||||
$i = 1;
|
||||
foreach ($productList as $row) {
|
||||
|
||||
?>
|
||||
<tr id="row-<?=$row['barcode']?>">
|
||||
<td class="text-center"><?=$i?></td>
|
||||
<td class="text-center"><?=$row['product_name']?></td>
|
||||
<td class="text-center"><?=$row['barcode']?></td>
|
||||
<td class="text-center"><?=($row['used'] == 1) ? '활성' : '비활성' ?></td>
|
||||
<td class="text-center">
|
||||
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#modify_modal_<?=$row['idx']?>"><i class="fa-solid fa-pen-to-square"></i></button>
|
||||
<a class="btn btn-danger" href="javascript:void(0);" role="button" onclick="confirmDelete('<?=$row['idx']?>')"><i class="fa-solid fa-trash-can"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 수정 모달 -->
|
||||
<div id="modify_modal_<?=$row['idx']?>" class="modal fade" tabindex="-1" aria-labelledby="modify_modal_<?=$row['idx']?>" aria-hidden="true">
|
||||
<div class="modal-dialog modal modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<form class="modify" id="modify_<?=$row['idx']?>" name="modify_<?=$row['idx']?>">
|
||||
<input type="hidden" name="idx" id="idx_<?=$row['idx']?>" value="<?=$row['idx']?>">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">정보 보기/수정</h5>
|
||||
</div>
|
||||
<div class="modal-body text-start">
|
||||
<div class="input-group mb-1">
|
||||
<span class="input-group-text col-md-2" id="product_name_<?=$row['idx']?>">품명</span>
|
||||
<input type="text" class="form-control" id="product_name_<?=$row['idx']?>" name="product_name" value="<?=$row['product_name'] ?>">
|
||||
<input type="radio" class="btn-check" name="used" id="used_<?=$row['idx']?>_1" value="1" <?=$row['used'] == "1" ? "checked": "" ?> required>
|
||||
<label class="btn btn-outline-primary" for="used_<?=$row['idx']?>_1">사용</label>
|
||||
<input type="radio" class="btn-check" name="used" id="used_<?=$row['idx']?>_0" value="0" <?=$row['used'] =="0" ? "checked" : "" ?> required>
|
||||
<label class="btn btn-outline-primary" for="used_<?=$row['idx']?>_0">미사용</label>
|
||||
</div>
|
||||
<div class="input-group mb-1">
|
||||
<span class="input-group-text col-md-2" id="barcode_<?=$row['idx']?>">바코드</span>
|
||||
<input type="text" class="form-control" id="barcode_<?=$row['idx']?>" name="barcode" value="<?=$row['barcode']?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" >수정</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-label="Close" data-bs-target="#modify_modal_<?=$row['idx']?>">닫기</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- 수정 모달 끝 -->
|
||||
|
||||
|
||||
<?php $i++; } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
include_once "tail.sub.php";
|
||||
include_once FG_MANAGER_PATH."/tail.php";
|
||||
26
manager/bakery/bakery_stock.ajax.author.update.php
Normal file
26
manager/bakery/bakery_stock.ajax.author.update.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
include_once '_common.php';
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") { // POST로 넘어온 값이 있다면
|
||||
// 변수 선언
|
||||
$date = trim($_POST["searchDate"]);
|
||||
$worker = trim($_POST['worker']);
|
||||
$author = trim($_POST['author']);
|
||||
|
||||
// update query
|
||||
$query = "INSERT INTO {$fg['bakery_author_table']} (date, worker, author) ";
|
||||
$query .= "VALUES('{$date}', '{$worker}', '{$author}') ";
|
||||
// 이미 값이 있는 경우 업데이트함
|
||||
$query .= "ON DUPLICATE KEY UPDATE worker = '{$worker}', author = '{$author}' ";
|
||||
|
||||
$result = sql_query($query);
|
||||
|
||||
if ($result) {
|
||||
echo json_encode(array('success' => true));
|
||||
} else {
|
||||
echo json_encode(array('success' => false, 'message' => 'Database error'));
|
||||
}
|
||||
} else {
|
||||
echo json_encode(array('success' => false, 'message' => 'Invalid request method'));
|
||||
}
|
||||
46
manager/bakery/bakery_stock.ajax.confirm.update.php
Normal file
46
manager/bakery/bakery_stock.ajax.confirm.update.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
include_once '_common.php';
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") { // POST로 넘어온 값을 처리
|
||||
$searchDate = $_POST['searchDate'];
|
||||
|
||||
// 임시 배열 생성
|
||||
$updates = [];
|
||||
|
||||
// POST 데이터 처리
|
||||
foreach ($_POST as $key => $value) {
|
||||
if (preg_match('/^(.*)-(\d+)$/', $key, $matches)) {
|
||||
$name = $matches[1];
|
||||
$barcode = $matches[2];
|
||||
|
||||
// 중복된 barcode에 대한 값들을 배열에 저장
|
||||
if (!isset($updates[$barcode])) {
|
||||
$updates[$barcode] = [];
|
||||
}
|
||||
$updates[$barcode][$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// 쿼리 생성
|
||||
foreach ($updates as $barcode => $fields) {
|
||||
$setClauses = [];
|
||||
foreach ($fields as $name => $value) {
|
||||
$setClauses[] = "{$name} = '{$value}'";
|
||||
}
|
||||
$setClause = implode(', ', $setClauses);
|
||||
|
||||
$query = "UPDATE {$fg['bakery_inventory_table']} SET {$setClause} WHERE date = '{$searchDate}' AND barcode = '{$barcode}'";
|
||||
// 쿼리 실행
|
||||
$result = sql_query($query);
|
||||
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
echo json_encode(array('status' => 'success'));
|
||||
} else {
|
||||
echo json_encode(array('status' => 'error', 'message' => 'Database error'));
|
||||
}
|
||||
} else {
|
||||
echo json_encode(array('status' => 'error', 'message' => 'Invalid request method'));
|
||||
}
|
||||
18
manager/bakery/bakery_stock.ajax.delete.php
Normal file
18
manager/bakery/bakery_stock.ajax.delete.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
include_once "_common.php";
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
// 기본적인 보안 조치로 모든 데이터는 필터링하여 보관됩니다.
|
||||
$idx = isset($_POST['idx']) ? intval($_POST['idx']) : 0;
|
||||
|
||||
// 데이터 행 삭제 쿼리
|
||||
$query = "DELETE FROM {$fg['bakery_inventory_table']} WHERE idx = $idx";
|
||||
|
||||
// 쿼리 실행
|
||||
$result = sql_query($query);
|
||||
|
||||
header("Content-Type: application/json");
|
||||
if ($result) {
|
||||
echo json_encode(["isSuccess" => true]);
|
||||
} else {
|
||||
echo json_encode(["isSuccess" => false]);
|
||||
}
|
||||
45
manager/bakery/bakery_stock.ajax.update.php
Normal file
45
manager/bakery/bakery_stock.ajax.update.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
include_once "_common.php";
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
|
||||
// POST 데이터 받아오기
|
||||
$searchDate = isset($_POST['searchDate']) ? $_POST['searchDate'] : '';
|
||||
$ord_by = isset($_POST['ord_by']) ? $_POST['ord_by'] : 'product_name ASC';
|
||||
|
||||
// getBakeryInvenData 함수 호출
|
||||
$results = getBakeryInvenData($searchDate, $ord_by);
|
||||
|
||||
// 데이터 출력
|
||||
$output = '';
|
||||
$i = 1;
|
||||
foreach ($results as $row) {
|
||||
// 재고 점검 후 일치하지 않으면 업데이트
|
||||
// 전날 재고
|
||||
$previous_stock = intval(getPrevStock($searchDate, $row['barcode']));
|
||||
// 현재고
|
||||
$current_stock = $previous_stock + $row['production'] - $row['inhouse_use'] - $row['recycling'] - $row['disposal'] - $row['sales'];
|
||||
|
||||
$output .= '<tr>';
|
||||
$output .= '<td class="text-center">' . htmlspecialchars($i) . '</td>';
|
||||
$output .= '<td class="text-center">' . htmlspecialchars($row['product_name']) . '</td>';
|
||||
$output .= '<td class="text-center">' . htmlspecialchars($row['barcode']) . '</td>';
|
||||
$output .= '<td class="text-end">' . number_format($previous_stock) . '</td>';
|
||||
$output .= '<td class="text-end"><span class="value">' . htmlspecialchars($row['production']) . '</span>';
|
||||
$output .= '<input class="form-control text-end d-none" type="text" value="' . htmlspecialchars($row['production']) . '" id="production-' . htmlspecialchars($row['barcode']) . '" product_name="' . htmlspecialchars($row['product_name']) . '" aria-label="production" readonly></td>';
|
||||
$output .= '<td class="text-end"><span class="value">' . htmlspecialchars($row['inhouse_use']) . '</span>';
|
||||
$output .= '<input class="form-control text-end d-none" type="text" value="' . htmlspecialchars($row['inhouse_use']) . '" id="inhouse_use-' . htmlspecialchars($row['barcode']) . '" product_name="' . htmlspecialchars($row['product_name']) . '" aria-label="inhouse_use" readonly></td>';
|
||||
$output .= '<td class="text-end"><span class="value">' . htmlspecialchars($row['recycling']) . '</span>';
|
||||
$output .= '<input class="form-control text-end d-none" type="text" value="' . htmlspecialchars($row['recycling']) . '" id="recycling-' . htmlspecialchars($row['barcode']) . '" product_name="' . htmlspecialchars($row['product_name']) . '" aria-label="recycling" readonly></td>';
|
||||
$output .= '<td class="text-end"><span class="value">' . htmlspecialchars($row['disposal']) . '</span>';
|
||||
$output .= '<input class="form-control text-end d-none" type="text" value="' . htmlspecialchars($row['disposal']) . '" id="disposal-' . htmlspecialchars($row['barcode']) . '" product_name="' . htmlspecialchars($row['product_name']) . '" aria-label="disposal" readonly></td>';
|
||||
$output .= '<td class="text-end">' . number_format($row['sales']) . '</td>';
|
||||
$output .= '<td class="text-end">' . number_format($row['sales_amount']) . '</td>';
|
||||
$output .= '<td class="text-end">' . number_format($row['menu_discount']) . '</td>';
|
||||
$output .= '<td class="text-end">' . number_format($row['payment_amount']) . '</td>';
|
||||
$output .= '<td class="text-end">' . number_format($current_stock) . '</td>';
|
||||
$output .= '</tr>';
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
echo $output;
|
||||
140
manager/bakery/bakery_stock.ajax.upload_inventory.php
Normal file
140
manager/bakery/bakery_stock.ajax.upload_inventory.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
include_once '_common.php';
|
||||
if (!isset($_SESSION['user_id'])) exit; // 로그인 되어있지 않으면 로그인 페이지로 보냄
|
||||
|
||||
// cafe24 절대경로
|
||||
// /firstgarden/www/
|
||||
|
||||
$is_debug = false; // 디버깅 시 true로 변경
|
||||
$is_test = false; // 상품별(분류별상품)으로 바코드 업데이트 시킬때
|
||||
$is_dataForceInsert = false; // 강제로 데이터 맞출때 씀
|
||||
|
||||
// 추후 table 생성하여 대/중/소분류에 들어갈 값을 넣도록 수정해야 함
|
||||
// 값을 추출할 소분류 설정
|
||||
$chkMajCat = array('보스코');
|
||||
$chkMidCat = array();
|
||||
$chkSmallCat = array(
|
||||
'보스코 베이커리'
|
||||
);
|
||||
// 제외항목
|
||||
// $excludeBarcode = array('900014', '900015');
|
||||
|
||||
// 라이브러리 로드
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/autoload.php';
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use Shuchkin\SimpleXLSX;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['file'])) {
|
||||
|
||||
if ($is_debug) var_dump($_FILES['file']);
|
||||
|
||||
// 변수 만들기
|
||||
$date = trim($_POST['searchDate']);
|
||||
$file = $_FILES['file'];
|
||||
$datetime = date('Y-m-d H:i:s');
|
||||
$uploadDir = 'uploads/';
|
||||
$uploadFile = $uploadDir . basename($file['name']);
|
||||
$newXlsxFile = $uploadDir . pathinfo($file['name'], PATHINFO_FILENAME) . '.xlsx';
|
||||
|
||||
// 디렉토리가 없는 경우 생성
|
||||
if (!is_dir($uploadDir)) {
|
||||
mkdir($uploadDir, 0777, true);
|
||||
}
|
||||
|
||||
// 데이터 처리
|
||||
if (move_uploaded_file($file['tmp_name'], $uploadFile)) {
|
||||
if ($is_debug) echo "파일 업로드 성공: " . htmlspecialchars(basename($file['name'])) . "<br>" . PHP_EOL;
|
||||
|
||||
try {
|
||||
// 파일 형식에 따라 변환
|
||||
$fileType = pathinfo($uploadFile, PATHINFO_EXTENSION);
|
||||
if ($fileType == 'xls') {
|
||||
// PHPSpreadsheet로 .xls 파일을 .xlsx 파일로 변환
|
||||
$spreadsheet = IOFactory::load($uploadFile);
|
||||
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
|
||||
$writer->save($newXlsxFile);
|
||||
|
||||
// 업로드된 파일을 새로운 .xlsx 파일로 설정
|
||||
$uploadFile = $newXlsxFile;
|
||||
}
|
||||
|
||||
// SimpleXLSX로 파일 읽기
|
||||
$xlsx = SimpleXLSX::parse($uploadFile);
|
||||
|
||||
if ($xlsx && !$is_test) {
|
||||
$data = [];
|
||||
foreach ($xlsx->rows() as $row) {
|
||||
// 대분류 열이 보스코일 때
|
||||
if (in_array($row[1], $chkMajCat)) {
|
||||
// 소분류 체크
|
||||
if (in_array($row[3], $chkSmallCat)) {
|
||||
// 바코드 체크
|
||||
if (!empty($row[4])) {
|
||||
// 데이터 설정
|
||||
$date = $date;
|
||||
$product_name = $row[6] ?? ''; // 품명
|
||||
$barcode = $row[4] ?? ''; // 바코드
|
||||
$sales = $row[7] ?? ''; // 판매
|
||||
$sales_amount = $row[8] ?? ''; // 판매금액
|
||||
$menu_discount = $row[9] ?? ''; // 할인액
|
||||
$payment_amount = $row[10] ?? ''; // 매출액
|
||||
|
||||
// `bakery_product_table`에 바코드가 있는지 확인
|
||||
$checkQuery = "SELECT used FROM {$fg['bakery_product_table']} WHERE barcode = '{$barcode}'";
|
||||
$checkResult = sql_query($checkQuery);
|
||||
$rowCount = sql_num_rows($checkResult);
|
||||
|
||||
if ($rowCount > 0) {
|
||||
$used = sql_fetch_array($checkResult)['used'];
|
||||
// 바코드가 존재하지만 used 값이 0인 경우 continue
|
||||
if ($used == 0) continue;
|
||||
} else {
|
||||
// 바코드가 없는 경우 추가
|
||||
$insertQuery = "INSERT INTO {$fg['bakery_product_table']} (product_name, barcode, used) VALUES ('{$product_name}', '{$barcode}', 1)";
|
||||
sql_query($insertQuery);
|
||||
}
|
||||
|
||||
// 바코드가 신규로 들어간 경우에는 used값이 항상 1이고, 기존 0인 경우는 이미 continue 처리가 되어있음
|
||||
|
||||
// 데이터 삽입
|
||||
$query = "INSERT INTO {$fg['bakery_inventory_table']} (date, barcode, sales, sales_amount, menu_discount, payment_amount, edit_datetime) ";
|
||||
$query .= " VALUES ('{$date}', '{$barcode}', '{$sales}', '{$sales_amount}', '{$menu_discount}', '{$payment_amount}', '{$datetime}') ";
|
||||
// 이미 값이 있는 경우 업데이트
|
||||
$query .= " ON DUPLICATE KEY UPDATE
|
||||
sales = '{$sales}',
|
||||
sales_amount = '{$sales_amount}',
|
||||
menu_discount = '{$menu_discount}',
|
||||
payment_amount = '{$payment_amount}',
|
||||
edit_datetime = '{$datetime}'
|
||||
";
|
||||
|
||||
$result = sql_query($query);
|
||||
|
||||
// 점검용, Query Print
|
||||
if ($is_debug) {
|
||||
print_r($query);
|
||||
echo "<br>" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo SimpleXLSX::parseError();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo '엑셀 파일 읽기 오류: ', $e->getMessage();
|
||||
}
|
||||
|
||||
// 파일 삭제
|
||||
if (file_exists($uploadFile)) {
|
||||
unlink($uploadFile);
|
||||
if ($is_debug) echo "파일 삭제 완료." . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo "파일 업로드 실패";
|
||||
}
|
||||
} else {
|
||||
echo "잘못된 요청";
|
||||
}
|
||||
281
manager/bakery/bakery_stock.php
Normal file
281
manager/bakery/bakery_stock.php
Normal file
@ -0,0 +1,281 @@
|
||||
<?php
|
||||
// 베이커리 재고관리
|
||||
include_once "_common.php";
|
||||
if (!isset($_SESSION['user_id'])) header( 'Location: FG_MANAGER_URL' ); // 로그인 되어있지 않으면 로그인 페이지로 보냄
|
||||
|
||||
include_once FG_MANAGER_PATH."/head.php";
|
||||
|
||||
// 검색 변수 초기화
|
||||
!isset($search) ?? "";
|
||||
!isset($where) ?? "";
|
||||
!isset($search_count) ?? "";
|
||||
|
||||
$is_debug = false; // 디버깅 시 true로 변경
|
||||
|
||||
// ord_by 로 넘어온 값이 있으면 반영
|
||||
$ord_by = isset($_REQUEST["ord_by"]) ? $_REQUEST["ord_by"] : "product_name ASC";
|
||||
|
||||
// 날짜로 검색한 경우 해당 날짜를 사용하고, 아닌 경우 오늘 날짜를 사용함
|
||||
$searchDate = isset($_REQUEST["searchDate"]) ? $_REQUEST["searchDate"] : date("Y-m-d"); // 검색일
|
||||
$prevDate = date("Y-m-d", strtotime("-1 day", strtotime($searchDate))); // 검색 전일
|
||||
$edit_datetime = date('Y-m-d H:i:s'); // 최종 수정 시간 업데이트
|
||||
|
||||
// 베이커리 계정인지 확인하고, 오늘 이후 날짜인 경우 생산-결제금액란을 공란으로 출력하기 위한 변수
|
||||
$is_bakery = ( $_SESSION['user_id'] == "bakery" ) ? true : false;
|
||||
$is_bakery_input = ( strtotime($searchDate) >= strtotime(date("Y-m-d")) ) ? true : false;
|
||||
|
||||
// 합계 변수 선언
|
||||
$t_prev_stock = $t_production = $t_inhouse_use = $t_recycling = $t_disposal = $t_sales = $t_sales_amount = $t_menu_discount = $t_payment_amount = $t_current_stock = 0;
|
||||
|
||||
|
||||
/*
|
||||
$chkQuery = "SELECT * FROM {$fg['bakery_product_table']} WHERE used = 1 ";
|
||||
|
||||
$cheQueryResult = sql_query($chkQuery);
|
||||
$a=1;
|
||||
while ($row = sql_fetch_array($cheQueryResult)) {
|
||||
echo $a;
|
||||
print_r($row);
|
||||
echo "<br>";
|
||||
$a++;
|
||||
}
|
||||
|
||||
echo "여기까지 테스트";
|
||||
*/
|
||||
|
||||
|
||||
bakeryPreUpdate($searchDate, $edit_datetime);
|
||||
|
||||
$authInfo = getAuthorInfo($searchDate);
|
||||
if (!$authInfo) {
|
||||
$authInfo = ['worker' => '', 'author' => ''];
|
||||
}
|
||||
?>
|
||||
<style>
|
||||
/* 베이커리용 CSS */
|
||||
|
||||
|
||||
.bakery tfoot {
|
||||
font-weight: 600;
|
||||
}
|
||||
.sort-icon {
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* 틀 고정
|
||||
.tableWrapper {
|
||||
overflow: auto;
|
||||
height: 700px;
|
||||
width: 100%;
|
||||
}
|
||||
#bakeryTable th {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
}
|
||||
*/
|
||||
</style>
|
||||
|
||||
<h2 class="mb-3">베이커리 일일현황</h2>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="row align-items-start flex-nowrap">
|
||||
<div class="col-auto input-group">
|
||||
<span class="input-group-text">날짜선택</span>
|
||||
<input type="date" class="form-control" id="searchDateInput" name="searchDate" max="9999-12-31" value="<?= $searchDate ?>">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<form>
|
||||
<input type="hidden" id="hiddenSearchDate" name="searchDate" value="<?= date("Y-m-d") ?>">
|
||||
<button type="submit" class="btn btn-primary mb-3">오늘</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="file" id="inventoryFileInput" style="display:none;" accept=".xls,.xlsx" />
|
||||
<div class="btn-group" role="group" aria-label="bakery-button">
|
||||
<?php if(!bakeryChkDate($searchDate, $_SESSION['user_id'])) {?>
|
||||
<button type="button" class="btn btn-primary" id="editButton" onclick="toggleEditMode()" >수정</button>
|
||||
<button type="button" class="btn btn-primary d-none" id="confirmButton" onclick="confirmEditMode()">확인</button>
|
||||
<?php } ?>
|
||||
<button type="button" class="btn btn-secondary" id="uploadInventoryButton">파일첨부</button>
|
||||
<button type="button" class="btn btn-success" id="exportExcelButton">엑셀변환</button>
|
||||
<?php if ($is_bakery || $is_admin) { ?>
|
||||
<button type="button" class="btn btn-success" id="print">양식출력</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" style="max-width:600px;">
|
||||
<form class="form-group" id="author" name="author" >
|
||||
<input type="hidden" name="searchDate" id="searchDate" value="<?=$searchDate?>">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="worker">근무자</span>
|
||||
<input style="width: 250px;" type="text" class="form-control" name="worker" id="worker" value="<?=$authInfo['worker']?>" required>
|
||||
<span class="input-group-text" id="author">작성자</span>
|
||||
<input style="width: 100px;" type="text" class="form-control" name="author" id="author" value="<?=$authInfo['author']?>" required>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tableWrapper">
|
||||
<table class="table table-striped align-middle table-hover bakery" id="bakeryTable">
|
||||
<colgroup>
|
||||
<col width="30px">
|
||||
<col width="150px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
</colgroup>
|
||||
<!-- 테이블 제목 -->
|
||||
<thead class="table-light">
|
||||
<tr class="align-middle">
|
||||
<th class="text-center no-click">No.</th>
|
||||
<th class="text-center" data-column="product_name" data-order="asc">품목 <span class="sort-icon" data-column="product_name"></span></th>
|
||||
<th class="text-center" data-column="barcode" data-order="asc">바코드 <span class="sort-icon" data-column="barcode"></span></th>
|
||||
<th class="text-center" data-column="previous_stock" data-order="asc">전일재고 <span class="sort-icon" data-column="previous_stock"></span></th>
|
||||
<th class="text-center" data-column="production" data-order="asc">생산 <span class="sort-icon" data-column="production"></span></th>
|
||||
<th class="text-center" data-column="inhouse_use" data-order="asc">업장사용 <span class="sort-icon" data-column="inhouse_use"></span></th>
|
||||
<th class="text-center" data-column="recycling" data-order="asc">재활용 <span class="sort-icon" data-column="recycling"></span></th>
|
||||
<th class="text-center" data-column="disposal" data-order="asc">폐기 <span class="sort-icon" data-column="disposal"></span></th>
|
||||
<th class="text-center" data-column="sales" data-order="asc">판매수량 <span class="sort-icon" data-column="sales"></span></th>
|
||||
<th class="text-center" data-column="unit_price" data-order="asc">단가 <span class="sort-icon" data-column="unit_price"></span></th>
|
||||
<th class="text-center" data-column="sales_amount" data-order="asc">판매금액 <span class="sort-icon" data-column="sales_amount"></span></th>
|
||||
<th class="text-center" data-column="menu_discount" data-order="asc">메뉴별할인 <span class="sort-icon" data-column="menu_discount"></span></th>
|
||||
<th class="text-center" data-column="payment_amount" data-order="asc">결제금액 <span class="sort-icon" data-column="payment_amount"></span></th>
|
||||
<th class="text-center" data-column="current_stock" data-order="asc">현재고 <span class="sort-icon" data-column="current_stock"></span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- 테이블 데이터 -->
|
||||
<tbody class="table-group-divider" id="bakeryTableBody">
|
||||
<?php
|
||||
// 데이터 가져와서 뿌리기
|
||||
$i = 1; // number 표시를 위한 변수
|
||||
foreach (getBakeryInvenData($searchDate, $ord_by) as $row) {
|
||||
|
||||
// 전일자 현재고 가져오기, 없으면 0을 반환함
|
||||
$previous_stock = getPrevStock($searchDate, $row['barcode']) ;
|
||||
// 현재고
|
||||
$current_stock = $previous_stock + $row['production'] - $row['inhouse_use'] - $row['recycling'] - $row['disposal'] - $row['sales'];
|
||||
|
||||
// 계산된 값과 DB에 저장된 값이 일치하지 않으면 업데이트
|
||||
if ( $row['current_stock'] != $current_stock ) bakeryCurrentStockUpdate($searchDate, $row['barcode'], $current_stock);
|
||||
|
||||
// 단가 만들기
|
||||
$unit_price = 0;
|
||||
if ( $row['sales_amount'] != 0 || $row['sales'] != 0 ) {
|
||||
$unit_price = $row['sales_amount'] / $row['sales'];
|
||||
}
|
||||
|
||||
|
||||
// 합계값 만들기, 전일재고와 현재고는 DB의 값을 사용하지 않음
|
||||
$t_prev_stock += $previous_stock;
|
||||
$t_production += $row['production'];
|
||||
$t_inhouse_use += $row['inhouse_use'];
|
||||
$t_recycling += $row['recycling'];
|
||||
$t_disposal += $row['disposal'];
|
||||
$t_sales += $row['sales'];
|
||||
$t_sales_amount += $row['sales_amount'];
|
||||
$t_menu_discount += $row['menu_discount'];
|
||||
$t_payment_amount += $row['payment_amount'];
|
||||
$t_current_stock += $current_stock;
|
||||
|
||||
?>
|
||||
<tr id="row-<?=$row['barcode']?>">
|
||||
<td class="text-center"><?=$i?></td>
|
||||
<td class="text-center">
|
||||
<a data-bs-toggle="modal" data-bs-target="#modify_modal_<?=$row['idx']?>">
|
||||
<?=htmlspecialchars($row['product_name'])?>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center"><?=$row['barcode']?></td>
|
||||
<td class="text-end"><?=number_format($previous_stock)?></td>
|
||||
<td class="text-end"><span class="value"><?=number_format($row['production'])?></span>
|
||||
<input class="form-control text-end d-none" type="text" value="<?=$row['production']?>" id="production-<?=$row['barcode']?>" product_name="<?=$row['product_name']?>" aria-label="production" readonly>
|
||||
</td>
|
||||
<td class="text-end"><span class="value"><?=number_format($row['inhouse_use'])?></span>
|
||||
<input class="form-control text-end d-none" type="text" value="<?=$row['inhouse_use']?>" id="inhouse_use-<?=$row['barcode']?>" product_name="<?=$row['product_name']?>" aria-label="inhouse_use" readonly>
|
||||
</td>
|
||||
<td class="text-end"><span class="value"><?=number_format($row['recycling'])?></span>
|
||||
<input class="form-control text-end d-none" type="text" value="<?=$row['recycling']?>" id="recycling-<?=$row['barcode']?>" product_name="<?=$row['product_name']?>" aria-label="recycling" readonly>
|
||||
</td>
|
||||
<td class="text-end"><span class="value"><?=number_format($row['disposal'])?></span>
|
||||
<input class="form-control text-end d-none" type="text" value="<?=$row['disposal']?>" id="disposal-<?=$row['barcode']?>" product_name="<?=$row['product_name']?>" aria-label="disposal" readonly>
|
||||
</td>
|
||||
<td class="text-end"><?=number_format($row['sales'])?></td>
|
||||
<td class="text-end"><?=number_format($unit_price)?></td>
|
||||
<td class="text-end"><?=number_format($row['sales_amount'])?></td>
|
||||
<td class="text-end"><?=number_format($row['menu_discount'])?></td>
|
||||
<td class="text-end"><?=number_format($row['payment_amount'])?></td>
|
||||
<td class="text-end"><?=number_format($current_stock)?></td>
|
||||
</tr>
|
||||
|
||||
<!-- 수정 모달 -->
|
||||
<div id="modify_modal_<?=$row['idx']?>" class="modal fade" tabindex="-1" aria-labelledby="modify_modal_<?=$row['idx']?>" aria-hidden="true">
|
||||
<div class="modal-dialog modal modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<form class="modify" id="modify_<?=$row['idx']?>" name="modify_<?=$row['idx']?>">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">정보 보기/수정</h5>
|
||||
</div>
|
||||
<div class="modal-body text-start">
|
||||
<div class="input-group mb-1">
|
||||
<span class="input-group-text col-md-2" id="product_name_<?=$row['idx']?>">품명</span>
|
||||
<input type="text" class="form-control" id="product_name_<?=$row['idx']?>" name="product_name" value="<?=$row['product_name'] ?>">
|
||||
<input type="radio" class="btn-check" name="used" id="used_<?=$row['idx']?>_1" value="1" <?=$row['used'] == "1" ? "checked": "" ?> required>
|
||||
<label class="btn btn-outline-primary" for="used_<?=$row['idx']?>_1">사용</label>
|
||||
<input type="radio" class="btn-check" name="used" id="used_<?=$row['idx']?>_0" value="0" <?=$row['used'] =="0" ? "checked" : "" ?> required>
|
||||
<label class="btn btn-outline-primary" for="used_<?=$row['idx']?>_0">미사용</label>
|
||||
</div>
|
||||
<div class="input-group mb-1">
|
||||
<span class="input-group-text col-md-2" id="barcode_<?=$row['idx']?>">바코드</span>
|
||||
<input type="text" class="form-control" id="barcode_<?=$row['idx']?>" name="barcode" value="<?=$row['barcode']?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" >수정</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-label="Close" data-bs-target="#modify_modal_<?=$row['idx']?>">닫기</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div>
|
||||
<!-- 수정 모달 끝 -->
|
||||
|
||||
<?php $i++;
|
||||
} // endforeach
|
||||
?>
|
||||
</tbody>
|
||||
<!-- 합계 데이터 출력 -->
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">합계</th>
|
||||
<td class="text-end" id="total_prev_stock"><?=number_format($t_prev_stock)?></th>
|
||||
<td class="text-end" id="total_production"><?=number_format($t_production)?></th>
|
||||
<td class="text-end" id="total_inhouse_use"><?=number_format($t_inhouse_use)?></th>
|
||||
<td class="text-end" id="total_recycling"><?=number_format($t_recycling)?></th>
|
||||
<td class="text-end" id="total_disposal"><?=number_format($t_disposal)?></th>
|
||||
<td class="text-end" id="total_sales"><?=number_format($t_sales)?></th>
|
||||
<td class="text-end" id="total_unit_price"></th>
|
||||
<td class="text-end" id="total_sales_amount"><?=number_format($t_sales_amount)?></th>
|
||||
<td class="text-end" id="total_menu_discount"><?=number_format($t_menu_discount)?></th>
|
||||
<td class="text-end" id="total_payment_amount"><?=number_format($t_payment_amount)?></th>
|
||||
<td class="text-end" id="total_current_stock"><?=number_format($t_current_stock)?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
include_once "tail.sub.php";
|
||||
include_once FG_MANAGER_PATH."/tail.php";
|
||||
34
manager/bakery/bakery_stock.update.php
Normal file
34
manager/bakery/bakery_stock.update.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
include_once '_common.php';
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") { // POST로 넘어온 값이 있다면
|
||||
// 변수 선언
|
||||
$product_name = trim($_POST["product_name"]);
|
||||
$barcode = trim($_POST['barcode']);
|
||||
$date = trim($_POST['searchDate']);
|
||||
$id = trim($_POST['id']);
|
||||
$value = trim($_POST['value']);
|
||||
$edit_datetime = date('Y-m-d H:i:s', strtotime($date)); // 최종 수정 시간 업데이트
|
||||
|
||||
// 리스트에서 product name을 가지고 와야 함, 사전에 post로 전달받거나 품목 테이블에서 가져와야함
|
||||
|
||||
// 기존에 동일한 date, barcode를 가진 값이 있는 경우 insert가 아닌 update 로 처리해야 함
|
||||
// update query
|
||||
$query = "INSERT INTO {$fg['bakery_inventory_table']} (date, barcode, {$id}, edit_datetime) ";
|
||||
$query .= "VALUES('{$date}', '{$barcode}', '{$value}', '{$edit_datetime}') ";
|
||||
$query .= "ON DUPLICATE KEY UPDATE
|
||||
{$id} = '{$value}',
|
||||
edit_datetime = '{$edit_datetime}'
|
||||
"; // 이미 값이 있는 경우 업데이트함
|
||||
|
||||
$result = sql_query($query);
|
||||
|
||||
if ($result) {
|
||||
echo json_encode(array('status' => 'success'));
|
||||
} else {
|
||||
echo json_encode(array('status' => 'error', 'message' => 'Database error'));
|
||||
}
|
||||
} else {
|
||||
echo json_encode(array('status' => 'error', 'message' => 'Invalid request method'));
|
||||
}
|
||||
96
manager/bakery/bakery_stock_excel.php
Normal file
96
manager/bakery/bakery_stock_excel.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
// 설정 파일 포함
|
||||
include_once('_common.php');
|
||||
if(!$_SESSION['user_id']) exit; // 로그인 되어있지 않으면 확인 불가
|
||||
// 검색 날짜
|
||||
$searchDate = $_POST['searchDate'];
|
||||
$ord_by = $_POST['ordBy'];
|
||||
$result = getBakeryInvenData($searchDate, $ord_by);
|
||||
|
||||
// UTF-8 BOM 추가
|
||||
echo "\xEF\xBB\xBF";
|
||||
// 헤더 설정
|
||||
header("Content-Type: application/vnd.ms-excel; charset=utf-8");
|
||||
header("Content-Description: PHP Generated Data");
|
||||
?>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<style type="text/css">
|
||||
.tit { background-color:#C0C0C0; height:30px; }
|
||||
.no-text { mso-number-format:'\@'; text-align:center; }
|
||||
</style>
|
||||
|
||||
<table cellspacing="0" cellpadding="0" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tit">품목</th>
|
||||
<th class="tit">바코드</th>
|
||||
<th class="tit">전일재고</th>
|
||||
<th class="tit">생산</th>
|
||||
<th class="tit">업장사용</th>
|
||||
<th class="tit">재활용</th>
|
||||
<th class="tit">폐기</th>
|
||||
<th class="tit">판매수량</th>
|
||||
<th class="tit">판매단가</th>
|
||||
<th class="tit">판매금액</th>
|
||||
<th class="tit">메뉴별할인</th>
|
||||
<th class="tit">결제금액</th>
|
||||
<th class="tit">현재고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$t_prev_stock = $t_production = $t_inhouse_use = $t_recycling = $t_disposal = $t_sales = $t_sales_amount = $t_menu_discount = $t_payment_amount = $t_current_stock = 0;
|
||||
|
||||
// 데이터 출력
|
||||
foreach ( $result as $row) {
|
||||
?>
|
||||
<tr>
|
||||
<td><?=$row['product_name']?></td>
|
||||
<td><?=$row['barcode']?></td>
|
||||
<td><?=number_format(getPrevStock($searchDate, $row['barcode']))?></td>
|
||||
<td><?=number_format($row['production'])?></td>
|
||||
<td><?=number_format($row['inhouse_use'])?></td>
|
||||
<td><?=number_format($row['recycling'])?></td>
|
||||
<td><?=number_format($row['disposal'])?></td>
|
||||
<td><?=number_format($row['sales'])?></td>
|
||||
<td><?=($row['sales'] != 0 && $row['sales_amount'] != 0) ? number_format($row['sales_amount']/$row['sales']) : 0 ?></td>
|
||||
<td><?=number_format($row['sales_amount'])?></td>
|
||||
<td><?=number_format($row['menu_discount'])?></td>
|
||||
<td><?=number_format($row['payment_amount'])?></td>
|
||||
<td><?=number_format($row['current_stock'])?></td>
|
||||
</tr>
|
||||
<?php
|
||||
|
||||
// 합계 함수 처리
|
||||
$t_prev_stock += $row['previous_stock'];
|
||||
$t_production += $row['production'];
|
||||
$t_inhouse_use += $row['inhouse_use'];
|
||||
$t_recycling += $row['recycling'];
|
||||
$t_disposal += $row['disposal'];
|
||||
$t_sales += $row['sales'];
|
||||
$t_sales_amount += $row['sales_amount'];
|
||||
$t_menu_discount += $row['menu_discount'];
|
||||
$t_payment_amount += $row['payment_amount'];
|
||||
$t_current_stock += $row['current_stock'];
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot class="table-group-divider">
|
||||
<tr class="bakery_total">
|
||||
<td>합계</td>
|
||||
<td></td>
|
||||
<td><?=number_format($t_prev_stock) ?></td>
|
||||
<td><?=number_format($t_production) ?></td>
|
||||
<td><?=number_format($t_inhouse_use) ?></td>
|
||||
<td><?=number_format($t_recycling) ?></td>
|
||||
<td><?=number_format($t_disposal) ?></td>
|
||||
<td><?=number_format($t_sales) ?></td>
|
||||
<td></td>
|
||||
<td><?=number_format($t_sales_amount) ?></td>
|
||||
<td><?=number_format($t_menu_discount) ?></td>
|
||||
<td><?=number_format($t_payment_amount) ?></td>
|
||||
<td><?=number_format($t_current_stock) ?></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
102
manager/bakery/bakery_stock_print.php
Normal file
102
manager/bakery/bakery_stock_print.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
// 베이커리팀용 양식출력 파일
|
||||
// 베이커리 재고관리
|
||||
include_once "_common.php";
|
||||
if(!$_SESSION['user_id']) exit;
|
||||
|
||||
// 검색 변수 초기화
|
||||
!isset($search) ?? "";
|
||||
!isset($where) ?? "";
|
||||
!isset($search_count) ?? "";
|
||||
|
||||
$is_debug = false; // 디버깅 시 true로 변경
|
||||
|
||||
// 날짜로 검색한 경우 해당 날짜를 사용하고, 아닌 경우 오늘 날짜를 사용함
|
||||
$searchDate = isset($_REQUEST["searchDate"]) ? $_REQUEST["searchDate"] : date("Y-m-d"); // 검색일
|
||||
$prevDate = date("Y-m-d", strtotime("-1 day", strtotime($searchDate))); // 검색 전일
|
||||
$edit_datetime = date('Y-m-d H:i:s'); // 최종 수정 시간 업데이트
|
||||
|
||||
bakeryPreUpdate($searchDate, $edit_datetime);
|
||||
|
||||
?>
|
||||
<style>
|
||||
@page {
|
||||
margin-left: 2cm;
|
||||
margin-right: 2cm;
|
||||
}
|
||||
.container { width: "100%"}
|
||||
.bakery tfoot {
|
||||
font-weight: 600;
|
||||
}
|
||||
.sort-icon {
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
<html>
|
||||
<head>
|
||||
<title>베이커리 일일현황</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h2 class="mb-3 text-center">베이커리 일일현황</h2>
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<div class="col-auto">
|
||||
날짜 : <?= $searchDate ?>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped align-middle table-hover bakery" id="bakeryTable">
|
||||
<colgroup>
|
||||
<col width="30px">
|
||||
<col width="150px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
<col width="70px">
|
||||
</colgroup>
|
||||
<!-- 테이블 제목 -->
|
||||
<thead class="table-light">
|
||||
<tr class="align-middle">
|
||||
<th class="text-center no-click">No.</th>
|
||||
<th class="text-center">품목</th>
|
||||
<th class="text-center">전일재고</th>
|
||||
<th class="text-center">생산</th>
|
||||
<th class="text-center">업장사용</th>
|
||||
<th class="text-center">재활용</th>
|
||||
<th class="text-center">폐기</th>
|
||||
<th class="text-center">현재고</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- 테이블 데이터 -->
|
||||
<tbody class="table-group-divider" id="bakeryTableBody">
|
||||
<?php
|
||||
// 데이터 가져와서 뿌리기
|
||||
$i = 1; // number 표시를 위한 변수
|
||||
foreach (getBakeryInvenData($searchDate) as $row) {
|
||||
if($is_debug) var_dump($row);
|
||||
// 전일자 현재고 가져오기, 없으면 0을 반환함
|
||||
$previous_stock = getPrevStock($searchDate, $row['barcode']) ;
|
||||
// 현재고
|
||||
$current_stock = $previous_stock + $row['production'] - $row['inhouse_use'] - $row['recycling'] - $row['disposal'] - $row['sales'];
|
||||
?>
|
||||
<tr id="row-<?=$row['barcode']?>">
|
||||
<td class="text-center"><?=$i?></td>
|
||||
<td class="text-center"><?=htmlspecialchars($row['product_name'])?></td>
|
||||
<td class="text-center"><?=$previous_stock?></td>
|
||||
<td class="text-end"><input class="form-control text-end" type="text" value=""></td>
|
||||
<td class="text-end"><input class="form-control text-end" type="text" value=""></td>
|
||||
<td class="text-end"><input class="form-control text-end" type="text" value=""></td>
|
||||
<td class="text-end"><input class="form-control text-end" type="text" value=""></td>
|
||||
<td class="text-center"><?=$current_stock?></td>
|
||||
</tr>
|
||||
<?php $i++;
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
264
manager/bakery/tail.sub.php
Normal file
264
manager/bakery/tail.sub.php
Normal file
@ -0,0 +1,264 @@
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var ordBy = <?php echo json_encode(isset($ord_by) ? $ord_by : ''); ?>;
|
||||
var searchDate = <?php echo json_encode(isset($searchDate) ? $searchDate : ''); ?>;
|
||||
|
||||
// 삭제 확인 및 처리 함수
|
||||
window.confirmDelete = function(idx) {
|
||||
if (confirm('정말 삭제하시겠습니까? 삭제한 후 복원할 수 없습니다.')) {
|
||||
var delete_url = 'bakery_product_list.ajax.delete.php';
|
||||
var formData = {
|
||||
idx: idx
|
||||
};
|
||||
|
||||
$.post(delete_url, formData, function(json) {
|
||||
if (json !== undefined && json.isSuccess) {
|
||||
alert('처리가 완료되었습니다.');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('처리중 에러가 발생하였습니다: ' + (json || '알 수 없는 오류'));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 수정 폼 전송 및 처리 함수 추가
|
||||
$('.modify').on('submit', function(event) {
|
||||
event.preventDefault(); // 기본 제출 동작 방지
|
||||
|
||||
var form = $(this);
|
||||
var formData = form.serialize(); // 폼 데이터 직렬화
|
||||
|
||||
$.post('bakery_product_list.ajax.update.php', formData, function(json) {
|
||||
console.log(json); // 반환된 데이터를 확인하기 위한 로그
|
||||
|
||||
if (json !== undefined && json.isSuccess) {
|
||||
alert('수정이 완료되었습니다.');
|
||||
location.reload();
|
||||
} else if(json !== undefined && !json.isSuccess) {
|
||||
alert('수정 중 에러가 발생하였습니다: ' + (json.message || '알 수 없는 오류'));
|
||||
} else {
|
||||
alert('수정 중 에러가 발생하였습니다. JSON 데이터가 정의되지 않았습니다.');
|
||||
}
|
||||
}).fail(function() {
|
||||
alert('AJAX 요청 실패.');
|
||||
});
|
||||
});
|
||||
|
||||
// 추가 폼 전송 및 처리 함수 추가
|
||||
$('.add').on('submit', function(event) {
|
||||
event.preventDefault(); // 기본 제출 동작 방지
|
||||
|
||||
var form = $(this);
|
||||
var formData = form.serialize(); // 폼 데이터 직렬화
|
||||
|
||||
$.post('bakery_product_list.ajax.add.php', formData, function(json) {
|
||||
console.log(json); // 반환된 데이터를 확인하기 위한 로그
|
||||
|
||||
if (json !== undefined && json.isSuccess) {
|
||||
alert('품목 추가가 완료되었습니다.');
|
||||
location.reload();
|
||||
} else if(json !== undefined && !json.isSuccess) {
|
||||
alert('추가 중 에러가 발생하였습니다: ' + (json.message || '알 수 없는 오류'));
|
||||
} else {
|
||||
alert('추가 중 에러가 발생하였습니다. JSON 데이터가 정의되지 않았습니다.');
|
||||
}
|
||||
}).fail(function() {
|
||||
alert('AJAX 요청 실패.');
|
||||
});
|
||||
});
|
||||
|
||||
function attachThClickEvent() {
|
||||
$('table').off('click', 'th'); // 기존 이벤트 제거
|
||||
$('table').on('click', 'th', function() {
|
||||
if ($(this).hasClass('no-click')) {
|
||||
return; // no-click 클래스를 가진 th 요소는 클릭 이벤트를 무시하고 이후 코드 실행 안함
|
||||
}
|
||||
console.log('th clicked:', $(this).data('column')); // 로그 추가
|
||||
const column = $(this).data('column');
|
||||
let order = $(this).data('order') || 'asc'; // 기본값 'asc'
|
||||
order = order === 'asc' ? 'desc' : 'asc';
|
||||
$(this).data('order', order);
|
||||
|
||||
sortTable(column, order); // 테이블 정렬 함수 호출
|
||||
});
|
||||
}
|
||||
|
||||
function sortTable(column, order) {
|
||||
const tbody = $('#bakeryTable tbody');
|
||||
const rows = tbody.find('tr').toArray();
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const aValue = $(a).find(`td:eq(${getColumnIndex(column)})`).text();
|
||||
const bValue = $(b).find(`td:eq(${getColumnIndex(column)})`).text();
|
||||
|
||||
if (order === 'asc') {
|
||||
return aValue.localeCompare(bValue, undefined, {numeric: true});
|
||||
} else {
|
||||
return bValue.localeCompare(aValue, undefined, {numeric: true});
|
||||
}
|
||||
});
|
||||
|
||||
$.each(rows, (index, row) => {
|
||||
tbody.append(row);
|
||||
// 정렬 후 No. 열의 값을 재설정
|
||||
$(row).find('td').eq(0).text(index + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function getColumnIndex(column) {
|
||||
return $(`#bakeryTable th[data-column="${column}"]`).index();
|
||||
}
|
||||
|
||||
attachThClickEvent();
|
||||
|
||||
// 날짜 입력값 변경 시 페이지 전체 갱신
|
||||
$('#searchDateInput').on('change', function() {
|
||||
const searchDate = $(this).val();
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('searchDate', searchDate);
|
||||
window.location.href = url.toString();
|
||||
});
|
||||
|
||||
|
||||
// 수정 버튼 클릭 시
|
||||
window.toggleEditMode = function() {
|
||||
document.querySelectorAll('tbody .value').forEach(span => {
|
||||
span.classList.toggle('d-none');
|
||||
});
|
||||
document.querySelectorAll('tbody input.form-control').forEach(input => {
|
||||
input.classList.toggle('d-none');
|
||||
if (input.hasAttribute('readonly')) {
|
||||
input.removeAttribute('readonly');
|
||||
} else {
|
||||
input.setAttribute('readonly', 'readonly');
|
||||
}
|
||||
});
|
||||
document.getElementById('editButton').classList.toggle('d-none');
|
||||
document.getElementById('confirmButton').classList.toggle('d-none');
|
||||
}
|
||||
|
||||
// 확인 버튼 클릭 시 데이터 업데이트 및 합계 갱신
|
||||
window.confirmEditMode = function() {
|
||||
var formData = new FormData();
|
||||
document.querySelectorAll('tbody input.form-control').forEach(input => {
|
||||
formData.append(input.id, input.value);
|
||||
});
|
||||
formData.append('searchDate', $('#searchDate').val());
|
||||
|
||||
$.ajax({
|
||||
url: 'bakery_stock.ajax.confirm.update.php',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
console.log('Data updated successfully'); // 로그 추가
|
||||
toggleEditMode();
|
||||
// updateTableBody({ ord_by: ordBy, searchDate: $('#searchDate').val() }); // 불필요한 호출 제거
|
||||
updateTotal(); // 합계 갱신
|
||||
// location.reload();
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error: ' + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 파일 업로드 버튼 클릭 시 파일 선택 창 열기
|
||||
$('#uploadInventoryButton').on('click', function() {
|
||||
$('#inventoryFileInput').click();
|
||||
});
|
||||
|
||||
// 파일 선택 시 AJAX로 파일 업로드
|
||||
$('#inventoryFileInput').on('change', function() {
|
||||
var file = $('#inventoryFileInput')[0].files[0];
|
||||
if (isValidExcelFile(file)) {
|
||||
var formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('searchDate', $('#searchDate').val());
|
||||
|
||||
$.ajax({
|
||||
url: 'bakery_stock.ajax.upload_inventory.php',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function(response) {
|
||||
alert('파일 업로드 완료');
|
||||
location.reload(); // 파일 업로드 후 페이지 새로고침
|
||||
},
|
||||
error: function() {
|
||||
alert('파일 업로드 실패');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
alert('엑셀 파일만 업로드할 수 있습니다.');
|
||||
}
|
||||
});
|
||||
|
||||
function isValidExcelFile(file) {
|
||||
const validExtensions = ['.xls', '.xlsx'];
|
||||
const fileName = file.name.toLowerCase();
|
||||
return validExtensions.some(ext => fileName.endsWith(ext));
|
||||
}
|
||||
|
||||
// 엑셀변환 ajax
|
||||
document.getElementById('exportExcelButton').addEventListener('click', function() {
|
||||
$.ajax({
|
||||
url: 'bakery_stock_excel.php',
|
||||
type: 'POST',
|
||||
data: { searchDate: $('#searchDate').val(), ordBy: ordBy },
|
||||
xhrFields: {
|
||||
responseType: 'blob'
|
||||
},
|
||||
success: function(data) {
|
||||
var blob = new Blob([data], { type: 'application/vnd.ms-excel' });
|
||||
var link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = 'bakery_data_' + $('#searchDate').val() + '.xls';
|
||||
link.click();
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
console.error('엑셀 파일 다운로드 실패:', textStatus, errorThrown);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// author 업데이트
|
||||
$('#author input').on('blur', function(event) {
|
||||
var formData = new FormData(document.getElementById('author')); // 폼 데이터 직렬화
|
||||
|
||||
console.log("Form Data:", formData); // 직렬화된 데이터 로그 확인
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'bakery_stock.ajax.author.update.php',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
console.log('데이터가 성공적으로 저장되었습니다.');
|
||||
} else {
|
||||
console.error('데이터 저장에 실패하였습니다: ' + response.message);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX 호출 중 오류가 발생하였습니다: ' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 출력 버튼 클릭 이벤트 추가
|
||||
$('#print').on('click', function() {
|
||||
var searchDate = $('#searchDate').val(); // searchDate 값을 가져옴
|
||||
var printWindow = window.open('bakery_stock_print.php?searchDate=' + searchDate, '_blank', 'width=800,height=600');
|
||||
printWindow.onload = function() {
|
||||
printWindow.print();
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user