본문 바로가기
ERP Project

물류팀 기능구현(1)

by kwh_coding 2023. 10. 10.

물류팀의 카테고리는 총 두가지로 

1. 재고관리

2. 입출고관리

가 있다. 

 

물류팀인만큼 물류팀의 주요 기능은 상품 / 제품의 관리를 하는 것이 주요한 기능이다. 

우리는 우선 상품테이블인 erp_goods 테이블과 상품 등급, 분류, 종류, 규격 그리고 제일 중요한 로트로 나누어 따로 관리할 수 있게 만들었다. (유지 보수의 용이성을 위해)

 

상품 사용 카테고리  
공동 테이블명 erp_goods  
Column Description Data Type Length Null Initial Value Primary Key Foreign Key Constraint Remark
칼럼명 설명 데이터 타입 길이 널(null)값 초기값 기본키 외래키 제약조건 비고
goods_no sequence INT       O      
goods_code 상품코드 VARCHAR 30 NOT NULL       UNIQUE 자동완성입력
goods_barcode 바코드 VARCHAR 30 NOT NULL         입력
goods_name 상품명 VARCHAR 100 NOT NULL          
goodskind_no 종류테이블 seq INT         O  
마우스/노트북/스피커/키보드 등등
goods_customerprice 소비자가 INT             부가세포함
goods_description 상품설명 VARCHAR 500            
goodsst_no 규격테이블 seq INT         O    
client_no1 거래처 테이블 seq INT         O   제조사
client_no2 거래처 테이블 seq INT         O   책임판매업자
goods_stockqty 재고수량 INT              
goodslev_no 상품 재고 등급 테이블 seq INT         O   상품재고등급
goods_location 재고위치 VARCHAR 50            
comcode_no 회사코드(고객사) 테이블 seq INT         O   회사코드 부여
                   
                   
상품-분류 테이블 사용 카테고리  
공동 테이블명 erp_goodssort  
Column Description Data Type Length Null Initial Value Primary Key Foreign Key Constraint Remark
칼럼명 설명 데이터 타입 길이 널(null)값 초기값 기본키 외래키 제약조건 비고
goodssort_no sequence INT       O      
goodssort_code 상품 분류코드 VARCHAR 30 NOT NULL       UNIQUE 자동완성 해서 입력
goodssort_name 상품 분류명 VARCHAR 30 NOT NULL        
상품/제품/반제품/원자재/부자재
                   
                   
상품-종류 테이블 사용 카테고리  
공동 테이블명 erp_goodskind  
Column Description Data Type Length Null Initial Value Primary Key Foreign Key Constraint Remark
칼럼명 설명 데이터 타입 길이 널(null)값 초기값 기본키 외래키 제약조건 비고
goodskind_no sequence INT       O      
goodssort_no 분류테이블 seq INT         O   분류
goodskind_code 상품 종류코드 VARCHAR 30 NOT NULL       UNIQUE 자동완성입력
goodskind_name 상품 종류명 VARCHAR 100 NOT NULL        
마우스/노트북/스피커/키보드 등등
                   
                   
상품-규격 테이블 사용 카테고리  
공동 테이블명 erp_goodsst  
Column Description Data Type Length Null Initial Value Primary Key Foreign Key Constraint Remark
칼럼명 설명 데이터 타입 길이 널(null)값 초기값 기본키 외래키 제약조건 비고
goodsst_no sequence INT       O      
goodsst_unit 단위 VARCHAR 30            
goodsst_spec 상품 사양 VARCHAR 300          
ex) cpu : 인텔i5 ....., 재질 등
goodsst_size 상품 사이즈 VARCHAR 300           100x100x100mm
goodsst_package 포장 사이즈 VARCHAR 300            
goodsst_ea 상품 사입량 INT             한 박스당 몇개
                   
                   
상품-재고등급 테이블 사용 카테고리  
공동 테이블명 erp_goodslev  
Column Description Data Type Length Null Initial Value Primary Key Foreign Key Constraint Remark
칼럼명 설명 데이터 타입 길이 널(null)값 초기값 기본키 외래키 제약조건 비고
goodslev_no sequence INT       O      
goodslev_grade 재고등급 VARCHAR 30 NOT NULL       UNIQUE
정상, 단상자파손, 스크래치, 전시상품, 파손
goodslev_description 재고등급 설명 VARCHAR 300            
                   
                   
상품-로트번호 테이블 사용 카테고리  
공동 테이블명 erp_goodslot  
Column Description Data Type Length Null Initial Value Primary Key Foreign Key Constraint Remark
칼럼명 설명 데이터 타입 길이 널(null)값 초기값 기본키 외래키 제약조건 비고
goodslot_no sequence INT       O      
goodslot_lot 로트번호 VARCHAR 30            
goodslot_qty 수량 INT              
goodslot_production 제조일자 DATE              
goodslot_expiry 유통기한 DATE              
goodslot_price 원가 INT              
goodslot_tax 세액 INT              
goodslot_total 총 원가 INT              
goods_no 상품 테이블 seq INT         O

 

재고관리에서는 기본적인 CRUD를 비롯하여, 수량만 변경을 한다거나 같은 상품이지만 로트번호는 다를 수 있는 경우를 신경써야 하기 때문에 생각보단 기능구현에 있어서 어려움을 겪었다.

 

우선, insert를 할 때 같은 상품이지만 로트번호는 다른 경우가 있기 때문에 로트추가 버튼을 누르면 새로운 행이 생기도록 했다.

(상품 등록 기본 상태)
(로트 추가 버튼을 누른 상태)
(한 번에 3개의 로트만 등록 가능)

이때 이용한 JS코드는 

  let lotIndex = 1; // 로트 정보 행 인덱스
  const maxLotCount = 3; // 최대 로트 정보 개수

  function addLotRow() {
    if (lotIndex < maxLotCount) {
      lotIndex++;
      const table = document.getElementById("lotTable");
      const row1 = table.insertRow(table.rows.length); // 첫 번째 행 추가
      const row2 = table.insertRow(table.rows.length); // 두 번째 행 추가

      // 첫 번째 로트 정보 행 추가
      row1.innerHTML = `
        <th>로트번호</th>
        <td><input type="text" id="goodslot_lot"` + lotIndex + ` name="goodslot_lot${lotIndex}"></td>
        <th>로트별수량</th>
        <td><input type="number" id="goodslot_qty${lotIndex}" name="goodslot_qty${lotIndex}"></td>
        <th>제조일자</th>
        <td><input type="date" id="goodslot_production${lotIndex}" name="goodslot_production${lotIndex}"></td>
      `;

      // 두 번째 로트 정보 행 추가
      row2.innerHTML = `
        <th>세액</th>
        <td><input type="number" id="goodslot_tax${lotIndex}" name="goodslot_tax${lotIndex}"></td>
        <th>원가</th>
        <td><input type="number" id="goodslot_price${lotIndex}" name="goodslot_price${lotIndex}"></td>
        <th>유통기한</th>
        <td><input type="date" id="goodslot_expiry${lotIndex}" name="goodslot_expiry${lotIndex}"></td>
      `;
    } else {
      alert("최대 3개의 로트 정보까지만 추가 가능합니다.");
    }
  }

  function removeLotRow() {
    if (lotIndex > 1) {
      const table = document.getElementById("lotTable");
      table.deleteRow(table.rows.length - 1); 
      table.deleteRow(table.rows.length - 1); 
      lotIndex--;
    } else {
      alert("최소한 한 개의 로트 정보가 필요합니다.");
    }
  }

//등록 버튼 클릭 시 실행되는 함수
  function validateAndSubmit() {
	
	const goodsCode = document.getElementById("goods_code").value;
	const goodsBarcode = document.getElementById("goods_barcode").value;
	const goodsName = document.getElementById("goods_name").value;
	const goodsStockqty = document.getElementById("goods_stockqty").value;
	const goodsstUnit = document.getElementById("goodsst_unit").value;
	const goodsstSpec = document.getElementById("goodsst_spec").value;
	const goodsstSize = document.getElementById("goodsst_size").value;
	const goodsstPackage = document.getElementById("goodsst_package").value;
	const goodsstEa = document.getElementById("goodsst_ea").value;
	const goodsCustomerprice = document.getElementById("goods_customerprice").value;
	
	
	
	console.log(goodsCode);
	console.log(goodsBarcode);
	
	if(goodsCode == ""){
		alert("상품코드를 입력해 주세요.");
		document.getElementById('goods_code').focus();
	    return false;
	}else if(goodsBarcode == ""){
		alert("바코드를 입력해 주세요.");
		document.getElementById('goods_barcode').focus();
		return false;
	}else if(goodsName == ""){
		alert("상품명을 입력해 주세요.");
		document.getElementById('goods_name').focus();
		return false;
	}else if(goodsCustomerprice == ""){
		alert("소비자가를 입력해 주세요.");
		document.getElementById("goods_customerprice").focus();
		return false;
	}else if(goodsStockqty <= 0){
		alert("재고수량을 입력해 주세요.")
		document.getElementById('goods_stockqty').focus();
		return false;
	}else if(goodsstUnit == ""){
		alert("단위를 입력해 주세요.");
		document.getElementById('goodsst_unit').focus();
		return false;
	}else if(goodsstSpec == ""){
		alert("상품사양을 입력해 주세요.");
		document.getElementById('goodsst_spec').focus();
		return false;
	}else if(goodsstSize == ""){
		alert("상품사이즈를 입력해 주세요.");
		document.getElementById('goodsst_size').focus();
		return false;
	}else if(goodsstPackage == ""){
		alert("포장사이즈를 입력해 주세요.");
		document.getElementById('goodsst_package').focus();
		return false;
	}else if(goodsstEa == ""){
		alert("상품 사입량을 입력해 주세요.");
		document.getElementById('goodsst_ea').focus();
		return false
	}
	
    let totalLotQty = 0;
    const lotQtyInput = document.getElementsByName("goodslot_qty");
    const goodslotProductionInput = document.getElementsByName("goodslot_production");
    const goodslotExpiryInput = document.getElementsByName("goodslot_expiry");

    var sum = 0;
    for (var h = 0; h < lotQtyInput.length; h++) {
      sum = Number(sum) + Number(lotQtyInput[h].value);
    }

    const stockQtyInput = document.getElementById("goods_stockqty");
    const stockQtyValue = parseInt(stockQtyInput.value) || 0;

    if (sum !== stockQtyValue) {
      alert("재고수량과 로트별수량의 합이 일치해야 합니다.");
      if (lotIndex >= 1) {
        const firstLotQtyInput = document.getElementById(`goodslot_qty1`);
        firstLotQtyInput.focus();
      }
      return false;
    }

    // 제조일자와 유통기한 비교
    for (var i = 0; i < lotQtyInput.length; i++) {
      const productionDate = new Date(goodslotProductionInput[i].value);
      const expiryDate = new Date(goodslotExpiryInput[i].value);

      if (expiryDate < productionDate) {
        alert("유통기한이 제조일자보다 빠릅니다. 유통기한을 다시 입력해 주세요.");
        goodslotExpiryInput[i].focus();
        return false;
      }
    }

    document.getElementById("create").submit();
  }

addLotRow 함수는 "로트 정보 추가" 버튼을 클릭했을 때 실행된다. 

먼저, 현재 로트 정보의 개수인 lotIndex를 확인하고 최대 로트 정보 개수인 maxLotCount보다 작을 때만 로트 정보를 추가할 수 있다. lotIndex를 증가시키고, 테이블에서 두 개의 새로운 행을 추가하고 각 행은 로트 정보를 입력하는 데 사용된다.  

첫 번째 행에는 로트 번호, 로트별 수량, 제조일자의 입력 필드가 생성되고, 두 번째 행에는 세액, 원가, 유통기한의 입력 필드가 생성된다.

 

(이때 추가되는 행에 대해서는 name값에 lotIndex의 값을 붙여 숫자를 추가했는데 이것은 추후에 Controller에 대한 설명을 할 때 마저 이야기하겠다.)

 

removeLotRow 함수는 "로트 정보 삭제" 버튼을 클릭했을 때 실행된다.

현재 추가된 로트 정보의 개수인 lotIndex를 확인하고, 최소한 한 개의 로트 정보가 남아있을 때만 로트 정보를 삭제할 수 있고, 테이블에서 마지막 두 행(로트 정보 행)을 삭제하고 lotIndex를 감소시킨다.

 

validateAndSubmit 함수는 "등록" 버튼을 클릭했을 때 실행되며, 입력된 데이터를 유효성 검사하고 제출하는 역할을 한다.

먼저, 상품 정보와 관련된 입력 필드의 내용을 변수에 저장한다. (ex. goodsCode, goodsBarcode, goodsName 등)

각 입력 필드에 대한 유효성 검사를 수행한다. 예를 들어, 상품코드, 바코드, 상품명, 소비자가, 재고 수량 등이 비어 있거나 유효하지 않으면 경고 메시지를 표시하고 해당 필드에 포커스를 준다.

로트 정보의 총 수량(sum)을 계산하고, 이 값이 상품의 재고 수량과 일치하는지 확인한다. 일치하지 않으면 경고 메시지를 표시하고 첫 번째 로트 정보의 수량 입력 필드에 포커스를 준다.

각 로트의 제조일자와 유통기한을 비교하여 유효성 검사를 하고, 유통기한이 제조일자보다 빠를 경우 경고 메시지를 표시하고 해당 유통기한 입력 필드에 포커스를 준다.

모든 유효성 검사를 통과하면 폼 데이터를 제출한다.

 

 

 

'ERP Project' 카테고리의 다른 글

물류팀 기능구현(2)  (0) 2023.10.12
재무팀 기능구현(2)  (0) 2023.10.10
재무팀 기능구현(1)  (0) 2023.09.20
인사팀 기능구현(2)  (0) 2023.09.20
인사팀 기능구현(1)  (0) 2023.09.20