LibreOffice/Скрипт - Статистика системы допуска
Приведённый ниже скрипт обрабатывает данные выгруженные из системы ограничения допуска в помещение.
Входными данными для скрипта является таблица на листе "исх данные" со следующими колонками:
- Дата/Время -
- Фамилия
- Имя
- Отчество
- Должность
- Событие
- Зона доступа
- Направление
- Табельный номер
- Подразделение
- Телефон
Скрипт создаёт общий отчёт, затем при утвердительном ответе на запросы а диалоговом окне, создаёт отчёты для каждого подразделения, страницы с исходными данными для каждого отдела и сводный отчёт по сотрудникам. Каждый отчёт создаётся на отдельной странице и к ячейкам применяются стили оформления: выделяются опоздания, переработки, работа в выходные дни. Также выдаются предупреждения о странных записях: двойной вход или выход, вход без выхода в этот же день и т.п. Подсчитывается итог трудочасов по человеку, отделу и предприятию. Создаются сводные отчёты за месяц для рабочего времени, переработок и опозданий.
Можно скачать файл с примером. В файле есть исходные данные, макрос и стили оформления.
Для запуска скрипта необходимо включить макросы в LibreOffice:
Сервис - Параметры - Безопасность - Безопасность макросов - Уровень безопасности: средний
и выполнить главную процедуру в макросе, которая вызовет все остальные:
Сервис - Макросы - Выполнить макрос - Учёт времени (пример) - Standard - WorkTime - WorktimeReport
Для просмотра и изменения кода макроса:
Сервис - Макросы - Управление макросами - LibreOffice Basic - Правка
Предварительные требования к листу с исходными данными:
- Данные берутся из 1-го листа, со 2-й строки (первая содержит название столбцов и не учитывается)
- Строки должны быть отсортированы по Подразделению (столбец J) и по вторичному ключу Фамилия (столбец B)
REM ***** LibreOffice BASIC for Calc 4.x *****
'
' Автор: Павел Малахов, 24pm@mail.ru
'
' Этот скрипт использует стили ячеек, которые желательно создать заранее:
' pm-Время - ячейки со временем
' pm-Заголовок - заголовок колонки в таблице
' pm-Заголовок общий - заголовок отчёта
' pm-Итого - итоговые ячейки
' pm-Сотрудник - заголовок отчёта по сотруднику
'
'
' Предварительные требования к листу с исходными данными:
' - Данные берутся из 1-го листа, со 2-й строки (первая содержит название столбцов и не учитывается)
' - Строки должны быть отсортированы по Подразделению (столбец J) и по вторичному ключу Фамилия (столбец B)
'
'
' Заметки:
' - Скрипт работает медленнее, если включен вид "Разметка страницы". Для ускорения нужно переключить в вид "Обычный"
'
' История изменений:2014.09.04 - первая полнофункциональная версия
' 2014.12.17 - Исправлено: Если был зарегистрирован только вход, то данные следующего человека учитывались в текущем. Добавлена переменная tab_person
'
'==== Общие для всех модулей процедуры и функции ====
'==== Константы ====
Const dayStatusHoliday = 0
Const dayStatusShort = 1
Const dayStatusWork = 2
'Определение времени начала и окончания рабочего дня в формате Часы:Минуты:Секунды
Const timeWorkStart As Date = "08:00:00"
Const timeWorkEnd As Date = "17:15:00"
Const timeWorkEndShort As Date = "15:00:00"
Const timeZero As Date = "00:00:01" 'если полный ноль, то это минус сутки
Const timeWorkOver As Date = "00:15:00" 'переработка < 15 мин не учитывается
' Преобразует дату в строку
' cDate() - не подходит, т.к. возвращает строку в формате "ММ.ДД.ГГГГ ЧЧ:ММ:СС"
Function DateTimeToString(dt As Date) As String
DateTimeToString = Year(dt) & "." & Month(dt) & "." & Day(dt) & " " & Hour(dt) & ":" & Minute(dt)
End Function 'DateTimeToString
' Возвращает количество дней в месяце
Function DaysInMonth(dt As Date) As Integer
Dim days As Integer
days = 28
If IsDate( "29." & Month(dt) & "." & Year(dt) ) Then
days = 29
End If
If IsDate("30." & Month(dt) & "." & Year(dt)) Then
days = 30
End If
If IsDate("31." & Month(dt) & "." & Year(dt)) Then
days = 31
End If
DaysInMonth = days
End Function 'DaysInMonth
' Возвращает статус текущего дня: выходной (0), предпраздничный (1) или рабочий (2)
Function DayStatus(dt As Date) As Integer
Dim status As Integer
Dim next_day As Date
status = -1
next_day = DateAdd("d", 1, dt)
If IsHoliday(dt) Then 'выходной
status = dayStatusHoliday
ElseIf ( (WeekDay(dt) = 6) OR isHoliday(next_day) ) Then 'короткий день, предпраздничный
status = dayStatusShort
Else 'обычный рабочий день
status = dayStatusWork
End If
DayStatus = status
End Function 'DayStatus
Function IsHoliday(dt As Date) As Boolean
Dim result As Boolean
Dim str_day As String
Dim holidays(27)
result = false
str_day = Month(dt) & "." & Day(dt)
holidays(0) = "1.1" '1—6 и 8 января — Новогодние каникулы
holidays(1) = "1.2"
holidays(2) = "1.3"
holidays(3) = "1.4"
holidays(4) = "1.5"
holidays(5) = "1.6"
holidays(6) = "1.7" '7 января — Рождество Христово
holidays(7) = "1.8"
holidays(8) = "2.22" '22—23 февраля — День защитника Отечества
holidays(9) = "2.23"
holidays(10) = "3.8" '8—10 марта — Международный женский день
holidays(11) = "3.9"
holidays(12) = "3.10"
holidays(13) = "5.1" '1—4 мая — Праздник Весны и Труда
holidays(14) = "5.2"
holidays(15) = "5.3"
holidays(16) = "5.4"
holidays(17) = "5.9" '9—11 мая — День Победы
holidays(18) = "5.10"
holidays(19) = "5.11"
holidays(20) = "6.12" '12—15 июня — День России
holidays(21) = "6.13"
holidays(22) = "6.14"
holidays(23) = "6.15"
holidays(24) = "11.1" '1—4 ноября — День народного единства
holidays(25) = "11.2"
holidays(26) = "11.3"
holidays(27) = "11.4"
'суббота и воскресенье
If (WeekDay(dt) = 7) OR (WeekDay(dt) = 1) Then
result = true
End If
'государственные праздники, выходные дни
For i=0 To UBound(holidays)
If str_day = holidays(i) Then
result = true
Exit For
End If
Next
IsHoliday = result
End Function 'IsHoliday
Function NameWeekDay(dt As Date) As String
Dim wd(7)
wd(0) = "Название дня недели"
wd(1) = "Вс"
wd(2) = "Пн"
wd(3) = "Вт"
wd(4) = "Ср"
wd(5) = "Чт"
wd(6) = "Пт"
wd(7) = "Сб"
NameWeekDay = wd(WeekDay(dt))
End Function 'NameWeekDay
Function NameMonth(i As Integer) As String
Dim m(12)
m(0) = "Название месяца"
m(1) = "Январь"
m(2) = "Февраль"
m(3) = "Март"
m(4) = "Апраель"
m(5) = "Май"
m(6) = "Июнь"
m(7) = "Июль"
m(8) = "Август"
m(9) = "Сентябрь"
m(10) = "Октябрь"
m(11) = "Ноябрь"
m(12) = "Декабрь"
NameMonth = m(i)
End Function 'NameMonth
'Возвращает буквенный индекс колонки (до 2-х букв) по её числовому значению от А до ZZ 26*26=676
Function ColumnLetter(num As Integer) As String
Dim letter(27)
Dim x, y As Integer
Dim str As String
letter(0) = ""
letter(1) = "A"
letter(2) = "B"
letter(3) = "C"
letter(4) = "D"
letter(5) = "E"
letter(6) = "F"
letter(7) = "G"
letter(8) = "H"
letter(9) = "I"
letter(10) = "J"
letter(11) = "K"
letter(12) = "L"
letter(13) = "M"
letter(14) = "N"
letter(15) = "O"
letter(16) = "P"
letter(17) = "Q"
letter(18) = "R"
letter(19) = "S"
letter(20) = "T"
letter(21) = "U"
letter(22) = "V"
letter(23) = "W"
letter(24) = "X"
letter(25) = "Y"
letter(26) = "Z"
'имя колонки до ZZ, num <= 676
If num > 25 Then
x = Int(num / 26) 'целая часть от делелния
y = num MOD 26 +1 'остаток
Else
x = num +1
y = 0
End If
'формирование названия колонки без ограничения
'x = Int(num / 26) 'целая часть от делелния
'While x > 0
' str = letter()
' x = x MOD 26 +1 'остаток
'Wend
ColumnLetter = letter(x) & letter(y)
End Function 'ColumnLetter
'==== Константы ====
'Определение колонок в исходной таблице
Const colDate = 0
Const colFamily = 1
Const colName = 2
Const colOtch = 3
Const colDolznost = 4
Const colDostup = 5
Const colZone = 6
Const colZoneName = 7
Const colTabelNumber= 8
Const colOtdel = 9
Const colRoom =10
'Определение колонок и строк в отчёте
Const rowReportStart= 3 'первая строка отчёта(начиная с заголовка колонок)
Const colRepFIO = 0 'колонка для вывода ФИО сотрудника и его отдела
Const colRepWeekDay = 0 'День недели
Const colRepDate = 1 'Дата
Const colRepTime = 2 'Интервал времени рабочего дня
Const colRepWorkDay = 3 'Общее время рабочего дня
Const colRepInside = 4 'Общее время в здании
Const colRepOutside = 5 'Общее время вне здания
Const colRepWorkOver= 6 'Общее время вне здания
Const colRepTimeLate = 7 'Общее время вне здания
Const colRepComment = 8 'Комментарий
Const colRepDebug = 9 'Отладочная информация
'==== Локальные переменные (только для этого модуля) ====
Private Doc As Object
Private sheet_source As Object ' Лист с данными
Private sheet_report As Object ' Лист с отчётом
Private cell As Object 'для форматирования ячеек. Доп инфо: http://www.openoffice.org/api/docs/common/ref/com/sun/star/table/CellProperties.html
Private str_msg, report_month_name As String 'строка предупреждений и название месяца отчёта
Private str1, str2, str3 As String 'времянки
Private str_fio, otdel_cur, otdel_prev, str_time As String
Private date_min, date_max As Date 'период отчёта
Private date_cur, date_next As Date 'дата в текущей и последующей строке
Private work_start, work_end As Date 'начало и конец рабочего дня
Private time_start, time_end As Date 'начало и конец рабочего дня сотрудника
Private time_all, time_work, time_rest As Date 'общее время, длительность нахождения в здании и вне здания
Private time1, time2 As Date 'промежуточные значения
Private row_source, row_report, row_sum_first As Integer 'номера текущих (обрабатываемых) строк в исходной таблице и отчёте
Private zone_cur, zone_next, tab_cur, tab_prev As Integer 'значения полей "зона доступа" и "табельный номер"
Private counter_persons, counter_otdel, counter_otdel_staff As Integer 'счётчики сотрудников, отделов и сотрудников в отделе на предприятии
Private row_otdel_source_start, row_otdel_source_end, row_otdel_report_start, row_otdel_report_end As Integer 'строки отчёта по отделу
Private first_record_in_day As Boolean 'первая запись у этого человека для этого дня
'Общие для всех массивов константы
Const aName = 0 'наименование предприятия, отдела, ФИО
Const aWorkDay = 1 'общее время рабочего дня
Const aInside = 2 'общее время в здании
Const aOutside = 3 'общее время вне здания
Const aWorkOver = 4 'общее время переработки
Const aTimeLate = 5 'общее время опоздания
Private array_ent(5) 'данные по предприятию
Private array_otdel(10,50) ' array_otdel(x,y) - таблица данных по всем отделам
' y = 0 - первая строка - название столбцов
' y = 1..50 - номер отдела, строка с данными по отделу
' x - номер столбца:
Const aoSrcStart = 6 'первая строка записей по отделу на листе с исходными данными
Const aoSrcEnd = 7 'последняя строка --"--
Const aoRepStart = 8 'первая строка записей по отделу на листе с общим отчётом
Const aoRepEnd = 9 'последняя строка --"--
Const aoOtdStaff = 10 'кол-во сотрудников в отделе
Private array_month(32,100,2) 'array_month(x,y,z) - данные по сотруднику
' x = 0 - колонки: 1-я = ФИО сотрудника
' x = 1 - колонки: 2-я = отдел
' x = 1..31 - данные по сотрудникам на каждый день месяца
' y = 0 - строки: 1-я название месяца и дней недели
' y = 1..100- номер сотрудника и его данные
' z - таблицы: разные отчётные данные
Const amTableWorkDay = 0
Const amTableWorkOver = 1
Const amTableTimeLate = 2
Const amOtdel = 1 'название отдела сотрудника
'==== Локальные процедуры и функции ====
' Вывод в ячейку отчётного листа:
' --- строки
Sub SetString (col As Integer, row As Integer, str As String)
sheet_report.getCellByPosition(col, row).setString(str)
End Sub
' --- формулы
Sub SetFormula (col As Integer, row As Integer, str As String)
sheet_report.getCellByPosition(col, row).setFormula(str)
End Sub
' --- числа
Sub SetValue (col As Integer, row As Integer, val As Double)
sheet_report.getCellByPosition(col, row).setValue(val)
End Sub
' Получить из ячейки листа с исходными данными значение в виде:
' --- строки
Function GetString (col As Integer, row As Integer) As String
GetString = sheet_source.getCellByPosition(col, row).getString()
End Function
' --- числа
Function GetValue (col As Integer, row As Integer) As Double
GetValue = sheet_source.getCellByPosition(col, row).getValue()
End Function
' Вывод заголовка отчёта и названия столбцов
Sub OUTHeader(sheet_dest As Object)
Dim c As Object
c = sheet_dest.getCellRangeByPosition(colRepDate, 0, colRepComment, 0)
c.Merge(true)
c.getCellByPosition(0,0).setString("Учёт рабочего времени")
c.CellStyle = "pm-Заголовок общий"
sheet_dest.getCellByPosition(colRepDate, rowReportStart).setString("Дата")
sheet_dest.getCellByPosition(colRepTime, rowReportStart).setString("Период работы")
sheet_dest.getCellByPosition(colRepWorkDay, rowReportStart).setString( "Рабочий день")
sheet_dest.getCellByPosition(colRepInside, rowReportStart).setString( "В здании")
sheet_dest.getCellByPosition(colRepOutside, rowReportStart).setString("Вне здания")
sheet_dest.getCellByPosition(colRepWorkOver, rowReportStart).setString("Переработка")
sheet_dest.getCellByPosition(colRepTimeLate, rowReportStart).setString("Опоздания")
sheet_dest.getCellByPosition(colRepComment, rowReportStart).setString("Комментарий")
c = sheet_dest.getCellRangeByPosition(colRepDate, rowReportStart, colRepComment, rowReportStart)
c.CellStyle = "pm-Заголовок"
End Sub 'OUTHeader
' Визуальное закрытие отчёта строкой с таким же форматом как и заголовок
Sub OUTFooter(sheet_dest As Object, row As Integer, str As String)
Dim c As Object
c = sheet_dest.getCellRangeByPosition(colRepDate, row, colRepComment, row)
c.Merge(true)
c.getCellByPosition(0,0).setString(str)
c.CellStyle = "pm-Заголовок"
End Sub 'OUTFooter
' Вывод статистики по пользователю
Sub OUTStatisticsPerson
SetString(colRepTime, row_report, "Итого: ")
SetFormula(colRepWorkDay, row_report, "=SUM(" & ColumnLetter(colRepWorkDay) & row_sum_first & ":" & ColumnLetter(colRepWorkDay) & row_report & ")")
SetFormula(colRepInside, row_report, "=SUM(" & ColumnLetter(colRepInside) & row_sum_first & ":" & ColumnLetter(colRepInside) & row_report & ")")
SetFormula(colRepOutside, row_report, "=SUM(" & ColumnLetter(colRepOutside) & row_sum_first & ":" & ColumnLetter(colRepOutside) & row_report & ")")
SetFormula(colRepWorkOver, row_report,"=SUM(" & ColumnLetter(colRepWorkOver)& row_sum_first & ":" & ColumnLetter(colRepWorkOver)& row_report & ")")
SetFormula(colRepTimeLate, row_report,"=SUM(" & ColumnLetter(colRepTimeLate)& row_sum_first & ":" & ColumnLetter(colRepTimeLate)& row_report & ")")
array_otdel(aWorkDay, counter_otdel+1) = array_otdel(aWorkDay, counter_otdel+1) + sheet_report.getCellByPosition(colRepWorkDay, row_report).getValue()
array_otdel(aInside, counter_otdel+1) = array_otdel(aInside, counter_otdel+1) + sheet_report.getCellByPosition(colRepInside, row_report).getValue()
array_otdel(aOutside, counter_otdel+1) = array_otdel(aOutside, counter_otdel+1) + sheet_report.getCellByPosition(colRepOutside, row_report).getValue()
array_otdel(aWorkOver, counter_otdel+1) = array_otdel(aWorkOver, counter_otdel+1)+ sheet_report.getCellByPosition(colRepWorkOver, row_report).getValue()
array_otdel(aTimeLate, counter_otdel+1) = array_otdel(aTimeLate, counter_otdel+1)+ sheet_report.getCellByPosition(colRepTimeLate, row_report).getValue()
cell = sheet_report.getCellRangeByPosition(colRepTime, row_report, colRepComment, row_report)
cell.CellStyle = "pm-Итого"
row_report = row_report + 1
End Sub 'OUTStatisticsPerson
' Вывод статистики по отделу
Sub OUTStatisticsOtdel
counter_otdel = counter_otdel + 1
SetString(colRepTime, row_report, "Итого по отделу: ")
SetValue(colRepWorkDay, row_report, array_otdel(aWorkDay, counter_otdel))
SetValue(colRepInside, row_report, array_otdel(aInside, counter_otdel))
SetValue(colRepOutside, row_report, array_otdel(aOutside, counter_otdel))
SetValue(colRepWorkOver, row_report, array_otdel(aWorkOver, counter_otdel))
SetValue(colRepTimeLate, row_report, array_otdel(aTimeLate, counter_otdel))
cell = sheet_report.getCellRangeByPosition(colRepTime, row_report, colRepComment, row_report)
cell.CellStyle = "pm-Итого"
row_report = row_report + 1
array_otdel(aName, counter_otdel) = otdel_prev
array_otdel(aoOtdStaff, counter_otdel) = counter_otdel_staff
array_otdel(aoSrcStart, counter_otdel+1) = row_source + 1
array_otdel(aoSrcEnd, counter_otdel) = row_source - 1
array_otdel(aoRepStart, counter_otdel+1) = row_report
array_otdel(aoRepEnd, counter_otdel) = row_report - 1
array_ent(aWorkDay) = array_ent(aWorkDay) + array_otdel(aWorkDay, counter_otdel)
array_ent(aInside) = array_ent(aInside) + array_otdel(aInside, counter_otdel)
array_ent(aOutside) = array_ent(aOutside) + array_otdel(aOutside, counter_otdel)
array_ent(aWorkOver) = array_ent(aWorkOver) + array_otdel(aWorkOver, counter_otdel)
array_ent(aTimeLate) = array_ent(aTimeLate) + array_otdel(aTimeLate, counter_otdel)
InitArrayOtdel(counter_otdel+1)
counter_otdel_staff = 0
End Sub 'OUTStatisticsOtdel
'Выводим итоги по всему предприятию
Sub OUTStatisticsEnterprise()
report_month_name = NameMonth(Month(date_min))
SetString(colRepTime, row_report, "Итого по предприятию: ")
SetValue(colRepWorkDay, row_report, array_ent(aWorkDay))
SetValue(colRepInside, row_report, array_ent(aInside))
SetValue(colRepOutside, row_report, array_ent(aOutside))
SetValue(colRepWorkOver, row_report, array_ent(aWorkOver))
SetValue(colRepTimeLate, row_report, array_ent(aTimeLate))
cell = sheet_report.getCellRangeByPosition(colRepTime, row_report, colRepComment, row_report)
cell.CellStyle = "pm-Итого"
row_report = row_report + 1
OUTFooter(sheet_report, row_report, "Всего человек: " & counter_persons)
row_report = row_report + 1
'Добавляем статистику в заголовок
SetString(colRepDate, 1, "Начало:")
SetString(colRepDate, 2, "Окончание:")
SetString(colRepTime, 1, DateTimeToString(date_min) )
SetString(colRepTime, 2, DateTimeToString(date_max) )
SetString(colRepWorkDay, 1, "Отделов:")
SetString(colRepWorkDay, 2, "Человек:")
SetString(colRepInside, 1, counter_otdel)
SetString(colRepInside, 2, counter_persons)
End Sub 'OUTStatisticsEnterprise
Sub InitArrayOtdel(i As Integer)
'Название полей в массиве отделов и номера строк данных для первого отдела
array_otdel(aName, 0) = "Отдел"
array_otdel(aoSrcStart, 0) = "Исх. начало"
array_otdel(aoSrcEnd, 0) = "Иск. конец"
array_otdel(aoRepStart, 0) = "Отчёт начало"
array_otdel(aoRepEnd, 0) = "Отчёт конец"
array_otdel(aWorDay, 0) = "Раб день"
array_otdel(aInside, 0) = "В здании"
array_otdel(aOutside, 0) = "Вне здания"
array_otdel(aWorkOver, 0) = "Переработка"
array_otdel(aTimeLate, 0) = "Опоздание"
array_otdel(aWorkDay, i) = TimeValue("00:00:00")
array_otdel(aInside, i) = TimeValue("00:00:00")
array_otdel(aOutside, i) = TimeValue("00:00:00")
array_otdel(aWorkOver, i) = TimeValue("00:00:00")
array_otdel(aTimeLate, i) = TimeValue("00:00:00")
End Sub 'InitArrayOtdel
Sub InitArrayEnterprise()
array_ent(aWorkDay) = TimeValue("00:00:00")
array_ent(aInside) = TimeValue("00:00:00")
array_ent(aOutside) = TimeValue("00:00:00")
array_ent(aWorkOver)= TimeValue("00:00:00")
array_ent(aTimeLate)= TimeValue("00:00:00")
End Sub 'InitArrayEnterprise
Sub InitArrayMonth()
Dim i,j As Integer
Dim m As String
For i=0 To 2
array_month(aName,0,i) = "ФИО"
array_month(amOtdel,0,i)= "Отдел"
For j=1 To 31
array_month(amOtdel + j,0,i) = j
Next
Next
End Sub 'InitArrayMonth
' Создание листа
Function CreateSheet(sheetName As String, index As Integer) As Object
' Удаляем (если есть) и создаём новый лист с именем sheetName
If Doc.Sheets.hasByName(sheetName) Then
Doc.Sheets.removeByName(sheetName)
End If
'Можно было бы не привязываться к номеру листа, но не ясно пока как получить индекс листа по его имени Sheet.getIndex() не работает
'Dim Sheet As Object
'Sheet = Doc.createInstance("com.sun.star.sheet.Spreadsheet")
'Doc.Sheets.insertByName(sheetName, Sheet)
Doc.Sheets.insertNewByName(sheetName, index)
CreateSheet = Doc.Sheets.getByName(sheetName)
End Function 'CreateSheet
'Показываем переработки и опоздания
Sub ShowOverTimeAndLate()
Dim dStatus, dDay As Integer
Dim dToday, time_over, time_late, tZero, t As Date
Dim str1, str2, str3, str4, str5 As String
tZero = TimeValue("00:00:01") 'не воспринимает константу timeZero в сравнении ниже (несмотря на то, что константа timeWorkOver воспринимается нормально)
dToday = DateValue(Month(time_start) & "/" & Day(time_start) & "/" & Year(time_start))
time_late = time_start - dToday - TimeValue(timeWorkStart)
' str2 = DateTimeToString(timeZero)
' str3 = DateTimeToString(tZero)
' str4 = DateTimeToString(t)
' str5 = DateTimeToString(time_late)
If time_late < tZero Then 'это уже не опоздание, а преждевременный приход
time_late = tZero
End If
dStatus = DayStatus(date_cur)
Select Case dStatus
Case dayStatusHoliday
cell.CellBackColor = RGB(255, 175, 175) 'помечаем всю строку как выходной день
SetValue(colRepWorkOver, row_report, time_work) 'всё время - переработка
time_late = tZero 'опозданий нет
Case dayStatusShort
cell.CellBackColor = RGB(175, 255, 175) 'помечаем всю строку как короткий день
time_over = time_end - dToday - TimeValue(timeWorkEndShort) - time_late
Case dayStatusWork
time_over = time_end - dToday - TimeValue(timeWorkEnd) - time_late
End Select
'отладка
str1 = DateTimeToString(time_over)
str2 = DateTimeToString(time_end)
str3 = DateTimeToString(dToday)
str4 = DateTimeToString(TimeValue(timeWorkEnd))
str5 = DateTimeToString(time_late)
dDay = Day(time_start)
array_month(dDay, counter_persons+1,amTableWorkDay) = time_work
t = TimeValue(timeWorkOver)
If dStatus <> dayStatusHoliday Then
If time_over > t Then 'задержка < timeWorkOver не считается за переработку
cell = sheet_report.getCellByPosition(colRepWorkOver, row_report)
cell.CellBackColor = RGB(255, 175, 175)
cell.Value = time_over
array_month(dDay, counter_persons+1,amTableWorkOver) = time_over
End If
If time_late > tZero Then
cell = sheet_report.getCellByPosition(colRepTimeLate, row_report)
cell.CellBackColor = RGB(255, 255, 99)
cell.Value = time_late
array_month(dDay, counter_persons+1,amTableTimeLate) = time_late
End If
End If
str5 = ""
End Sub 'ShowOverTimeAndLate
'Переносим исходные данные каждого отдела на отдельный лист
Sub SplitSource()
Dim i As Integer
Dim sheet_out As Object 'лист отдела для копирования туда данных
Dim c As Object 'ячейка, временный объект
Dim range_header As New com.sun.star.table.CellRangeAddress 'область заголовка
Dim cell_header As New com.sun.star.table.CellAddress 'адрес ячейки куда копировать заголовок
Dim range_data As New com.sun.star.table.CellRangeAddress 'область данных
Dim cell_data As New com.sun.star.table.CellAddress 'адрес ячейки куда копировать данные
cell_header.Sheet = 2 'Индекс листа задаётся в функции CreateSheet
cell_header.Column = 0
cell_header.Row = 1
range_header.Sheet = 0 'лист с исходными данными
range_header.StartColumn= 0
range_header.StartRow = 0
range_header.EndColumn = 10
range_header.EndRow = 0
For i=1 To counter_otdel
sheet_out = CreateSheet(array_otdel(aName, i) & " (исх)",2)
cell_data.Sheet = 2 'Индекс листа задаётся в функции CreateSheet
cell_data.Column= 0
cell_data.Row = 2
range_data.Sheet = 0
range_data.StartColumn = 0
range_data.StartRow = array_otdel(aoSrcStart, i)
range_data.EndColumn = 10
range_data.EndRow = array_otdel(aoSrcEnd, i)
c = sheet_out.getCellRangeByPosition(0, 0, 10, 0)
c.Merge(true)
c.getCellByPosition(0,0).setString("Исходные данные по отделу: " & array_otdel(aName, i))
c.CellStyle = "pm-Заголовок"
sheet_out.getCellByPosition(0, 0).setString("Исходные данные по отделу: " & array_otdel(aName, i) ) ' & " -- " & j)
sheet_out.copyRange(cell_header, range_header)
sheet_out.copyRange(cell_data, range_data)
Col = sheet_out.Columns(0)
Col.OptimalWidth = true
Next
End Sub 'SplitSource
'Переносим отчёт по каждому отделу на отдельный лист
Sub SplitReport()
Dim i,j As Integer
Dim sheet_out As Object 'лист отдела для копирования туда данных
Dim c As Object 'ячейка, временный объект
Dim range_header As New com.sun.star.table.CellRangeAddress 'область заголовка
Dim cell_header As New com.sun.star.table.CellAddress 'адрес ячейки куда копировать заголовок
Dim range_data As New com.sun.star.table.CellRangeAddress 'область данных
Dim cell_data As New com.sun.star.table.CellAddress 'адрес ячейки куда копировать данные
For i=1 To counter_otdel
sheet_out = CreateSheet(array_otdel(aName, i), 2)
cell_data.Sheet = 2 'Индекс листа задаётся в функции CreateSheet
cell_data.Column= 0
cell_data.Row = rowReportStart + 1
range_data.Sheet = 1 'лист с общим отчётом
range_data.StartColumn = 0
range_data.StartRow = array_otdel(aoRepStart, i)
range_data.EndColumn = colRepComment
range_data.EndRow = array_otdel(aoRepEnd, i)
sheet_out.copyRange(cell_data, range_data)
OUTHeader(sheet_out)
'Добавляем статистику в заголовок
sheet_out.getCellByPosition(colRepDate, 1).SetString("Начало:")
sheet_out.getCellByPosition(colRepDate, 2).SetString("Окончание:")
sheet_out.getCellByPosition(colRepTime, 1).SetString(DateTimeToString(date_min) )
sheet_out.getCellByPosition(colRepTime, 2).SetString(DateTimeToString(date_max) )
sheet_out.getCellByPosition(colRepWorkDay, 1).SetString("Отдел:")
sheet_out.getCellByPosition(colRepWorkDay, 2).SetString("Человек:")
sheet_out.getCellByPosition(colRepInside, 1).SetString(array_otdel(aName, i) )
sheet_out.getCellByPosition(colRepInside, 2).SetString(array_otdel(aoOtdStaff, i) )
OUTFooter(sheet_out, rowReportStart + array_otdel(aoRepEnd, i) - array_otdel(aoRepStart, i) + 2, "Всего человек: " & array_otdel(aoOtdStaff, i))
'Выравниваем ширину столбцов
For j=0 To colRepComment
Col = sheet_out.Columns(j)
Col.OptimalWidth = true
Next
Next
End Sub 'SplitReport
'Сводный отчёт за месяц
Sub MonthReport()
Dim i, j, t, row, row_start, row_end, col, col_end, col_date_start As Integer
Dim sheet_out, c As Object 'лист отчёта и ячейка для форматирования
Dim str As String
sheet_out = CreateSheet("Сводный отчёт", 2)
'Добавляем статистику в заголовок
sheet_out.getCellByPosition(0,0).setString("Начало:")
sheet_out.getCellByPosition(0,1).setString("Окончание:")
sheet_out.getCellByPosition(1,0).setString(DateTimeToString(date_min))
sheet_out.getCellByPosition(1,1).setString(DateTimeToString(date_max))
sheet_out.getCellByPosition(2,0).setString("Отделов:")
sheet_out.getCellByPosition(2,1).setString("Человек:")
sheet_out.getCellByPosition(3,0).setString(counter_otdel)
sheet_out.getCellByPosition(3,1).setString(counter_persons)
row = 2
col_date_start = 3
For t=0 To 2 'Выводим даннные трёх таблиц: Общее рабочее время, Переработка и Опоздания
'Заголовок таблицы
Select Case t
Case 0
str = "Сводный отчёт рабочего времени за "
Case 1
str = "Сводный отчёт переработок за "
Case 2
str = "Сводный отчёт опозданий за "
End Select
col_end = UBound(array_month) + 1 - (31 - DaysInMonth(date_min) )
c = sheet_out.getCellRangeByPosition(0, row, col_end, row)
c.Merge(true)
c.getCellByPosition(0,0).setString(str & report_month_name)
c.CellStyle = "pm-Заголовок общий"
row = row + 1
'Оформляем заголовок
c = sheet_out.getCellRangeByPosition(0, row, col_end, row)
c.CellStyle = "pm-Заголовок"
'Выводим данные по всем сотрудникам
row_start = row + 1
For i=0 To counter_persons
'Выводим порядковый номер
If i > 0 Then
sheet_out.getCellByPosition(0, row).SetString( i )
sheet_out.getCellByPosition(col_end +1, row).SetFormula("=SUM(" & ColumnLetter(col_date_start) & row +1 & ":" & ColumnLetter(col_end) & row +1 & ")")
End If
'Выводим ФИО, отдел и отчёт по дням
'days_in_month =
For j=0 To col_end - 1
If Not IsEmpty(array_month(j,i,t)) Then
If IsNumeric(array_month(j,i,t)) Or IsDate(array_month(j,i,t)) Then
sheet_out.getCellByPosition(j+1, row).SetValue( array_month(j,i,t) )
Else
sheet_out.getCellByPosition(j+1, row).SetString( array_month(j,i,t) )
End If
End If
Next
row = row + 1
Next 'все сотрудники
row_end = row - 1
row = row + 1
'выделяем выходные и считаем статистику за день
Dim dDay As Date
Dim dStatus As Integer
for col = col_date_start To col_end
dDay = DateValue(Month(time_start) & "/" & col - col_date_start +1 & "/" & Year(time_start))
c = sheet_out.getCellRangeByPosition(col, row_start, col, row_end + 1)
dStatus = DayStatus(dDay)
Select Case dStatus
Case dayStatusHoliday
c.CellBackColor = RGB(255, 175, 175) 'помечаем как выходной день
Case dayStatusShort
c.CellBackColor = RGB(175, 255, 175) 'помечаем как короткий день
End Select
sheet_out.getCellByPosition(col, row -1).SetFormula("=SUM(" & ColumnLetter(col) & row_start + 1 & ":" & ColumnLetter(col) & row_end +1 & ")")
Next
'статистика за месяц по предприятию
sheet_out.getCellByPosition(col, row -1).SetFormula("=SUM(" & ColumnLetter(col) & row_start + 1 & ":" & ColumnLetter(col) & row_end +1 & ")")
'применяем преднастроенный стиль отображения времени
c = sheet_out.getCellRangeByPosition(col_date_start, row_start, col_end+1, row_end +1)
c.CellStyle = "pm-Время"
c = sheet_out.getCellRangeByPosition(col_date_start, row_end +1, col_end+1, row_end +1)
c.CellStyle = "pm-Итого"
c = sheet_out.getCellRangeByPosition(col_end+1, row_start, col_end+1, row_end +1)
c.CellStyle = "pm-Итого"
Next 'все таблицы
'Выравниваем ширину столбцов
For i=0 To col_end + 1
c = sheet_out.Columns(i)
c.OptimalWidth = true
Next
' For col=0 To 700
' colLetter = ColumnLetter(col)
' sheet_out.getCellByPosition(col, 0).SetString(colLetter)
' Next
End Sub 'MonthReport
'
'------- Главная процедура -------------------------------------------------------------
'
Sub WorktimeReport
'Инициализируем переменные всего отчёта
Doc = ThisComponent
sheet_source = Doc.Sheets (0)
sheet_report = CreateSheet("Отчёт (общий)", 1)
'не работает почему-то: число записей черезчур велико. НАДО: проверить синтаксис метода!
'msgbox ("Начинаю формировать отчёт. Количество записей: " & sheet_source.Rows().getCount())
row_source = 1
row_report = rowReportStart
row_otdel_source_start = row_source
row_otdel_report_start = row_report
counter_persons = 0
counter_otdel = 0
counter_otdel_staff = 0
tab_prev = -1 'для учёта самой первой строки данных
otdel_prev = "none"
InitArrayOtdel(counter_otdel+1)
InitArrayEnterprise()
InitArrayMonth()
'Выводим заголовок отчёта
OUTHeader(sheet_report)
row_report = row_report + 1
array_otdel(aoSrcStart, 1) = row_source
array_otdel(aoRepStart, 1) = row_report
'Цикл = Человек. Обходим все строки в таблице
Do
'Считываем исходные данные
date_cur = GetValue(colDate, row_source)
str_fio = GetString(colFamily, row_source) & " " & GetString(colName, row_source) & " " & GetString(colOtch, row_source)
zone_cur = GetValue(colZone, row_source)
tab_cur = GetValue(colTabelNumber, row_source)
tab_next = GetValue(colTabelNumber, row_source + 1)
otdel_cur = GetString(colOtdel, row_source)
If tab_cur <> tab_prev Then 'Новый человек.
For i=0 to 2
array_month(aName, counter_persons+1,i) = str_fio
array_month(amOtdel,counter_persons+1,i) = otdel_cur
Next
If tab_prev <> -1 Then 'Это не первый человек в списке. Выводим статистику по предыдущему
OUTStatisticsPerson()
If otdel_cur <> otdel_prev Then 'Новый отдел. Выводим статистику по предыдущему отделу
OUTStatisticsOtdel()
End If
End If
'Выводим заголовок для нового человека
cell = sheet_report.getCellRangeByPosition(colRepDate, row_report, colRepComment, row_report)
cell.Merge(true)
cell.getCellByPosition(0,0).setString(str_fio & " (" & otdel_cur & ", таб. № " & tab_cur & ")") 'координаты (0,0) внутри области
cell.CellStyle = "pm-Сотрудник"
counter_persons = counter_persons + 1
counter_otdel_staff = counter_otdel_staff + 1
row_report = row_report + 1
row_sum_first = row_report +1
End If
date_min = date_cur
date_max = date_cur
date_prev = DateValue("01.01.2000")
zone_prev = -1
Otdel_prev = "none"
first_record_in_day = true
tab_person = tab_cur 'чтобы отслеживать смену человека в том же дне
'Цикл = День. Обработка всех записей для человека в пределах одного дня. Формируем отчёт по каждому дню.
While (date_prev <= date_cur) and (tab_cur = tab_person)
date_next = GetValue(colDate, row_source + 1)
time1 = TimeValue( Hour(date_prev) & ":" & Minute(date_prev) & ":" & Second(date_prev) )
time2 = TimeValue( Hour(date_cur) & ":" & Minute(date_cur) & ":" & Second(date_cur) )
str_time = time1 & ", " & time2
str_msg = ""
If first_record_in_day Then 'вычисляем начало и конец рабочего дня
time_start = date_cur
SetString(colRepWeekDay, row_report, NameWeekDay(date_cur) )
SetString(colRepDate, row_report, Year(date_cur) & "." & Month(date_cur) & "." & Day(date_cur))
If date_cur > date_max Then
date_max = date_cur
End If
If date_cur < date_min Then
date_min = date_cur
End If
End If
If zone_cur = zone_prev Then 'повторная регистрация на том же считывателе
If zone_cur = 1 Then
str_msg = str_msg & " Повторный вход: " & str_time
Else
str_msg = str_msg & " Повторный выход: " & str_time
End If
Else 'запись не повторная, нужно учесть
If first_record_in_day Then 'первая запись в текущем дне
time_work = 0
time_rest = 0
Else
If zone_cur = 0 Then 'выход из здания, считаем проведённое в нём время
time_work = time2 - time1 + time_work
Else 'вход в здание, считаем сколько человек отсутствовал
time_rest = time2 - time1 + time_rest
End If
End If
End If
If (Day(date_cur) <> Day(date_next)) Or (tab_cur <> tab_next) Then ' последняя запись для текущего дня
time_end = date_cur
time_all = time_end - time_start
str_time = Hour(time_start) & ":" & Minute(time_start) & " - " & Hour(time_end) & ":" & Minute(time_end)
'SetString(colRepDebug, row_report, "Последняя: " & str_date_cur )
SetString(colRepTime, row_report, str_time)
SetValue(colRepWorkDay, row_report, time_all)
SetValue(colRepInside, row_report, time_work)
SetValue(colRepOutside, row_report, time_rest)
cell = sheet_report.getCellRangeByPosition(colRepWorkDay, row_report, colRepTimeLate, row_report)
cell.CellStyle = "pm-Время"
'cell = sheet_report.getCellRangeByPosition(colRepWeekDay, row_report, colRepComment, row_report)
'Вычисляем время опозданий и переработок
ShowOverTimeAndLate()
'array_month(counter_persons+1, amOtdel, amTableTimeLate) = otdel_cur
time_work = timeZero
time_rest = timeZero
If first_record_in_day Then
str_msg = str_msg & " Только одна регистрация! "
End If
first_record_in_day = true 'следующая запись будет первой в новом дне
row_report = row_report + 1
Else 'ещё одна запись для текущего дня
first_record_in_day = false
End If
SetString(colRepComment, row_report, str_msg)
'переходим на следующую строку
date_prev = date_cur
zone_prev = zone_cur
row_source = row_source + 1
date_cur = GetValue(colDate, row_source)
zone_cur = GetValue(colZone, row_source)
tab_prev = tab_cur
tab_cur = GetValue(colTabelNumber, row_source)
tab_next = GetValue(colTabelNumber, row_source + 1)
Wend 'обработка одного дня
otdel_prev = otdel_cur
str_fio = GetString(colFamily, row_source) & " " & GetString(colName, row_source) & " " & GetString(colOtch, row_source)
otdel_cur = GetString(colOtdel, row_source)
tab_cur = GetValue(colTabelNumber, row_source)
'SetString(colRepDebug, row_report, row_source & ". ФИО: " & str_fio & "; таб.№ " & tab_cur)
Loop Until str_fio = " " 'обработка одного человека
'Выводим итоги по последнему человеку, отделу и всему предприятию
OUTStatisticsPerson()
OUTStatisticsOtdel()
OUTStatisticsEnterprise()
'регулируем ширину столбцов согласно содержимому
Dim Col As Object
For i=0 To colRepComment
Col = sheet_report.Columns(i)
Col.OptimalWidth = true
Next
'Создаём листы отчёта для каждого отдела
If MsgBox ("Общий отчёт для " & counter_persons & " человек сформирован. Создать отдельные листы отчётов по отделам?", MB_YESNO + MB_DEFBUTTON2 + MB_ICONQUESTION) = IDYES Then
SplitReport()
EndIf
'Копируем в отдельные листы исходные данные по отделам и их отчёты
If MsgBox ("Создать отдельные листы с исходными данными по отделам?", MB_YESNO + MB_DEFBUTTON2 + MB_ICONQUESTION) = IDYES Then
SplitSource()
EndIf
'Cоздаём сводный отчёт по всем дням месяца и всем сотрудникам
If MsgBox ("Составить сводный отчёт на каждый день по каждому сотруднику?", MB_YESNO + MB_DEFBUTTON2 + MB_ICONQUESTION) = IDYES Then
MonthReport()
EndIf
'отладка: выводим весь массив отделов
'For i=0 To counter_otdel
' For j=0 To 4
' SetString(colRepDebug + j, row_report + i, array_otdel(j, i) )
' Next
'Next
MsgBox ("Создание отчёта завершено.", MB_OK)
End Sub 'WorktimeReport
'=========================================================================================
'Фиктивная процедура с полезной информацией. Для удобства доступа через "Каталог объектов"
Sub HELP_AND_SAMPLES()
'--Диалоговое окно
'Dim iBox as Integer
'iBox = MB_YESNO + MB_DEFBUTTON2 + MB_ICONQUESTION
'If MsgBox ("Do you want to continue?", iBox) = IDYES Then
'--Ячейка
'cell.HoriJustify = com.sun.star.table.CellHoriJustify.CENTER -- выравнивание по горизонтали
'cell.CellBackColor = RGB(125, 125, 255) -- фон
'cell.clearContents(com.sun.star.sheet.CellFlags.HARDATTR) -- очищаем всё форматирование
'if Cell.Type = com.sun.star.table.CellContentType.EMPTY Then -- пустая ячейка
'& chr(13) & -- перенос строки в ячейке
'LBound(MyArray), UBound(MyArray) -- нижний и верхний индексы массива
'Sheet.Rows().getCount() -- выдаёт большое число, а не номер последней заполненной строки
'=SUM(G2;G87) -- суммирование диапазона, букву можно получать через функцию: G = Letter(colName)
'======= Internet links =======
'http://ru.scribd.com/doc/99684510/LibreOffice-3-Basic-Guide -- PDF книга
'http://forumooo.ru - русско-язычный форум
'https://forum.openoffice.org/en/forum/ - форум с примерами
'https://wiki.openoffice.org/wiki/Documentation/BASIC_Guide/Structure_of_Spreadsheets
'http://www.openoffice.org/api/docs/common/ref/com/sun/star/table/TableRow.html
'
'https://help.libreoffice.org/Basic/Basic_Help/ru Справка по LibreOffice Basic]
End Sub