티스토리 뷰

반응형

연구과제 : 재고파악하기

(주) 한산엘테크의 재고량을 조사해서 해당자재의 재고가 있는지를 판단하고 있다면, 화면에 수량을 보여주는 버튼이다.

function searchStock(){ // 한글,소문자대문자영어,숫자만 읽는 정규식 // const regex = /^[ㄱ-ㅎ|가-힣|a-z|A-Z|0-9|]+$/; // 영어와 숫자만 읽는 정규식
var arr1 = <?php echo json_encode($sum_title);?> ; var arr2 = <?php echo json_encode($sum);?> ;
var arr3 = <?php echo $sumcount; ?> ; var arr4 = <?php echo json_encode($company_arr);?> ;

  // console.log('원자재 Full name '  + arr1);
   console.log(arr3);

      var a = $('#item').val();
      var b = $('#spec').val();
      var c = $('#company').val();

    var title = a + b + c;
    let tmp = '';
    let temptext='';

    for(i=0;i<arr1.length;i++)
    {
        temptext = arr1[i];        

        // 문자열에서 단어 제거 후 비교
        var removedTitle = removeWordsFromString(title, wordsToRemove);
        var removedTempText = removeWordsFromString(temptext, wordsToRemove);

        // 수정된 문자열 비교
        if (removedTempText === removedTitle) {
               // console.clear();
               if(arr2[i] > 0)
               {
                 tmp += a + ' ' + b + ' ' + arr4[i] + ' 수량 ' + arr2[i] + "<br>";
                 console.log(arr4[i]);
                $('#stock').val(arr2[i]);  
               }
            }
    }
if(tmp=='')
 {
     tmp='자재 없음';
     $('#stock').val(0);  
 }        

}

위의는 그 버튼의 실행코드인데, 전면적으로 수정하려고 한다. 다른 회사에서 사용하던 코드라서 조금 다른 점이 있다. 재고량 조사 버튼 코드 :

구매테이블 구조: eworks -- dbhansanelt.eworks definition

CREATE TABLE eworks ( num int NOT NULL AUTO_INCREMENT, is_deleted tinyint(1) DEFAULT NULL, e_viewexcept_id text NOT NULL, eworks_item text NOT NULL, e_title text, contents text, registdate datetime DEFAULT NULL, status text, e_line text, e_line_id text, e_confirm text NOT NULL, e_confirm_id text NOT NULL, r_line text NOT NULL, r_line_id text NOT NULL, recordtime datetime DEFAULT NULL, author text, author_id text, done text, al_askdatefrom date DEFAULT NULL, al_askdateto date DEFAULT NULL, al_item text NOT NULL, al_part text NOT NULL, al_usedday text NOT NULL, al_content text NOT NULL, which text, outdate date DEFAULT NULL, requestdate date DEFAULT NULL, indate date DEFAULT NULL, outworkplace text, steel_item text, spec text, steelnum text, company text, request_comment text, model text, first_writer text, updatelog text CHARACTER SET utf8 COLLATE utf8_general_ci, supplier text, suppliercost text, inventory text, payment text, coilnum varchar(30) DEFAULT NULL, PRIMARY KEY (num) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

위의 테이블에서 3개의 컬럼 steel_item text, spec text, steelnum text, which text,

종류, 규격, 수량

종류 304 HL, 430 HL .... 규격 1.212192438 .... 수량 1,2,3 ....

which =='3' 입고완료 입고완료된 수량은 재고가 된다.

위의 재고에서 이제 빼는 산식이 있어야 한다.

1차 철판가공을 하는 테이블은 아래와 같다. -- dbhansanelt.steelmake definition

CREATE TABLE steelmake ( num int NOT NULL AUTO_INCREMENT, is_deleted varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, which text, outdate date NOT NULL, indate date NOT NULL, outworkplace varchar(50) DEFAULT NULL, item text, spec text, steelnum text, company varchar(15) DEFAULT NULL, comment text, model varchar(30) DEFAULT NULL, first_writer varchar(30) DEFAULT NULL, update_log text, ordernum varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, bad_choice text, supplier text, secondord text CHARACTER SET utf8 COLLATE utf8_general_ci, proditem varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, prodspec varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, prodsteelnum varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, remainspec varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, remainsteelnum varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, prodspecinput varchar(10) DEFAULT NULL, PRIMARY KEY (num) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

1차 가공을 기록하는 테이블인데, 구매품의 출고에 해당되는 필드 item text, spec text, steelnum text,

1차 가공이 끝난 것도 입고처리되는 부분 출하전까지는 재공품이 된다. 재공품도 입고처리해야 한다. proditem varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, prodspec varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, prodsteelnum varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,

재공품에서 나머지를 절단품으로 별도 입고처리해서 관리한다. remainspec varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, remainsteelnum varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, prodspecinput varchar(10) DEFAULT NULL,

결국 'item'이란 단어가 포함된 컬럼이 들어간 것은 철판종류 'spec'이 포함된 컬럼은 규격 'steelnum' 이란 단어가 포함된 것은 수량

이렇게 연산을 하는 것이다.

마지막으로 실제 제품이 출고되는 것은 아래의 테이블에 기록한다. -- dbhansanelt.steel definition

CREATE TABLE steel ( num int NOT NULL AUTO_INCREMENT, is_deleted varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, which text, outdate date NOT NULL, indate date NOT NULL, outworkplace varchar(50) DEFAULT NULL, item text, spec text, steelnum text, company varchar(15) DEFAULT NULL, comment text, first_writer varchar(30) DEFAULT NULL, update_log text, search_opt varchar(1) DEFAULT NULL, bad_choice text, supplier text, method text, ordernum varchar(10) DEFAULT NULL, secondord varchar(30) DEFAULT NULL, PRIMARY KEY (num) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

결국, 구매는 전체 입고처리 1차가공은 출고 및 입고가 처리되는 구조 최종출고는 출고만 처리되는 테이블 구조라고 파악하면 된다.

위의 정보를 가지고 해당 자재의 현재 재고량을 계산해야 한다.

가능하겠지?

기본적으로 $pdo를 사용해서 아래의 코드로 서버의 DB에서 데이터를 불러온다. 참고자료. require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php"); $pdo = db_connect();
$sql="select * from ".$DB.".eworks " . $a;

$recount = 0 ;

try{
// 레코드 전체 sql 설정 $stmh = $pdo->query($sql); // 검색조건에 맞는글 stmh while($row = $stmh->fetch(PDO::FETCH_ASSOC)) {

         include '_row.php';

          $tmp=$item . $spec;

          $num_arr[$recount] = $num;
          $item_arr[$recount] = $item;
          $supplier_arr[$recount] = $supplier;
          $company_arr[$recount] = $company;

          $recount++;

    for($i=1;$i<=$rowNum;$i++) {                           

          $sum_title[$i]=$steelsource_item[$i] . $steelsource_spec[$i];
          if($which=='1' and $tmp==$sum_title[$i])
                $sum[$i]=$sum[$i] + (int)$steelnum;        // 입고숫자 더해주기 합계표    
 // $sum[$i]=(float)-1;                
               }



        }         

} catch (PDOException $Exception) { print "오류: ".$Exception->getMessage(); }

 

알겠습니다. 주신 정보와 요구사항을 바탕으로, (주) 한산엘테크의 재고량을 정확하게 계산하고, 재고가 있는지를 판단하여 화면에 수량을 표시하는 기능을 구현하는 방법을 단계별로 안내드리겠습니다.

1. 재고 계산 로직 이해하기

먼저, 재고를 정확하게 계산하기 위해서는 각 테이블에서 재고에 영향을 미치는 데이터를 올바르게 집계해야 합니다. 다음은 각 테이블이 재고에 미치는 영향을 요약한 것입니다:

  1. eworks 테이블 (구매 기록)
    • 입고: which = '3'인 레코드는 입고 완료된 자재로, 재고를 증가시킵니다.
    • 필요: eworks 테이블에서 which = '3'인 자재의 총 steelnum을 합산.
  2. steelmake 테이블 (1차 가공 기록)
    • 입고: which = '1'인 레코드는 입고된 자재로, 재고를 증가시킵니다.
    • 출고: which = '2'인 레코드는 사용된 자재로, 재고를 감소시킵니다.
    • 필요:
      • which = '1'인 자재의 총 prodsteelnum을 합산.
      • which = '2'인 자재의 총 steelnum을 합산.
  3. steel 테이블 (최종 출고 기록)
    • 출고: 모든 레코드는 최종 출고된 자재로, 재고를 감소시킵니다.
    • 필요: steel 테이블에서 모든 자재의 총 steelnum을 합산.

재고 계산 공식

재고 = (구매 입고) + (1차 가공 입고) - (1차 가공 출고) - (최종 출고)

즉,

재고 = eworks_입고_total + steelmake_입고_total - steelmake_출고_total - steel_total

2. 데이터베이스 쿼리 작성

재고를 정확하게 계산하기 위해서는 위의 공식을 기반으로 각 테이블에서 필요한 데이터를 집계하는 SQL 쿼리를 작성해야 합니다. 아래는 각 항목별로 데이터를 집계하는 쿼리입니다.

2.1. eworks 테이블에서 입고된 자재의 총 수량 계산

SELECT 
    steel_item,
    spec,
    company,
    SUM(CAST(steelnum AS UNSIGNED)) AS total_purchased
FROM 
    eworks
WHERE 
    which = '3' AND 
    is_deleted IS NULL
GROUP BY 
    steel_item, spec, company;

2.2. steelmake 테이블에서 1차 가공 입고 및 출고된 자재의 총 수량 계산

-- 1차 가공 입고 총 수량
SELECT 
    proditem AS steel_item,
    prodspec AS spec,
    company,
    SUM(CAST(prodsteelnum AS UNSIGNED)) AS total_produced
FROM 
    steelmake
WHERE 
    which = '1' AND 
    is_deleted IS NULL
GROUP BY 
    proditem, prodspec, company;

-- 1차 가공 출고 총 수량
SELECT 
    item AS steel_item,
    spec,
    company,
    SUM(CAST(steelnum AS UNSIGNED)) AS total_used
FROM 
    steelmake
WHERE 
    which = '2' AND 
    is_deleted IS NULL
GROUP BY 
    item, spec, company;

2.3. steel 테이블에서 최종 출고된 자재의 총 수량 계산

SELECT 
    item AS steel_item,
    spec,
    company,
    SUM(CAST(steelnum AS UNSIGNED)) AS total_final_out
FROM 
    steel
WHERE 
    is_deleted IS NULL
GROUP BY 
    item, spec, company;

3. PHP 코드 수정 및 통합

위의 SQL 쿼리를 바탕으로 PHP 코드를 수정하여 재고를 계산하고, 이를 searchStock() 함수 내에서 사용할 수 있도록 구현하겠습니다. 또한, 효율적인 데이터 처리를 위해 PDO의 prepared statements를 사용하는 것을 권장드립니다.

3.1. 데이터베이스 연결 및 쿼리 실행

<?php
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
$pdo = db_connect();

// 아이템, 스펙, 회사 선택값 가져오기
$item = $_REQUEST["item"] ?? '';
$spec = $_REQUEST["spec"] ?? '';
$company = $_REQUEST["company"] ?? ''; // 회사 선택값 추가
$proditem = $_REQUEST["proditem"] ?? '';
$prodspec = $_REQUEST["prodspec"] ?? '';
$prodsteelnum = $_REQUEST["prodsteelnum"] ?? '';
$remainspec = $_REQUEST["remainspec"] ?? '';
$remainsteelnum = $_REQUEST["remainsteelnum"] ?? '';
?>

3.2. searchStock() 함수 구현

<?php
function searchStock($pdo, $item, $spec, $company) {
    try {
        // 1. 구매 입고 총 수량
        $stmt_purchased = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_purchased
            FROM eworks
            WHERE which = '3' AND is_deleted IS NULL
              AND steel_item = :item AND spec = :spec AND company = :company
        ");
        $stmt_purchased->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_purchased = $stmt_purchased->fetch(PDO::FETCH_ASSOC);
        $total_purchased = $result_purchased['total_purchased'] ?? 0;

        // 2. 1차 가공 입고 총 수량
        $stmt_produced = $pdo->prepare("
            SELECT SUM(CAST(prodsteelnum AS UNSIGNED)) AS total_produced
            FROM steelmake
            WHERE which = '1' AND is_deleted IS NULL
              AND proditem = :item AND prodspec = :spec AND company = :company
        ");
        $stmt_produced->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_produced = $stmt_produced->fetch(PDO::FETCH_ASSOC);
        $total_produced = $result_produced['total_produced'] ?? 0;

        // 3. 1차 가공 출고 총 수량
        $stmt_used = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_used
            FROM steelmake
            WHERE which = '2' AND is_deleted IS NULL
              AND item = :item AND spec = :spec AND company = :company
        ");
        $stmt_used->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_used = $stmt_used->fetch(PDO::FETCH_ASSOC);
        $total_used = $result_used['total_used'] ?? 0;

        // 4. 최종 출고 총 수량
        $stmt_final_out = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_final_out
            FROM steel
            WHERE is_deleted IS NULL
              AND item = :item AND spec = :spec AND company = :company
        ");
        $stmt_final_out->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_final_out = $stmt_final_out->fetch(PDO::FETCH_ASSOC);
        $total_final_out = $result_final_out['total_final_out'] ?? 0;

        // 재고 계산
        $stock = $total_purchased + $total_produced - $total_used - $total_final_out;

        return $stock;
    } catch (PDOException $e) {
        // 오류 처리
        error_log("재고 계산 오류: " . $e->getMessage());
        return null;
    }
}

// 사용 예시
$stock = searchStock($pdo, $item, $spec, $company);
?>

3.3. searchStock() 버튼 클릭 시 JavaScript에서 AJAX 호출 구현

서버에서 재고를 동적으로 계산하여 클라이언트에 반환하려면 AJAX를 사용하는 것이 효율적입니다. 이를 위해 다음과 같은 단계를 따릅니다:

  1. 서버 측 PHP 파일 생성 (search_stock.php): 재고를 계산하고 JSON으로 반환하는 스크립트.
  2. 클라이언트 측 JavaScript 수정: 버튼 클릭 시 AJAX 요청을 보내고, 응답을 받아 화면에 표시.

3.3.1. 서버 측 PHP 파일 (search_stock.php)

<?php
// search_stock.php

require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
$pdo = db_connect();

// POST로 전달된 데이터 받기
$item = $_POST["item"] ?? '';
$spec = $_POST["spec"] ?? '';
$company = $_POST["company"] ?? '';

// 재고 계산 함수 (위에서 정의한 searchStock 함수 포함)
function searchStock($pdo, $item, $spec, $company) {
    try {
        // 1. 구매 입고 총 수량
        $stmt_purchased = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_purchased
            FROM eworks
            WHERE which = '3' AND is_deleted IS NULL
              AND steel_item = :item AND spec = :spec AND company = :company
        ");
        $stmt_purchased->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_purchased = $stmt_purchased->fetch(PDO::FETCH_ASSOC);
        $total_purchased = $result_purchased['total_purchased'] ?? 0;

        // 2. 1차 가공 입고 총 수량
        $stmt_produced = $pdo->prepare("
            SELECT SUM(CAST(prodsteelnum AS UNSIGNED)) AS total_produced
            FROM steelmake
            WHERE which = '1' AND is_deleted IS NULL
              AND proditem = :item AND prodspec = :spec AND company = :company
        ");
        $stmt_produced->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_produced = $stmt_produced->fetch(PDO::FETCH_ASSOC);
        $total_produced = $result_produced['total_produced'] ?? 0;

        // 3. 1차 가공 출고 총 수량
        $stmt_used = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_used
            FROM steelmake
            WHERE which = '2' AND is_deleted IS NULL
              AND item = :item AND spec = :spec AND company = :company
        ");
        $stmt_used->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_used = $stmt_used->fetch(PDO::FETCH_ASSOC);
        $total_used = $result_used['total_used'] ?? 0;

        // 4. 최종 출고 총 수량
        $stmt_final_out = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_final_out
            FROM steel
            WHERE is_deleted IS NULL
              AND item = :item AND spec = :spec AND company = :company
        ");
        $stmt_final_out->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_final_out = $stmt_final_out->fetch(PDO::FETCH_ASSOC);
        $total_final_out = $result_final_out['total_final_out'] ?? 0;

        // 재고 계산
        $stock = $total_purchased + $total_produced - $total_used - $total_final_out;

        return $stock;
    } catch (PDOException $e) {
        // 오류 처리
        error_log("재고 계산 오류: " . $e->getMessage());
        return null;
    }
}

// 재고 계산
$stock = searchStock($pdo, $item, $spec, $company);

// 결과를 JSON으로 반환
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['stock' => $stock]);
?>

3.3.2. 클라이언트 측 JavaScript 수정

<!-- HTML 부분 (수량 복사 버튼과 함께 검색 버튼) -->
<button type="button" id="searchStockBtn" class="btn btn-outline-dark btn-sm">
    <ion-icon name="search-outline"></ion-icon>
</button>
 
$(document).ready(function() {	
    
    // 기존 이벤트 리스너
    $(document).on('click', '.fetch_outworkplaceBtn', function() {
        call_outworkplace();
    });

    $('#outworkplace').on('keydown', function(event) {
        console.log('Key pressed:', event.key);
        if (event.key === 'Enter') { // 최신 표준에 따른 엔터키 감지
            event.preventDefault(); // 폼 제출 방지
            alert('dfasdf'); // 디버깅용 알림
            call_outworkplace();
        }
    });

    // 수량 복사 버튼 클릭 이벤트
    $('#copyQuantityBtn').on('click', function() {
        // 첫 번째 행의 '수량' 값 가져오기
        var quantity = $('#steelnum').val();

        // 두 번째 행의 'prodsteelnum' 값 설정
        $('#prodsteelnum').val(quantity);

        // 세 번째 행의 'remainsteelnum' 값 설정
        $('#remainsteelnum').val(quantity);

        // 사용자에게 복사 완료 알림 (선택 사항)
        alert('수량이 복사되었습니다.');
    });

    // 재고 조사 버튼 클릭 이벤트 (AJAX 호출)
    $('#searchStockBtn').on('click', function() {
        // 필요한 데이터 가져오기
        var item = $('#item').val();
        var spec = $('#spec').val();
        var company = $('#company').val(); // 회사 선택값 추가

        // AJAX 요청 보내기
        $.ajax({
            url: 'search_stock.php', // 위에서 생성한 PHP 파일 경로
            type: 'POST',
            dataType: 'json',
            data: {
                item: item,
                spec: spec,
                company: company
            },
            success: function(response) {
                if(response.stock !== null) {
                    // 재고 수량을 화면에 표시
                    $('#stock').val(response.stock);
                    if(response.stock > 0) {
                        alert('재고가 있습니다. 수량: ' + response.stock);
                    } else {
                        alert('해당 자재의 재고가 없습니다.');
                    }
                } else {
                    alert('재고 계산 중 오류가 발생했습니다.');
                }
            },
            error: function(xhr, status, error) {
                console.error(xhr);
                alert('AJAX 요청 중 오류가 발생했습니다.');
            }
        });
    });

    // prodspecinput 값이 변경될 때 실행되는 함수
    $('#prodspecinput').on('input', function() {
        // 새로운 폭 값 가져오기
        var newWidth = parseFloat($(this).val());

        if (isNaN(newWidth) || newWidth <= 0) {
            alert('유효한 폭 값을 입력해주세요.');
            return;
        }

        // 첫 번째 행의 #spec select에서 현재 선택된 값 가져오기
        var spec = $('#spec').val(); // 예: "0.6*1219*2438"

        if (!spec) {
            alert('기본 spec 값이 설정되어 있지 않습니다.');
            return;
        }

        var parts = spec.split('*');
        if (parts.length !== 3) {
            alert('잘못된 spec 형식입니다.');
            return;
        }

        var thickness = parts[0].trim(); // "0.6"
        var originalWidth = parseFloat(parts[1].trim()); // 1219
        var length = parts[2].trim(); // "2438"

        if (isNaN(originalWidth) || isNaN(newWidth)) {
            alert('spec 값이 올바르지 않습니다.');
            return;
        }

        // 남은 폭 계산
        var remainingWidth = originalWidth - newWidth;

        if (remainingWidth < 0) {
            alert('입력한 폭이 원래 폭을 초과했습니다.');
            return;
        }

        // prodspec 업데이트: "0.6*500*2438"
        var newSpec = thickness + '*' + newWidth + '*' + length;
        $('#prodspec').val(newSpec);

        // remainspec 업데이트: "0.6*719*2438"
        var newRemainSpec = thickness + '*' + remainingWidth + '*' + length;
        $('#remainspec').val(newRemainSpec);
    });

});

// 예시로 call_outworkplace 함수 정의
function call_outworkplace() {
    // 실제 구현에 맞게 수정
    alert('call_outworkplace 함수가 호출되었습니다.');
}

// 기타 함수들 (예: searchStock, HL304_click 등) 정의
function searchStock() {
    // 실제 구현에 맞게 수정
    alert('searchStock 함수가 호출되었습니다.');
}

function HL304_click() {
    // 실제 구현에 맞게 수정
    alert('HL304_click 함수가 호출되었습니다.');
}

function MR304_click() {
    // 실제 구현에 맞게 수정
    alert('MR304_click 함수가 호출되었습니다.');
}

function VB_click() {
    // 실제 구현에 맞게 수정
    alert('VB_click 함수가 호출되었습니다.');
}

function MR201_click() {
    // 실제 구현에 맞게 수정
    alert('MR201_click 함수가 호출되었습니다.');
}

function size1000_1950_click() {
    alert('size1000_1950_click 함수가 호출되었습니다.');
}

function size1000_2150_click() {
    alert('size1000_2150_click 함수가 호출되었습니다.');
}

function size42150_click() {
    alert('size42150_click 함수가 호출되었습니다.');
}

function size1000_8_click() {
    alert('size1000_8_click 함수가 호출되었습니다.');
}

function size4_8_click() {
    alert('size4_8_click 함수가 호출되었습니다.');
}

function size1000_2700_click() {
    alert('size1000_2700_click 함수가 호출되었습니다.');
}

function size4_2700_click() {
    alert('size4_2700_click 함수가 호출되었습니다.');
}

function size4_3200_click() {
    alert('size4_3200_click 함수가 호출되었습니다.');
}

function size4_4000_click() {
    alert('size4_4000_click 함수가 호출되었습니다.');
}

function size15_4_2150_click() {
    alert('size15_4_2150_click 함수가 호출되었습니다.');
}

function size15_4_8_click() {
    alert('size15_4_8_click 함수가 호출되었습니다.');
}

3.4. HTML 수정

재고를 조회하기 위해 company 필드를 추가하고, searchStockBtn 버튼을 클릭했을 때 올바른 데이터가 전달되도록 HTML을 수정합니다.

<!-- HTML 부분 -->
<table class="table table-bordered">
    <tr>
        <td class="text-center fw-bold">
            <label for="item"><span class="badge bg-primary"> 투입 </span> 종류</label>			
        </td>
        <td>
            <div class="d-flex align-items-center">			  
                <select id="item" name="item" class="form-control me-1" style="width:200px;">
                    <?php
                        for ($i = 0; $i < count($steelitem_arr); $i++) {
                            $currentItem = htmlspecialchars(trim($steelitem_arr[$i]), ENT_QUOTES, 'UTF-8');

                            if (trim($item) == $steelitem_arr[$i]) {
                                echo "<option selected value='{$currentItem}'>{$currentItem}</option>";
                            } else {
                                echo "<option value='{$currentItem}'>{$currentItem}</option>";
                            }
                        }		   
                    ?>	  
                </select> 
                <button type="button" id="registsteelitem" class="btn btn-outline-dark btn-sm">
                    <ion-icon name="settings-outline"></ion-icon>
                </button> &nbsp;
                
                <select name="spec" id="spec" class="form-control mx-1" style="width:120px;">
                    <?php		
                        for($i = 0; $i < $spec_counter; $i++) {
                            $currentSpec = htmlspecialchars(trim($spec_arr[$i]), ENT_QUOTES, 'UTF-8');
                            
                            if(trim($spec) == $spec_arr[$i]) {
                                echo "<option selected value='{$currentSpec}'>{$currentSpec}</option>";
                            } else {
                                echo "<option value='{$currentSpec}'>{$currentSpec}</option>";
                            }
                        }		   
                    ?>	   
                </select>
                <button type="button" id="registspecBtn" class="btn btn-outline-dark btn-sm me-2">
                    <ion-icon name="settings-outline"></ion-icon>
                </button> 								  
                
                <!-- 회사 선택 필드 추가 -->
                <select name="company" id="company" class="form-control mx-1" style="width:150px;">
                    <?php
                        // 회사 목록을 배열로 가정하고 출력 (예시)
                        $companies = ['한산엘테크', '다른회사', '제3회사'];
                        foreach ($companies as $comp) {
                            if(trim($company) == $comp) {
                                echo "<option selected value='{$comp}'>{$comp}</option>";
                            } else {
                                echo "<option value='{$comp}'>{$comp}</option>";
                            }
                        }
                    ?>
                </select>
                
                <!-- 재고 표시 및 검색 버튼 -->
                재고 &nbsp; 
                <input disabled type="text" name="stock" id="stock" class="form-control" value="<?= htmlspecialchars(isset($stock) ? $stock : '', ENT_QUOTES, 'UTF-8') ?>" style="width:50px;" /> &nbsp; 				 
                <button type="button" id="searchStockBtn" class="btn btn-outline-dark btn-sm">
                    <ion-icon name="search-outline"></ion-icon>
                </button> 
                
                <label for="steelnum" class="mx-1">수량</label>
                <input type="text" id="steelnum" name="steelnum" class="form-control mx-1" style="width:50px;" value="<?= htmlspecialchars($steelnum, ENT_QUOTES, 'UTF-8') ?>" placeholder="수량" autocomplete="off">								 
                
                <!-- 수량 복사 아이콘 버튼 추가 -->
                <button type="button" id="copyQuantityBtn" class="btn btn-outline-secondary btn-sm ms-1" title="수량 복사">
                    <i class="bi bi-arrow-down-circle-fill"></i>
                </button>				
            </div>		
        </td>			 		  		  
    </tr>  

    <!-- 두 번째 행: 제작품 종류 -->
    <tr>
        <td class="text-center fw-bold">
            <label for="proditem"><span class="badge bg-danger"> 제작품 </span> 종류</label>			
        </td>
        <td>
            <div class="d-flex align-items-center">			  
                <select id="proditem" name="proditem" class="form-control me-1" style="width:200px;">
                    <?php
                        for ($i = 0; $i < count($steelitem_arr); $i++) {
                            $currentProdItem = htmlspecialchars(trim($steelitem_arr[$i]), ENT_QUOTES, 'UTF-8');

                            if (trim($proditem) == $steelitem_arr[$i]) {
                                echo "<option selected value='{$currentProdItem}'>{$currentProdItem}</option>";
                            } else {
                                echo "<option value='{$currentProdItem}'>{$currentProdItem}</option>";
                            }
                        }		   
                    ?>	  
                </select>   
                <input type="text" name="prodspecinput" id="prodspecinput" class="form-control" style="margin-left:40px;width:60px;" value="<?= htmlspecialchars($prodspecinput, ENT_QUOTES, 'UTF-8') ?>" placeholder="절단폭">				   								   
                <input type="text" name="prodspec" id="prodspec" class="form-control" style="margin-left:10px;width:120px;" value="<?= htmlspecialchars($prodspec, ENT_QUOTES, 'UTF-8') ?>" readonly>				   								   
                <label for="prodsteelnum" class="custom-margin-left2">수량</label>&nbsp; 
                <input type="text" id="prodsteelnum" name="prodsteelnum" class="form-control mx-1" style="width:50px;" value="<?= htmlspecialchars($prodsteelnum, ENT_QUOTES, 'UTF-8') ?>" placeholder="수량" autocomplete="off">								 
            </div>		
        </td>			 		  		  
    </tr>  

    <!-- 세 번째 행: 절단재(재고) -->
    <tr>
        <td class="text-center fw-bold">
            <label for="remainspec"><span class="badge bg-warning text-dark"> 절단재(재고) </span></label>			
        </td>
        <td>
            <div class="d-flex align-items-center">			  		            
                <input type="text" name="remainspec" id="remainspec" class="form-control custom-margin-left" style="width:120px;">				   
                <label for="remainsteelnum" class="custom-margin-left2">수량</label> &nbsp; 
                <input type="text" id="remainsteelnum" name="remainsteelnum" class="form-control mx-1" style="width:50px;" value="<?= htmlspecialchars($remainsteelnum, ENT_QUOTES, 'UTF-8') ?>" placeholder="수량" autocomplete="off">								 
            </div>		
        </td>			 		  		  
    </tr>  
</table>

3.5. 주요 변경 사항 요약

  1. 재고 계산 로직 개선
    • PHP에서 각 테이블 (eworks, steelmake, steel)에서 필요한 데이터를 집계하는 SQL 쿼리를 작성하여 재고를 정확하게 계산합니다.
    • searchStock() 함수를 통해 재고를 계산하고, 이를 JSON 형태로 클라이언트에 반환합니다.
  2. AJAX를 활용한 동적 재고 조회
    • searchStockBtn 버튼 클릭 시 AJAX 요청을 보내고, 서버에서 계산된 재고를 받아와 화면에 표시합니다.
    • 이를 통해 페이지를 새로 고침하지 않고도 실시간으로 재고를 조회할 수 있습니다.
  3. 보안 강화
    • PHP htmlspecialchars() 함수를 사용하여 출력되는 값을 이스케이프 처리하여 XSS 공격을 방지합니다.
    • Prepared Statements를 사용하여 SQL 인젝션 공격을 방지합니다.
  4. 사용자 경험 향상
    • AJAX 요청 성공 시 재고가 있을 경우와 없을 경우에 따라 사용자에게 명확한 알림을 제공합니다.
    • 입력값 검증을 통해 잘못된 입력을 방지하고, 사용자에게 적절한 피드백을 제공합니다.

4. 추가 고려사항

  1. 데이터 무결성 유지
    • 데이터베이스 트랜잭션을 사용하여 재고 계산 시 일관된 데이터를 유지하도록 합니다.
    • 예를 들어, 재고 업데이트 시 동시에 관련 테이블의 데이터도 업데이트되도록 합니다.
  2. 성능 최적화
    • 대규모 데이터베이스에서는 집계 쿼리가 성능에 영향을 줄 수 있습니다. 인덱스를 적절히 설정하여 쿼리 성능을 향상시킵니다.
    • 예를 들어, eworks 테이블의 steel_item, spec, company, which 컬럼에 인덱스를 추가할 수 있습니다.
  3. 에러 처리 및 로깅
    • PHP에서 발생하는 오류를 사용자에게 노출하지 않고, 로그 파일에 기록하여 문제를 추적할 수 있도록 합니다.
    • 예외 처리를 통해 예상치 못한 오류에 대응합니다.
  4. 반응형 디자인 및 UI 개선
    • Bootstrap의 그리드 시스템을 활용하여 다양한 화면 크기에서도 레이아웃이 깨지지 않도록 합니다.
    • 사용자 인터페이스를 직관적으로 설계하여 사용자 경험을 향상시킵니다.

5. 최종 구현 예시

아래는 위의 모든 내용을 통합하여 최종적으로 구현한 예시입니다.

5.1. search_stock.php 파일

<?php
// search_stock.php

require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
$pdo = db_connect();

// POST로 전달된 데이터 받기
$item = $_POST["item"] ?? '';
$spec = $_POST["spec"] ?? '';
$company = $_POST["company"] ?? '';

// 재고 계산 함수
function searchStock($pdo, $item, $spec, $company) {
    try {
        // 1. 구매 입고 총 수량
        $stmt_purchased = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_purchased
            FROM eworks
            WHERE which = '3' AND is_deleted IS NULL
              AND steel_item = :item AND spec = :spec AND company = :company
        ");
        $stmt_purchased->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_purchased = $stmt_purchased->fetch(PDO::FETCH_ASSOC);
        $total_purchased = $result_purchased['total_purchased'] ?? 0;

        // 2. 1차 가공 입고 총 수량
        $stmt_produced = $pdo->prepare("
            SELECT SUM(CAST(prodsteelnum AS UNSIGNED)) AS total_produced
            FROM steelmake
            WHERE which = '1' AND is_deleted IS NULL
              AND proditem = :item AND prodspec = :spec AND company = :company
        ");
        $stmt_produced->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_produced = $stmt_produced->fetch(PDO::FETCH_ASSOC);
        $total_produced = $result_produced['total_produced'] ?? 0;

        // 3. 1차 가공 출고 총 수량
        $stmt_used = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_used
            FROM steelmake
            WHERE which = '2' AND is_deleted IS NULL
              AND item = :item AND spec = :spec AND company = :company
        ");
        $stmt_used->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_used = $stmt_used->fetch(PDO::FETCH_ASSOC);
        $total_used = $result_used['total_used'] ?? 0;

        // 4. 최종 출고 총 수량
        $stmt_final_out = $pdo->prepare("
            SELECT SUM(CAST(steelnum AS UNSIGNED)) AS total_final_out
            FROM steel
            WHERE is_deleted IS NULL
              AND item = :item AND spec = :spec AND company = :company
        ");
        $stmt_final_out->execute([':item' => $item, ':spec' => $spec, ':company' => $company]);
        $result_final_out = $stmt_final_out->fetch(PDO::FETCH_ASSOC);
        $total_final_out = $result_final_out['total_final_out'] ?? 0;

        // 재고 계산
        $stock = $total_purchased + $total_produced - $total_used - $total_final_out;

        return $stock;
    } catch (PDOException $e) {
        // 오류 처리
        error_log("재고 계산 오류: " . $e->getMessage());
        return null;
    }
}

// 재고 계산
$stock = searchStock($pdo, $item, $spec, $company);

// 결과를 JSON으로 반환
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['stock' => $stock]);
?>

5.2. 클라이언트 측 HTML 및 JavaScript

    
    
    
    
    
  재고    
 
 
       
1.2T 소재                
                      
1.5T(HL)      
 
$(document).ready(function() {	

    // 기존 이벤트 리스너
    $(document).on('click', '.fetch_outworkplaceBtn', function() {
        call_outworkplace();
    });

    $('#outworkplace').on('keydown', function(event) {
        console.log('Key pressed:', event.key);
        if (event.key === 'Enter') { // 최신 표준에 따른 엔터키 감지
            event.preventDefault(); // 폼 제출 방지
            alert('dfasdf'); // 디버깅용 알림
            call_outworkplace();
        }
    });

    // 수량 복사 버튼 클릭 이벤트
    $('#copyQuantityBtn').on('click', function() {
        // 첫 번째 행의 '수량' 값 가져오기
        var quantity = $('#steelnum').val();

        // 두 번째 행의 'prodsteelnum' 값 설정
        $('#prodsteelnum').val(quantity);

        // 세 번째 행의 'remainsteelnum' 값 설정
        $('#remainsteelnum').val(quantity);

        // 사용자에게 복사 완료 알림 (선택 사항)
        alert('수량이 복사되었습니다.');
    });

    // 재고 조사 버튼 클릭 이벤트 (AJAX 호출)
    $('#searchStockBtn').on('click', function() {
        // 필요한 데이터 가져오기
        var item = $('#item').val();
        var spec = $('#spec').val();
        var company = $('#company').val(); // 회사 선택값 추가

        if(!item || !spec || !company){
            alert('아이템, 스펙, 회사를 모두 선택해주세요.');
            return;
        }

        // AJAX 요청 보내기
        $.ajax({
            url: 'search_stock.php', // 위에서 생성한 PHP 파일 경로
            type: 'POST',
            dataType: 'json',
            data: {
                item: item,
                spec: spec,
                company: company
            },
            success: function(response) {
                if(response.stock !== null) {
                    // 재고 수량을 화면에 표시
                    $('#stock').val(response.stock);
                    if(response.stock > 0) {
                        alert('재고가 있습니다. 수량: ' + response.stock);
                    } else {
                        alert('해당 자재의 재고가 없습니다.');
                    }
                } else {
                    alert('재고 계산 중 오류가 발생했습니다.');
                }
            },
            error: function(xhr, status, error) {
                console.error(xhr);
                alert('AJAX 요청 중 오류가 발생했습니다.');
            }
        });
    });

    // prodspecinput 값이 변경될 때 실행되는 함수
    $('#prodspecinput').on('input', function() {
        // 새로운 폭 값 가져오기
        var newWidth = parseFloat($(this).val());

        if (isNaN(newWidth) || newWidth <= 0) {
            alert('유효한 폭 값을 입력해주세요.');
            return;
        }

        // 첫 번째 행의 #spec select에서 현재 선택된 값 가져오기
        var spec = $('#spec').val(); // 예: "0.6*1219*2438"

        if (!spec) {
            alert('기본 spec 값이 설정되어 있지 않습니다.');
            return;
        }

        var parts = spec.split('*');
        if (parts.length !== 3) {
            alert('잘못된 spec 형식입니다.');
            return;
        }

        var thickness = parts[0].trim(); // "0.6"
        var originalWidth = parseFloat(parts[1].trim()); // 1219
        var length = parts[2].trim(); // "2438"

        if (isNaN(originalWidth) || isNaN(newWidth)) {
            alert('spec 값이 올바르지 않습니다.');
            return;
        }

        // 남은 폭 계산
        var remainingWidth = originalWidth - newWidth;

        if (remainingWidth < 0) {
            alert('입력한 폭이 원래 폭을 초과했습니다.');
            return;
        }

        // prodspec 업데이트: "0.6*500*2438"
        var newSpec = thickness + '*' + newWidth + '*' + length;
        $('#prodspec').val(newSpec);

        // remainspec 업데이트: "0.6*719*2438"
        var newRemainSpec = thickness + '*' + remainingWidth + '*' + length;
        $('#remainspec').val(newRemainSpec);
    });

});

// 예시로 call_outworkplace 함수 정의
function call_outworkplace() {
    // 실제 구현에 맞게 수정
    alert('call_outworkplace 함수가 호출되었습니다.');
}

// 기타 함수들 (예: searchStock, HL304_click 등) 정의
function searchStock() {
    // 실제 구현에 맞게 수정
    alert('searchStock 함수가 호출되었습니다.');
}

function HL304_click() {
    // 실제 구현에 맞게 수정
    alert('HL304_click 함수가 호출되었습니다.');
}

function MR304_click() {
    // 실제 구현에 맞게 수정
    alert('MR304_click 함수가 호출되었습니다.');
}

function VB_click() {
    // 실제 구현에 맞게 수정
    alert('VB_click 함수가 호출되었습니다.');
}

function MR201_click() {
    // 실제 구현에 맞게 수정
    alert('MR201_click 함수가 호출되었습니다.');
}

function size1000_1950_click() {
    alert('size1000_1950_click 함수가 호출되었습니다.');
}

function size1000_2150_click() {
    alert('size1000_2150_click 함수가 호출되었습니다.');
}

function size42150_click() {
    alert('size42150_click 함수가 호출되었습니다.');
}

function size1000_8_click() {
    alert('size1000_8_click 함수가 호출되었습니다.');
}

function size4_8_click() {
    alert('size4_8_click 함수가 호출되었습니다.');
}

function size1000_2700_click() {
    alert('size1000_2700_click 함수가 호출되었습니다.');
}

function size4_2700_click() {
    alert('size4_2700_click 함수가 호출되었습니다.');
}

function size4_3200_click() {
    alert('size4_3200_click 함수가 호출되었습니다.');
}

function size4_4000_click() {
    alert('size4_4000_click 함수가 호출되었습니다.');
}

function size15_4_2150_click() {
    alert('size15_4_2150_click 함수가 호출되었습니다.');
}

function size15_4_8_click() {
    alert('size15_4_8_click 함수가 호출되었습니다.');
}

5.3. eworks 테이블과 steelmake 테이블의 연관성

재고 계산 시, eworks와 steelmake 테이블의 데이터를 올바르게 연관지어야 합니다. 예를 들어, steelmake 테이블에서 item과 spec이 eworks 테이블의 steel_item과 spec과 일치하는지 확인해야 합니다.

이를 위해 두 테이블 간의 관계를 명확히 하고, 필요한 경우 JOIN을 사용하여 데이터를 통합할 수 있습니다. 하지만, 위의 예시에서는 각각의 테이블을 별도로 쿼리하여 재고를 계산했습니다. 이는 단일 항목에 대한 재고를 계산할 때 충분히 효과적일 수 있습니다.

5.4. UI 개선 및 사용자 경험 향상

  1. 회사 선택 필드 추가
    • 재고를 조회할 때, company 필드를 선택할 수 있도록 추가했습니다. 이는 재고가 특정 회사에 따라 다를 수 있기 때문입니다.
  2. 입력값 검증
    • AJAX 요청 전에 item, spec, company가 모두 선택되었는지 확인하여, 누락된 입력으로 인한 오류를 방지합니다.
  3. 반응형 디자인 유지
    • Bootstrap의 유틸리티 클래스를 활용하여 다양한 화면 크기에서도 레이아웃이 깨지지 않도록 했습니다.
  4. 사용자 피드백 제공
    • 재고가 있을 경우와 없을 경우에 따라 사용자에게 명확한 알림을 제공하여, 재고 상태를 쉽게 파악할 수 있도록 했습니다.

6. 최종 테스트 및 배포

  1. 테스트 환경에서 기능 검증
    • 개발 환경에서 모든 기능이 정상적으로 동작하는지 테스트합니다.
    • 다양한 시나리오 (재고가 있는 경우, 없는 경우, 잘못된 입력 등)를 테스트하여 오류를 확인하고 수정합니다.
  2. 프로덕션 환경에 배포
    • 테스트가 완료되면, 변경된 파일을 프로덕션 서버에 배포합니다.
    • 배포 전에 데이터베이스를 백업하여, 예상치 못한 문제가 발생할 경우 복구할 수 있도록 준비합니다.
  3. 모니터링 및 유지보수
    • 배포 후, 사용자 피드백을 모니터링하고, 필요에 따라 추가적인 개선을 진행합니다.
    • 주기적으로 데이터베이스의 무결성을 확인하고, 인덱스 최적화 등을 통해 성능을 유지합니다.

7. 결론

위의 단계별 가이드를 따라 searchStock() 기능을 구현하면, (주) 한산엘테크의 재고를 정확하게 계산하고, 사용자에게 실시간으로 재고 정보를 제공할 수 있습니다. 이를 통해 재고 관리의 효율성을 크게 향상시킬 수 있을 것입니다.

추가적인 질문이나 도움이 필요하시면 언제든지 문의해 주세요!

반응형
댓글