привет

Немного альтернативной истории — Да, победили, и что толку? — вздыхал Саня. — Столько крови пролили, в половине Европы социализм установили. Теперь нам никогда не войти в европейскую семью народов, мы не уважали их ценности. А знаешь, сколько немок изнасиловали наши солдаты, когда заняли Берлин? Миллион! Это же ужасно! Саня, либеральный сетевой журналист, отлично...

Далее

#котозависимость Та же проблема...

Далее

Креатив в Магнитогорске Как и раньше продолжаю замечать необычные вещи, когда гуляю по городу. Некоторые кеативные люди заставляют улыбаться. Лифт, на котором можно отправиться в "Ж". Магазин, в котором продается самая не свежая выпечка в городе.

Далее

С днем рождения меня! https://www.youtube.com/watch?v=DL3pP7FsRGE

Далее

Реализуем плановый (производственный) календарь

Рубрика : Программизмы, Разное

Иногда, когда какой-то кусок работы организации полу-автоматизирован, то есть часть работы выполняется автоматически по расписанию, а часть делают люди (например сотрудники работают по заданию, которое выдается им каждый день автоматически), возникает необходимость сделать так, чтобы в выходные и праздничные дни задания не выдавались. Да, собственно, и логично, ведь в эти дни сотрудники не работают. Сделать это не сложно. Единственная проблема для пользователей в том, что каждый год необходимо будет заполнять производственный календарь (либо «перекачивать» его с какого-либо ресурса). А вот для программиста работы побольше.

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

-- Создание таблицы
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, вполне можно проанализировать работу человека за полгода. Для наглядности можно построить график работы сотрудника.
Применение производственного календаря очень обширно. Можно приводить много примеров.
В дальнейшем я приведу пример формы для заполнения планового календаря и рассмотрю возможности перекачки данных из других источников.
Возникшие вопросы жду в комментариях.

Прокомментировать