Files
sam-kd/lot/list_stock.php
hskwon aca1767eb9 초기 커밋: 5130 레거시 시스템
- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경
- DB 연결 하드코딩 → .env 기반으로 변경
- MySQL strict mode DATE 오류 수정
2025-12-10 20:14:31 +09:00

670 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once($_SERVER['DOCUMENT_ROOT'] . "/session.php");
if (!isset($_SESSION["level"]) || $_SESSION["level"] > 5) {
sleep(1);
header("Location:" . $WebSite . "login/login_form.php");
exit;
}
include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
$title_message = '절곡 생산품 재고';
?>
<link href="css/style.css" rel="stylesheet">
<title> <?=$title_message?> </title>
<style>
.stock-table th, .stock-table td {
padding: 8px!important;
vertical-align: middle;
}
.stock-group {
margin-bottom: 30px;
}
.stock-summary {
background-color: #f8f9fa;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
}
.stock-card {
border: 1px solid #dee2e6;
border-radius: 8px;
margin-bottom: 20px;
}
.stock-card-header {
background-color: #e9ecef;
padding: 10px 15px;
border-bottom: 1px solid #dee2e6;
font-weight: bold;
}
.stock-card-body {
padding: 15px;
}
.stock-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f1f3f4;
}
.stock-item:last-child {
border-bottom: none;
}
.stock-info {
flex: 1;
}
.stock-quantity {
text-align: right;
font-weight: bold;
min-width: 100px;
}
.stock-positive {
color: #28a745;
}
.stock-zero {
color: #dc3545;
}
.stock-warning {
color: #ffc107;
}
</style>
</head>
<body>
<?php
$header = isset($_REQUEST['header']) ? $_REQUEST['header'] : '';
require_once($_SERVER['DOCUMENT_ROOT'] . '/myheader.php');
function checkNull($strtmp) {
return ($strtmp !== null && trim($strtmp) !== '');
}
// 필터링 값 가져오기
$search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
$prodNameSelect = isset($_REQUEST['prodNameSelect']) ? $_REQUEST['prodNameSelect'] : '';
$specNameSelect = isset($_REQUEST['specNameSelect']) ? $_REQUEST['specNameSelect'] : '';
$slengthNameSelect = isset($_REQUEST['slengthNameSelect']) ? $_REQUEST['slengthNameSelect'] : '';
$dateFrom = isset($_REQUEST['dateFrom']) ? $_REQUEST['dateFrom'] : '2024-01-01';
$dateTo = isset($_REQUEST['dateTo']) ? $_REQUEST['dateTo'] : date('Y-m-d');
$selectedGroups = isset($_REQUEST['selectedGroups']) ? $_REQUEST['selectedGroups'] : ['가이드레일', '케이스', '하단마감재', '기타'];
$mode = isset($_REQUEST["mode"]) ? $_REQUEST["mode"] : '';
$tablename = 'lot';
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
$pdo = db_connect();
// 품목명을 매핑하는 배열
$prodNames = [
'R' => '가이드레일(벽면형)',
'S' => '가이드레일(측면형)',
'G' => '연기차단재',
'B' => '하단마감재(스크린)',
'T' => '하단마감재(철재)',
'L' => 'L - Bar',
'C' => '케이스'
];
// 종류명을 매핑하는 배열
$specNames = [
'I' => '화이바원단',
'S' => 'SUS(마감)',
'U' => 'SUS(마감)2',
'E' => 'EGI(마감)',
'A' => '스크린용',
'D' => 'D형',
'C' => 'C형',
'M' => '본체',
'T' => '본체(철재)',
'B' => '후면코너부',
'L' => '린텔부',
'P' => '점검구',
'F' => '전면부'
];
// 모양&길이를 매핑하는 배열
$slengthNames = [
'53' => 'W50 × 3000',
'54' => 'W50 × 4000',
'83' => 'W80 × 3000',
'84' => 'W80 × 4000',
'12' => '1219',
'24' => '2438',
'30' => '3000',
'35' => '3500',
'40' => '4000',
'41' => '4150',
'42' => '4200',
'43' => '4300'
];
// 품목 그룹 분류
$prodGroups = [
'가이드레일' => ['R', 'S'],
'케이스' => ['C'],
'하단마감재' => ['B', 'T'],
'기타' => ['G', 'L']
];
// 재고 계산 함수
function calculateStock($pdo, $prod, $spec, $slength, $dateFrom = '', $dateTo = '') {
// 생산량 조회 (기간 필터 적용)
$productionSql = "SELECT COALESCE(SUM(surang), 0) as total_production
FROM lot
WHERE prod = ? AND spec = ? AND slength = ?
AND is_deleted IS NULL";
$productionParams = [$prod, $spec, $slength];
if (!empty($dateFrom)) {
$productionSql .= " AND reg_date >= ?";
$productionParams[] = $dateFrom;
}
if (!empty($dateTo)) {
$productionSql .= " AND reg_date <= ?";
$productionParams[] = $dateTo;
}
$stmt = $pdo->prepare($productionSql);
$stmt->execute($productionParams);
$production = $stmt->fetch(PDO::FETCH_ASSOC);
$totalProduction = $production['total_production'];
// 사용량 조회 (절곡작업일지에서 파싱)
$usageSql = "SELECT COALESCE(SUM(quantity), 0) as total_usage
FROM bending_work_log
WHERE prod_code = ? AND spec_code = ? AND slength_code = ?
AND is_deleted IS NULL";
$usageParams = [$prod, $spec, $slength];
if (!empty($dateFrom)) {
$usageSql .= " AND work_date >= ?";
$usageParams[] = $dateFrom;
}
if (!empty($dateTo)) {
$usageSql .= " AND work_date <= ?";
$usageParams[] = $dateTo;
}
$stmt = $pdo->prepare($usageSql);
$stmt->execute($usageParams);
$usage = $stmt->fetch(PDO::FETCH_ASSOC);
$totalUsage = $usage['total_usage'];
// 재고 = 생산량 - 사용량
$stock = $totalProduction - $totalUsage;
return [
'production' => $totalProduction,
'usage' => $totalUsage,
'stock' => $stock
];
}
// 그룹별 재고 데이터 조회
function getGroupStockData($pdo, $prodGroup, $prodNames, $specNames, $slengthNames, $dateFrom = '', $dateTo = '') {
$groupData = [];
foreach ($prodGroup as $prodCode) {
$prodName = $prodNames[$prodCode] ?? $prodCode;
// 해당 품목의 모든 종류와 길이 조합 조회 (기간 필터 적용)
$sql = "SELECT DISTINCT prod, spec, slength
FROM lot
WHERE prod = ? AND is_deleted IS NULL";
$params = [$prodCode];
if (!empty($dateFrom)) {
$sql .= " AND reg_date >= ?";
$params[] = $dateFrom;
}
if (!empty($dateTo)) {
$sql .= " AND reg_date <= ?";
$params[] = $dateTo;
}
$sql .= " ORDER BY spec, slength";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$combinations = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($combinations as $combo) {
$spec = $combo['spec'];
$slength = $combo['slength'];
$stockInfo = calculateStock($pdo, $prodCode, $spec, $slength, $dateFrom, $dateTo);
if ($stockInfo['stock'] > 0 || $stockInfo['production'] > 0) {
$groupData[] = [
'prod_code' => $prodCode,
'prod_name' => $prodName,
'spec_code' => $spec,
'spec_name' => $specNames[$spec] ?? $spec,
'slength_code' => $slength,
'slength_name' => $slengthNames[$slength] ?? $slength,
'production' => $stockInfo['production'],
'usage' => $stockInfo['usage'],
'stock' => $stockInfo['stock']
];
}
}
}
return $groupData;
}
try {
// 각 그룹별 재고 데이터 조회 (선택된 그룹만)
$allStockData = [];
foreach ($prodGroups as $groupName => $prodCodes) {
if (in_array($groupName, $selectedGroups)) {
$allStockData[$groupName] = getGroupStockData($pdo, $prodCodes, $prodNames, $specNames, $slengthNames, $dateFrom, $dateTo);
}
}
} catch (PDOException $Exception) {
print "오류: ".$Exception->getMessage();
}
?>
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
<input type="hidden" id="mode" name="mode" value="<?=$mode?>">
<input type="hidden" id="num" name="num">
<input type="hidden" id="tablename" name="tablename" value="<?=$tablename?>">
<input type="hidden" id="header" name="header" value="<?=$header?>">
<?php include $_SERVER['DOCUMENT_ROOT'] . '/lot/modal.php'; ?>
<!-- 모달 2: myModal -->
<div id="myModal" class="modal" tabindex="-1" data-bs-backdrop="static">
<div class="modal-content" style="width:800px; z-index: 1050;"> <!-- z-index로 순서 조정 -->
<div class="modal-header">
<span class="modal-title">절곡품 생산 LOT </span>
<span class="close">&times;</span>
</div>
<div class="modal-body">
<div class="custom-card"></div>
</div>
</div>
</div>
<!-- 모달 3: historyModal -->
<div id="historyModal" class="modal" tabindex="-1" data-bs-backdrop="static" style="display:none;">
<div class="modal-content" style="width:800px; z-index: 1100;">
<div class="modal-header">
<span class="modal-title">자재 입고/사용 이력</span>
<span class="close-history" style="cursor:pointer;">&times;</span>
</div>
<div class="modal-body" style="max-height:500px; overflow-y:auto;"></div>
</div>
</div>
<div class="container-fluid">
<div class="card justify-content-center text-center mt-1 mb-5">
<div class="card-header">
<div class="d-flex justify-content-center align-items-center">
<span class="text-center fs-5"> <?=$title_message?> </span>
<button type="button" class="btn btn-dark btn-sm mx-2" onclick='location.reload();' > <i class="bi bi-arrow-clockwise"></i> </button>
</div>
</div>
<div class="card-body">
<div class="container mt-2 mb-4">
<div class="d-flex justify-content-center align-items-center flex-wrap">
▷ 전체 재고 현황 &nbsp;
<!-- 기간 필터 -->
<div class="inputWrap30">
<input type="date" id="dateFrom" name="dateFrom" class="form-control" style="width:150px;" value="<?= $dateFrom ?>" placeholder="시작일">
</div>
&nbsp;~&nbsp;
<div class="inputWrap30">
<input type="date" id="dateTo" name="dateTo" class="form-control" style="width:150px;" value="<?= $dateTo ?>" placeholder="종료일">
</div>
&nbsp;
<!-- 품목명 select box -->
<div class="inputWrap30">
<select id="prodNameSelect" name="prodNameSelect" class="form-select w-auto mx-1" style="font-size:0.7rem; height:32px;" >
<option value="">품목명 선택</option>
<?php foreach($prodNames as $key => $value) { ?>
<option value="<?= $key ?>" <?= ($prodNameSelect == $key) ? 'selected' : '' ?>><?= $value ?></option>
<?php } ?>
</select>
</div>
&nbsp;
<!-- 종류명 select box -->
<div class="inputWrap30">
<select id="specNameSelect" name="specNameSelect" class="form-select w-auto mx-1" style="font-size:0.7rem; height:32px;" >
<option value="">종류명 선택</option>
<?php foreach($specNames as $key => $value) { ?>
<option value="<?= $key ?>" <?= ($specNameSelect == $key) ? 'selected' : '' ?>><?= $value ?></option>
<?php } ?>
</select>
</div>
&nbsp;
<!-- 모양&길이 select box -->
<div class="inputWrap30">
<select id="slengthNameSelect" name="slengthNameSelect" class="form-select w-auto mx-1" style="font-size:0.7rem; height:32px;" >
<option value="">모양&길이 선택</option>
<?php foreach($slengthNames as $key => $value) { ?>
<option value="<?= $key ?>" <?= ($slengthNameSelect == $key) ? 'selected' : '' ?>><?= $value ?></option>
<?php } ?>
</select>
</div>
&nbsp;
<!-- 기존 검색 input -->
<div class="inputWrap30">
<input type="text" id="search" class="form-control text-start" style="width:150px;" name="search" value="<?= $search ?>" autocomplete="off" onKeyPress="if (event.keyCode==13){ enter(); }">
<button class="btnClear"></button>
</div>
&nbsp;&nbsp;
<button class="btn btn-outline-dark btn-sm" type="button" id="searchBtn"> <i class="bi bi-search"></i> </button> &nbsp;&nbsp;&nbsp;&nbsp;
<button id="newBtn" type="button" class="btn btn-dark btn-sm me-2"> <i class="bi bi-pencil-square"></i> 신규 </button>
<button id="workLogBtn" type="button" class="btn btn-info btn-sm me-2"> <i class="bi bi-journal-text"></i> 작업일지 </button>
<?php if($user_name=='개발자') { ?>
<button id="uploadBtn" type="button" class="btn btn-dark btn-sm me-2"> <i class="bi bi-box-arrow-up"></i> 업로드 </button>
<?php } ?>
</div>
<!-- 그룹별 체크박스 -->
<div class="d-flex justify-content-center align-items-center mt-2">
<span class="me-2">그룹 선택:</span>
<button type="button" class="btn btn-sm btn-outline-primary me-2" id="selectAllGroups">전체선택</button>
<button type="button" class="btn btn-sm btn-outline-secondary me-2" id="deselectAllGroups">전체해제</button>
<?php foreach($prodGroups as $groupName => $prodCodes): ?>
<div class="form-check form-check-inline me-3">
<input class="form-check-input group-checkbox" type="checkbox" id="group_<?= $groupName ?>"
name="selectedGroups[]" value="<?= $groupName ?>"
<?= in_array($groupName, $selectedGroups) ? 'checked' : '' ?>>
<label class="form-check-label" for="group_<?= $groupName ?>">
<?= $groupName ?>
</label>
</div>
<?php endforeach; ?>
</div>
<!-- 전체 재고 요약 -->
<div class="stock-summary">
<h5 class="mb-3">전체 재고 요약</h5>
<div class="row">
<?php
$totalStock = 0;
$totalProduction = 0;
$totalUsage = 0;
foreach ($allStockData as $groupName => $groupData) {
foreach ($groupData as $item) {
$totalProduction += $item['production'];
$totalUsage += $item['usage'];
$totalStock += $item['stock'];
}
}
?>
<div class="col-md-4">
<div class="text-center">
<h6>총 생산량</h6>
<span class="fs-4 text-primary"><?= number_format($totalProduction) ?></span>
</div>
</div>
<div class="col-md-4">
<div class="text-center">
<h6>총 사용량</h6>
<span class="fs-4 text-warning"><?= number_format($totalUsage) ?></span>
</div>
</div>
<div class="col-md-4">
<div class="text-center">
<h6>총 재고량</h6>
<span class="fs-4 <?= $totalStock > 0 ? 'text-success' : 'text-danger' ?>"><?= number_format($totalStock) ?></span>
</div>
</div>
</div>
</div>
<!-- 그룹별 재고 현황 -->
<?php foreach ($allStockData as $groupName => $groupData): ?>
<?php if (!empty($groupData)): ?>
<div class="stock-group">
<div class="stock-card">
<div class="stock-card-header">
<h5 class="mb-0"><?= $groupName ?> 재고 현황</h5>
</div>
<div class="stock-card-body">
<div class="table-responsive">
<table class="table table-hover stock-table">
<thead class="table-primary">
<tr>
<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>
<th class="text-center">상태</th>
</tr>
</thead>
<tbody>
<?php
$groupTotalStock = 0;
$groupTotalProduction = 0;
$groupTotalUsage = 0;
foreach ($groupData as $item):
$groupTotalProduction += $item['production'];
$groupTotalUsage += $item['usage'];
$groupTotalStock += $item['stock'];
// 재고 상태 결정
$stockStatus = '';
$stockClass = '';
if ($item['stock'] > 0) {
$stockStatus = '재고있음';
$stockClass = 'stock-positive';
} elseif ($item['stock'] == 0) {
$stockStatus = '재고없음';
$stockClass = 'stock-zero';
} else {
$stockStatus = '부족';
$stockClass = 'stock-warning';
}
?>
<tr class="stock-history-row" style="cursor:pointer;"
data-prod="<?= $item['prod_code'] ?>"
data-spec="<?= $item['spec_code'] ?>"
data-slength="<?= $item['slength_code'] ?>">
<td class="text-center"><?= $item['prod_name'] ?></td>
<td class="text-center"><?= $item['spec_name'] ?></td>
<td class="text-center"><?= $item['slength_name'] ?></td>
<td class="text-center"><?= number_format($item['production']) ?></td>
<td class="text-center"><?= $item['prod_code'] . $item['spec_code'] . $item['slength_code'] ?></td>
<td class="text-center"><?= number_format($item['usage']) ?></td>
<td class="text-center <?= $stockClass ?>"><?= number_format($item['stock']) ?></td>
<td class="text-center <?= $stockClass ?>"><?= $stockStatus ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot class="table-secondary">
<tr>
<td colspan="3" class="text-end fw-bold">소계:</td>
<td class="text-center fw-bold"><?= number_format($groupTotalProduction) ?></td>
<td class="text-center fw-bold">-</td>
<td class="text-center fw-bold"><?= number_format($groupTotalUsage) ?></td>
<td class="text-center fw-bold <?= $groupTotalStock > 0 ? 'text-success' : 'text-danger' ?>"><?= number_format($groupTotalStock) ?></td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
</div>
</form>
<script>
var loader = document.getElementById('loadingOverlay');
if(loader)
loader.style.display = 'none';
$(document).ready(function() {
$("#newBtn").on("click", function() {
loadForm('insert');
});
$("#searchBtn").on("click", function() {
$("#board_form").submit();
});
$("#workLogBtn").on("click", function() {
window.open('bending_work_log.php', '_blank');
});
// upload
$("#uploadBtn").on("click", function() {
popupCenter('uploadgrid.php' , '업로드', 1800, 800);
});
// 필터링 기능
$("#prodNameSelect, #specNameSelect, #slengthNameSelect").on("change", function() {
filterTable();
});
$("#search").on("keyup", function() {
filterTable();
});
// 전체선택/전체해제 기능
$("#selectAllGroups").on("click", function() {
$(".group-checkbox").prop("checked", true);
// 검색버튼 클릭 효과 추가 (폼 submit)
$("#board_form").submit();
});
$("#deselectAllGroups").on("click", function() {
$(".group-checkbox").prop("checked", false);
// selectedGroups[] input을 모두 제거
$("input[name='selectedGroups[]']").remove();
// 빈 selectedGroups를 명시적으로 추가
if($("#board_form input[name='selectedGroups[]']").length === 0) {
$("#board_form").append('<input type="hidden" name="selectedGroups[]" value="">');
}
// 검색버튼 클릭 효과 추가 (폼 submit)
$("#board_form").submit();
});
// 그룹 체크박스 변경 시 폼 제출
$(".group-checkbox").on("change", function() {
$("#board_form").submit();
});
// tr 클릭 시 히스토리 모달
$(document).on('click', '.stock-history-row', function() {
var prod = $(this).data('prod');
var spec = $(this).data('spec');
var slength = $(this).data('slength');
$("#historyModal .modal-body").html('<div class="text-center py-5">Loading...</div>');
$("#historyModal").show();
$.ajax({
type: "POST",
url: "fetch_history.php",
data: { prod: prod, spec: spec, slength: slength },
dataType: "html",
success: function(response) {
// console.log(response);
$("#historyModal .modal-body").html(response);
},
error: function(jqxhr, status, error) {
$("#historyModal .modal-body").html('<div class="text-danger">AJAX Error: '+status+" "+error+"</div>");
}
});
});
// 모달 닫기
$(document).on('click', '.close-history', function() {
$("#historyModal").hide();
});
});
function filterTable() {
var prodFilter = $("#prodNameSelect").val();
var specFilter = $("#specNameSelect").val();
var slengthFilter = $("#slengthNameSelect").val();
var searchFilter = $("#search").val().toLowerCase();
$(".stock-table tbody tr").each(function() {
var row = $(this);
var prod = row.find("td:eq(0)").text();
var spec = row.find("td:eq(1)").text();
var slength = row.find("td:eq(2)").text();
var allText = row.text().toLowerCase();
var showRow = true;
if (prodFilter && !prod.includes(prodFilter)) showRow = false;
if (specFilter && !spec.includes(specFilter)) showRow = false;
if (slengthFilter && !slength.includes(slengthFilter)) showRow = false;
if (searchFilter && !allText.includes(searchFilter)) showRow = false;
row.toggle(showRow);
});
}
function loadForm(mode, num = null) {
if (num == null) {
$("#mode").val('insert');
} else {
$("#mode").val('modify');
$("#num").val(num);
}
$.ajax({
type: "POST",
url: "fetch_modal.php",
data: { mode: mode, num: num },
dataType: "html",
success: function(response) {
$(".modal-body .custom-card").html(response);
$("#myModal").show();
},
error: function(jqxhr, status, error) {
console.log("AJAX Error: ", status, error);
}
});
}
function enter() {
$("#board_form").submit();
}
$(document).ready(function(){
// 방문기록 남김
var title_message = '<?php echo $title_message ; ?>';
saveMenuLog(title_message);
});
</script>
</body>
</html>