Get data from excel to office word array using VBA - arrays

I have an excel file where are stored in columns some text and keywords.
I want to use the data in excel to make some Advanced search in Word using vba. But I'm getting an error trying to transpose the data from excel cells to an array in vba word.
I have used the transpose excel function but it doesn't handle more than 255 characters so I can't get cell's value that exceeds 255 characters.
I would be thankfull if someone could give me a hand.
Option Explicit
Dim strArray
Dim range As range
Dim i As Long
Dim numberOfUniqMatches As Integer
Dim totalMatches As Integer
Sub HighlightMatchesAndSummarize()
totalMatches = 0
'************************************ GET DATA FROM EXCEL ***************************************
Dim xlApp As Object
Dim xlBook As Object
Const strWorkBookName As String = "D:\keyword_source_3.xlsx"
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If Err Then
Set xlApp = CreateObject("Excel.Application")
End If
On Error GoTo 0
Set xlBook = xlApp.Workbooks.Open(FileName:=strWorkBookName)
'xlApp.Visible = True
xlApp.Visible = False
'transpose excel cells in our arrays
strArray = xlApp.Transpose(xlApp.ActiveSheet.range("A1:A20" & AlRow).Value)
Set xlBook = Nothing
xlApp.Quit
Set xlApp = Nothing
'
' End of data extraction
'/******************************** SEARCH LOOP START **********************************
For i = 1 To UBound(strArray)
numberOfUniqMatches = 0
Set range = ActiveDocument.range
With range.Find
.Text = strArray(i)
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchFuzzy = False
.MatchPhrase = True
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While .Execute(Forward:=True) = True
numberOfUniqMatches = numberOfUniqMatches + 1
totalMatches = totalMatches + 1
range.HighlightColorIndex = wdYellow
Loop
End With
Next
'
' End of search loop
' Display message if no matching word is found
If totalMatches <= 0 Then
MsgBox "Sorry! No matching keyword found."
Else
MsgBox "Search ended: " & totalMatches & " matching word(s)."
End If
End Sub

Change this:
strArray = xlApp.Transpose(xlApp.ActiveSheet.range("A1:A20" & AlRow).Value)
To:
'remove the transpose (and fix the range...)
strArray = xlApp.ActiveSheet.range("A1:A" & AlRow).Value
Then in your loop:
For i = 1 To UBound(strArray, 1) '<<<<<<<
numberOfUniqMatches = 0
Set range = ActiveDocument.range
With range.Find
.Text = strArray(i, 1) '<<<<<<<
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchFuzzy = False
.MatchPhrase = True
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While .Execute(Forward:=True) = True
numberOfUniqMatches = numberOfUniqMatches + 1
totalMatches = totalMatches + 1
range.HighlightColorIndex = wdYellow
Loop
End With
Next

Saerch for Byte in your code and replace it by Long. Ctrl+H is the shortcut for Replace.

Related

Convert Comma Separated String to Array for Text Conversion

I am creating an application [In Access] to convert text files to excel files because my company does a lot of them. So I created a table that I keep the File Name, Num of Cols, and a 3rd field with the common separated list of the datatypes for the columns.
Everything is working except I cannot get the comma separated list to work as an array. First, I call the ImportText File:
Call ImportTextFile("TestFileName", 7, ConvertStringToArray(",,,,,,2"))
Then I ConvertSTringToArray:
Function ConvertStringToArray(ByVal StringToConvert As String) As Variant
Dim rawArray() As String
Dim varArray() As Variant
rawArray = Split(StringToConvert, ",")
ReDim varArray(LBound(rawArray) To UBound(rawArray))
Dim i As Long: For i = LBound(rawArray) To UBound(rawArray)
varArray(i) = rawArray(i)
Next i
ConvertStringToArray = varArray
End Function
Then it passes to ImportTextFile (Up until here aDataTypes is passed as an Array.):
Public Sub ImportTextFile(ByVal strFileName As String, ByVal iNumOfCols As Integer, Optional aDataTypes As Variant = Nothing)
On Error GoTo Sub_Err
Dim xl As New Excel.Application: Set xl = New Excel.Application
xl.DisplayAlerts = False
Dim sPathAndFile As String: sPathAndFile = cPath & strFileName
Dim wb As Workbook: Set wb = xl.Workbooks.Add
Dim ws As Worksheet: Set ws = wb.Sheets(1)
With ws.QueryTables.Add(Connection:="TEXT;" & sPathAndFile & ".txt", Destination:=ws.Range("$A$1"))
.FieldNames = True
.RowNumbers = False
.RefreshStyle = xlInsertDeleteCells
.SaveData = True
.TextFilePlatform = 437
.TextFileStartRow = 1
.TextFileParseType = xlDelimited
.TextFileTextQualifier = xlTextQualifierDoubleQuote
.TextFileConsecutiveDelimiter = False
.TextFileTabDelimiter = False
.TextFileSemicolonDelimiter = False
.TextFileCommaDelimiter = True
.TextFileSpaceDelimiter = False
If IsArray(aDataTypes) Then
.TextFileColumnDataTypes = aDataTypes
End If
.TextFileTrailingMinusNumbers = True
.Refresh BackgroundQuery:=False
End With
End Sub
However, it crashes on this line:
.TextFileColumnDataTypes = aDataTypes
What am I missing? Why isn't this working?
The Error message that I receive is:
Invalid Procedure Call or Argument
TextFileColumnDataTypes expects an array of XlColumnDataType values, but you're passing in an array of strings.
Maybe consider reworking your array function:
Function FormatsArray(ByVal StringToConvert As String) As Variant
Dim i As Long
Dim rawArray() As String
Dim varArray As Variant, v
rawArray = Split(StringToConvert, ",")
ReDim varArray(LBound(rawArray) To UBound(rawArray))
For i = LBound(rawArray) To UBound(rawArray)
v = Trim(rawArray(i))
If Len(v) > 0 Then 'specific format supplied?
varArray(i) = CLng(v)
Else
varArray(i) = xlGeneralFormat 'use default
End If
Next i
FormatsArray = varArray
End Function

vba for each element loop error occurs at second loop

I'm new to VBA and I'm trying to scrape data from a website. I've used nested loop. When the innermost loop finishes for the first time, the next loop starts for marakez.
Actual problem is that when 'for each in schl2' loop repeats for second time, IE crashes and loop is unable to proceed. I have mentioned in code.
Here is my code
Sub ResultDownloader()
' here I define elemnts for the loop
Dim sht As Worksheet
Set sht = ThisWorkbook.Sheets("LocData")
Dim LastRow As Long
Dim i As Long
Dim imagePath As Object
LastRow = sht.Cells(sht.Rows.Count, "D").End(xlUp).Row
startrec = sht.Cells(sht.Rows.Count, "E").End(xlUp).Row
startrec = startrec + 1
Dim IE As Object
Dim Doc As HTMLDocument
' Set IE = CreateObject("InternetExplorer.Application")
Set IE = CreateObject("InternetExplorer.Application")
' here I define Object to sendkeys
Dim SHELL_OBJECT
SHELL_OBJECT = "WScript.Shell"
Set objShell = CreateObject(SHELL_OBJECT)
Record2Strt = (sht.Cells(sht.Rows.Count, "E").End(xlUp).Row) + 1
IE.Visible = True
IE.Navigate "some_url"
Do While IE.Busy
Application.Wait DateAdd("s", 1, Now)
Loop
Dim HTMLdoc As HTMLDocument
Dim selectElement, selectElement2, selectElement3 As HTMLSelectElement
Dim evtChange As Object
Set Doc = IE.Document
Dim dist1, tehsl1, mrkz1, schl1 As Object
Dim dist2, tehsl2, mrkz2, schl2 As Variant
Dim distlen, thsllen, mrkzlen, schllen As Byte
Dim dst, tsl, mrkz, schl As Byte
Dim elt3, elt4, elt5, elt6 As Variant
Set evtChange = Doc.createEvent("HTMLEvents")
evtChange.initEvent "change", True, False
Set dist1 = Doc.querySelector("Select[name=districts]")
Set dist2 = dist1.querySelectorAll("option")
distlen = dist1.querySelectorAll("option").Length
dst = 0
For Each elt3 In dist2
distnme = elt3.innerText
If distnme <> "All Districts" Then
dist1.getElementsByTagName("option")(dst).Selected = True
Set selectElement2 = Doc.getElementsByTagName("option")(dst)
selectElement2.dispatchEvent evtChange
Application.Wait DateAdd("s", 0.5, Now)
Set tehsl1 = Doc.querySelector("Select[name=tehsil]")
Set tehsl2 = tehsl1.querySelectorAll("option")
thsllen = tehsl1.querySelectorAll("option").Length
tsl = 0
For Each elt4 In tehsl2
thslnme = elt4.innerText
If thslnme <> "All Tehsils" Then
Set tehsl1 = Doc.querySelector("Select[name=tehsil]")
tehsl1.getElementsByTagName("option")(tsl).Selected = True
Set selectElement3 = tehsl1.getElementsByTagName("option")(tsl)
selectElement3.dispatchEvent evtChange
Application.Wait DateAdd("s", 0.5, Now)
Set mrkz1 = Doc.querySelector("Select[name=markaz]")
Set mrkz2 = mrkz1.querySelectorAll("option")
mrkzlen = mrkz1.querySelectorAll("option").Length
mrkz = 0
For Each elt5 In mrkz2
mrkznm = elt5.innerText
If mrkznm <> "All Marakez" Then
Set mrkz1 = Doc.querySelector("Select[name=markaz]")
mrkz1.getElementsByTagName("option")(mrkz).Selected = True
Set selectElement4 = mrkz1.getElementsByTagName("option")(mrkz)
selectElement4.dispatchEvent evtChange
Application.Wait DateAdd("s", 0.5, Now)
Set schl1 = Doc.querySelector("Select[name=school]")
Set schl2 = schl1.querySelectorAll("option")
schllen = schl1.querySelectorAll("option").Length
schl = 0
' second loop problem
' when for each elt6 in schl2 starts IE crashes
On Error Resume Next
For Each elt6 In schl2
Application.Wait DateAdd("s", 0.5, Now)
schlnm = elt6.innerText
If schlnm <> "All Schools" Then
Set schl1 = Doc.querySelector("Select[name=school]")
schl1.getElementsByTagName("option")(schl).Selected = True
Set selectElement5 = schl1.getElementsByTagName("option")(schl)
selectElement5.dispatchEvent evtChange
sht.Range("A" & LastRow + 1).Value = LastRow
sht.Range("B" & LastRow + 1).Value = distnme
sht.Range("C" & LastRow + 1).Value = thslnme
sht.Range("D" & LastRow + 1).Value = mrkznm
sht.Range("E" & LastRow + 1).Value = schlnm
LastRow = LastRow + 1
End If 'for school
schl = schl + 1
If schllen = schl Then
GoTo new_marakez
On Error Resume Next
End If
Next 'ele6
End If 'for marakez
new_marakez:
mrkz = mrkz + 1
If mrkzlen = mrkz Then
Exit For
GoTo new_tehsil
End If
Next 'ele5
On Error Resume Next
End If 'for tehsils
new_tehsil:
tsl = tsl + 1
If thsllen = tsl Then
GoTo new_dist
End If
Next 'ele4
On Error Resume Next
End If 'for districts
new_dist:
dst = dst + 1
If distlen = dst Then
GoTo stopp
End If
Next 'ele 3
On Error Resume Next
stopp:
End Sub
There is apparently a bug when using querySelectorAll with a generic object for your elements, in your case here 'schl2.', and using a for each...next loop. I solved this by using a standard for...next loop basically limiting the for loop, in your case, schl2.Length - 1. However, this will not work unless you define schl2 as MSHTML.IHTMLDOMChildrenCollection. If you leave this as generic, the schl2.Length will be NULL. The code below shows how I got around the problem.
`'Create html object to hold IE Document
Set html = IE.Document
Debug.Print "********* GET FIELDS ******" & vbCrLf
Dim res1 As MSHTML.IHTMLDOMChildrenCollection
Set res1 = html.querySelectorAll("#HtmlOutputReportResults2_Explorer_Filters_Column option:checked")
For r = 0 To res1.Length - 1
If res1(r).innerText <> "..." Then
Debug.Print "res1.Text: " & res1(r).innerText
End If
Next
Debug.Print vbCrLf & "********* GET OPERATORS ******" & vbCrLf
Dim res2 As MSHTML.IHTMLDOMChildrenCollection
Set res2 = html.querySelectorAll("#HtmlOutputReportResults2_Explorer_Filters_Operator option:checked")
For r = 0 To res2.Length - 1
If res2(r).innerText <> "..." Then
Debug.Print "res2.Text: " & res2(r).innerText
End If
Next`

Find loop not working correctly

I am trying to design a vba macro script for Mac Office 2011 that uses find in column A to find a filename previously selected by the user.
The user selects a .csv file, then macro looks through column A to find filename without .csv extension. Once found, it offsets by one column (to column B) and imports the csv information.
What I currently have is not doing the find and then select? I can't seem to figure out what I am doing wrong here.
The csv will import, but just next to the cell I had active before running macro. This is why I think the Find is not working.
Any help would be most appreciated.
Sub CSVauto()
'
' CSVauto Macro
'
' Keyboard Shortcut: Option+Cmd+x
'
' Declaring and setting variables for choosing CSV to import
Dim csvFileName As Variant
''Prompt window to choose csv file
csvFileName = Application.GetOpenFilename(FileFilter:="")
If csvFileName = False Then Exit Sub
'Setting a variable to find Experimental form name in Data Summary
Dim whatToFind As String 'Declaring that variable
If Right(csvFileName, 4) = ".csv" Then
whatToFind = Replace(csvFileName, ".csv", "")
Else
MsgBox "Selected File Not .csv)"
End If
'Looping through A column to find csvFileName without .csv extension
Set cell = Range("A:A").Find(What:=whatToFind, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False)
If Not cell Is Nothing Then
cell.Select
End If
'Speeding macro up by making it work in background
Sheets("DataSummary").DisplayPageBreaks = False
Application.DisplayAlerts = False
Dim MyRange As Range
Set MyRange = ActiveCell.Offset(0, 1)
MyRange.Select
'xlOverwriteCells
On Error Resume Next
'Formatting for CSV and input
With MyRange.Parent.QueryTables.Add(Connection:="TEXT;" & csvFileName, Destination:=MyRange)
.FieldNames = True
.RowNumbers = False
.FillAdjacentFormulas = False
.RefreshOnFileOpen = False
.BackgroundQuery = True
.RefreshStyle = xlOverwriteCells
.SavePassword = False
.SaveData = True
.AdjustColumnWidth = False
.TextFilePromptOnRefresh = False
.TextFilePlatform = xlMacintosh
.TextFileStartRow = 1
.TextFileParseType = xlDelimited
.TextFileTextQualifier = xlTextQualifierDoubleQuote
.TextFileConsecutiveDelimiter = True
.TextFileTabDelimiter = True
.TextFileSemicolonDelimiter = False
.TextFileCommaDelimiter = True
.TextFileSpaceDelimiter = True
.TextFileColumnDataTypes = Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
.Refresh BackgroundQuery:=False
.UseListObject = False
End With
'Formatting DataSummary sheet to fit "requirements" :)
Cells.Replace What:=">=", Replacement:="", LookAt:=xlPart, SearchOrder:=xlByColumns, MatchCase:=False
Cells.Replace What:="C121", Replacement:="C2", LookAt:=xlPart, _
SearchOrder:=xlByColumns, MatchCase:=False
Cells.Replace What:="P1211", Replacement:="P21", LookAt:=xlPart, _
SearchOrder:=xlByColumns, MatchCase:=False
Cells.Select
With Selection
.HorizontalAlignment = xlLeft
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
End With
With Selection
.HorizontalAlignment = xlCenter
.Orientation = 0
.AddIndent = False
.ShrinkToFit = False
End With
Range("A4").Select
' Set Do loop to stop when an empty cell is reached.
Do Until IsEmpty(ActiveCell)
' Insert your code here.
' Step down 1 row from present location.
ActiveCell.Offset(1, 0).Select
Loop
'undoing everything working in background
Sheets("DataSummary").DisplayPageBreaks = True
Application.ScreenUpdating = True
End Sub
Take a look at where you "Set cell =...." you are having it look for whatToFind.
In your if/else statement above that, you never set whatToFind in the "else" statement. You need to set whatToFind as something in the else statement, if I am reading your request correctly.
It looks to me what you are asking for is to find a file that is NOT a .csv then to perform the function of searching/offset.
Please correct me if I am wrong or please clarify.
EDIT
This code should work for you. I tried it with your code with this inserted just below the if/else statement
Dim filename As Variant
filename = Mid(whatToFind, InStrRev(whatToFind, "/") + 1)
MsgBox filename

Make PivotTable name dynamic in vb code

In Excel 2010, I recorded a macro of steps to create mulitple pivot tables (some on different sheets). However, I am struggling to get the code to accept a "dynamic" PivotTable name. My code originally wanted to automatically assign the next PivotTable number. For example, "PivotTable23", "PivotTable24", etc. Since I never know what the next number will be in the workbook, I changed it to the following and of course it does not work (I am new to using vb code):
Sub TestContinueSD()
'
' TestContinueSD Macro
'
'
Sheets.Add
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _
"Stmt_Volumes!R1C1:R46154C42", Version:=xlPivotTableVersion14). _
CreatePivotTable TableDestination:="Sheet1!R3C1", TableName:=PivotTables(1) _
, DefaultVersion:=xlPivotTableVersion14
Sheets("Sheet1").Select
Cells(3, 1).Select
ActiveSheet.PivotTables(1).Name = "TOTAL"
With ActiveSheet.PivotTables("TOTAL").PivotFields("desc")
.Orientation = xlPageField
.Position = 1
End With
ActiveSheet.PivotTables("TOTAL").AddDataField ActiveSheet.PivotTables( _
"TOTAL").PivotFields("id"), "Sum of id", xlSum
With ActiveSheet.PivotTables("TOTAL").PivotFields("Sum of id")
.Caption = "Count of id"
.Function = xlCount
End With
With ActiveSheet.PivotTables("TOTAL").PivotFields("sfreq")
.Orientation = xlColumnField
.Position = 1
End With
With ActiveSheet.PivotTables("TOTAL").PivotFields("txt")
.Orientation = xlPageField
.Position = 1
End With
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Selection.Font.Bold = True
With Selection.Font
.Name = "Calibri"
.Size = 14
.Strikethrough = False
.Superscript = False
.Subscript = False
.OutlineFont = False
.Shadow = False
.Underline = xlUnderlineStyleNone
.ThemeColor = xlThemeColorLight1
.TintAndShade = 0
.ThemeFont = xlThemeFontMinor
End With
ActiveCell.FormulaR1C1 = "TOTAL (ALL)"
Range("A1:B1").Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlBottom
.WrapText = False
.Orientation = 0
.AddIndent = False
.IndentLevel = 0
.ShrinkToFit = False
.ReadingOrder = xlContext
.MergeCells = False
End With
Selection.Merge
Sheets("Sheet1").Select
Sheets("Sheet1").Name = "SUMMARY"
End Sub
You can set the name of the PivotTable by setting the appropriate parameter of the CreatePivotTable method call:
ActiveWorkbook.PivotCaches.Create(...). _
CreatePivotTable TableDestination:="Sheet1!R3C1", TableName:="TOTAL" _
, DefaultVersion:=xlPivotTableVersion14
See http://msdn.microsoft.com/en-us/library/office/ff839885%28v=office.15%29.aspx for details on the CreatePivotTable method.

Efficient method to use excel checksheet data to create and populate a list in a new sheet

I am working on a project, which takes a checksheet that the user creates and fills out
and, when the user runs a macro, creates a new workbook that extrapolates and expands the checksheet data, as shown here
What it does is it goes through each of those number labor codes, and runs down the checksheet for all the applicable items, addending them to the list.
Now...I have this working fine, and run through the basic testing. I save the checksheet as an array and pass it through to the new workbook, filtering and creating the new workbook line-by-line.
I just can't help but think that there's a much easier way to do this, as the way I'm doing it now just doesn't seem to be the simplest and most stable way.
I'm open to sharing my code I have so far, but was wondering if you were given this senario, how you would approach it.
Here is the link to my file: https://www.dropbox.com/s/2gobdx1rcabquew/Checksheet_Template_R3.0%20-%20StkOvrflw.xls
Main module, which checks for errors and corrects formatting:
Option Explicit
Public FamilyName As String
Public ModelName As String
Public TaskArray() As Variant
Public TaskArrayRowCount As Integer
Public TaskArrayColCount As Integer
Sub CreateTemplate()
Application.EnableEvents = False
Application.ScreenUpdating = False
'Main SubModule. Runs Formatting and Template Generation
Dim thisWB As Workbook
Dim TaskArray() As Variant
Dim i As Range
Dim MajMinYesNo As Boolean
Dim OPOYesNo As Boolean
If MsgBox("Are you ready to generate the Template?", vbYesNo, "Ready?") = vbNo Then
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
MajMinYesNo = False
OPOYesNo = False
Set thisWB = ActiveWorkbook
FamilyName = thisWB.Names("Family_Name").RefersToRange
ModelName = thisWB.Names("Model_No").RefersToRange
Call CreateArray(thisWB)
'Scans Form_Type Column for "R", "S", or "A-E"
For Each i In Range("CS_FormType")
If i Like "[RS]" Then
MajMinYesNo = True
ElseIf i Like "[A-E]" Then
OPOYesNo = True
End If
Next
'Generates Templates As Needed
If MajMinYesNo Then
If MsgBox("Generate Major/Minor Template?", vbYesNo) = vbYes Then
Call MajorMinor_Generate.GenerateMajorMinor(thisWB)
End If
End If
If OPOYesNo Then
If MsgBox("Generate OPO Template?", vbYesNo) = vbYes Then
Call OPO_Generate.GenerateOPO(thisWB)
End If
End If
Application.EnableEvents = True
Application.ScreenUpdating = True
MsgBox ("DONE!")
End Sub
Sub CreateArray(thisWB As Workbook)
'Checks formatting and creates array TaskArray() with all the checksheet data
With thisWB.Sheets(1)
'Confirms equal number of rows in columns "CS_TaskNo", "CS_FormType", and "CS_Task"
If (Not Range("CS_TaskNo").Rows.count = Range("CS_FormType").Rows.count) _
Or (Not Range("CS_TaskNo").Rows.count = Range("CS_Task").Rows.count) Then
MsgBox ("Task_No, Form_Type, and Task_Desc row count does not match. Please fix and try again")
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
Call FormatCheck
Application.Union(Range("CS_Heading"), Range("CS_TaskNo"), Range("CS_FormType"), Range("CS_Task"), Range("CS_LaborCodes"), Range("CS_Checks")).Name = "TaskArray"
TaskArrayRowCount = Range("TaskArray").Rows.count
TaskArrayColCount = Range("TaskArray").Columns.count
ReDim TaskArray(TaskArrayRowCount, TaskArrayColCount)
TaskArray = Range("TaskArray").Value
End With
End Sub
Sub FormatCheck()
'Checks for valid labor codes and Form Types
If (Not CheckFormType()) Or (Not CheckLC()) Then
MsgBox ("Errors found, please check red-highlighted cells")
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
End Sub
Function CheckFormType()
'Returns False if there's a bad Form_Type entry in range "CS_FormType", True if all OK
Dim i As Range
Dim ReturnVal As Boolean
ReturnVal = True
For Each i In Range("CS_FormType")
Trim (UCase(i.Value))
If Not (i Like "[ABCDEFRS]") Then
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End If
Next
CheckFormType = ReturnVal
End Function
Function CheckLC()
'Returns False if there's a bad error code, True if all OK _
Formats labor code ranges to add spaces as needed and checks _
labor codes for proper format (###X or ##X). Skips any labor _
codes starting with "28X"
Dim LaborCode As String
Dim LaborCodeLength As Integer
Dim i As Range
Dim j As Integer
Dim LCCell As Range
Dim LCArray() As String
Dim ReturnVal As Boolean
ReturnVal = True
For Each i In Range("CS_LaborCodes")
Trim (UCase(i.Value))
LaborCode = i.Value
If Not Left(LaborCode, 3) Like "28?" Then
LaborCodeLength = Len(LaborCode)
'If string LaborCode is > 4, safe to assume it is a range of labor codes 123A-123F
Select Case LaborCodeLength
Case Is > 4
'Formats Labor Code Range String by adding spaces if necessary (i.e. 123A-123F to 123A - 123F)
For j = 2 To LaborCodeLength Step 1
If (IsNumeric(Mid(LaborCode, j, 1))) And Not IsNumeric(Mid(LaborCode, j + 1, 1)) And Not (Mid(LaborCode, j + 2, 1) = " ") Then
LaborCode = Left(LaborCode, j + 1) & " " & Mid(LaborCode, j + 2)
ElseIf IsNumeric(Mid(LaborCode, j, 1)) And Not (Mid(LaborCode, j - 1, 1) = " ") And Not IsNumeric(Mid(LaborCode, j - 1, 1)) Then
LaborCode = Left(LaborCode, j - 1) & " " & Mid(LaborCode, j)
End If
Next
i = LaborCode
LCArray = Split(LaborCode, " ")
'confirms the labor codes are valid
If (Not IsLaborCode(LCArray(0))) Or (Not IsLaborCode(LCArray(2))) Or (Not IsLaborCodeRange(LCArray(0), LCArray(2))) Then
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End If
Case 0 To 4
If Not (IsLaborCode(LaborCode)) Then
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End If
Case Else
Highlight (Cells(i.Row, i.Column))
ReturnVal = False
End Select
End If
Next
CheckLC = ReturnVal
End Function
Function IsLaborCode(LC As String) As Boolean
'returns True if Labor Code is valid, False if invalid _
Labor Code is valid if it is 2 or 3 numbers followed by a letter _
labor code format : ###X or ##X
If LC Like "###[A-Z]" Or LC Like "##[A-Z]" Then
IsLaborCode = True
Else
IsLaborCode = False
End If
End Function
Function IsLaborCodeRange(LCOne As String, LCTwo As String) As Boolean
'returns True if the LC range is valid, False if invalid. _
checks the numerical values to make sure they match and _
makes sure the letters are ascending
If (StrComp(Left(LCOne, Len(LCOne) - 1), Left(LCTwo, Len(LCTwo) - 1)) = 0) And LCOne < LCTwo Then
IsLaborCodeRange = True
Else
IsLaborCodeRange = False
End If
End Function
And here is the other module which actually takes the array and creates the new workbook:
Sub GenerateMajorMinor(thisWB As Workbook)
Dim newWB As Workbook
Dim MajMinArray() As Variant
Set newWB = Workbooks.Add
With newWB
Call FormatWorkbook
Call CreateMajMinArray(newWB, MajMinArray)
Call PopulateItemMaster(MajMinArray)
Call PopulateLaborLink(MajMinArray)
Call SaveFile(newWB, thisWB)
End With
End Sub
Sub SaveFile(newWB As Workbook, thisWB As Workbook)
'saves new workbook into the same file path as the checksheet
Dim i As Integer
Dim FileSavePath As String
Dim FamNameSave As String
FamNameSave = Replace(FamilyName, "/", "_")
i = 1
FileSavePath = thisWB.Path + "/Template (Minor and Major)_" + FamNameSave + ".xls"
a: If Dir(FileSavePath) <> "" Then
FileSavePath = thisWB.Path + "/Template (Minor and Major)_" + FamNameSave + "(" + CStr(i) + ").xls"
i = i + 1
GoTo a:
End If
newWB.SaveAs FileSavePath, FileFormat:=56
End Sub
Sub FormatWorkbook()
'Names and formats sheets
Sheets(1).Name = "Item_Master"
Sheets(2).Name = "Labor_Link"
With Sheets(1)
.Range("A1") = "Company_No"
.Range("B1") = "Family_Name"
.Range("C1") = "Form_Type"
.Range("D1") = "Record_Status"
.Range("E1") = "Task_Desc"
.Range("F1") = "Task_No"
.Range("G1") = "Task_Seq"
.Range("H1") = "Is_Parametric"
End With
With Sheets(2)
.Range("A1") = "Company_Name"
.Range("B1") = "Family_Name"
.Range("C1") = "Form_Type"
.Range("D1") = "Labor_Code"
.Range("E1") = "Print_Control"
.Range("F1") = "Record_Status"
.Range("G1") = "Task_No"
End With
End Sub
Sub CreateMajMinArray(newWB As Workbook, MajMinArray As Variant)
'creates array, removing any OPO/BTS labor codes
With Sheets(3)
Application.EnableEvents = True
Application.ScreenUpdating = True
Dim rng As Range
Set rng = .Range(.Range("A1"), .Cells(TaskArrayRowCount, TaskArrayColCount))
rng = TaskArray
For i = 1 To .Range("A1").End(xlDown).Row Step 1
If .Cells(i, 2) Like "[A-E]" Then
.Rows(i).Delete
i = i - 1
End If
Next
For i = 1 To .Range("A1").End(xlToRight).Column Step 1
If Left(.Cells(1, i), 3) Like "28E" Then
.Columns(i).Delete
i = i - 1
End If
Next
ReDim MajMinArray(.Range("A1").End(xlDown).Row, .Range("A1").End(xlToRight).Column)
MajMinArray = .Range(.Range("A1"), .Cells(.Range("A1").End(xlDown).Row, .Range("A1").End(xlToRight).Column)).Value
.Cells.Clear
End With
End Sub
Sub PopulateItemMaster(MajMinArray As Variant)
With Sheets(1)
'Populates "Item_Master" Sheet
For i = 2 To UBound(MajMinArray) Step 1
.Cells(i, 2) = FamilyName
.Cells(i, 3) = MajMinArray(i, 2)
.Cells(i, 4) = "1"
.Cells(i, 5) = MajMinArray(i, 3)
.Cells(i, 6) = MajMinArray(i, 1)
.Cells(i, 7) = MajMinArray(i, 1)
Next
End With
End Sub
Sub PopulateLaborLink(MajMinArray As Variant)
Dim i As Integer
Dim LaborCode As String
Dim RowCount As Long
Dim LCArray() As String
Dim LastLetter As String
Dim LastFormType As String
'Initializes RowCount and PrintControl
RowCount = 2
PrintControl = 10
With Sheets(2)
For i = 4 To UBound(MajMinArray, 2) Step 1
LaborCode = Trim(MajMinArray(1, i))
'If Labor Code String length is > 4, safe to assume that it is a range of labor codes
Select Case Len(LaborCode)
Case Is > 4
LCArray = Split(LaborCode, " ")
'checks to see if LCArray(0) and LCArray(2) has values
If LCArray(0) = "" Or LCArray(2) = "" Then
MsgBox ("Error with Labor Code range. Please check and re-run")
Application.EnableEvents = True
Application.ScreenUpdating = True
End
End If
LastLetter = Chr(Asc(Right$(LCArray(2), 1)) + 1)
LCArray(2) = Replace(LCArray(2), Right$(LCArray(2), 1), LastLetter)
Do
Call PrintLaborLinkLines(MajMinArray, LCArray(0), RowCount, i)
LastLetter = Chr(Asc(Right$(LCArray(0), 1)) + 1)
LCArray(0) = Replace(LCArray(0), Right$(LCArray(0), 1), LastLetter)
Loop Until LCArray(0) = LCArray(2)
Erase LCArray()
Case Is <= 4
Call PrintLaborLinkLines(MajMinArray, LaborCode, RowCount, i)
End Select
Next
End With
End Sub
Sub PrintLaborLinkLines(MajMinArray As Variant, LaborCode As String, RowCount As Long, i As Integer)
Dim PrintControl As Long
PrintControl = 10
With Sheets(2)
For x = 2 To UBound(MajMinArray) Step 1
If UCase(MajMinArray(x, i)) = "Y" Then
If LastFormType <> MajMinArray(x, 2) Then
PrintControl = 10
End If
.Cells(RowCount, 2) = FamilyName
.Cells(RowCount, 3) = MajMinArray(x, 2)
.Cells(RowCount, 4) = LaborCode
.Cells(RowCount, 5) = PrintControl
.Cells(RowCount, 6) = "1"
.Cells(RowCount, 7) = MajMinArray(x, 1)
RowCount = RowCount + 1
PrintControl = PrintControl + 10
LastFormType = MajMinArray(x, 2)
End If
Next
End With
End Sub
If restructuring the order of the data on the new sheet is possible it seems as though you could copy only visible cells and then write a simple loop to bring in any data that is not explicit (ie Labor Code).

Resources