Прокрутите все листы, если ячейка = строка, скопируйте три ячейки на специальный лист - PullRequest
0 голосов
/ 28 декабря 2011

Я работаю с Excel 2007. У меня есть система счетов, в которой я набираю номер заказа на покупку, и он генерирует новый счет с этим номером заказа в качестве имени листа, а также копирует его в ячейку на том же листе,затем он заполняется и сохраняется вручную.Каждый счет имеет один из четырнадцати типов расходных материалов (например, расходные материалы для печати или чистящие средства, выбранные из раскрывающегося списка) в ячейке C6.Все это прекрасно работает.
Я хочу отслеживать, сколько потрачено на каждый тип поставки, поэтому мне нужно просмотреть каждый счет-фактуру, проверить тип поставки и скопировать три несмежные ячейки (дата (A6: B6), po # (F6: G6) и количество (G39)) в строке таблицы «Стиль чековой книжки» для этого типа поставки.

Полагаю, псевдокод будет выглядеть следующим образом:

  • Для каждого листа проверьте тип поставки в ячейке c6
  • Если тип поставки печатается,
  • записать значения трех ячеек в новой строке на лист с именем Printing, в противном случае перейдите к следующему
  • , если тип поставки очищает,
  • запишите значения трех ячеек вновая строка на листе под названием «Очистка»
  • и т. д., «если» я до смерти.

Вот то, что у меня есть, что просто прошел все листы и скопировал клетки безсортировка их по типу поставки - тогда я попытался использовать только Печать счетов-фактур, но безуспешно.

Sub CopyRangeFromPrintingWorksheets()


Dim sh As Worksheet
Dim DestSh As Worksheet
Dim Last As Long
Dim CopyRng As Range

With Application
    .ScreenUpdating = False
    .EnableEvents = False
End With
'Delete the sheet "Printing" if it exist
Application.DisplayAlerts = False
On Error Resume Next
ActiveWorkbook.Worksheets("Printing").Delete
On Error GoTo 0
Application.DisplayAlerts = True

'Add a worksheet with the name
Set DestSh = ActiveWorkbook.Worksheets.Add
DestSh.Name = "Printing"

'loop through all worksheets and copy the data to the DestSh
For Each sh In ActiveWorkbook.Worksheets
    'If sh.Name <> DestSh.Name Then
    If InStr(1, Worksheets(wks.Name).Range("C6:E6").Value, "Printing/Stationary 532-110", vbTextCompare) = 1 Then
    ' If LCase(Left(sh.Name, 4)) = "tly-" Then

        'Find the last row with data on the DestSh
        Last = LastRow(DestSh)

        'Fill in the range that you want to copy
        Set CopyRng = sh.Range("G3")


        'Test if there enough rows in the DestSh to copy all the data
        If Last + CopyRng.Rows.Count > DestSh.Rows.Count Then
            MsgBox "There are not enough rows in the Destsh"
            GoTo ExitTheSub
        End If

        'This copies values/formats, want to copy the
        'values or want to copy everything
        CopyRng.Copy
        With DestSh.Cells(Last + 1, "B")
            .PasteSpecial xlPasteValues
            .PasteSpecial xlPasteFormats
            Application.CutCopyMode = False
        End With

        'Optional: This will copy the sheet name in the A column
        DestSh.Cells(Last + 1, "A").Resize(CopyRng.Rows.Count).Value = sh.Name
        'Copy ordered by cell to column C
        DestSh.Cells(Last + 1, "D").Resize(CopyRng.Rows.Count).Value = sh.Range("G39")
        'Copy date cell to proper column
        DestSh.Cells(Last + 1, "C").Resize(CopyRng.Rows.Count).Value = sh.Range("C6")
        DestSh.Cells(Last + 1, "E").Resize(CopyRng.Rows.Count).Value = sh.Range("E8")
    End If
Next

ExitTheSub:

Application.GoTo DestSh.Cells(1)

'AutoFit the column width in the DestSh sheet
DestSh.Columns.AutoFit

With Application
    .ScreenUpdating = True
    .EnableEvents = True
End With

Я даже исследовал выбранный случай, но все еще не имел успеха.Пробовал записывать марко и смотреть на этот код без вдохновения.Кажется, что это не должно быть так сложно ... но я не знаю наиболее практичный способ сделать это.Указатель в правильном направлении был бы фантастическим!

Ответы [ 2 ]

2 голосов
/ 29 декабря 2011

Я пришел к тому же выводу, что и Фог, но в другом направлении. Ниже я работаю с вашим кодом, предлагающим изменения, но мне трудно поверить, что вы могли бы сделать эту работу.

Точка 1

У вас есть несколько открытых книг? Вы переключаетесь между ними перед запуском макроса? Предположим, у вас открыто три рабочие книги (A, B и C). Предположим также, что этот макрос и счета находятся в рабочей книге A. Если вы используете рабочую книгу C при запуске макроса, C будет ActiveWorkBook. Можно запускать макросы в нескольких книгах, но это добавляет сложности, без которой, я полагаю, можно обойтись. Если при запуске макроса открыта только одна рабочая книга, ActiveWorkbook..

не требуется.

Точка 2

Мне не нравится использовать On Error для ошибок, которых я могу избежать. Это, вероятно, не важно, но удалить что-то, а затем воссоздать это, кажется мне неправильным. Я бы сделал это так:

Dim DestSh as Worksheet
Dim Found As Boolean
Dim InxWS As Integer

Found = False
For InxWS = 1 To Worksheets.Count
  If Worksheets(InxWS).Name = "Printing" Then
    Found = True
    ' Use whichever of the following two statements is most appropriate
    ' This completely deletes the contents of the worksheet
    Worksheets(InxWS).Cells.EntireRow.Delete
    ' This deletes the contents of the worksheet but keeps the column widths
    Worksheets(InxWS).Cells.EntireRow.ClearContents
    Exit For
  End If
Next
If Not Found Then
  Set DestSh = Worksheets.Add
  DestSh.Name = "Printing"
End If

Точка 3

Не вижу ничего плохого в следующем:

For Each sh In Worksheets
  If sh.Name <> DestSh.Name Then 


  End If
Next

For Each - это очень хороший способ работы с таблицами.

Вам нужен какой-то метод проверки, не проверяете ли вы лист «Печать». Однако, если конечный лист всегда будет «Печать», я мог бы написать sh.Name <> "Printing".

С другой стороны, если бы я хотел похвастаться, я бы написал:

Const DestShName as String = "Printing"
 :      :
DestSh.Name = DestShName
 :      :
If sh.Name <> DestShName Then 

С помощью этого кода я мог бы изменить имя листа назначения, изменив выражение Const (константа).

Точка 4

Рассмотрим:

Worksheets(wks.Name).Range("C6:E6").Value

Что такое wks? Переменная для sh?

Я думаю, у вас нет Option Explicit в качестве первой строки вашего модуля. Option Explicit говорит, что вы хотите запретить использование необъявленных переменных.

Worksheets(sh.Name) совпадает с sh.

Я предполагаю, что "C6: E6" были объединены. Если вы хотите значение объединенной области, используйте верхнюю левую ячейку. Итак Range("C6").Value.

Ваш выбранный случай будет в форме:

With sh
  Select Case .Range("C6").Value
    Case "Printing"
      ' Do something
    Case "Cleaning"
      ' Do something
    Case "Stationary"
      ' Do something
    Case "Books"
      :     :
    Case Else
      ' Do something about an unknown supply type
  End Select
End With

Точка 5

Если я правильно понял, у вас есть 14 типов поставок, каждый со своим листом назначения. Вам понадобится Select Case в цикле, чтобы подготовить листы назначения. Тип поставки совпадает с названием листа? Если нет, то это будет очень грязно, особенно если вы добавите другой тип поставки.

Возможно, стоит подумать о массивах.

Dim InxShST as Integer 
Dim SheetNameList() as String
Dim SupplyTypeList() as String

SheetNameList = Array("Print", "Clean", "Stat", ... )
SupplyTypeList = Array("Printing supplies", "Cleaning supplies", ... ) 

При одинаковом порядке названий листов и типов поставки вы можете найти тип поставки в заказе на поставку и преобразовать его в имя листа. Если вы добавляете новый тип поставки, просто добавьте новое значение в конец каждого массива.

Вернемся к пункту 2. Я предлагаю вам забыть о добавлении рабочих листов VBA; создайте 14 листов вручную.

Код становится:

For InxWS = 1 To Worksheets.Count
  For InxShST = LBound(SheetNameList) To UBound(SheetNameList)
    If Worksheets(InxWS).Name = SheetNameList(InxShST) Then
      Worksheets(InxWS).Cells.EntireRow.ClearContents
      Exit For
    End If
  Next
Next

Я признаю, что это сложнее, но готовит столько листов, сколько вам нужно. У вас есть два цикла: один для рабочих листов и один для имен рабочих листов. Когда вы получаете совпадение, у вас есть лист, который необходимо очистить. LBound обозначает Нижнюю границу. UBound обозначает верхнюю границу. Второй For-Loop подстраивается под размер массива.

Вы можете использовать:

  For Each SheetNameCrnt In SheetNameList

Это может выглядеть проще. Но с помощью индекса вы можете связать SheetNameList(InxShST) с SupplyTypeList(InxShST)

Другие баллы

Вы уверены, что хотите один лист для каждого заказа на покупку? Сколько заказов на покупку у вас есть в день. 10? 100? 500? Это может быть очень неуправляемая рабочая тетрадь.

Из других операторов вашего макроса я предполагаю, что у вас есть фиксированный заголовок, а затем по одной строке данных на каждый заказанный продукт. Вы не определяете природу этих строк, но я предполагаю, что вы хотите скопировать их на соответствующий лист.

Возможно, я мог бы догадаться о структуре этих строк данных, но я должен поставить под сомнение ваш дизайн. Если я закажу у вас картридж для принтера и немного мыльного порошка, нужно ли мне два заказа на покупку? Я не думаю, что вы выиграете мой бизнес.

2 голосов
/ 29 декабря 2011

Решение этого в VBA возможно, но оно будет довольно громоздким и хрупким.

Одна из проблем этого подхода заключается в том, что у вас есть данные, хранящиеся в нескольких местах, которые могут быть не синхронизированы. Кроме того, вам нужны три разных вида ваших данных:

  • просмотр одной записи (т.е. один заказ на покупку)
  • просмотр нескольких записей (т. Е. "Стиль чековой книжки"), возможно сгруппированный или отфильтрованный по типу поставки
  • сводное представление (итоги за заданный диапазон дат и / или тип поставки)

Это довольно тривиально в Access или более серьезной системе баз данных, но небольшой поиск заставляет меня поверить, что в Excel вы можете получить два из вышеперечисленных, но не все три. Тем не менее, приведенные ниже ссылки могут несколько помочь:

Вы можете использовать трехмерную ссылку для суммирования данных, но я не думаю, что вы можете создать сводную таблицу из трехмерной ссылки (для представления в стиле чековой книжки):

http://office.microsoft.com/en-us/excel-help/create-a-3-d-reference-to-the-same-cell-range-on-multiple-worksheets-HP010102346.aspx

Вы также можете консолидировать несколько рабочих листов в сводную таблицу, но похоже, что исходные данные уже должны быть в виде чековой книжки, поэтому нет способа получить представление данных в виде единого счета:

http://office.microsoft.com/en-us/excel-help/consolidate-multiple-worksheets-into-one-pivottable-report-HA010226585.aspx


Итог: если у вас есть время посвятить этому, я бы рекомендовал перенести решение в Access.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...