PROGRAMMING WORKSHOP

자재관리 | DashBoard

STEP_1 ---------------------------------

자재관리라는 제목때문에 다른 산업분야의 분들이 혹시라도..
자재관리는 나와는 상관없는데..라고 생각하시면 오산
정보관리라고 하는 근본적인 컨셉을 갖고 있는 것이니 각자의 분야의
정보관리에 응용을 하면 되는 것..VBA는 똑 같은 것
그러니 모두 열공하시기를 바라며..!!!

자재관리코너에서 자재의 청구, 청구와 동시에 정보의 보관등을
하였었다
그리고 DB에 보관도 하는 작업도 하였었다
이제 위와 같은 작업을 통하여 얻어진 축적된 정보를 이용하여
분석이나 보고서나 현황을 볼수 있는 DashBoard를 만들어 보자
우선 DashBoard라는 것을 만들어 보도록 하자
DashBoard란
자동차를 타고 갈때 기름이 얼마나 남았는지
현재 몇키로시속으로 달리고 있는지
RPM이 얼마인지..등등을 계기판(Dash Board)를 통하여 보면서
운전을 한다
그 컨셉을 그대로 활용하는 것이다



외국에서는 어떤 일을 하던 현황을 한눈에 볼수 있게 하는
도구를 만들어 놓는 것이 중요하여 많이들 활용하는데
우리는 별로 데이타의 중요성을 몰라서 그런 것인지
데이타를 믿지 못하여 그런 것인지..별로 관심이 없는 것 같지만
앞으로는 달라 질 것이다
제대로 된 원시정보를 기반으로 항상 현상태를 관찰유지하게
하는 것이 필요하게 될 것이다
어떻게 만드는 것이 잘 만드는 것인지에 대한 원칙은 없다
하나의 원칙은 쏘스데이타가 신빙성이 있는 진실한 정보가 원칙일 것이다
나머지는 어떤 것을 관심있게 볼것인지는 사람마다
기업마다 부서마다 관심사에 맞는 것을 만들면 되는 것이다
길동이가 문제를 보는 시각과 꺽정이가 문제를 보는 시각중에
사장님의 시각과 같다면 시각이 같은 사람이 만든 보고서를 좋아 할 것이다
그러니 이런 보고서, 분석, 현황판(DashBoard)등을 만들때는 자신의 기호 보다는
누가 볼것인지, 누구에게 보여줄 것인지 그렇게 보는 사람(일종의 소비자)의
요구, 시각을 충족 시켜주어야 할 것이다
그래야 잘 만드는 소루션이 되는 것이다..
고집이 쐬고집이라서 지가 만드는 것이 최고라고 해보았자 소비자(사장,임원,부장등등)가
싫어 하면 , 시각과 다르면 말짱 허당이다
그러니 엑셀프로그래머는 그런 소비자의 시각도 생각하는 스마트한 사람이
되어야 할 것이다 ^____^
실은 DB에 축적된 정보를 갖여와서 엑셀에서 정리를 한후
보고서를 만드는 것을 하여야 하지만
DB에 축적된 정보가 없으니 문제를 제시한 분의 엑셀시트상의
정보를 활용하도록 하자
보고서나 분석을 할때 , 어떤 분석을 하고자 할때 원시쏘스의
저장단계에서 아차차...그것을 빼먹었군...데이타가 한참 축적된 후에
새로운 정보 휠드를 만들어 넣으려면 골치가 아파질 것이다
그래서 몇번을 그런 과정을 거치면서
회사나 조직의 표준이 만들어 지는 셈이다
원시소스를 잘 구성하여야 하는 점이 가장 기본인 것이다
쓸데 없는 정보를 많이 보관하여도 문제고..
중요한 정보가 빠져도 문제고..
그것이 회사나 조직의 보호하여야할 지적자산이 되는 것이다
보내주신 자료시트의 휠드명을 보면
입고일
자재코드...이것도 별의미없으니 없애고
대분류코드...이것도 대분류명으로 바꿔버리고
자재명
규격 ...별 의미없으니 없애고
단위 .....별 의미없으니 없애고
계약단가
집행단가
입고단가
입고수량
입고금액 ...입고*입구수량이라는 계산휠드같은 것은 원본에서 삭제
업체명
담당자....현장명별로 담당자를 정리하고
부서명...없애고..
공사코드...없애고..
공사명....현장명으로 바꾸고..
등등...

이것을 학습하기 위하여 좀 고치도록 하자
물론 프로그래밍적으로 고치자..이것도 학습의 목적이니까..
이렇게 대시보드나 보고서의 원본이 되는 것은 좀더 간략하게
최적화하는 것이 좋다
특히 외부화일과 연결되거나 한 참조주소등..
합계를 낸다거나 한 수식과 함수들 ..
더 이상 쓸일이 없는 것은 모두 없애 버리는 것이 원본의 최적화작업이다
이런 최적화를 해주지 않으면 효율이 떨어진다
엑셀화일을 알뜰 살뜰 관리하는 방법중의 하나이다
각각의 현장명을 7개로 하고
각현장에는 담당자가 3명씩있는 것으로 시나리오를 짜서
현장명과 담당자를 아래의 코드를 실행하여 채워넣는다

Sub fillDummyDatas() Dim rCol As Range Dim iCol As Integer Dim rX As Range, sCharge As String, sField As String Set rCol = Worksheets("원본").Range("A1").CurrentRegion iCol = rCol.Rows(1).Find("현장명", , , xlWhole).Column Set rCol = rCol.Columns(iCol) For Each rX In rCol.Cells fillDummyData sCharge, sField rX = sField rX.Offset(, -1) = sCharge Next End Sub Sub fillDummyData(sCharge As String, sField As String) Dim sCharges(1 To 7) As String Dim iRandom As Integer sCharges(1) = "도다리,고등어,숭어" sCharges(2) = "너구리,염소,원숭이" sCharges(3) = "진달래,개나리,민들레" sCharges(4) = "백두산,한라산,태백산" sCharges(5) = "포도,사과,수박" sCharges(6) = "한강,낙동강,소양강" sCharges(7) = "꼴뚜기,낙지,쭈꾸미" Randomize iRandom = Int(Rnd() * 7) + 1 sField = String(3, Chr(64 + iRandom)) sCharge = Split(sCharges(iRandom), ",")(Int(Rnd() * 3)) End Sub

좀 다양한 연습을 위하여 그림과 같이 바꾸는 것이다



수식과 이름짓기등을 우선 최대한 활용해보자
아래의 그림과 같이..



현대의 일을 하는 사람들은 다른 말로
[정보가공업자]들이라고 보는 것이 좋다
허접한 정보들은 조합하여 여하히 정보를 얼마나 설득력있게
만들어 공급하느냐는 바로 그 사람의 실력이 된다
그러니 엑셀은 평생 비서인 셈이다

***[LOG-IN]***

STEP_2 ---------------------------------

수식과 함수로 처리하고 있지만 수식과 함수를 잘 활용할줄 알고 VBA로 좀더 자동화를 하는 것이 좋다 수식과 함수는, 회사에서 후배들을 지도하시는 고참님들에게도 도움이 필수다..
초보가 수식과 함수를 물어 보는데..
나는 VBA밖에 몰라!!라고 하면 좀 그렇다
잘 챙기시기를..
수식으로 하던 VBA로 하던 분석을 하는데 있어서 기본은 테이블을 분석할 변수들(각 휠드에 대한 유일한 값들)을 정리해 놓은 시트가 하나 있어야 할 것이다 VBA로 테이블을 보고 필요한 변수들을 뽑아내서 정리를 해 놓자

Sub refreshCodes() '입고일 대분류 자재명 계약단가 입고단가 입고수량 업체명 담당자 현장명 Const CATEGORY_LABEL As String = "대분류" Const DATE_LABEL As String = "입고일" Const MTRL_LABEL As String = "자재명" Const FLD_LABEL As String = "현장명" Const CHARGE_LABEL As String = "담당자" Dim rTbl As Range, rRow As Range Dim rFld As Range Dim shtCode As Worksheet On Error Resume Next Set rTbl = Worksheets("원본").Range("A1").CurrentRegion Set rFld = rTbl.Rows(1) Set rTbl = rTbl.Offset(1).Resize(rTbl.Rows.Count - 1) Set shtCode = Worksheets("코드") shtCode.Cells.Clear ' 대분류 filterThis CATEGORY_LABEL, rFld, shtCode, rTbl, 1 '월별 filterThis DATE_LABEL, rFld, shtCode, rTbl, 3 '현장명 filterThis FLD_LABEL, rFld, shtCode, rTbl, 5 '현장별 담당자 filterThis CHARGE_LABEL, rFld, shtCode, rTbl, 7 With shtCode.Cells.Font .Size = 10 .FontName = "맑은 고딕" End With End Sub Sub filterThis(sLabel As String, rFld As Range, shtCode As Worksheet, rTbl As Range, iTargetCol As Integer) Dim iCol As Integer, rC As Range Dim iX As Integer iCol = rFld.Find(sLabel, , , xlWhole).Column On Error Resume Next iX = 1 With shtCode.Columns(iTargetCol).Cells(iX) If sLabel = "담당자" Then .Resize(, 2) = Array("현장명", "담당자") Else .Value = sLabel End If If sLabel = "입고일" Then Dim datMax As Date Dim datMin As Date datMax = Application.Max(rTbl.Columns(iCol)) datMin = Application.Min(rTbl.Columns(iCol)) datMax = DateSerial(Year(datMax), Month(datMax), 1) datMin = DateSerial(Year(datMin), Month(datMin), 1) Do .Offset(iX) = Format(datMin, "yyyy 년 mm월") iX = iX + 1 datMin = DateAdd("m", 1, datMin) Loop While datMin <= datMax ElseIf sLabel = "담당자" Then Dim oCol As New Collection Dim iCol_ As Integer Dim sX As String Dim varX As Variant iCol_ = rFld.Find("현장명", , , xlWhole).Column For Each rC In rTbl.Columns(iCol).Cells sX = Intersect(rTbl.Columns(iCol_), rC.EntireRow) & "|" & rC oCol.Add sX, sX Next For Each varX In oCol .Offset(iX).Resize(, 2) = Split(varX, "|") iX = iX + 1 Next .CurrentRegion.Sort .Cells(1), xlAscending, , , , , , xlYes Else For Each rC In rTbl.Columns(iCol).Cells If .Resize(iX).Find(rC.Value, , , xlWhole) Is Nothing Then .Offset(iX) = rC iX = iX + 1 End If Next End If End With End Sub

유일한 값을 찾는 것을 일부러 두가지로 해 놓았다
학습을 위하여..
Collection개체로 처리하는 것과 다른 방법으로 처리하는 것
비교하여 상황에 따라서 처리하는 것이 좋다
세상은 방법이 다양하니까, 한가지만 고집할 것도 아닌것!!

테이블에서 휠드의 위치,열의 위치를 찾는 것도 상수로 하지 않고
사용자가 휠드위치를 수시로 바꿀수 있다는 상황을 감안하여
휠드명으로 휠드열위치를 찾는 습관이 좋다

이 소루션 덕분에 아주 옛날 실무자시절을 수식과 함수로 용써가면서
추억하면서 그려댄다



***[LOG-IN]***

STEP_3 ---------------------------------

이제 VBA를 좀더 활용해 보자
수식이 많이 연결되었을때 VBA를 돌리고 싶으면 항상 유의할점은
VBA가 돌면서 값이 셀에 갱신되면서 갱신될때마다 수식의 재계산이
일어나는 것이다
눈에 보이지 않아서 그렇지 엄청나게 CPU가 열나지게 일을 하게 될 것이다
당연히 효율이 떨어지고...속도가 버벅거린다
그래서 항상 어떤 모듈의 시작과 종료 부분에

Application.Calculation = xlCalculationManual
...
...
하고 싶은 VBA작업
...
...
Application.Calculation = xlCalculationAutomatic

와 같이 계산엔진을 쉬게 해주어야 한다
또 속도의 효율에 관련된 것은
만약 시트에 많은 값을 옮기고,주고,받고를 하는 작업을
순환하면서 일일이 하는 작업을 눈으로 보고 싶지 않고 그냥
속도를 올리고 싶다면

Application.ScreenUpdating=False|True

를 프로시져시작전과 후에 주는 것이 좋을 것이다
또 하나더..
만약 워크시트의 Change이벤트프로시져에 어떤 작업을 하는 내용을
작성한 상태에서 셀의 값을 계속 주고 받고 한다면
워크시트의 Change이벤트를 쓸데없이 값이 바뀔때마다 공회전을
돌게 될 것이다
이것도 억시..

Application.EnableEvents=False|True

로 시작전과 후에서 통제를 해주는 것이 VBA를 잘하는 사람인가..
못하는 사람인가를 판별하는 기준이 될수 있다

어떤 목적한 일을 수행하는데..의도하지 않은 엑셀의 기능이
일을 하는 것을 통제 하여 주는 것도 프로그램을 하는 일중의 하나라는 것을
잊으면 안 될 것이다

엑셀에 내가 쩜하나 찍으면 엑셀이 자동으로 어떤 일을 하려고
하는지 알고 있어야 엑셀을 제대로 알고 있는 것
콘트롤을 시트에 몇개 달아 보자



콤보상자에서 선택된 변수를 이용하여 데이타원본을 휠터하고
휠터된 내용을 DashBoard창의 한곳에 요약하여 보여주자
그런데 날짜콤보상자의 내용은 [2012년1월] 이라는 형식으로 되어 있다
이것을 휠터 하려면 2012년 1월1일 부터 2012년 1월31일까지라는 형식의
조건문을 문자열로 구성하여 엑셀의 휠터메소드에 전달하여야 할 것이다
요런 부분이 한참 신나게 VBA 실력이 확장되는데..갑자기 벽이
막히는 기분이 드는 부분들이고..
그러나 통과하고 나면 실력이 향상되는 부분이 되는 장벽인 것이다
콤보상자의 문자열 정보를 얻어서
시작 날짜와 종료날짜를 유효한 날짜정보로 바꾸어서
이것을 다시 문자열로 재구성하야 엑셀의 휠트메소드에 전달하면 된다
이런 각각의 데이타의 휠터하고자하는 휠드의 조건을 주는 부분을
중점적으로 잘 관찰하시면 좋을 것이다
간단한 대쉬보드를 만들려다가 이런 저런 기능을 자꾸 붙이게 된다
이 대쉬보드 코너를 모두 소화시키면
정보의 요약 집계등의 테크닉을 모두 섭렵하게 될 것이다
물론 손으로 직접 입력하고, 의아한 것이 있으면 열심히 질문을 하여
꼭 알고야 말겠다는 집념이 있다면...

***[LOG-IN]***

STEP_4 ---------------------------------

첫화면에 Step_1,Step_2,Step_3 버튼등이 만들어져 있다
이것을 크릭하면 해당화면으로 수평방향으로 이동하여 선택하게 되어 있다
그런데 공교롭게 만들다보면 어라..Step_1은 휠터의 결과에 따라서 행을
아주 많이 소모할수 있게 된다는 것을 알게 된다..
에라...그렇다고 Step_1화면과 Step_2화면사이를 그냥 대강 짐작하여
넓게 간격을 벌려 놓는다고 하는 것은 어리석은 짓이다
범위에 이름을 지어서 사용하는 것은 그래서 편리한 것이다
Step_1버튼이 참조하는 화면을 맨 밑으로 이동시켜 놓는다면 훨씬
안정적이다



또한 현장명을 하나 이상을 선택할 경우도 있고, 월도 한달이 아닌 몇개월을
선택할수도 있다..그러니 콤보상자 보다는 목록상자가 좋을 것이고..
중요한 것은 현재 시트가 복잡해 진 상태이다
이럴때 UserForm의 콘트롤을 시트상에 박아 놓는 것은 효율적이지 못하고
버벅거리는 수가 많다
이럴때는 UserForm을 독자적으로 띄워주는 것이 좋다
Page_1버튼을 크릭하면 UserForm이 또 다른 작은 콘트롤패널로 시트위에
나타나게 하는 것이 좋을 것이다
그렇게 변경하면서
콤보상자와 목록상자의 용도
UserForm의 적극적 활용에 대하여 익숙해져 보도록 하자

UserForm에 콘트롤을 그려 넣을때 그래픽솜씨가 없어서 쩔쩔맨다
이럴때는 수학적으로 런타임으로 계산으로 위치와 규격을 잡도록 하는 것이 좋다
UserForm이 초기화될때 각 콘트롤의 위치와 속성등을 런타임으로 설정하는 것이
정확하고 마우스로 콘트롤을 다루는데 능숙하지 않을때는 좋고
또한 효율적으로도 런타임으로 처리하는 습관이 좋은 것이다
그런데 목록상자의 높이를 RunTime에 설정하려고 하는데 죽어도 안된다
이것은 반드시 목록상자를 Userform에 그린후
디자인타임에 각 목록상자의 IntegralHeight속성을 False로 하여 주어야 한다
그러지 않으면 목록상자의 높이가 제멋대로 나타날수 있다



현장마다 담당자가 다르니..현장을 여러곳을 선택하면 담당자선택은
불필요하게 하도록 콘트롤하여야 할 것이다
아래의 그림과 같이 현장을 여러곳 선택하면 담당자는 모두 없어지고
하나의 현장만 선택할때 해당 담당자가 나타나게 콘트롤하여 주는 것이
지능이 좀더 높은 인터페이스가 될 것이다



목록상자에서 항목이 하나만 선택될때는 어떤 것이 선택되었는지
알기 쉽지만...ListBox.List(ListBox.ListIndex) 의 값을 변수에 담으면 되지만
여러개가 선택된 것을 알아낼때는 초보님들이 헷갈린다..
목록상자의 Selected 속성이 있다
각 항목이 Selected되었는지를 알아 내야 한다

For iX = 0 To ListBoxFields.ListCount - 1 If ListBoxFields.Selected(iX) Then iSelected = iSelected + 1 sCurrent = ListBoxFields.List(iX) End If Next

항목을 순환하면서 해당 항목이 선택되었는지 확인하고 선택된 것을
수집하여야 한다
아래의 화일에는 UserForm의 인터페이스만 움직이게 꾸몄다
다음 화일에서는 UserForm을 통하여 조건 선택후 휠터링을 하여
현재 DashBoard시트에 뿌릴 것인지..다른 시트에 뿌릴 것인지, 아니면
다른 통합문서에 뿌릴 것인지를 실행해 보도록 하자

***[LOG-IN]***