13#include <QCryptographicHash>
15#include <QRandomGenerator>
18using namespace Db::Plugins;
20namespace Db::Plugins {
21 QString null(
const QString& x) {
22 return (x ==
"0") ?
"" : x;
26bool DatabasePluginPostgres::m_upgraded =
false;
29DatabasePluginPostgres::~DatabasePluginPostgres() {
34DatabasePluginPostgres::DatabasePluginPostgres(QObject *parent) : Db::Plugins::
DatabasePlugin(parent) {
35 Q_ASSERT(parent != NULL);
36 setObjectName(
"DatabasePluginPostgres");
37 m_temporaryTableTicketsCreated =
false;
41bool DatabasePluginPostgres::open() {
42 m_db = QSqlDatabase::addDatabase(
"QPSQL", QUuid::createUuid().toString().toUtf8());
44 m_db.setDatabaseName ( m_databasename );
45 m_db.setHostName ( m_hostname );
46 m_db.setPort ( m_port );
47 m_db.setUserName ( m_username );
48 m_db.setPassword ( m_password );
51 PDEBUG <<
"Cannot connect to database Postgres";
52 PDEBUG << m_db.lastError().text();
62void DatabasePluginPostgres::upgrade() {
63 if (m_upgraded) {
return; }
67 for (
int version=0; version < 1000; version++) {
69 qx.prepare(
"select * from version where version = :version");
70 qx.bindValue(
":version", version);
76 QString patchname = QString(
":/postgres/patch.%1.sql").arg(version, 3, 10, QChar(
'0'));
77 QFile file(patchname);
78 if (!file.open(QIODevice::ReadOnly)) {
82 PDEBUG <<
"aplying db patch " << patchname;
84 while (!file.atEnd()) {
88 line = file.readLine();
90 }
while (!line.contains(
";") && !file.atEnd());
91 command = command.trimmed();
92 if (command.isEmpty()) {
continue; }
94 if (!q.exec(QString::fromUtf8(command))) {
95 QSqlError e = q.lastError();
96 if (e.type() != QSqlError::NoError) {
97 PDEBUG << q.lastQuery();
98 PDEBUG << e.databaseText();
99 PDEBUG << e.driverText();
111bool DatabasePluginPostgres::close() {
117void DatabasePluginPostgres::begin() {
123void DatabasePluginPostgres::commit() {
129void DatabasePluginPostgres::changePassword(
const QString& login,
const QString& oldpassword,
const QString& newpassword) {
131 QString md5new = QString::fromUtf8(QCryptographicHash::hash(newpassword.toUtf8(), QCryptographicHash::Md5).toHex());
132 QString md5old = QString::fromUtf8(QCryptographicHash::hash(oldpassword.toUtf8(), QCryptographicHash::Md5).toHex());
133 QList<Dbt::Users> list;
135 q.prepare(R
"'(select true from users where "user" = :userid and admin)'");
136 q.bindValue(":userid", userId());
138 bool admin = q.next();
143 q.prepare(
"update users set password = :newpassword where login = :login;");
146 PDEBUG <<
"not admin";
147 q.prepare(
"update users set password = :newpassword where login = :login and password = :oldpassword and enabled;");
149 q.bindValue(
":userid", userId());
150 q.bindValue(
":login", login);
151 q.bindValue(
":newpassword", md5new);
152 q.bindValue(
":oldpassword", md5old);
158QList<Dbt::Users> DatabasePluginPostgres::authenticate(
const QString& login,
const QString& password) {
159 QString md5 = QString::fromUtf8(QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex());
160 QList<Dbt::Users> list;
162 q.prepare(
"select \"user\", login, name, lang, enabled, admin from users where login = :login and password = :password and enabled;");
163 q.bindValue(
":login", login);
164 q.bindValue(
":password", md5);
169 x.user = q.value(i++).toInt();
170 x.login = q.value(i++).toString();
171 x.name = q.value(i++).toString();
172 x.lang = q.value(i++).toString();
173 x.enabled = q.value(i++).toBool();
174 x.admin = q.value(i++).toBool();
182QList<Dbt::Users> DatabasePluginPostgres::users(
int id) {
183 QList<Dbt::Users> list;
186 select "user", login, name, lang, enabled, admin
188 where (:id1 <= 0 or :id2 = "user");
190 q.bindValue(":id1",
id);
191 q.bindValue(
":id2",
id);
196 x.user = q.value(i++).toInt();
197 x.login = q.value(i++).toString();
198 x.name = q.value(i++).toString();
199 x.lang = q.value(i++).toString();
200 x.enabled = q.value(i++).toBool();
201 x.admin = q.value(i++).toBool();
209void DatabasePluginPostgres::remove(
const Dbt::Categories&
id) {
211 q.prepare(R
"'(delete from categories where category = :id;)'");
212 q.bindValue(":id",
id.category);
217QVariant DatabasePluginPostgres::currval(
const QString& sequence) {
220 q.prepare(R
"'(select currval(:sequence);)'");
221 q.bindValue(":sequence", sequence);
230QVariant DatabasePluginPostgres::save(
const Dbt::Categories& data) {
233 QVariant parent_category = (data.parent_category.toInt() > 0)
234 ? data.parent_category.toInt()
237 QVariant category = (data.category.toInt() > 0)
238 ? data.category.toInt()
239 : QVariant(QVariant::Int);
241 if (parent_category.toInt() == category.toInt()) {
242 parent_category = QVariant();
249 q.prepare(R
"'(select 1 from categories where category = :category;)'");
250 q.bindValue(":category", data.category.toInt());
252 bool exists = q.next();
253 if (exists && ! parent_category.isNull()) {
255 update categories set
261 q.bindValue(0, parent_category);
262 q.bindValue(1, data.description);
263 q.bindValue(2, data.price);
264 q.bindValue(3, category.toInt());
268 if (exists && parent_category.isNull()) {
270 update categories set
275 q.bindValue(0, data.description);
276 q.bindValue(1, data.price);
277 q.bindValue(2, category.toInt());
283 insert into categories (parent_category, description, price) values (?, ?, ?);
285 q.bindValue(0, parent_category);
286 q.bindValue(1, data.description);
287 q.bindValue(2, data.price);
289 category = currval("categories_category_seq");
292 insert into users_categories ("user", category) values (?, ?) on conflict do nothing;
294 q.bindValue(0, userId());
295 q.bindValue(1, category);
301 if (!data.users.isEmpty()) {
302 q.prepare(R
"'(delete from users_categories where category = ?;)'");
303 q.bindValue(0, category);
307 for (
int i=0; i<data.users.size(); i++) {
309 insert into users_categories ("user", category) values (?, ?) on conflict do nothing;
311 q.bindValue(0, data.users[i].toInt());
312 q.bindValue(1, category);
322QList<Dbt::ClientSettings> DatabasePluginPostgres::clientSettings() {
323 QList<Dbt::ClientSettings> list;
327 select multiple_timesheets, show_price, can_change_category, edit_categories,
328 show_multiple_timesheets, show_show_price, show_can_change_category, show_edit_categories
329 from client_settings;
333 Dbt::ClientSettings x;
335 x.multiple_timesheets = q.value(i++).toBool();
336 x.show_price = q.value(i++).toBool();
337 x.can_change_category = q.value(i++).toBool();
338 x.edit_categories = q.value(i++).toBool();
339 x.show_multiple_timesheets = q.value(i++).toBool();
340 x.show_show_price = q.value(i++).toBool();
341 x.show_can_change_category = q.value(i++).toBool();
342 x.show_edit_categories = q.value(i++).toBool();
349QList<Dbt::ServerInfo> DatabasePluginPostgres::serverInfo() {
350 QList<Dbt::ServerInfo> list;
353 select name, description from server_info;
359 x.name = q.value(i++).toString();
360 x.description = q.value(i++).toString();
367QVariant DatabasePluginPostgres::save(
const Dbt::ServerInfo& data) {
371 update server_info set
373 description = :description
376 q.bindValue(":name", data.name);
377 q.bindValue(
":description", data.description);
383QVariant DatabasePluginPostgres::save(
const Dbt::EmployeeCanOpenDoor& data) {
386 insert into attendance.employee_can_open_door (employee, door)
387 values (:employee, :door)
389 q.bindValue(":employee", data.employee);
390 q.bindValue(
":door", data.door);
395QVariant DatabasePluginPostgres::save(
const Dbt::DepartmentHasManager& data) {
398 insert into attendance.department_has_manager (department, "user")
399 values (:department, :user)
401 q.bindValue(":department", data.department);
402 q.bindValue(
":user", data.user);
407QVariant DatabasePluginPostgres::save(
const Dbt::DepartmentHasMember& data) {
410 insert into attendance.department_has_member (department, employee)
411 values (:department, :employee)
413 q.bindValue(":department", data.department);
414 q.bindValue(
":employee", data.employee);
419QVariant DatabasePluginPostgres::save(
const Dbt::EmployeeHasRfid& data) {
422 insert into attendance.employee_has_rfid (rfid, employee)
423 values (:rfid, :employee)
424 on conflict (rfid) do update set employee = excluded.employee
426 q.bindValue(":rfid", data.rfid);
427 q.bindValue(
":employee", data.employee);
433QVariantList pgArrayToVariantList(
const QVariant& input) {
434 QStringList x = input.toString().replace(
"{",
"").replace(
"}",
"").split(
",");
436 for (
int i=0; i<x.size(); i++) {
443QList<Dbt::Categories> DatabasePluginPostgres::categories(
const QString&
id) {
444 QList<Dbt::Categories> list;
448 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
449 from categories c, users u
450 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
451 where (:id1 <= 0 or :id2 = c.category)
452 and u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
455 q.bindValue(":user", userId());
456 q.bindValue(
":id1",
id.toInt());
457 q.bindValue(
":id2",
id.toInt());
462 x.category = q.value(i++).toString();
463 x.parent_category = null(q.value(i++).toString());
464 x.description = q.value(i++).toString();
465 x.price = q.value(i++).toDouble();
466 x.users = pgArrayToVariantList(q.value(i++));
467 x.description_tree = q.value(i++).toString();
475QList<Dbt::Categories> DatabasePluginPostgres::categoriesToRoot(
const QString&
id) {
476 QList<Dbt::Categories> list;
478 int xid =
id.toInt();
481 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
482 from categories c, users u
483 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
484 where :id = c.category
485 and u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
489 q.bindValue(
":user", userId());
490 q.bindValue(
":id", xid);
492 bool found = q.next();
493 PDEBUG << xid << found;
494 if (!found) {
return list; }
497 x.category = q.value(i++).toString();
498 x.parent_category = null(q.value(i++).toString());
499 x.description = q.value(i++).toString();
500 x.price = q.value(i++).toDouble();
501 x.users = pgArrayToVariantList(q.value(i++));
502 x.description_tree = q.value(i++).toString();
504 xid = x.parent_category.toInt();
511QList<Dbt::Categories> DatabasePluginPostgres::subcategories(
const QString&
id) {
512 QList<Dbt::Categories> list;
516 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
517 from categories c, users u
518 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
519 where ((:id1 <= 0 and c.parent_category is null) or :id2 = c.parent_category)
520 and u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
523 q.bindValue(":user", userId());
524 q.bindValue(
":id1",
id.toInt());
525 q.bindValue(
":id2",
id.toInt());
530 x.category = q.value(i++).toString();
531 x.parent_category = null(q.value(i++).toString());
532 x.description = q.value(i++).toString();
533 x.price = q.value(i++).toDouble();
534 x.users = pgArrayToVariantList(q.value(i++));
535 x.description_tree = q.value(i++).toString();
543QList<Dbt::Categories> DatabasePluginPostgres::siblingcategories(
const QString&
id) {
544 QList<Dbt::Categories> list;
548 with recursive tree as (
549 select category, parent_category
550 from categories where category = :id
552 select c.category, c.parent_category
553 from tree t, categories c
555 t.category = c.parent_category
557 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
558 from categories c, users u
559 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
560 where u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
561 and c.category not in (select category from tree)
564 q.bindValue(":user", userId());
565 q.bindValue(
":id",
id.toInt());
570 x.category = q.value(i++).toString();
571 x.parent_category = null(q.value(i++).toString());
572 x.description = q.value(i++).toString();
573 x.price = q.value(i++).toDouble();
574 x.users = pgArrayToVariantList(q.value(i++));
575 x.description_tree = q.value(i++).toString();
583void DatabasePluginPostgres::remove(
const Dbt::Users&
id) {
586 q.prepare(R
"'(select 1 from users where "user" = :id and admin and enabled;)'");
587 q.bindValue(":id", userId());
589 if (!q.next()) {
return; }
591 q.prepare(R
"'(delete from users where "user" = :id;)'");
592 q.bindValue(":id",
id.user);
597QVariant DatabasePluginPostgres::save(
const Dbt::Users& data) {
600 q.prepare(R
"'(select 1 from users where "user" = :id and admin and enabled;)'");
601 q.bindValue(":id", userId());
603 if (!q.next()) {
return QVariant(); }
606 q.prepare(R
"'(select 1 from users where "user" = :id;)'");
607 q.bindValue(":id", data.user);
619 q.bindValue(":id1", data.user);
620 q.bindValue(
":login", data.login);
621 q.bindValue(
":name", data.name);
622 q.bindValue(
":lang", data.lang);
623 q.bindValue(
":enabled", data.enabled);
624 q.bindValue(
":admin", data.admin);
626 return QVariant(data.user);
630 insert into users (login, name, lang, enabled, admin)
631 select :login, :name, :lang, :enabled, :admin
632 where not exists (select 1 from users where "user" = :id1);
634 q.bindValue(":id1", data.user);
635 q.bindValue(
":login", data.login);
636 q.bindValue(
":name", data.name);
637 q.bindValue(
":lang", data.lang);
638 q.bindValue(
":enabled", data.enabled);
639 q.bindValue(
":admin", data.admin);
642 return currval(
"users_user_seq");
650QList<Dbt::StatusOrder> DatabasePluginPostgres::statusOrder(
const QString&
id) {
651 QList<Dbt::StatusOrder> list;
655 select id, category, previous_status, next_status from status_order
656 where (:id1 <= 0 or :id2 = id);
658 q.bindValue(":id1",
id.toInt());
659 q.bindValue(
":id2",
id.toInt());
664 x.id = q.value(i++).toInt();
665 x.category = q.value(i++);
666 x.previous_status = q.value(i++);
667 x.next_status = q.value(i++);
676void DatabasePluginPostgres::remove(
const Dbt::StatusOrder&
id) {
678 q.prepare(R
"'(delete from status_order where id = :id;)'");
679 q.bindValue(":id",
id.
id);
684QVariant DatabasePluginPostgres::save(
const Dbt::StatusOrder& data) {
687 q.prepare(R
"'(select 1 from status_order where id = :id;)'");
688 q.bindValue(":id", data.id);
692 update status_order set
693 category = :category,
694 previous_status = :previous_status,
695 next_status = :next_status
698 q.bindValue(":category", data.category);
699 q.bindValue(
":previous_status", data.previous_status);
700 q.bindValue(
":next_status", data.next_status);
701 q.bindValue(
":id", data.id);
703 return QVariant(data.id);
708 insert into status_order (category, previous_status, next_status)
709 select :category, :previous_status, :next_status
711 q.bindValue(":category", data.category);
712 q.bindValue(
":previous_status", data.previous_status);
713 q.bindValue(
":next_status", data.next_status);
714 q.bindValue(
":id", data.id);
716 return currval(
"status_order_id_seq");
724void DatabasePluginPostgres::createTemporaryTableTickets(
int ticket,
bool all) {
725 if (m_temporaryTableTicketsCreated) {
return; }
726 m_temporaryTableTicketsCreated =
true;
729 create temporary table temporary_tickets (
732 date timestamp with time zone,
740 PDEBUG <<
"Vybiram VSE" << ticket;
742 insert into temporary_tickets (ticket, category, date, price, description, "user")
743 select t.ticket, t.category, t.date, t.price, t.description, t."user"
744 from tickets t, users u
745 where (t.category in (select category from users_categories where "user" = u."user") or u.admin = true)
747 and (:ticket1 <= 0 or :ticket2 = t.ticket)
751 PDEBUG <<
"Vybiram pouze otevrene" << ticket;
755 select status from statuses where closed
757 ticket_last_status as (
758 select t.ticket, tl.status
760 left join lateral (select tn.ticket, tn.status from ticket_status tn where tn.ticket = t.ticket order by ticket, date desc limit 1) tl using (ticket)
763 select distinct ts.ticket from ticket_last_status ts, ending_status es where ts.status = es.status
766 select t1.ticket from tickets t1 where t1.ticket not in (select ticket from closed_tickets)
769 insert into temporary_tickets (ticket, category, date, price, description, "user")
770 select t.ticket, t.category, t.date, t.price, t.description, t."user"
771 from tickets t, users_categories uc
772 where t.ticket in (select ticket from active_tickets)
773 and uc."user" = :user
774 and t.category = uc.category
775 and (:ticket1 <= 0 or :ticket2 = t.ticket)
779 q.bindValue(":user", userId());
780 q.bindValue(
":ticket1", ticket);
781 q.bindValue(
":ticket2", ticket);
784 q.exec(
"select count(1) from temporary_tickets;");
786 PDEBUG <<
"Vybranych vet" << q.value(0).toInt();
791QList<Dbt::Tickets> DatabasePluginPostgres::tickets(
bool all) {
792 return tickets(-1, all);
796QList<Dbt::Tickets> DatabasePluginPostgres::tickets(
int ticket,
bool all) {
797 createTemporaryTableTickets(ticket, all);
798 QList<Dbt::Tickets> list;
802 select ticket, category, date, price, description, "user"
803 from temporary_tickets ;
809 x.ticket = q.value(i++);
810 x.category = q.value(i++);
811 x.date = q.value(i++).toDateTime();
812 x.price = q.value(i++).toDouble();
813 x.description = q.value(i++).toString();
814 x.user = q.value(i++).toInt();
821QList<Dbt::Tickets> DatabasePluginPostgres::tickets(
const Dbt::Categories& category,
bool all) {
822 createTemporaryTableTickets(-1, all);
823 QList<Dbt::Tickets> list;
827 select ticket, category, date, price, description, "user"
828 from temporary_tickets
829 where category = :category;
831 q.bindValue(":category", category.category);
836 x.ticket = q.value(i++);
837 x.category = q.value(i++);
838 x.date = q.value(i++).toDateTime();
839 x.price = q.value(i++).toDouble();
840 x.description = q.value(i++).toString();
841 x.user = q.value(i++).toInt();
849QList<Dbt::TicketsVw> DatabasePluginPostgres::ticketsVw(
bool all) {
850 return ticketsVw(-1, all);
854QList<Dbt::TicketsVw> DatabasePluginPostgres::ticketsVw(
int ticket,
bool all) {
855 QList<Dbt::Tickets> list1 = tickets(ticket,all);
856 QList<Dbt::TicketsVw> list;
858 for (
int i=0; i<list1.size(); i++) {
859 Dbt::TicketsVw x = list1[i];
860 x.timesheets = ticketTimesheets(list1[i].ticket.toInt(), all);
861 x.statuses = ticketStatus(list1[i].ticket.toInt(), all);
862 x.values = ticketValues(list1[i].ticket.toInt(), all);
863 x.files = ticketFiles(list1[i].ticket.toInt(), all);
871QList<Dbt::TicketsVw> DatabasePluginPostgres::ticketsVw(
const Dbt::Categories& category,
bool all) {
872 QList<Dbt::Tickets> list1 = tickets(category, all);
873 QList<Dbt::TicketsVw> list;
875 for (
int i=0; i<list1.size(); i++) {
876 Dbt::TicketsVw x = list1[i];
877 x.timesheets = ticketTimesheets(list1[i].ticket.toInt(),
true);
878 x.statuses = ticketStatus(list1[i].ticket.toInt(),
true);
879 x.values = ticketValues(list1[i].ticket.toInt(),
true);
880 x.files = ticketFiles(list1[i].ticket.toInt(),
true);
888void DatabasePluginPostgres::remove(
const Dbt::Tickets&
id) {
890 q.prepare(R
"'(delete from tickets where ticket = :id;)'");
891 q.bindValue(":id",
id.ticket);
896QVariant DatabasePluginPostgres::save(
const Dbt::Tickets& data) {
901 q.prepare(R
"'(select 1 from tickets where ticket = :ticket;)'");
902 q.bindValue(":ticket", data.ticket);
907 if (!data.created && found) {
910 category = :category,
913 description = :description,
915 where ticket = :ticket
917 q.bindValue(":category", data.category);
918 q.bindValue(
":date", data.date);
919 q.bindValue(
":price", data.price);
920 q.bindValue(
":description", data.description);
921 q.bindValue(
":user", data.user);
922 q.bindValue(
":ticket", data.ticket);
924 return QVariant(data.ticket);
927 if (data.created || !found) {
929 insert into tickets (category, date, price, description, "user")
930 values (:category, :date, :price, :description, :user);
932 q.bindValue(":category", data.category);
933 q.bindValue(
":date", data.date);
934 q.bindValue(
":price", data.price);
935 q.bindValue(
":description", data.description);
936 q.bindValue(
":user", data.user);
938 return currval(
"tickets_ticket_seq");
942 qFatal(
"Should not happen");
949QList<T> remapTicket(
const QList<T>& input,
int ticket) {
951 QListIterator<T> iterator(input);
952 while (iterator.hasNext()) {
953 T x = iterator.next();
955 if (ticket <= 0) { x.id = 0; }
962QVariant DatabasePluginPostgres::save(
const Dbt::TicketsVw& data) {
965 int ticket = save(
dynamic_cast<const Dbt::Tickets&
>(data)).toInt();
966 removeTicketsDetails(ticket);
967 save(remapTicket(data.timesheets, ticket));
968 save(remapTicket(data.statuses, ticket));
969 save(remapTicket(data.values, ticket));
970 save(remapTicket(data.files, ticket));
977void DatabasePluginPostgres::removeTicketsDetails(
int ticket) {
979 q.exec(QString(R
"'(delete from ticket_timesheets where ticket = %1;)'").arg(ticket));
980 q.exec(QString(R"'(delete from ticket_status where ticket = %1;)'").arg(ticket));
981 q.exec(QString(R"'(delete from ticket_values where ticket = %1;)'").arg(ticket));
982 q.exec(QString(R"'(delete from ticket_files where ticket = %1;)'").arg(ticket));
986QList<Dbt::TicketStatus> DatabasePluginPostgres::ticketStatus(int ticket,
bool all) {
987 createTemporaryTableTickets(ticket, all);
988 QList<Dbt::TicketStatus> list;
992 select ts.id, ts.ticket, ts."user", ts.date, ts.description, ts.status, s.color, s.description, s.closed, s.can_be_run, s.ignored
993 from temporary_tickets t, ticket_status ts, statuses s
994 where t.ticket = ts.ticket
995 and ts.status = s.status
996 and t.ticket = :ticket
998 q.bindValue(":ticket", ticket);
1001 Dbt::TicketStatus x;
1003 x.id = q.value(i++).toInt();
1004 x.ticket = q.value(i++).toInt();
1005 x.user = q.value(i++).toInt();
1006 x.date = q.value(i++).toDateTime();
1007 x.description = q.value(i++).toString();
1008 x.status = q.value(i++).toString();
1009 x.status_color = q.value(i++).toString();
1010 x.status_description = q.value(i++).toString();
1011 x.status_closed = q.value(i++).toBool();
1012 x.status_can_be_run = q.value(i++).toBool();
1013 x.status_ignored = q.value(i++).toBool();
1020QList<Dbt::TicketStatus> DatabasePluginPostgres::ticketStatus(
bool all) {
1021 return ticketStatus(-1, all);
1025QList<Dbt::TicketStatus> DatabasePluginPostgres::ticketStatus(
int id) {
1026 QList<Dbt::TicketStatus> list;
1030 select ts.id, ts.ticket, ts."user", ts.date, ts.description, ts.status, s.color, s.description, s.close, s.can_be_run, s.ignored
1031 from ticket_status ts, users u, tickets t, users_categories uc, statuses s
1033 and t.ticket = ts.ticket
1034 and t.category = uc.category
1035 and u."user" = ts."user"
1036 and ts.status = s.status
1038 q.bindValue(":id",
id);
1039 q.bindValue(
":user", userId());
1042 Dbt::TicketStatus x;
1044 x.id = q.value(i++).toInt();
1045 x.ticket = q.value(i++).toInt();
1046 x.user = q.value(i++).toInt();
1047 x.date = q.value(i++).toDateTime();
1048 x.description = q.value(i++).toString();
1049 x.status = q.value(i++).toString();
1050 x.status_color = q.value(i++).toString();
1051 x.status_description = q.value(i++).toString();
1052 x.status_closed = q.value(i++).toBool();
1053 x.status_can_be_run = q.value(i++).toBool();
1054 x.status_ignored = q.value(i++).toBool();
1062void DatabasePluginPostgres::remove(
const Dbt::TicketStatus&
id) {
1064 q.prepare(R
"'(delete from ticket_status where id = :id;)'");
1065 q.bindValue(":id",
id.
id);
1070QVariant DatabasePluginPostgres::save(
const Dbt::TicketStatus& data) {
1074 if (!data.created) {
1075 q.prepare(R
"'(select 1 from ticket_status where id = :id;)'");
1076 q.bindValue(":id", data.id);
1081 if (!data.created && found) {
1083 update ticket_status set
1087 description = :description,
1091 q.bindValue(":id", data.id);
1092 q.bindValue(
":user", data.user);
1093 q.bindValue(
":ticket", data.ticket);
1094 q.bindValue(
":date", data.date);
1095 q.bindValue(
":description", data.description);
1096 q.bindValue(
":status", data.status);
1098 return QVariant(data.id);
1101 if (data.created || !found) {
1103 insert into ticket_status (ticket, "user", date, description, status)
1104 values (:ticket, :user, :date, :description, :status)
1106 q.bindValue(":user", data.user);
1107 q.bindValue(
":ticket", data.ticket);
1108 q.bindValue(
":date", data.date);
1109 q.bindValue(
":description", data.description);
1110 q.bindValue(
":status", data.status);
1113 return currval(
"ticket_status_id_seq");
1117 qFatal(
"Should not happen");
1122QVariant DatabasePluginPostgres::save(
const Dbt::AppendStatuses& data) {
1123 PDEBUG << data.toMap();
1126 QStringList statusesL;
1131 statusesX = statusesL.join(
",");
1133 QString categoriesX;
1134 QStringList categoriesL;
1135 for (
int i=0; i<data.
categories.size(); i++) {
1136 categoriesL <<
"'" + data.
categories[i].toString() +
"'";
1139 categoriesX = categoriesL.join(
",");
1142 q.prepare(QString(R
"'(
1147 select * from statuses
1148 where status in (%1)
1152 -- select new status, if possible
1155 from statuses s, status_order o
1156 where status = :status
1157 and s.status = o.next_status
1158 and o.previous_status in (select status from x_statuses)
1161 -- select current user
1165 where "user" = :userid
1168 -- select valid categories
1171 from categories c, x_users u
1172 where c.category in (%2)
1174 or c.category in (select category from users_categories where "user" = u."user")
1178 -- select tickets with statuses, user, categories
1182 join x_categories using (category)
1183 left join lateral (select ts.status
1184 from ticket_status ts,
1186 where t.ticket = ts.ticket
1187 and s.status = ts.status
1190 limit 1) ls on (true)
1191 where ls.status in (select status from x_statuses)
1194 insert into ticket_status (ticket, "user", description, status)
1195 select t.ticket, u."user", :description, n.status
1196 from x_tickets t, x_users u, x_newstatuses n
1198 )'").arg(statusesX).arg(categoriesX));
1199 q.bindValue(":user", data.
status);
1200 q.bindValue(
":status", data.
status);
1202 q.bindValue(
":userid", userId());
1203 PDEBUG << q.lastBoundQuery();
1210QList<Dbt::TicketTimesheets> DatabasePluginPostgres::ticketTimesheets(
int ticket,
bool all) {
1211 createTemporaryTableTickets(ticket, all);
1212 QList<Dbt::TicketTimesheets> list;
1215 select tt.id, tt.ticket, tt."user", tt.date_from, tt.date_to
1216 from temporary_tickets t, ticket_timesheets tt
1217 where t.ticket = tt.ticket
1218 and t.ticket = :ticket
1219 order by tt.date_from -- must be sorted!
1222 q.bindValue(":ticket", ticket);
1226 Dbt::TicketTimesheets x;
1227 x.id = q.value(i++).toInt();
1228 x.ticket = q.value(i++).toInt();
1229 x.user = q.value(i++).toInt();
1230 x.date_from = q.value(i++).toDateTime();
1231 x.date_to = q.value(i++).toDateTime();
1238QList<Dbt::TicketTimesheets> DatabasePluginPostgres::ticketTimesheets(
int id) {
1239 QList<Dbt::TicketTimesheets> list;
1242 select tt.id, tt.ticket, tt."user", tt.date_from, tt.date_to
1243 from ticket_timesheets tt, tickets t, users_categories uc
1244 where t.ticket = tt.ticket
1245 and t.category = uc.category
1246 and uc."user" = :user
1248 order by tt.date_from
1251 q.bindValue(":user", userId());
1252 q.bindValue(
":id",
id);
1256 Dbt::TicketTimesheets x;
1257 x.id = q.value(i++).toInt();
1258 x.ticket = q.value(i++).toInt();
1259 x.user = q.value(i++).toInt();
1260 x.date_from = q.value(i++).toDateTime();
1261 x.date_to = q.value(i++).toDateTime();
1268QList<Dbt::TicketTimesheets> DatabasePluginPostgres::ticketTimesheets(
bool all) {
1269 return ticketTimesheets(-1, all);
1273QList<Dbt::TicketTimesheets> DatabasePluginPostgres::runningTimesheets(
int ticket) {
1274 QList<Dbt::TicketTimesheets> list;
1277 select tt.id, tt.ticket, tt."user", tt.date_from, tt.date_to
1278 from ticket_timesheets tt, tickets t, users_categories uc
1279 where t.ticket = tt.ticket
1280 and t.category = uc.category
1281 and uc."user" = :user
1282 and tt.date_to is null
1283 and (:ticket1 = tt.ticket or :ticket2 <= 0)
1286 q.bindValue(":user", userId());
1287 q.bindValue(
":ticket1", ticket);
1288 q.bindValue(
":ticket2", ticket);
1292 Dbt::TicketTimesheets x;
1293 x.id = q.value(i++).toInt();
1294 x.ticket = q.value(i++).toInt();
1295 x.user = q.value(i++).toInt();
1296 x.date_from = q.value(i++).toDateTime();
1297 x.date_to = q.value(i++).toDateTime();
1304QList<Dbt::TicketTimesheets> DatabasePluginPostgres::startTimesheet(
int ticket) {
1305 QList<Dbt::TicketTimesheets> list;
1311 from ticket_timesheets tt, tickets t, users_categories uc
1312 where t.ticket = tt.ticket
1313 and t.category = uc.category
1314 and uc."user" = :user
1315 and :ticket = tt.ticket
1316 and tt.date_to is null
1319 q.bindValue(":user", userId());
1320 q.bindValue(
":ticket", ticket);
1329 from tickets t, users_categories uc
1330 where t.category = uc.category
1331 and uc."user" = :user
1332 and :ticket = t.ticket
1335 q.bindValue(":user", userId());
1336 q.bindValue(
":ticket", ticket);
1344 insert into ticket_timesheets (ticket, "user", date_from)
1345 values (:ticket, :user, now())
1347 q.bindValue(":user", userId());
1348 q.bindValue(
":ticket", ticket);
1350 QVariant
id = currval(
"ticket_timesheets_id_seq").toInt();
1351 if (
id.isNull() || !
id.isValid()) {
1355 list = ticketTimesheets(
id.toInt());
1360QList<Dbt::TicketTimesheets> DatabasePluginPostgres::stopTimesheet(
int ticket) {
1361 QList<Dbt::TicketTimesheets> list;
1368 from ticket_timesheets tt, tickets t, users_categories uc
1369 where t.ticket = tt.ticket
1370 and t.category = uc.category
1371 and uc."user" = :user
1372 and :ticket = tt.ticket
1373 and tt.date_to is null
1376 q.bindValue(":user", userId());
1377 q.bindValue(
":ticket", ticket);
1378 q.setForwardOnly(
false);
1380 if (q.size() != 1) {
1385 int id = q.value(0).toInt();
1389 update ticket_timesheets set date_to = now() where id = :id;
1391 q.bindValue(":id",
id);
1394 list = ticketTimesheets(ticket,
true);
1396 int remove_secs = 10;
1397 int round2_mins = 5;
1398 int round5_mins = 5;
1400 int remove_singles_mins = 3;
1403 for (
int i=list.size()-1; i>=0; i--) {
1404 Dbt::TicketTimesheets& x = list[i];
1405 if (x.date_from.secsTo(x.date_to) > remove_secs) {
continue; }
1410 for (
int i=list.size()-1; i>=0; i--) {
1411 Dbt::TicketTimesheets& x = list[i];
1413 secs = x.date_from.secsTo(x.date_to);
1414 secs = secs / (round2_mins * 60) + ( (secs % (round2_mins*60)) ? 1 : 0);
1415 secs = secs * (round2_mins * 60);
1416 x.date_to = x.date_from.addSecs(secs);
1420 for (
int i=list.size()-1; i>=1; i--) {
1421 Dbt::TicketTimesheets& x0 = list[i-1];
1422 Dbt::TicketTimesheets& x1 = list[i];
1424 if (x0.date_to < x1.date_from) {
continue; }
1425 x0.date_to = x1.date_to;
1430 for (
int i=list.size()-1; i>=1; i--) {
1431 Dbt::TicketTimesheets& x0 = list[i-1];
1432 Dbt::TicketTimesheets& x1 = list[i];
1433 if (x0.date_to.addSecs(join_mins*60) < x1.date_from) {
continue; }
1434 x0.date_to = x1.date_to;
1439 for (
int i=list.size()-1; i>=0; i--) {
1440 Dbt::TicketTimesheets& x = list[i];
1442 secs = x.date_from.secsTo(x.date_to);
1443 secs = secs + (round5_mins * 60) -1;
1444 secs = secs / (round5_mins * 60);
1445 secs = secs * (round5_mins * 60);
1446 x.date_to = x.date_from.addSecs(secs);
1450 for (
int i=list.size()-1; i>=0; i--) {
1451 Dbt::TicketTimesheets& x = list[i];
1452 int secs = x.date_from.secsTo(x.date_to);
1453 if (secs <= 0) {
continue; }
1454 if (secs >= remove_singles_mins * 60) {
continue; }
1458 q.prepare(R
"'(delete from ticket_timesheets where ticket = :ticket;)'");
1459 q.bindValue(":ticket", ticket);
1462 q.prepare(R
"'(insert into ticket_timesheets (ticket, "user", date_from, date_to)
1463 values (:ticket, :user, :date_from, :date_to);
1465 for (
int i=0; i<list.size(); i++) {
1466 Dbt::TicketTimesheets& x = list[i];
1467 q.bindValue(
":ticket", ticket);
1468 q.bindValue(
":user", userId());
1469 q.bindValue(
":date_from", x.date_from);
1470 q.bindValue(
":date_to", x.date_to);
1475 return list.mid(list.size()-1);
1479QList<Dbt::TicketTimesheets> DatabasePluginPostgres::toggleTimesheet(
int ticket) {
1480 QList<Dbt::TicketTimesheets> list;
1486 from tickets t, users_categories uc
1487 where t.category = uc.category
1488 and uc."user" = :user
1489 and :ticket = t.ticket
1492 q.bindValue(":user", userId());
1493 q.bindValue(
":ticket", ticket);
1502 from ticket_timesheets tt, tickets t, users_categories uc
1503 where t.ticket = tt.ticket
1504 and t.category = uc.category
1505 and uc."user" = :user
1506 and :ticket = tt.ticket
1507 and tt.date_to is null
1510 q.bindValue(":user", userId());
1511 q.bindValue(
":ticket", ticket);
1512 q.setForwardOnly(
false);
1514 bool found = (q.size() == 1);
1515 int id = (found) ? q.next(), q.value(0).toInt() : 0;
1520 insert into ticket_timesheets (ticket, "user", date_from)
1521 values (:ticket, :user, now())
1523 q.bindValue(":user", userId());
1524 q.bindValue(
":ticket", ticket);
1526 QVariant newid = currval(
"ticket_timesheets_id_seq");
1527 if (newid.isNull() || !newid.isValid()) {
1536 update ticket_timesheets set date_to = now() where id = :id;
1538 q.bindValue(":id",
id);
1542 list = ticketTimesheets(
id);
1547QVariant DatabasePluginPostgres::save(
const Dbt::TicketTimesheets& data) {
1551 if (!data.created) {
1552 q.prepare(R
"'(select 1 from ticket_timesheets where id = :id;)'");
1553 q.bindValue(":id", data.id);
1558 if (!data.created && found) {
1560 update ticket_timesheets set
1563 date_from = :date_from,
1567 q.bindValue(":id", data.id);
1568 q.bindValue(
":user", data.user);
1569 q.bindValue(
":ticket", data.ticket);
1570 q.bindValue(
":date_from", data.date_from);
1571 q.bindValue(
":date_to", data.date_to);
1573 return QVariant(data.id);
1576 if (data.created || !found) {
1578 insert into ticket_timesheets (ticket, "user", date_from, date_to)
1579 values (:ticket, :user, :date_from, :date_to)
1581 q.bindValue(":user", data.user);
1582 q.bindValue(
":ticket", data.ticket);
1583 q.bindValue(
":date_from", data.date_from);
1584 q.bindValue(
":date_to", data.date_to);
1587 return currval(
"ticket_timesheets_id_seq");
1595void DatabasePluginPostgres::remove(
const Dbt::TicketTimesheets&
id) {
1597 q.prepare(R
"'(delete from ticket_timesheets where id = :id;)'");
1598 q.bindValue(":id",
id.
id);
1603QList<Dbt::TicketValues> DatabasePluginPostgres::ticketValues(
int ticket,
bool all) {
1604 createTemporaryTableTickets(ticket, all);
1605 QList<Dbt::TicketValues> list;
1608 select tv.id, tt.ticket, tv.name, tv.value, tv."user", tv.date
1609 from temporary_tickets tt, ticket_values tv
1610 where tt.ticket = tv.ticket
1616 Dbt::TicketValues x;
1617 x.id = q.value(i++).toInt();
1618 x.ticket = q.value(i++).toInt();
1619 x.name = q.value(i++).toString();
1620 x.value =
JSON::data(q.value(i++).toByteArray());
1621 x.user = q.value(i++).toInt();
1622 x.date = q.value(i++).toDateTime();
1629QList<Dbt::TicketValues> DatabasePluginPostgres::ticketValues(
int id) {
1630 QList<Dbt::TicketValues> list;
1633 select tv.id, tv.ticket, tv.date, tv.name, tv.value, tv."user"
1634 from tickets tt, ticket_values tv, users_categories uc
1635 where tt.ticket = tv.ticket
1636 and tt.category = uc.category
1641 q.bindValue(":id",
id);
1642 q.bindValue(
":user", userId());
1646 Dbt::TicketValues x;
1647 x.id = q.value(i++).toInt();
1648 x.ticket = q.value(i++).toInt();
1649 x.date = q.value(i++).toDateTime();
1650 x.name = q.value(i++).toString();
1651 x.value =
JSON::data(q.value(i++).toByteArray());
1652 x.user = q.value(i++).toInt();
1659QList<Dbt::TicketValues> DatabasePluginPostgres::ticketValues(
bool all) {
1660 return ticketValues(-1, all);
1664QVariant DatabasePluginPostgres::save(
const Dbt::TicketValues& data) {
1668 if (!data.created) {
1669 q.prepare(R
"'(select 1 from ticket_values where id = :id;)'");
1670 q.bindValue(":id", data.id);
1675 if (!data.created && found) {
1676 q.prepare(QString(R
"'(
1677 update ticket_values set
1684 )'").arg(QString::fromUtf8(JSON::json(data.value))));
1685 q.bindValue(":id", data.id);
1686 q.bindValue(
":ticket", data.ticket);
1687 q.bindValue(
":user", data.user);
1688 q.bindValue(
":date", data.date);
1689 q.bindValue(
":name", data.name);
1692 return QVariant(data.id);
1695 if (data.created || !found) {
1696 q.prepare(QString(R
"'(
1697 insert into ticket_values (ticket, "user", date, name, value)
1698 select :ticket, :user, :date, :name, '%1'
1699 where not exists (select 1 from ticket_values where id = :id);
1700 )'").arg(QString::fromUtf8(JSON::json(data.value))));
1701 q.bindValue(":id", data.id);
1702 q.bindValue(
":ticket", data.ticket);
1703 q.bindValue(
":user", data.user);
1704 q.bindValue(
":date", data.date);
1705 q.bindValue(
":name", data.name);
1709 return currval(
"ticket_values_id_seq");
1713 qFatal(
"Should not happen");
1718void DatabasePluginPostgres::remove(
const Dbt::TicketValues&
id) {
1720 q.prepare(R
"'(delete from ticket_values where id = :id;)'");
1721 q.bindValue(":id",
id.
id);
1726QList<Dbt::Statuses> DatabasePluginPostgres::statuses(
const QString&
id) {
1727 QList<Dbt::Statuses> list;
1729 if (
id.isEmpty() ||
id ==
"") {
1731 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored,
1732 n.status, n.description, n.abbreviation, n.color, n.closed, n.can_be_run, n.ignored
1734 left join status_order o on (s.status = o.previous_status)
1735 left join statuses n on (o.next_status = n.status)
1736 order by s.status, n.status
1740 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored,
1741 n.status, n.description, n.abbreviation, n.color, n.closed, n.can_be_run, n.ignored
1743 left join status_order o on (s.status = o.previous_status)
1744 left join statuses n on (o.next_status = n.status)
1745 where (:id = s.status)
1746 order by s.status, n.status
1749 q.bindValue(":id",
id);
1754 if (!x.status.isEmpty() && x.status != q.value(0).toString()) {
1758 x.status = q.value(i++).toString();
1759 x.description = q.value(i++).toString();
1760 x.abbreviation = q.value(i++).toString();
1761 x.color = q.value(i++).toString();
1762 x.closed = q.value(i++).toBool();
1763 x.can_be_run = q.value(i++).toBool();
1764 x.ignored = q.value(i++).toBool();
1765 x.can_have_next =
true;
1766 if (!q.value(i).isNull()) {
1768 n.status = q.value(i++).toString();
1769 n.description = q.value(i++).toString();
1770 n.abbreviation = q.value(i++).toString();
1771 n.color = q.value(i++).toString();
1772 n.closed = q.value(i++).toBool();
1773 n.can_be_run = q.value(i++).toBool();
1774 n.ignored = q.value(i++).toBool();
1778 if (!x.status.isEmpty()) {
1785QList<Dbt::Statuses> DatabasePluginPostgres::statuses(
const QString& category,
const QString& previousStatus) {
1786 if ((category.isEmpty() || category ==
"") &&
1787 (previousStatus.isEmpty() || previousStatus ==
"")) {
return statuses(QString()); }
1788 int categoryi = category.toInt();
1790 QString ps = (previousStatus.isEmpty()) ?
"0" : previousStatus;
1791 auto results = [&q]() {
1792 QList<Dbt::Statuses> list;
1797 x.status = q.value(i++).toString();
1798 x.description = q.value(i++).toString();
1799 x.abbreviation = q.value(i++).toString();
1800 x.color = q.value(i++).toString();
1801 x.closed = q.value(i++).toBool();
1802 x.can_be_run = q.value(i++).toBool();
1803 x.ignored = q.value(i++).toBool();
1809 bool findNullCategory = (category.isEmpty() || category ==
"");
1810 if (!findNullCategory) {
1811 q.prepare(R
"'(select 1 from status_order where category = :category;)'");
1812 q.bindValue(":category", categoryi);
1814 findNullCategory = !q.next();
1817 if (findNullCategory && (previousStatus.isEmpty() || previousStatus ==
"")) {
1819 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1820 from statuses s, status_order o
1821 where s.status = o.next_status
1822 and (o.previous_status is null or o.previous_status = '')
1823 and (o.category is null);
1828 if (findNullCategory) {
1830 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1831 from statuses s, status_order o
1832 where s.status = o.next_status
1833 and o.previous_status = :previous_status
1834 and (o.category is null);
1836 q.bindValue(":previous_status", ps);
1840 if (!findNullCategory && (previousStatus.isEmpty() || previousStatus ==
"")) {
1842 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1843 from statuses s, status_order o
1844 where s.status = o.next_status
1845 and (o.previous_status is null or o.previous_status = '')
1846 and o.category = :category;
1848 q.bindValue(":category", categoryi);
1853 if (!findNullCategory) {
1855 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1856 from statuses s, status_order o
1857 where s.status = o.next_status
1858 and o.previous_status = :previous_status
1859 and o.category = :category;
1861 q.bindValue(":previous_status", ps);
1862 q.bindValue(
":category", categoryi);
1866 return QList<Dbt::Statuses>();
1871void DatabasePluginPostgres::remove(
const Dbt::Statuses&
id) {
1873 q.prepare(R
"'(delete from statuses where status = :id;)'");
1874 q.bindValue(":id",
id.status);
1879QVariant DatabasePluginPostgres::save(
const Dbt::Statuses& data) {
1885 description = :description,
1886 abbreviation = :abbreviation,
1889 can_be_run = :can_be_run,
1891 where status = :status
1893 q.bindValue(":description", data.description);
1894 q.bindValue(
":abbreviation", data.abbreviation);
1895 q.bindValue(
":color", data.color);
1896 q.bindValue(
":closed", data.closed);
1897 q.bindValue(
":can_be_run", data.can_be_run);
1898 q.bindValue(
":ignored", data.ignored);
1899 q.bindValue(
":status", data.status);
1903 insert into statuses (status, description, abbreviation, color, closed, can_be_run, ignored)
1904 select :status1, :description, :abbreviation, :color, :closed, :can_be_run, :ignored
1905 where not exists (select 1 from statuses where status = :status2);
1907 q.bindValue(":status1", data.status);
1908 q.bindValue(
":description", data.description);
1909 q.bindValue(
":abbreviation", data.abbreviation);
1910 q.bindValue(
":color", data.color);
1911 q.bindValue(
":closed", data.closed);
1912 q.bindValue(
":can_be_run", data.can_be_run);
1913 q.bindValue(
":ignored", data.ignored);
1914 q.bindValue(
":status2", data.status);
1918 delete from status_order
1919 where previous_status = :status
1921 q.bindValue(":status", data.status);
1924 for (
int i=0; i<data.next.size(); i++) {
1926 insert into status_order (previous_status, next_status)
1927 values (:previous_status, :next_status);
1929 q.bindValue(":previous_status", data.status);
1930 q.bindValue(
":next_status", data.next[i].status);
1936 return QVariant(data.status);
1940QList<Dbt::StatusTemplates> DatabasePluginPostgres::statusTemplates(
int id) {
1943 QList<Dbt::StatusTemplates> list;
1948QList<Dbt::TicketFiles> DatabasePluginPostgres::ticketFiles(
int ticket,
bool all) {
1949 createTemporaryTableTickets(ticket, all);
1950 QList<Dbt::TicketFiles> list;
1954 select f.id, f."user", f.date, f.ticket, f.name, f.type, f.content
1955 from temporary_tickets t, ticket_files f
1956 where t.ticket = f.ticket
1963 x.id = q.value(i++).toInt();
1964 x.user = q.value(i++).toInt();
1965 x.date = q.value(i++).toDateTime();
1966 x.ticket = q.value(i++).toInt();
1967 x.name = q.value(i++).toString();
1968 x.type = q.value(i++).toString();
1969 x.content = q.value(i++).toByteArray();
1977QList<Dbt::TicketFiles> DatabasePluginPostgres::ticketFiles(
int id) {
1978 QList<Dbt::TicketFiles> list;
1982 select f.id, f."user", f.date, f.ticket, f.name, f.type, f.content
1983 from ticket_files f, users u, tickets t, users_categories uc
1985 and t.ticket = f.ticket
1986 and t.category = uc.category
1987 and u."user" = f."user"
1990 q.bindValue(":id",
id);
1991 q.bindValue(
":user", userId());
1996 x.id = q.value(i++).toInt();
1997 x.user = q.value(i++).toInt();
1998 x.date = q.value(i++).toDateTime();
1999 x.ticket = q.value(i++).toInt();
2000 x.name = q.value(i++).toString();
2001 x.type = q.value(i++).toString();
2002 x.content = q.value(i++).toByteArray();
2010QList<Dbt::TicketFiles> DatabasePluginPostgres::ticketFiles(
bool all) {
2011 return ticketFiles(-1, all);
2015QVariant DatabasePluginPostgres::save(
const Dbt::TicketFiles& data) {
2019 if (!data.created) {
2020 q.prepare(R
"'(select 1 from ticket_files where id = :id;)'");
2021 q.bindValue(":id", data.id);
2026 if (!data.created && found) {
2028 update ticket_files set
2037 q.bindValue(":id", data.id);
2038 q.bindValue(
":ticket", data.ticket);
2039 q.bindValue(
":user", data.user);
2040 q.bindValue(
":date", data.date);
2041 q.bindValue(
":name", data.name);
2042 q.bindValue(
":type", data.type);
2043 q.bindValue(
":content", data.content);
2045 return QVariant(data.id);
2048 if (data.created || !found) {
2050 insert into ticket_files (ticket, "user", date, name, type, content)
2051 values (:ticket, :user, :date, :name, :type, :content);
2053 q.bindValue(":id", data.id);
2054 q.bindValue(
":ticket", data.ticket);
2055 q.bindValue(
":user", data.user);
2056 q.bindValue(
":date", data.date);
2057 q.bindValue(
":name", data.name);
2058 q.bindValue(
":type", data.type);
2059 q.bindValue(
":content", data.content);
2061 return currval(
"ticket_files_id_seq");
2069void DatabasePluginPostgres::remove(
const Dbt::TicketFiles&
id) {
2071 q.prepare(R
"'(delete from ticket_files where id = :id;)'");
2072 q.bindValue(":id",
id.
id);
2077QList<Dbt::UsersCategories> DatabasePluginPostgres::usersCategories(
int id,
int user,
const QString& category) {
2078 QList<Dbt::UsersCategories> list;
2080 auto retvals = [&]() {
2084 Dbt::UsersCategories x;
2085 x.id = q.value(i++).toInt();
2086 x.user = q.value(i++).toInt();
2087 x.category = q.value(i++).toString();
2094 q.prepare(R
"'(select id, "user", category from users_categories where id = :id)'");
2095 q.bindValue(":id",
id);
2099 if (
id <= 0 && category.isEmpty() && user > 0) {
2100 q.prepare(R
"'(select id, "user", category from users_categories where "user" = :user)'");
2101 q.bindValue(":user", user);
2105 if (
id <= 0 && category.isEmpty() && user <= 0) {
2106 q.prepare(R
"'(select id, "user", category from users_categories)'");
2110 if (
id <= 0 && !category.isEmpty() && user > 0) {
2111 q.prepare(R
"'(select id, "user", category from users_categories where "user" = :user and category = :category)'");
2112 q.bindValue(":user", user);
2113 q.bindValue(
":category", category);
2117 if (
id <= 0 && !category.isEmpty() && user <= 0) {
2118 q.prepare(R
"'(select id, "user", category from users_categories where category = :category)'");
2119 q.bindValue(":category", category);
2128QList<Dbt::CategoriesOverview> DatabasePluginPostgres::categoriesOverview(
const QStringList& statuses) {
2130 QStringList statusesL;
2131 for (
int i=0; i<statuses.size(); i++) {
2132 statusesL <<
"'" + statuses[i] +
"'";
2135 statusesX = statusesL.join(
",");
2137 QList<Dbt::CategoriesOverview> list;
2139 q.prepare(QString(R
"'(
2140 with recursive tree as (
2141 select category, parent_category, description, 0::int as depth, format('%04s', category) as ordering
2142 from categories where parent_category is null
2144 select c.category, c.parent_category, c.description, t.depth + 1 as depth, t.ordering || format('%04s', c.category) as ordering
2145 from tree t, categories c
2147 t.category = c.parent_category
2152 where "user" = :userid
2157 where status in (%1)
2161 select t.*, ls.*, tts.*
2163 left join lateral (select ts.status
2164 from ticket_status ts,
2166 where t.ticket = ts.ticket
2167 and s.status = ts.status
2170 limit 1) ls on (true)
2171 left join lateral (select to_hours(sum(tt.date_to - coalesce(tt.date_from, now()))) as time
2172 from ticket_timesheets tt,
2174 where tt.ticket = t.ticket
2175 -- and (tt."user" = u."user" or u.admin = true)
2176 group by tt.ticket) tts on (true)
2177 where ls.status in (select status from x_statuses)
2181 select c.depth, c.category, c.description, x.price, x.time, x.tickets_count, c.ordering
2182 into temporary table xxx
2184 join x_users u on true
2185 left join users_categories uc on (uc.category = c.category and uc."user" = u."user")
2186 left join (select category, count(1) as tickets_count, sum(time) as time, round(sum(price*time)) as price
2189 ) x on (c.category = x.category)
2190 where (uc.category is not null or u.admin = true)
2193 )'").arg(statusesX));
2194 q.bindValue(":userid", userId());
2197 select 'DETAIL' as type, x.depth, x.category, x.description, x.price, x.time, x.tickets_count, x.ordering
2200 select 'SUM', null, null, 'Total', sum(price), sum(time), sum(tickets_count), null
2207 Dbt::CategoriesOverview x;
2208 x.type = q.value(i++).toString();
2209 x.depth = q.value(i++).toInt();
2210 x.category = q.value(i++).toString();
2211 x.description = q.value(i++).toString();
2212 x.price = q.value(i++).toDouble();
2213 x.time = q.value(i++).toDouble();
2214 x.tickets_count = q.value(i++).toInt();
2215 x.ordering = q.value(i++).toString();
2222QList<Dbt::Overview> DatabasePluginPostgres::overview(
const QString& category,
const QStringList& statuses) {
2224 QStringList statusesL;
2225 for (
int i=0; i<statuses.size(); i++) {
2226 statusesL <<
"'" + statuses[i] +
"'";
2229 statusesX =
"array[" +statusesL.join(
",") +
"]";
2231 QList<Dbt::Overview> list;
2234 select key, category, statuses
2235 from overview_params
2236 where category = :category
2237 and statuses = )'" + statusesX );
2238 q.bindValue(":category", category);
2241 list = overview(q.value(0).toString());
2245 quint64 rnd = QRandomGenerator::global()->generate64();
2246 QString rnds = QString::number(rnd, 36).toUpper();
2247 q.prepare(
"insert into overview_params (key, category, statuses) values (:key, :category, " + statusesX +
");");
2248 q.bindValue(
":key", rnds);
2249 q.bindValue(
":category", category);
2251 list = overview(rnds);
2257QList<Dbt::Overview> DatabasePluginPostgres::overview(
const QString& overviewId) {
2258 QList<Dbt::Overview> list;
2260 Dbt::Overview overview;
2261 overview.id = overviewId.toUpper();
2264 select c.category, c.parent_category, c.description, c.price
2265 from categories c, overview_params p
2266 where c.category = p.category
2269 q.bindValue(":key", overviewId.toUpper());
2271 if (!q.next()) {
return list; }
2272 overview.category.category = q.value(0).toString();
2273 overview.category.parent_category = null(q.value(1).toString());
2274 overview.category.description = q.value(2).toString();
2275 overview.category.price = q.value(3).toDouble();
2277 q.exec(R
"'(create temporary table overview_categories_tmp(category int);)'");
2278 q.exec(R"'(create temporary table overview_statuses_tmp(status text);)'");
2281 with recursive tree(category, parent_category, depth) as (
2284 select category, parent_category, 0 as depth
2286 where category in (select category from overview_params where key = :key)
2290 select c.category, c.parent_category, t.depth+1 as depth
2292 left join categories c on (c.parent_category = t.category)
2294 and c.category is not null
2297 insert into overview_categories_tmp (category)
2302 q.bindValue(":key", overviewId.toUpper());
2305 q.prepare(R
"'(insert into overview_statuses_tmp select unnest(statuses) from overview_params where key = :key;)'");
2306 q.bindValue(":key", overviewId.toUpper());
2311 ticket_last_status as not materialized (
2312 select t.ticket, tl.status, st.description
2314 left join lateral (select tn.ticket, tn.status
2315 from ticket_status tn, statuses s
2316 where tn.ticket = t.ticket
2317 and tn.status = s.status
2319 order by ticket, date desc
2322 left join statuses st on (st.status = tl.status)
2324 ticket_timesheets_sum as not materialized (
2325 select ticket, "user", sum(date_to - date_from) as duration
2326 from ticket_timesheets
2327 group by ticket, "user"
2331 select t.ticket, t.description, t."user", u.name, t.price as hour_price, to_hours(ts.duration), round(to_hours(ts.duration) * t.price), ls.description
2333 left join ticket_last_status ls using (ticket)
2334 left join ticket_timesheets_sum ts using (ticket, "user")
2335 left join users u using ("user")
2336 where ls.status in (select status from overview_statuses_tmp)
2337 and category in (select category from overview_categories_tmp)
2340 -- Součet za jednotlivé tickety
2342 select g.ticket, g.description, -1, null, null, g.duration, g.price, st.description
2344 select t.ticket, t.description, -1, null, null, to_hours(sum(ts.duration)) as duration, sum(to_hours(ts.duration) * t.price) as price
2346 left join ticket_timesheets_sum ts using (ticket, "user")
2347 left join ticket_last_status ls using (ticket)
2348 where ls.status in (select status from overview_statuses_tmp)
2349 and category in (select category from overview_categories_tmp)
2352 left join ticket_last_status st using(ticket)
2355 -- Součet za jednotlivé statusy
2356 select null, null, -2, null, null, to_hours(sum(ts.duration)) as duration, round(sum(to_hours(ts.duration) * t.price)) as price, ls.description
2357 from ticket_last_status ls
2358 left join tickets t using (ticket)
2359 left join ticket_timesheets_sum ts using (ticket)
2360 where ls.status in (select status from overview_statuses_tmp)
2361 and category in (select category from overview_categories_tmp)
2362 group by ls.description
2365 -- Součet za jednotlivé lidi
2367 select null, null, -3, u.name, null, to_hours(sum(ts.duration)) as duration, round(sum(to_hours(ts.duration) * t.price)) as price, null
2368 from ticket_last_status ls
2369 left join tickets t using (ticket)
2370 left join users u using ("user")
2371 left join ticket_timesheets_sum ts using (ticket)
2372 where ls.status in (select status from overview_statuses_tmp)
2373 and category in (select category from overview_categories_tmp)
2374 group by t."user", u.name
2378 if (q.value(2).toInt() == -1) {
2379 Dbt::Overview::TicketsSum s;
2380 s.ticket = q.value(0).toInt();
2381 s.description = q.value(1).toString();
2382 s.duration = q.value(5).toDouble();
2383 s.price = q.value(6).toDouble();
2384 s.status = q.value(7).toString();
2385 overview.ticketsSum << s;
2388 if (q.value(2).toInt() == -2) {
2389 Dbt::Overview::StatusSum s;
2390 s.duration = q.value(5).toDouble();
2391 s.price = q.value(6).toDouble();
2392 s.status = q.value(7).toString();
2393 overview.statusSum << s;
2396 if (q.value(2).toInt() == -3) {
2397 Dbt::Overview::UserSum s;
2398 s.user_name = q.value(3).toString();
2399 s.duration = q.value(5).toDouble();
2400 s.price = q.value(6).toDouble();
2401 overview.userSum << s;
2406 Dbt::Overview::Tickets t;
2407 t.ticket = q.value(i++).toInt();
2408 t.description = q.value(i++).toString();
2409 t.user = q.value(i++).toInt();
2410 t.user_name = q.value(i++).toString();
2411 t.hour_price = q.value(i++).toDouble();
2412 t.duration = q.value(i++).toDouble();
2413 t.price = q.value(i++).toDouble();
2414 t.status = q.value(i++).toString();
2415 overview.tickets << t;
2420 ticket_last_status as not materialized (
2421 select t.ticket, tl.status
2423 left join lateral (select tn.ticket, tn.status
2424 from ticket_status tn, statuses s
2425 where tn.ticket = t.ticket
2426 and tn.status = s.status
2428 order by ticket, date desc
2432 ticket_timesheets_sum as not materialized (
2433 select ticket, "user", date_to::date as date, sum(date_to - date_from) as duration
2434 from ticket_timesheets
2435 group by ticket, "user", date_to::date
2438 select t.ticket, t.description, t."user", u.name, t.price as hour_price, ts.date, to_hours(ts.duration), round(to_hours(ts.duration) * t.price)
2440 left join users u using ("user")
2441 left join ticket_last_status ls using (ticket)
2442 left join ticket_timesheets_sum ts using (ticket, "user")
2443 left join categories c using (category)
2444 where ls.status in (select status from overview_statuses_tmp)
2445 and category in (select category from overview_categories_tmp)
2449 select null, null, -1, null, null, null, to_hours(sum(duration)), sum(round(to_hours(ts.duration) * t.price))
2451 left join ticket_last_status ls using (ticket)
2452 left join ticket_timesheets_sum ts using (ticket, "user")
2453 where ls.status in (select status from overview_statuses_tmp)
2454 and category in (select category from overview_categories_tmp)
2458 if (q.value(2).toInt() == -1) {
2459 overview.sum.duration = q.value(6).toDouble();
2460 overview.sum.price = q.value(7).toDouble();
2464 Dbt::Overview::Days t;
2465 t.ticket = q.value(i++).toInt();
2466 t.description = q.value(i++).toString();
2467 t.user = q.value(i++).toInt();
2468 t.user_name = q.value(i++).toString();
2469 t.hour_price = q.value(i++).toDouble();
2470 t.date = q.value(i++).toDateTime();
2471 t.duration = q.value(i++).toDouble();
2472 t.price = q.value(i++).toDouble();
2481QList<Dbt::OverviewList> DatabasePluginPostgres::overviewList() {
2483 QList<Dbt::OverviewList> list;
2485 QList<Dbt::Statuses> statusesList = statuses(QString());
2486 QHash<QString, Dbt::Statuses> statusesHash;
2487 for (
int i=0; i<statusesList.size(); i++) {
2488 const Dbt::Statuses& item = statusesList[i];
2489 statusesHash[item.status] = item;
2493 select o.key, o.statuses, c.category, c.parent_category, c.description, c.price
2494 from overview_params o
2495 left join categories c using (category)
2499 Dbt::OverviewList x;
2500 x.key = q.value(i++).toString();
2502 QStringList statuses = q.value(i++).toString().replace(
"{",
"").replace(
"}",
"").split(
",");
2503 for (
int i=0; i<statuses.size(); i++) {
2504 x.statuses << statusesHash[statuses[i]];
2508 c.category = q.value(i++).toString();
2509 c.parent_category = q.value(i++).toString();
2510 c.description = q.value(i++).toString();
2511 c.price = q.value(i++).toDouble();
2520void DatabasePluginPostgres::remove(
const Dbt::OverviewList& x) {
2522 q.prepare(R
"'(delete from overview_params where key = :key;)'");
2523 q.bindValue(":key", x.key);
2528QList<Dbt::Departments> DatabasePluginPostgres::departments(
int department) {
2530 QList<Dbt::Departments> list;
2531 q.prepare(R
"'(select department, abbr, description from attendance.departments where department = :key1 or :key2 <= 0;)'");
2532 q.bindValue(":key1", department);
2533 q.bindValue(
":key2", department);
2538 x.department = q.value(i++).toInt();
2539 x.abbr = q.value(i++).toString();
2540 x.description = q.value(i++).toString();
2547QList<Dbt::Doors> DatabasePluginPostgres::doors(
int door) {
2549 QList<Dbt::Doors> list;
2550 q.prepare(R
"'(select door, description from attendance.doors where door = :key1 or :key2 <= 0;)'");
2551 q.bindValue(":key1", door);
2552 q.bindValue(
":key2", door);
2557 x.door = q.value(i++).toInt();
2558 x.description = q.value(i++).toString();
2564QList<Dbt::Employees> DatabasePluginPostgres::employees(
int employee) {
2566 QList<Dbt::Employees> list;
2567 q.prepare(R
"'(select e.employee, e.firstname, e.surname, e.active, e."user", coalesce(u.login, '') as login,
2568 e.work_hours_mode, e.rounding_interval,
2569 e.saturdays_paid, e.sundays_paid, e.auto_breaks, e.overtime_paid
2570 from attendance.employees e
2571 left join users u on (u."user" = e."user")
2572 where e.employee = :key1 or :key2 <= 0;)'");
2573 q.bindValue(":key1", employee);
2574 q.bindValue(
":key2", employee);
2579 x.employee = q.value(i++).toInt();
2580 x.firstname = q.value(i++).toString();
2581 x.surname = q.value(i++).toString();
2582 x.active = q.value(i++).toBool();
2583 x.user = q.value(i++).toInt();
2584 x.login = q.value(i++).toString();
2585 x.work_hours_mode = q.value(i++).toString();
2586 x.rounding_interval = q.value(i++).toString();
2587 x.saturdays_paid = q.value(i++).toBool();
2588 x.sundays_paid = q.value(i++).toBool();
2589 x.auto_breaks = q.value(i++).toBool();
2590 x.overtime_paid = q.value(i++).toBool();
2596QList<Dbt::EventTypes> DatabasePluginPostgres::eventTypes(
const QString& eventType) {
2598 QList<Dbt::EventTypes> list;
2599 q.prepare(R
"X(select event_type, description, end_state, passage, arrival, vacation, sick_leave,
2600 compensatory_leave, business_trip, break_time, unpaid_leave, sick_care, doctor, paid_obstacle
2601 from attendance.event_types
2602 where event_type = :key1 or :key2 = '')X");
2603 q.bindValue(":key1", ((eventType.isNull()) ? QString(
"") : eventType));
2604 q.bindValue(
":key2", ((eventType.isNull()) ? QString(
"") : eventType));
2609 x.event_type = q.value(i++).toString();
2610 x.description = q.value(i++).toString();
2611 x.end_state = q.value(i++).toBool();
2612 x.passage = q.value(i++).toBool();
2613 x.arrival = q.value(i++).toBool();
2614 x.vacation = q.value(i++).toBool();
2615 x.sick_leave = q.value(i++).toBool();
2616 x.compensatory_leave = q.value(i++).toBool();
2617 x.business_trip = q.value(i++).toBool();
2618 x.break_time = q.value(i++).toBool();
2619 x.unpaid_leave = q.value(i++).toBool();
2620 x.sick_care = q.value(i++).toBool();
2621 x.doctor = q.value(i++).toBool();
2622 x.paid_obstacle = q.value(i++).toBool();
2628QList<Dbt::Events> DatabasePluginPostgres::events(
int event,
int employee,
const QDate& month,
int limit,
int offset) {
2630 QList<Dbt::Events> list;
2631 QString sql = QStringLiteral(R
"X(select
2644 from attendance.events_view
2645 where (event = :key1 or :key2 <= 0)
2646 and (:employee <= 0 or employee = :employee))X");
2647 if (month.isValid()) {
2648 sql += QStringLiteral(
" and date >= :date_from and date < :date_to");
2650 sql += QStringLiteral(
" order by date");
2651 if (limit > 0) { sql += QStringLiteral(
" limit :limit"); }
2652 if (offset > 0) { sql += QStringLiteral(
" offset :offset"); }
2653 sql += QStringLiteral(
";");
2656 q.bindValue(
":key1", event);
2657 q.bindValue(
":key2", event);
2658 q.bindValue(
":employee", employee);
2659 if (month.isValid()) {
2660 QDateTime from(QDate(month.year(), month.month(), 1), QTime(0,0,0));
2661 QDateTime to = from.addMonths(1);
2662 q.bindValue(
":date_from", from);
2663 q.bindValue(
":date_to", to);
2665 if (limit > 0) q.bindValue(
":limit", limit);
2666 if (offset > 0) q.bindValue(
":offset", offset);
2671 x.event = q.value(i++).toInt();
2672 x.date = q.value(i++).toDateTime();
2673 x.event_type = q.value(i++).toString();
2674 x.event_description = q.value(i++).toString();
2675 x.employee = q.value(i++).toInt();
2676 x.firstname = q.value(i++).toString();
2677 x.surname = q.value(i++).toString();
2678 x.valid = q.value(i++).toBool();
2679 x.user_edited = q.value(i++).toInt();
2680 x.user_edited_name = q.value(i++).toString();
2681 x.error = q.value(i++).toString();
2682 x.note = q.value(i++).toString();
2688QList<Dbt::DepartmentHasManager> DatabasePluginPostgres::departmentHasManager(
const Dbt::DepartmentHasManager& p) {
2690 QList<Dbt::DepartmentHasManager> list;
2691 q.prepare(R
"'(select department, "user" from attendance.department_has_manager
2692 where (department = :key11 or :key12 <= 0)
2693 and ("user" = :key21 or :key22 <= 0);
2695 q.bindValue(":key11", p.department);
2696 q.bindValue(
":key12", p.department);
2697 q.bindValue(
":key21", p.user);
2698 q.bindValue(
":key22", p.user);
2701 Dbt::DepartmentHasManager x;
2703 x.department = q.value(i++).toInt();
2704 x.user = q.value(i++).toInt();
2710QList<Dbt::DepartmentHasMember> DatabasePluginPostgres::departmentHasMember(
const Dbt::DepartmentHasMember& p) {
2712 QList<Dbt::DepartmentHasMember> list;
2713 q.prepare(R
"'(select department, employee from attendance.department_has_member
2714 where (department = :key11 or :key12 <= 0)
2715 and (employee = :key21 or :key22 <= 0);
2717 q.bindValue(":key11", p.department);
2718 q.bindValue(
":key12", p.department);
2719 q.bindValue(
":key21", p.employee);
2720 q.bindValue(
":key22", p.employee);
2723 Dbt::DepartmentHasMember x;
2725 x.department = q.value(i++).toInt();
2726 x.employee = q.value(i++).toInt();
2732QList<Dbt::EmployeeCanOpenDoor> DatabasePluginPostgres::employeeCanOpenDoor(
const Dbt::EmployeeCanOpenDoor& p) {
2734 QList<Dbt::EmployeeCanOpenDoor> list;
2735 q.prepare(R
"'(select door, employee from attendance.employee_can_open_door
2736 where (door = :key11 or :key12 <= 0)
2737 and (employee = :key21 or :key22 <= 0);
2739 q.bindValue(":key11", p.door);
2740 q.bindValue(
":key12", p.door);
2741 q.bindValue(
":key21", p.employee);
2742 q.bindValue(
":key22", p.employee);
2745 Dbt::EmployeeCanOpenDoor x;
2747 x.door = q.value(i++).toInt();
2748 x.employee = q.value(i++).toInt();
2754QList<Dbt::EmployeeHasRfid> DatabasePluginPostgres::employeeHasRfid(
const Dbt::EmployeeHasRfid& p) {
2756 QList<Dbt::EmployeeHasRfid> list;
2757 q.prepare(R
"'(select employee, rfid from attendance.employee_has_rfid
2758 where (employee = :key11 or :key12 <= 0)
2759 and (rfid = :key21 or :key22 <= 0);
2761 q.bindValue(":key11", p.employee);
2762 q.bindValue(
":key12", p.employee);
2763 q.bindValue(
":key21", p.rfid);
2764 q.bindValue(
":key22", p.rfid);
2767 Dbt::EmployeeHasRfid x;
2769 x.employee = q.value(i++).toInt();
2770 x.rfid = q.value(i++).toInt();
2776QList<Dbt::Rfids> DatabasePluginPostgres::rfids(
int rfid) {
2778 QList<Dbt::Rfids> list;
2780 select r.rfid, r.rfid_id, r.valid, r.note,
2781 coalesce(ehr.employee, 0) as employee,
2782 coalesce(e.firstname, '') as firstname,
2783 coalesce(e.surname, '') as surname
2784 from attendance.rfids r
2785 left join attendance.employee_has_rfid ehr using (rfid)
2786 left join attendance.employees e on (e.employee = ehr.employee)
2787 where r.rfid = :key1 or :key2 <= 0;
2789 q.bindValue(":key1", rfid);
2790 q.bindValue(
":key2", rfid);
2795 x.rfid = q.value(i++).toInt();
2796 x.rfid_id = q.value(i++).toString();
2797 x.valid = q.value(i++).toBool();
2798 x.note = q.value(i++).toString();
2799 x.employee = q.value(i++).toInt();
2800 x.name = q.value(i++).toString();
2801 x.surname = q.value(i++).toString();
2807QList<Dbt::Rfids> DatabasePluginPostgres::rfidsById(
const QString& rfidId) {
2809 QList<Dbt::Rfids> list;
2811 select r.rfid, r.rfid_id, r.valid, r.note,
2812 coalesce(ehr.employee, 0) as employee,
2813 coalesce(e.firstname, '') as firstname,
2814 coalesce(e.surname, '') as surname
2815 from attendance.rfids r
2816 left join attendance.employee_has_rfid ehr using (rfid)
2817 left join attendance.employees e on (e.employee = ehr.employee)
2818 where r.rfid_id = :rfid_id;
2820 q.bindValue(":rfid_id", rfidId);
2825 x.rfid = q.value(i++).toInt();
2826 x.rfid_id = q.value(i++).toString();
2827 x.valid = q.value(i++).toBool();
2828 x.note = q.value(i++).toString();
2829 x.employee = q.value(i++).toInt();
2830 x.name = q.value(i++).toString();
2831 x.surname = q.value(i++).toString();
2837QList<Dbt::Holidays> DatabasePluginPostgres::holidays(
const QDate& date) {
2839 QList<Dbt::Holidays> list;
2840 if (date.isValid()) {
2841 q.prepare(R
"'(select date, description from attendance.holidays where date = :d;)'");
2842 q.bindValue(":d", date);
2844 q.prepare(R
"'(select date, description from attendance.holidays order by date desc;)'");
2850 x.date = q.value(i++).toDate();
2851 x.description = q.value(i++).toString();
2857QList<Dbt::WorkCalendar> DatabasePluginPostgres::workCalendar(
const QDate& period) {
2859 QList<Dbt::WorkCalendar> list;
2860 if (period.isValid()) {
2861 q.prepare(R
"'(select period, working_days, holidays, hours8, hours85 from attendance.work_calendar where period = :p order by period;)'");
2862 q.bindValue(":p", period);
2864 q.prepare(R
"'(select period, working_days, holidays, hours8, hours85 from attendance.work_calendar order by period;)'");
2868 Dbt::WorkCalendar x;
2870 x.period = q.value(i++).toDate();
2871 x.working_days = q.value(i++).toInt();
2872 x.holidays = q.value(i++).toInt();
2873 x.hours8 = q.value(i++).toString();
2874 x.hours85 = q.value(i++).toString();
2880void DatabasePluginPostgres::generateWorkCalendar(
int year) {
2882 q.prepare(
"select attendance.generate_work_calendar(:y);");
2883 q.bindValue(
":y", year);
2887void DatabasePluginPostgres::copyHolidays(
int fromYear,
int toYear) {
2889 q.prepare(
"select attendance.copy_holidays(:fy, :ty);");
2890 q.bindValue(
":fy", fromYear);
2891 q.bindValue(
":ty", toYear);
2895void DatabasePluginPostgres::remove(
const Dbt::Departments& data) {
2896 PDEBUG << data.department;
2898 q.prepare(R
"'(delete from attendance.departments where department = :key;)'");
2899 q.bindValue(":key", data.department);
2903void DatabasePluginPostgres::remove(
const Dbt::Doors& data) {
2905 q.prepare(R
"'(delete from attendance.doors where door = :key;)'");
2906 q.bindValue(":key", data.door);
2910void DatabasePluginPostgres::remove(
const Dbt::Employees& data) {
2912 q.prepare(R
"'(delete from attendance.employees where employee = :key;)'");
2913 q.bindValue(":key", data.employee);
2917void DatabasePluginPostgres::remove(
const Dbt::EventTypes& data) {
2919 q.prepare(R
"'(delete from attendance.event_types where event_type = :key;)'");
2920 q.bindValue(":key", data.event_type);
2924void DatabasePluginPostgres::remove(
const Dbt::Events& data) {
2926 q.prepare(R
"'(delete from attendance.events where event = :key;)'");
2927 q.bindValue(":key", data.event);
2931void DatabasePluginPostgres::remove(
const Dbt::DepartmentHasManager& data) {
2933 q.prepare(R
"'(delete from attendance.department_has_manager where department = :key1 and "user" = :key2;)'");
2934 q.bindValue(":key1", data.department);
2935 q.bindValue(
":key2", data.user);
2939void DatabasePluginPostgres::remove(
const Dbt::DepartmentHasMember& data) {
2941 q.prepare(R
"'(delete from attendance.department_has_member where department = :key and employee = :key2;)'");
2942 q.bindValue(":key1", data.department);
2943 q.bindValue(
":key2", data.employee);
2947void DatabasePluginPostgres::remove(
const Dbt::EmployeeCanOpenDoor& data) {
2949 q.prepare(R
"'(delete from attendance.employee_can_open_door where employee = :key1 and door = :key2;)'");
2950 q.bindValue(":key1", data.employee);
2951 q.bindValue(
":key2", data.door);
2955void DatabasePluginPostgres::remove(
const Dbt::EmployeeHasRfid& data) {
2957 q.prepare(R
"'(delete from attendance.employee_has_rfid where employee = :key1 and rfid = :key2;)'");
2958 q.bindValue(":key1", data.employee);
2959 q.bindValue(
":key2", data.rfid);
2963void DatabasePluginPostgres::remove(
const Dbt::Rfids& data) {
2965 q.prepare(R
"'(delete from attendance.rfids where rfid = :key;)'");
2966 q.bindValue(":key", data.rfid);
2970void DatabasePluginPostgres::remove(
const Dbt::Holidays& data) {
2972 q.prepare(R
"'(delete from attendance.holidays where date = :key;)'");
2973 q.bindValue(":key", data.date);
2977void DatabasePluginPostgres::remove(
const Dbt::WorkCalendar& data) {
2979 q.prepare(R
"'(delete from attendance.work_calendar where period = :key;)'");
2980 q.bindValue(":key", data.period);
2984void DatabasePluginPostgres::remove(
const Dbt::AttendanceSummary& data) {
2986 q.prepare(R
"'(delete from attendance.summary where employee = :key1 and month = :key2;)'");
2987 q.bindValue(":key1", data.employee);
2988 q.bindValue(
":key2", data.month);
2993QVariant DatabasePluginPostgres::save(
const Dbt::Departments& data) {
2996 q.prepare(R
"'(select 1 from attendance.departments where department = :key)'");
2997 q.bindValue(":key", data.department);
3001 update attendance.departments set
3003 description = :description
3004 where department = :department
3006 q.bindValue(":abbr", data.abbr);
3007 q.bindValue(
":description", data.description);
3008 q.bindValue(
":department", data.department);
3010 return QVariant(data.department);
3015 insert into attendance.departments (abbr, description)
3016 values (:abbr, :description)
3018 q.bindValue(":abbr", data.abbr);
3019 q.bindValue(
":description", data.description);
3021 return currval(
"attendance.departments_department_seq");
3029QVariant DatabasePluginPostgres::save(
const Dbt::Employees& data) {
3032 q.prepare(R
"'(select 1 from attendance.employees where employee = :key)'");
3033 q.bindValue(":key", data.employee);
3037 update attendance.employees set
3038 firstname = :firstname,
3042 work_hours_mode = :work_hours_mode,
3043 rounding_interval = :rounding_interval,
3044 saturdays_paid = :saturdays_paid,
3045 sundays_paid = :sundays_paid,
3046 auto_breaks = :auto_breaks,
3047 overtime_paid = :overtime_paid
3048 where employee = :employee
3050 q.bindValue(":firstname", data.firstname);
3051 q.bindValue(
":surname", data.surname);
3052 q.bindValue(
":active", data.active);
3053 q.bindValue(
":user", (data.user == 0) ? QVariant() : data.user);
3054 q.bindValue(
":work_hours_mode", data.work_hours_mode);
3055 q.bindValue(
":rounding_interval", data.rounding_interval);
3056 q.bindValue(
":saturdays_paid", data.saturdays_paid);
3057 q.bindValue(
":sundays_paid", data.sundays_paid);
3058 q.bindValue(
":auto_breaks", data.auto_breaks);
3059 q.bindValue(
":overtime_paid", data.overtime_paid);
3060 q.bindValue(
":employee", data.employee);
3062 return QVariant(data.employee);
3067 insert into attendance.employees (firstname, surname, active, "user", work_hours_mode,
3068 rounding_interval, saturdays_paid, sundays_paid,
3069 auto_breaks, overtime_paid)
3070 values (:firstname, :surname, :active, :user, :work_hours_mode,
3071 :rounding_interval, :saturdays_paid, :sundays_paid,
3072 :auto_breaks, :overtime_paid)
3074 q.bindValue(":firstname", data.firstname);
3075 q.bindValue(
":surname", data.surname);
3076 q.bindValue(
":active", data.active);
3077 q.bindValue(
":user", (data.user == 0) ? QVariant() : data.user);
3078 q.bindValue(
":work_hours_mode", data.work_hours_mode);
3079 q.bindValue(
":rounding_interval", data.rounding_interval);
3080 q.bindValue(
":saturdays_paid", data.saturdays_paid);
3081 q.bindValue(
":sundays_paid", data.sundays_paid);
3082 q.bindValue(
":auto_breaks", data.auto_breaks);
3083 q.bindValue(
":overtime_paid", data.overtime_paid);
3085 return currval(
"attendance.employees_employee_seq");
3093QVariant DatabasePluginPostgres::save(
const Dbt::Doors& data) {
3096 q.prepare(R
"'(select 1 from attendance.doors where door = :key)'");
3097 q.bindValue(":key", data.door);
3101 update attendance.doors set
3102 description = :description
3105 q.bindValue(":description", data.description);
3106 q.bindValue(
":door", data.door);
3108 return QVariant(data.door);
3113 insert into attendance.doors (description)
3114 values (:description)
3116 q.bindValue(":description", data.description);
3118 return currval(
"attendance.doors_door_seq");
3126QVariant DatabasePluginPostgres::save(
const Dbt::EventTypes& data) {
3129 q.prepare(R
"'(select 1 from attendance.event_types where event_type = :key)'");
3130 q.bindValue(":key", data.event_type);
3134 update attendance.event_types set
3135 description = :description,
3137 end_state = :end_state,
3139 vacation = :vacation,
3140 sick_leave = :sick_leave,
3141 compensatory_leave = :compensatory_leave,
3142 business_trip = :business_trip,
3143 break_time = :break_time,
3144 unpaid_leave = :unpaid_leave,
3145 sick_care = :sick_care,
3147 paid_obstacle = :paid_obstacle
3148 where event_type = :event_type
3151 q.bindValue(":description", data.description);
3152 q.bindValue(
":passage", data.passage);
3153 q.bindValue(
":end_state", data.end_state);
3154 q.bindValue(
":arrival", data.arrival);
3155 q.bindValue(
":vacation", data.vacation);
3156 q.bindValue(
":sick_leave", data.sick_leave);
3157 q.bindValue(
":compensatory_leave", data.compensatory_leave);
3158 q.bindValue(
":business_trip", data.business_trip);
3159 q.bindValue(
":break_time", data.break_time);
3160 q.bindValue(
":unpaid_leave", data.unpaid_leave);
3161 q.bindValue(
":sick_care", data.sick_care);
3162 q.bindValue(
":event_type", data.event_type);
3163 q.bindValue(
":doctor", data.doctor);
3164 q.bindValue(
":paid_obstacle", data.paid_obstacle);
3166 return QVariant(data.event_type);
3171 insert into attendance.event_types (
3172 event_type, description,
3173 passage, end_state, arrival, vacation, sick_leave, compensatory_leave,
3174 business_trip, break_time, unpaid_leave, sick_care, doctor, paid_obstacle)
3176 :event_type, :description,
3177 :passage, :end_state, :arrival, :vacation, :sick_leave, :compensatory_leave,
3178 :business_trip, :break_time, :unpaid_leave, :sick_care, :doctor, :paid_obstacle
3181 q.bindValue(":event_type", data.event_type);
3182 q.bindValue(
":description", data.description);
3183 q.bindValue(
":passage", data.passage);
3184 q.bindValue(
":end_state", data.end_state);
3185 q.bindValue(
":arrival", data.arrival);
3186 q.bindValue(
":vacation", data.vacation);
3187 q.bindValue(
":sick_leave", data.sick_leave);
3188 q.bindValue(
":compensatory_leave", data.compensatory_leave);
3189 q.bindValue(
":business_trip", data.business_trip);
3190 q.bindValue(
":break_time", data.break_time);
3191 q.bindValue(
":unpaid_leave", data.unpaid_leave);
3192 q.bindValue(
":sick_care", data.sick_care);
3193 q.bindValue(
":doctor", data.doctor);
3194 q.bindValue(
":paid_obstacle", data.paid_obstacle);
3196 return data.event_type;
3205QVariant DatabasePluginPostgres::save(
const Dbt::Events& data) {
3207 int event = data.event;
3210 q.prepare(R
"'(select 1 from attendance.events where event = :key)'");
3211 q.bindValue(":key", data.event);
3215 update attendance.events set
3217 event_type = :etype,
3218 employee = :employee,
3220 user_edited = :user_edited
3221 where event = :event
3223 q.bindValue(":date", data.date.isValid() ? data.date : QDateTime::currentDateTime());
3224 q.bindValue(
":etype", data.event_type);
3225 q.bindValue(
":employee", data.employee);
3226 q.bindValue(
":valid", data.valid);
3227 q.bindValue(
":user_edited", data.user_edited);
3228 q.bindValue(
":event", data.event);
3234 insert into attendance.events
3235 (date, event_type, employee, valid, user_edited)
3237 (:date, :event_type, :employee, :valid, :user_edited)
3240 q.bindValue(":date", data.date.isValid() ? data.date : QDateTime::currentDateTime());
3241 q.bindValue(
":event_type", data.event_type);
3242 q.bindValue(
":employee", data.employee);
3243 q.bindValue(
":valid", data.valid);
3244 q.bindValue(
":user_edited", data.user_edited);
3246 event = currval(
"attendance.events_event_seq").toInt();
3249 q.prepare(R
"'(delete from attendance.event_notes where event = :key)'");
3250 q.bindValue(":key", event);
3253 if (!data.note.isNull() && data.note !=
"") {
3254 q.prepare(R
"'(insert into attendance.event_notes (event, note) values (:key, :note);)'");
3255 q.bindValue(":key", event);
3256 q.bindValue(
":note", data.note);
3263 return QVariant(event);
3267QVariant DatabasePluginPostgres::save(
const Dbt::Rfids& data) {
3271 PDEBUG << data.rfid << data.employee;
3275 q.prepare(R
"'(select 1 from attendance.rfids where rfid = :key)'");
3276 q.bindValue(":key", data.rfid);
3280 update attendance.rfids set
3286 q.bindValue(":rfid_id", data.rfid_id);
3287 q.bindValue(
":valid", data.valid);
3288 q.bindValue(
":note", data.note);
3289 q.bindValue(
":rfid", data.rfid);
3296 insert into attendance.rfids (rfid_id, valid, note)
3297 values (:rfid_id, :valid, :note)
3299 q.bindValue(":rfid_id", data.rfid_id);
3300 q.bindValue(
":valid", data.valid);
3301 q.bindValue(
":note", data.note);
3303 key = currval(
"attendance.rfids_rfid_seq");
3306 if (data.employee <= 0) {
3307 q.prepare(R
"'(delete from attendance.employee_has_rfid where rfid = :rfid;)'");
3308 q.bindValue(":rfid", key);
3312 if (data.employee > 0) {
3314 insert into attendance.employee_has_rfid (rfid, employee)
3315 values (:rfid, :employee)
3316 on conflict (rfid) do update set employee = excluded.employee
3318 q.bindValue(":rfid", key);
3319 q.bindValue(
":employee", data.employee);
3330QVariant DatabasePluginPostgres::save(
const Dbt::Holidays& data) {
3332 q.prepare(R
"'(select 1 from attendance.holidays where date = :key)'");
3333 q.bindValue(":key", data.date);
3336 q.prepare(R
"'(update attendance.holidays set description = :description where date = :date)'");
3338 q.prepare(R
"'(insert into attendance.holidays (date, description) values (:date, :description))'");
3340 q.bindValue(":date", data.date);
3341 q.bindValue(
":description", data.description);
3347QVariant DatabasePluginPostgres::save(
const Dbt::WorkCalendar& data) {
3349 q.prepare(R
"'(select 1 from attendance.work_calendar where period = :key)'");
3350 q.bindValue(":key", data.period);
3353 q.prepare(R
"'(update attendance.work_calendar set working_days = :working_days, holidays = :holidays, hours8 = :hours8, hours85 = :hours85 where period = :period)'");
3355 q.prepare(R
"'(insert into attendance.work_calendar (period, working_days, holidays, hours8, hours85) values (:period, :working_days, :holidays, :hours8, :hours85))'");
3357 q.bindValue(":period", data.period);
3358 q.bindValue(
":working_days", data.working_days);
3359 q.bindValue(
":holidays", data.holidays);
3360 q.bindValue(
":hours8", data.hours8);
3361 q.bindValue(
":hours85", data.hours85);
3367Dbt::UserEmployeeAccess DatabasePluginPostgres::canAccessAttendance(
int employee) {
3371 select :user::integer as "user",
3372 :employee::integer as "employee"
3375 employee_in_my_departments as (
3376 select distinct mem.employee, true as allowed
3377 from attendance.department_has_manager man,
3378 attendance.department_has_member mem,
3379 attendance.employees e,
3381 where mem.employee = e.employee
3382 and mem.employee = p.employee
3383 and man."user" = p."user"
3384 and man.department = mem.department
3387 employee_in_my_user as (
3388 select distinct e.employee, true as allowed
3389 from attendance.employees e,
3391 where e."user" = p."user"
3392 and e.employee = p.employee
3397 coalesce((select allowed from employee_in_my_departments), false) as can_write,
3398 coalesce((select allowed from employee_in_my_departments), (select allowed from employee_in_my_user), false) as can_read
3402 q.bindValue(":user", userId());
3403 q.bindValue(
":employee", employee);
3405 Dbt::UserEmployeeAccess x;
3407 x.employee = employee;
3409 x.can_write = q.value(2).toBool();
3410 x.can_read = q.value(3).toBool();
3416QList<Dbt::Employees> DatabasePluginPostgres::attendanceChecklist(
const QDate& month) {
3417 QList<Dbt::Employees> list;
3422 :user::integer as "user",
3423 :month::date as month
3428 from attendance.events e
3429 join params p on true
3430 join attendance.event_types et using (event_type)
3432 and e.date >= p.month
3433 and e.date < p.month + '1month'::interval
3434 and not et.passage -- průchody nebrad
3435 and not et.end_state -- ukončení nebrat
3439 employees_accessible as (
3440 select distinct employee
3441 from attendance.department_has_manager man,
3442 attendance.department_has_member mem,
3444 where man."user" = p."user"
3445 and man.department = mem.department
3449 select e.employee, e.firstname, e.surname, e.active, e."user", coalesce(u.login, '') as login,
3450 e.work_hours_mode, e.rounding_interval,
3451 e.saturdays_paid, e.sundays_paid, e.auto_breaks, e.overtime_paid
3452 from employees_list el
3453 join employees_accessible ea using (employee)
3454 left join attendance.employees e using (employee)
3455 left join users u using ("user")
3457 order by e.surname, e.firstname
3459 q.bindValue(":user", userId());
3460 q.bindValue(
":month", month);
3465 x.employee = q.value(i++).toInt();
3466 x.firstname = q.value(i++).toString();
3467 x.surname = q.value(i++).toString();
3468 x.active = q.value(i++).toBool();
3469 x.user = q.value(i++).toInt();
3470 x.login = q.value(i++).toString();
3471 x.work_hours_mode = q.value(i++).toString();
3472 x.rounding_interval = q.value(i++).toString();
3473 x.saturdays_paid = q.value(i++).toBool();
3474 x.sundays_paid = q.value(i++).toBool();
3475 x.auto_breaks = q.value(i++).toBool();
3476 x.overtime_paid = q.value(i++).toBool();
3483QList<Dbt::AttendanceChecklist> DatabasePluginPostgres::attendanceChecklist(
int employee,
const QDate& month) {
3484 QList<Dbt::AttendanceChecklist> list;
3485 Dbt::UserEmployeeAccess access = canAccessAttendance(employee);
3486 if (!access.can_read) {
return list; }
3493 :employee::integer as employee,
3494 :month::timestamp with time zone as month
3497 /* Vybere záznamy z tabulky attendance.events a označí všechny chyby */
3503 t.description as event_description,
3507 case when e.valid and t.end_state and prev.end_state then 'U' else -- unexpected end
3508 case when e.valid and not t.end_state and not next.end_state then 'M' else -- missing end
3509 case when e.valid and not t.end_state and next.end_state is null then 'M' else -- missing end, last record
3511 end end end as error
3513 from attendance.events e
3514 left join attendance.event_types t using (event_type)
3515 join params p on true
3517 /* Předchozí záznam */
3518 left join lateral (select *
3519 from attendance.events ep
3520 left join attendance.event_types tp using (event_type)
3523 and ep.date < e.date
3524 and ep.employee = e.employee
3530 /* Následující záznam */
3531 left join lateral (select *
3532 from attendance.events en
3533 left join attendance.event_types tn using (event_type)
3536 and en.date > e.date
3537 and en.employee = e.employee
3543 where p.employee = e.employee
3544 and e.date > p.month -- Omezení na aktuální měsíc a kousekzení na aktuální měsíc a kousekzení na aktuální měsíc a kousekzení na aktuální měsíc a kousek
3545 and e.date < p.month + '1 month'::interval + '2 days'::interval
3546 and not t.passage -- Omezení na datové typy, které se týkají docházky, tj. typu "Záčátek" a "Konec"
3549 /* Vyhodí záznamy s chybami "nadbytečný odchod" */
3551 select event, date, event_type, false as generated, employee, valid, user_edited, error, n.note as note
3553 left join attendance.event_notes n using (event)
3555 and (error is null or error != 'U')
3559 /* Vybere záznamy s chybou "chybějící odchod", upraví je tak, aby se tvářily jako chybějící záznam */
3560 events_generated_ends as (
3561 select null::integer as event,
3562 case when next.date is null then ef1.date + '8 hours'::interval else next.date - '1 second'::interval end as date,
3567 u."user" as user_edited,
3568 null::text as error,
3571 from events_fixed1 ef1
3572 join (select * from attendance.event_types where end_state limit 1) et on true
3573 join (select * from users where admin order by "user" limit 1) u on true
3575 /* Následující záznam */
3576 left join lateral (select *
3577 from attendance.events ep
3578 left join attendance.event_types tn using (event_type)
3580 and ep.date > ef1.date
3581 and ep.employee = ef1.employee
3592 Vytvoří opravený seznam, výstupem jsou vždy perfektně seřazené dvojice BEGIN - END za sebou.
3596 -- vybere všechny bezchybné záznamy
3597 select * from events_fixed1
3599 -- vybere všechny vygenerované ukončovací záznamy
3600 -- a připojí je k těm bezchybnatým
3601 select * from events_generated_ends
3607 Spojí související dvojice BEGIN - END do jednoho širokého záznamu
3614 e.event as start_event,
3615 e.date as start_date,
3616 e.event_type as start_event_type,
3617 e.note as start_note,
3618 e.error as start_error,
3619 e.user_edited as start_user_edited,
3620 lead(e.event) over w as end_event,
3621 lead(e.date) over w as end_date,
3622 lead(e.event_type) over w as end_event_type,
3623 lead(e.note) over w as end_note,
3624 lead(e.error) over w as end_error,
3625 lead(e.user_edited) over w as end_user_edited,
3626 lead(e.generated) over w as end_generated
3629 window w as (partition by employee order by e.date)
3632 where x.start_event_type != 'END'
3633 and x.start_date < p.month + '1 month'::interval
3638 Doplní k záznamům zaokrouhlení dolů, výsledek dá do soupce "hours"
3642 make_interval(secs =>
3644 extract(epoch from (end_date - start_date)) /
3645 nullif(extract(epoch from emp.rounding_interval), 0)
3646 ) * extract(epoch from emp.rounding_interval)
3648 from events_paired ep
3649 join attendance.employees emp using (employee)
3658 sum(x2.should_be) over (partition by employee order by x2.start_date) as should_be_cumulative
3661 * case when not emp.saturdays_paid and extract(dow from x1.start_date) = 6 then 0 else 1 end
3662 * case when not emp.sundays_paid and extract(dow from x1.start_date) = 0 then 0 else 1 end
3666 null::integer as start_event,
3667 d.day as start_date,
3668 null::text as start_event_type,
3669 null::text as start_note,
3670 null::text as start_error,
3671 null::integer as start_user_edited,
3672 null::integer as end_event,
3673 null::timestamp with time zone as end_date,
3674 null::text as end_event_type,
3675 null::text as end_note,
3676 null::text as end_error,
3677 null::integer as end_user_edited,
3678 false as end_generated,
3679 sum(er.rounded_hours) as rounded_hours,
3680 sum(sum(er.rounded_hours)) over (partition by p.employee order by d.day) as cumulative_hours
3682 cross join generate_series(
3683 date_trunc('month', p.month),
3684 date_trunc('month', p.month) + '1 month'::interval - '1 day'::interval,
3687 left join events_rounded er on (er.employee = p.employee and date_trunc('day', er.start_date) = d.day)
3688 group by p.employee, d.day
3690 join attendance.employees emp using (employee)
3694 events_daily_plus_records as (
3695 select *, null::interval as cumulative_hours, null::interval as should_be, null::interval should_be_cumulative
3700 order by employee, start_date
3703 select employee, start_event, start_date, start_event_type, start_note, start_error, start_user_edited, end_event,
3704 end_date, end_event_type, end_note, end_error, end_user_edited, end_generated,
3705 rounded_hours, cumulative_hours, should_be, should_be_cumulative
3706 into temporary table attendance_checklist
3707 from events_daily_plus_records;
3709 q.bindValue(":employee", employee);
3710 q.bindValue(
":month", month);
3714 select extract('dow' from ac.start_date) as dow, h.date is not null as holiday, h.description as holiday_description,
3715 ac.start_event, ac.start_date, ac.start_event_type, st.description as start_event_type_description, ac.start_note, ac.start_error, ac.start_user_edited, su.name as start_user_edited_name,
3716 ac.end_event, ac.end_date, ac.end_event_type, et.description as end_event_type_description, ac.end_note, ac.end_error, ac.end_user_edited, eu.name as end_user_edited_name, ac.end_generated,
3717 extract(epoch from ac.rounded_hours)/3600.0 as rounded_hours,
3718 extract(epoch from ac.cumulative_hours)/3600.0 as cumulative_hours,
3719 extract(epoch from ac.should_be)/3600.0 as should_be,
3720 extract(epoch from ac.should_be_cumulative)/3600.0 as should_be_cumulative
3721 from attendance_checklist ac
3722 left join users su on (su."user" = ac.start_user_edited)
3723 left join users eu on (eu."user" = ac.end_user_edited)
3724 left join attendance.event_types st on (st.event_type = ac.start_event_type)
3725 left join attendance.event_types et on (et.event_type = ac.end_event_type)
3726 left join attendance.holidays h on (h.date = ac.start_date)
3730 Dbt::AttendanceChecklist checklist;
3731 checklist.month = month;
3732 checklist.can_write = access.can_write;
3735 Dbt::AttendanceDays x;
3736 x.dow = q.value(i++).toInt();
3737 x.holiday = q.value(i++).toBool();
3738 x.holiday_description = q.value(i++).toString();
3740 x.start_event = q.value(i++).toInt();
3741 x.start_date = q.value(i++).toDateTime();
3742 x.start_event_type = q.value(i++).toString();
3743 x.start_event_description = q.value(i++).toString();
3744 x.start_event_note = q.value(i++).toString();
3745 x.start_event_error = q.value(i++).toString();
3746 x.start_user_edited = q.value(i++).toInt();
3747 x.start_user_edited_name = q.value(i++).toString();
3749 x.end_event = q.value(i++).toInt();
3750 x.end_date = q.value(i++).toDateTime();
3751 x.end_event_type = q.value(i++).toString();
3752 x.end_event_description = q.value(i++).toString();
3753 x.end_event_note = q.value(i++).toString();
3754 x.end_event_error = q.value(i++).toString();
3755 x.end_user_edited = q.value(i++).toInt();
3756 x.end_user_edited_name = q.value(i++).toString();
3757 x.end_generated = q.value(i++).toBool();
3758 x.rounded_hours = q.value(i++).toDouble();
3759 x.cumulative_hours = q.value(i++).toDouble();
3760 x.should_be = q.value(i++).toDouble();
3761 x.should_be_cumulative = q.value(i++).toDouble();
3762 checklist.days << x;
3765 QList<Dbt::Employees> ex = employees(employee);
3766 if (!ex.isEmpty()) {
3767 checklist.employee = ex[0];
3773 select ac.*, et.*, extract(epoch from rounded_hours)/3600.0 as hours
3774 from attendance_checklist ac
3775 left join attendance.event_types et on (et.event_type = ac.start_event_type)
3778 select count(1) as days
3779 from ( select date(e.start_date)
3782 group by date(e.start_date)
3783 order by date(e.start_date)
3787 select wc.working_days, wc.holidays, wc.hours8, wc.hours85
3788 from attendance.work_calendar wc
3789 where wc.period = date((select date_trunc('month', start_date) from extracted limit 1))
3794 case when e.start_date < e.end_date and (e.end_date - start_date ) >= '5 hours'::interval
3795 then extract(epoch from make_interval(secs =>
3797 extract(epoch from (e.end_date - e.start_date)) /
3798 nullif(extract(epoch from emp.rounding_interval), 0)
3799 ) * extract(epoch from emp.rounding_interval))) / 3600.0
3802 from (select employee,
3803 greatest (start_date, date_trunc('day', start_date) + '14 hours'::interval) as start_date,
3804 least (end_date, date_trunc('day', start_date) + '22 hours'::interval) as end_date
3808 join attendance.employees emp using (employee)
3812 case when e.start_date < e.end_date and (e.end_date - start_date ) >= '2 hours'::interval
3813 then extract(epoch from make_interval(secs =>
3815 extract(epoch from (e.end_date - e.start_date)) /
3816 nullif(extract(epoch from emp.rounding_interval), 0)
3817 ) * extract(epoch from emp.rounding_interval))) / 3600.0
3820 from (select employee,
3821 greatest (start_date, date_trunc('day', start_date) + '22 hours'::interval) as start_date,
3822 least (end_date, date_trunc('day', start_date) + '30 hours'::interval) as end_date
3826 join attendance.employees emp using (employee)
3830 sum(case when extract('dow' from e.start_date) = 0 and e.start_date < e.end_date and (e.end_date - start_date ) >= '1 hours'::interval
3831 then extract(epoch from make_interval(secs =>
3833 extract(epoch from (e.end_date - e.start_date)) /
3834 nullif(extract(epoch from emp.rounding_interval), 0)
3835 ) * extract(epoch from emp.rounding_interval))) / 3600.0
3838 sum(case when extract('dow' from e.start_date) = 6 and e.start_date < e.end_date and (e.end_date - start_date ) >= '1 hours'::interval
3839 then extract(epoch from make_interval(secs =>
3841 extract(epoch from (e.end_date - e.start_date)) /
3842 nullif(extract(epoch from emp.rounding_interval), 0)
3843 ) * extract(epoch from emp.rounding_interval))) / 3600.0
3846 from (select employee,
3847 greatest(start_date, date_trunc('day', start_date)) as start_date,
3848 least (end_date, date_trunc('day', start_date) + '1day'::interval) as end_date
3852 join attendance.employees emp using (employee)
3857 sum(case when arrival then er.hours else 0 end) as arrival,
3858 sum(case when vacation then er.hours else 0 end) as vacation,
3859 sum(case when sick_leave then er.hours else 0 end) as sick_leave,
3860 sum(case when compensatory_leave then er.hours else 0 end) as compensatory_leave,
3861 sum(case when business_trip then er.hours else 0 end) as business_trip,
3862 sum(case when break_time then er.hours else 0 end) as break_time,
3863 sum(case when unpaid_leave then er.hours else 0 end) as unpaid_leave,
3864 sum(case when sick_care then er.hours else 0 end) as sick_care,
3865 sum(case when paid_obstacle then er.hours else 0 end) as paid_obstacle,
3866 sum(case when doctor then er.hours else 0 end) as doctor,
3867 sum(case when arrival and h.date is not null then 1 else 0 end) as holidays
3869 left join attendance.holidays h on (h.date = date(er.start_date))
3873 c.working_days as calendar_working_days,
3874 c.holidays as calendar_holidays,
3875 -- c.hours8 as calendar_hours8,
3876 -- c.hours85 as calendar_hours85,
3884 s.compensatory_leave,
3892 from summaries s, day_counts d, calendar c, afternoons a, nights n, weekdays w
3897 Dbt::AttendanceSummary& x = checklist.summary_calculated;
3898 x.days = q.value(i++).toInt();
3899 x.calendar_working_days = q.value(i++).toInt();
3900 x.calendar_holidays = q.value(i++).toInt();
3901 x.afternoon = q.value(i++).toDouble();
3902 x.night = q.value(i++).toDouble();
3903 x.sunday = q.value(i++).toDouble();
3904 x.saturday = q.value(i++).toDouble();
3905 x.arrival = q.value(i++).toDouble();
3906 x.vacation = q.value(i++).toDouble();
3907 x.sick_leave = q.value(i++).toDouble();
3908 x.compensatory_leave = q.value(i++).toDouble();
3909 x.business_trip = q.value(i++).toDouble();
3910 x.break_time = q.value(i++).toDouble();
3911 x.unpaid_leave = q.value(i++).toDouble();
3912 x.sick_care = q.value(i++).toDouble();
3913 x.paid_obstacle = q.value(i++).toDouble();
3914 x.doctor = q.value(i++).toDouble();
3917 const QList<Dbt::AttendanceSummary> summaryList = attendanceSummary(employee, month);
3918 if (!summaryList.isEmpty()) {
3919 checklist.summary_saved = summaryList.first();
3929QList<Dbt::AttendanceSummary> DatabasePluginPostgres::attendanceSummary(
int employee,
const QDate& month) {
3930 QList<Dbt::AttendanceSummary> list;
3932 QString sql = QStringLiteral(R
"'(
3945 s.compensatory_leave,
3959 from attendance.summary s
3960 left join users u on u."user" = s.locked_user
3961 left join attendance.employees e using (employee)
3962 left join attendance.work_calendar wc on wc.period = s.month
3963 where (s.employee = :key1 or :key2 <= 0)
3965 if (month.isValid()) {
3966 sql += QStringLiteral(
" and s.month = :month");
3968 sql += QStringLiteral(
" order by s.month, e.surname, e.firstname;");
3970 q.bindValue(
":key1", employee);
3971 q.bindValue(
":key2", employee);
3972 if (month.isValid()) {
3973 q.bindValue(
":month", month);
3978 Dbt::AttendanceSummary x;
3979 x.month = q.value(i++).toDate();
3980 x.employee = q.value(i++).toInt();
3981 x.firstname = q.value(i++).toString();
3982 x.surname = q.value(i++).toString();
3983 x.days = q.value(i++).toInt();
3984 x.locked = q.value(i++).toBool();
3985 x.locked_user = q.value(i++).toInt();
3986 x.locked_user_name = q.value(i++).toString();
3987 x.arrival = q.value(i++).toDouble();
3988 x.vacation = q.value(i++).toDouble();
3989 x.sick_leave = q.value(i++).toDouble();
3990 x.compensatory_leave = q.value(i++).toDouble();
3991 x.business_trip = q.value(i++).toDouble();
3992 x.break_time = q.value(i++).toDouble();
3993 x.unpaid_leave = q.value(i++).toDouble();
3994 x.sick_care = q.value(i++).toDouble();
3995 x.paid_obstacle = q.value(i++).toDouble();
3996 x.doctor = q.value(i++).toDouble();
3997 x.afternoon = q.value(i++).toDouble();
3998 x.night = q.value(i++).toDouble();
3999 x.sunday = q.value(i++).toDouble();
4000 x.saturday = q.value(i++).toDouble();
4001 x.holiday = q.value(i++).toDouble();
4002 x.calendar_working_days = q.value(i++).toInt();
4003 x.calendar_holidays = q.value(i++).toInt();
4009QVariant DatabasePluginPostgres::save(
const Dbt::AttendanceSummary& data) {
4010 PDEBUG << data.employee << data.month;
4012 q.prepare(R
"(select 1 from attendance.summary where employee = :employee and month = :month)");
4013 q.bindValue(":employee", data.employee);
4014 q.bindValue(
":month", data.month);
4018 update attendance.summary set
4021 vacation = :vacation,
4022 sick_leave = :sick_leave,
4023 compensatory_leave = :compensatory_leave,
4024 business_trip = :business_trip,
4025 break_time = :break_time,
4026 unpaid_leave = :unpaid_leave,
4027 sick_care = :sick_care,
4028 paid_obstacle = :paid_obstacle,
4030 afternoon = :afternoon,
4033 saturday = :saturday,
4036 locked_user = :locked_user
4037 where employee = :employee and month = :month
4041 insert into attendance.summary (
4042 employee, month, days, work, vacation, sick_leave,
4043 compensatory_leave, business_trip, break_time, unpaid_leave,
4044 sick_care, paid_obstacle, doctor, afternoon, night,
4045 sunday, saturday, holiday, locked, locked_user
4047 :employee, :month, :days, :arrival, :vacation, :sick_leave,
4048 :compensatory_leave, :business_trip, :break_time, :unpaid_leave,
4049 :sick_care, :paid_obstacle, :doctor, :afternoon, :night,
4050 :sunday, :saturday, :holiday, :locked, :locked_user
4054 q.bindValue(":days", data.days);
4055 q.bindValue(
":arrival", data.arrival);
4056 q.bindValue(
":vacation", data.vacation);
4057 q.bindValue(
":sick_leave", data.sick_leave);
4058 q.bindValue(
":compensatory_leave", data.compensatory_leave);
4059 q.bindValue(
":business_trip", data.business_trip);
4060 q.bindValue(
":break_time", data.break_time);
4061 q.bindValue(
":unpaid_leave", data.unpaid_leave);
4062 q.bindValue(
":sick_care", data.sick_care);
4063 q.bindValue(
":paid_obstacle", data.paid_obstacle);
4064 q.bindValue(
":doctor", data.doctor);
4065 q.bindValue(
":afternoon", data.afternoon);
4066 q.bindValue(
":night", data.night);
4067 q.bindValue(
":sunday", data.sunday);
4068 q.bindValue(
":saturday", data.saturday);
4069 q.bindValue(
":holiday", data.holiday);
4070 q.bindValue(
":locked", data.locked);
4071 q.bindValue(
":locked_user", data.locked_user == 0 ? QVariant() : data.locked_user);
4072 q.bindValue(
":employee", data.employee);
4073 q.bindValue(
":month", data.month);
4078QList<Dbt::AttendancePresent> DatabasePluginPostgres::attendancePresent() {
4079 QList<Dbt::AttendancePresent> list;
4082 select p.employee, p.firstname, p.surname, p.active, p."user",
4083 coalesce(u.login, '') as login,
4084 p.work_hours_mode, p.rounding_interval,
4085 p.saturdays_paid, p.sundays_paid, p.auto_breaks, p.overtime_paid,
4086 p.date, p.event_type, p.present
4087 from attendance.present p
4088 left join users u on u."user" = p."user"
4089 order by p.surname, p.firstname
4094 Dbt::AttendancePresent x;
4095 x.employee.employee = q.value(i++).toInt();
4096 x.employee.firstname = q.value(i++).toString();
4097 x.employee.surname = q.value(i++).toString();
4098 x.employee.active = q.value(i++).toBool();
4099 x.employee.user = q.value(i++).toInt();
4100 x.employee.login = q.value(i++).toString();
4101 x.employee.work_hours_mode = q.value(i++).toString();
4102 x.employee.rounding_interval = q.value(i++).toString();
4103 x.employee.saturdays_paid = q.value(i++).toBool();
4104 x.employee.sundays_paid = q.value(i++).toBool();
4105 x.employee.auto_breaks = q.value(i++).toBool();
4106 x.employee.overtime_paid = q.value(i++).toBool();
4107 x.date = q.value(i++).toDateTime();
4108 x.event_type = q.value(i++).toString();
4109 x.present = q.value(i++).toBool();
4115QList<Dbt::AttendanceRecent> DatabasePluginPostgres::attendanceRecent(
int employee) {
4116 QList<Dbt::AttendanceRecent> list;
4117 Dbt::UserEmployeeAccess access = canAccessAttendance(employee);
4118 if (!access.can_read) {
return list; }
4124 :employee::integer as employee
4127 /* Vybere záznamy z tabulky attendance.events a označí všechny chyby */
4133 t.description as event_description,
4137 case when e.valid and t.end_state and prev.end_state then 'U' else -- unexpected end
4138 case when e.valid and not t.end_state and not next.end_state then 'M' else -- missing end
4139 case when e.valid and not t.end_state and next.end_state is null then 'M' else -- missing end, last record
4141 end end end as error
4143 from attendance.events e
4144 left join attendance.event_types t using (event_type)
4147 /* Předchozí záznam */
4148 left join lateral (select *
4149 from attendance.events ep
4150 left join attendance.event_types tp using (event_type)
4153 and ep.date < e.date
4154 and ep.employee = e.employee
4160 /* Následující záznam */
4161 left join lateral (select *
4162 from attendance.events en
4163 left join attendance.event_types tn using (event_type)
4166 and en.date > e.date
4167 and en.employee = e.employee
4173 where p.employee = e.employee
4174 and e.date > now() - '1month'::interval
4175 and not t.passage -- Omezení na datové typy, které se týkají docházky, tj. typu "Záčátek" a "Konec"
4178 /* Vyhodí záznamy s chybami "nadbytečný odchod" */
4180 select event, date, event_type, false as generated, employee, valid, user_edited, error, n.note as note
4182 left join attendance.event_notes n using (event)
4184 and (error is null or error != 'U')
4188 /* Vybere záznamy s chybou "chybějící odchod", upraví je tak, aby se tvářily jako chybějící záznam */
4189 events_generated_ends as (
4190 select null::integer as event,
4191 case when next.date is null then ef1.date + '8 hours'::interval else next.date - '1 second'::interval end as date,
4196 u."user" as user_edited,
4197 null::text as error,
4200 from events_fixed1 ef1
4201 join (select * from attendance.event_types where end_state limit 1) et on true
4202 join (select * from users where admin order by "user" limit 1) u on true
4204 /* Následující záznam */
4205 left join lateral (select *
4206 from attendance.events ep
4207 left join attendance.event_types tn using (event_type)
4209 and ep.date > ef1.date
4210 and ep.employee = ef1.employee
4221 Vytvoří opravený seznam, výstupem jsou vždy perfektně seřazené dvojice BEGIN - END za sebou.
4225 -- vybere všechny bezchybné záznamy
4226 select * from events_fixed1
4228 -- vybere všechny vygenerované ukončovací záznamy
4229 -- a připojí je k těm bezchybnatým
4230 select * from events_generated_ends
4236 Spojí související dvojice BEGIN - END do jednoho širokého záznamu
4243 e.event as start_event,
4244 e.date as start_date,
4245 e.event_type as start_event_type,
4246 e.note as start_note,
4247 e.error as start_error,
4248 e.user_edited as start_user_edited,
4249 lead(e.event) over w as end_event,
4250 lead(e.date) over w as end_date,
4251 lead(e.event_type) over w as end_event_type,
4252 lead(e.note) over w as end_note,
4253 lead(e.error) over w as end_error,
4254 lead(e.user_edited) over w as end_user_edited,
4255 lead(e.generated) over w as end_generated
4258 window w as (partition by employee order by e.date)
4261 where x.start_event_type != 'END'
4272 select x.employee, x.event, x.date, x.event_type, x.note, null, null, null, null, null, null, null, null, null
4274 select distinct on (e.employee) e.*, n.note
4275 from attendance.events e
4277 left join attendance.event_types t using (event_type)
4278 left join attendance.event_notes n using (event)
4279 where p.employee = e.employee
4281 and (e.date > (select max(start_date) from events_paired)
4282 or e.date > (select max(end_date) from events_paired where not end_generated))
4283 order by e.employee, e.date desc
4286 q.bindValue(":employee", employee);
4289 Dbt::AttendanceRecent x;
4291 x.employee = q.value(i++).toInt();
4292 x.start_event = q.value(i++).toInt();
4293 x.start_date = q.value(i++).toDateTime();
4294 x.start_event_type = q.value(i++).toString();
4295 x.start_note = q.value(i++).toString();
4296 x.start_error = q.value(i++).toString();
4297 x.start_user_edited = q.value(i++).toInt();
4298 x.end_event = q.value(i++).toInt();
4299 x.end_date = q.value(i++).toDateTime();
4300 x.end_event_type = q.value(i++).toString();
4301 x.end_note = q.value(i++).toString();
4302 x.end_error = q.value(i++).toString();
4303 x.end_user_edited = q.value(i++).toInt();
4304 x.end_generated = q.value(i++).toBool();
static QVariant data(const QByteArray &json)
Converts json to data.
static QByteArray json(const QVariant &data)
Converts data to json.
QVariantList categories
categories which are appended with new statuse
QString description
new status description
QVariantList recent_status
recent statuses