Иногда, когда какой-то кусок работы организации полу-автоматизирован, то есть часть работы выполняется автоматически по расписанию, а часть делают люди (например сотрудники работают по заданию, которое выдается им каждый день автоматически), возникает необходимость сделать так, чтобы в выходные и праздничные дни задания не выдавались. Да, собственно, и логично, ведь в эти дни сотрудники не работают. Сделать это не сложно. Единственная проблема для пользователей в том, что каждый год необходимо будет заполнять производственный календарь (либо «перекачивать» его с какого-либо ресурса). А вот для программиста работы побольше.
Для реализации любого ограничения работы на выходные и праздничные дни нужно сделать источник, при помощи которого мы будет проверять: а какой сегодня день? В моем случае это будет таблица:
-- Создание таблицы create table BILLING.SPCALENDAR ( id NUMBER(30) not null, spdate DATE, day_status NUMBER ) -- Создание индекса create index BILLING.SPCALENDAR_ID on BILLING.SPCALENDAR (ID) -- Создание первичного ключа alter table BILLING.SPCALENDAR add constraint PK_SPCALENDAR primary key (ID); -- Создание последовательности create sequence billing.seq_spCaledar; --Создание триггера для автоинкремента CREATE OR REPLACE TRIGGER billing.trig_inc_spCaledar_id BEFORE INSERT on BILLING.spcalendar REFERENCING NEW AS N OLD AS O FOR EACH ROW declare id_rec number; begin select seq_spCaledar.nextval into id_rec from dual; :n.id := id_rec; end;
Где spdate — это день, day_status — это статус дня (1 — рабочий, 0 — не рабочий). Так как в моем случае не важно, по какой причине день нерабочий (выходной это или праздничный день), статус имеет всего два состояния. Если для решение вашей задачи это актуально, то это можно вполне реализовать еще одни состоянием статуса и дополнительными проверками.
Приведу несколько примеров использования календаря. Код для примеров будет на PL/SQL. В будующем опубликую еще на нескольких языках.
Задача: Определить количество рабочих дней в интервале.
Допустим у нас есть p_begin — начальная дата и p_end -конечная дата.
Напишем функцию, которая будет возвращать day_count — количество рабочих дней в указанном периоде.
CREATE OR REPLACE FUNCTION WORK_DAY_IN_PERIOD(p_begin date, p_end date) RETURN NUMBER IS l_count_work_day number; BEGIN select count(*) into l_count_work_day from billing.spcalendar cal where cal.spdate between p_begin and p_end and cal.day_status = 1; return l_count_work_day; EXCEPTION WHEN OTHERS THEN NULL; END;
Задача: Распределить определенное значение пропорционально рабочим дням в интервале.
p_count_task — количество заданий на период
CREATE OR REPLACE FUNCTION get_count_tasks_for_day(p_count_task number, p_begin_date date, p_end_date date) RETURN NUMBER IS l_count_work_day number; l_count_task_for_day number; BEGIN --получим количество рабочих дней за месяц начиная с переданной даты l_count_work_day := WORK_DAY_IN_PERIOD(trunc(p_begin_date), trunc(p_end_date)); --посчитаем количество заданий на 1 день l_count_task_for_day := round(p_count_task / l_count_work_day); return l_count_task_for_day; EXCEPTION WHEN OTHERS THEN NULL; END;
Задача: Подсчитать среднее время выполнения заданий (если известно, что с января по июнь работник выполнил X заданий,
найти сколько времени он в среднем тратил на одно задание).
TYPE work_group IS RECORD( number_months NUMBER, --номер месяца time_task NUMBER); --среднее время на задач TYPE work_table IS TABLE OF work_group INDEX BY BINARY_INTEGER; TYPE m_count_tasks IS TABLE OF number INDEX BY BINARY_INTEGER; CREATE OR REPLACE FUNCTION calc_time_work(p_count_task_half_year m_count_tasks) RETURN work_table IS l_count_work_day number; l_time_work number; l_months_count number; l_count_work_day_month number; t work_table; BEGIN --получим количество рабочих дней что с января по май l_count_work_day := WORK_DAY_IN_PERIOD(to_date('01.01.2013', 'dd.mm.rrrr'), to_date('01.03.2013', 'dd.mm.rrrr')); --посчитаем отработанное время l_time_work := l_count_work_day * 8 * 60 * 60; --посчитаем среднее время на задачу за каждый месяц l_months_count := 6; --с января по июнь (за полгода) FOR i IN 1 .. l_group_count LOOP BEGIN --получаем время отработанное за месяц l_count_work_day_month := WORK_DAY_IN_PERIOD(add_month(to_date('01.01.2013', 'dd.mm.rrrr'), i - 1), add_month(to_date('01.01.2013', 'dd.mm.rrrr'), i)); l_count_work_day_month := l_count_work_day_month * 8 * 60 * 60; t(i).number_months := i; t(1).time_task := round((l_count_work_day_month / p_count_task_half_year(i)), 2); --среднее время на задачу в течении месяца END; return t; EXCEPTION WHEN OTHERS THEN NULL; END;
По данным, которые вернет эта функция в переменную t, вполне можно проанализировать работу человека за полгода. Для наглядности можно построить график работы сотрудника.
Применение производственного календаря очень обширно. Можно приводить много примеров.
В дальнейшем я приведу пример формы для заполнения планового календаря и рассмотрю возможности перекачки данных из других источников.
Возникшие вопросы жду в комментариях.