13#include <QCryptographicHash>
15#include <QRandomGenerator>
17using namespace Db::Plugins;
19namespace Db::Plugins {
20 QString null(
const QString& x) {
21 return (x ==
"0") ?
"" : x;
25bool DatabasePluginPostgres::m_upgraded =
false;
28DatabasePluginPostgres::~DatabasePluginPostgres() {
33DatabasePluginPostgres::DatabasePluginPostgres(QObject *parent) : Db::Plugins::
DatabasePlugin(parent) {
34 Q_ASSERT(parent != NULL);
35 setObjectName(
"DatabasePluginPostgres");
36 m_temporaryTableTicketsCreated =
false;
40bool DatabasePluginPostgres::open() {
41 m_db = QSqlDatabase::addDatabase(
"QPSQL", QUuid::createUuid().toString().toUtf8());
43 m_db.setDatabaseName ( m_databasename );
44 m_db.setHostName ( m_hostname );
45 m_db.setPort ( m_port );
46 m_db.setUserName ( m_username );
47 m_db.setPassword ( m_password );
50 PDEBUG <<
"Cannot connect to database Postgres";
51 PDEBUG << m_db.lastError().text();
61void DatabasePluginPostgres::upgrade() {
62 if (m_upgraded) {
return; }
66 q.exec(
"select version from version;");
67 int version = (q.next()) ? q.value(0).toInt() : -1;
70 QString patchname = QString(
":/postgres/patch.%1.sql").arg(version, 3, 10, QChar(
'0'));
71 QFile file(patchname);
72 if (!file.open(QIODevice::ReadOnly)) {
75 PDEBUG <<
"aplying db patch " << patchname;
77 while (!file.atEnd()) {
81 line = file.readLine();
83 }
while (!line.contains(
";") && !file.atEnd());
84 command = command.trimmed();
85 if (command.isEmpty()) {
continue; }
87 if (!q.exec(QString::fromUtf8(command))) {
88 QSqlError e = q.lastError();
89 if (e.type() != QSqlError::NoError) {
90 PDEBUG << q.lastQuery();
91 PDEBUG << e.databaseText();
92 PDEBUG << e.driverText();
98 q.exec(QString(
"update version set version = %1;").arg(version));
105bool DatabasePluginPostgres::close() {
111void DatabasePluginPostgres::begin() {
117void DatabasePluginPostgres::commit() {
123void DatabasePluginPostgres::changePassword(
const QString& login,
const QString& oldpassword,
const QString& newpassword) {
125 QString md5new = QString::fromUtf8(QCryptographicHash::hash(newpassword.toUtf8(), QCryptographicHash::Md5).toHex());
126 QString md5old = QString::fromUtf8(QCryptographicHash::hash(oldpassword.toUtf8(), QCryptographicHash::Md5).toHex());
127 QList<Dbt::Users> list;
129 q.prepare(R
"'(select true from users where "user" = :userid and admin)'");
130 q.bindValue(":userid", userId());
132 bool admin = q.next();
137 q.prepare(
"update users set password = :newpassword where login = :login;");
140 PDEBUG <<
"not admin";
141 q.prepare(
"update users set password = :newpassword where login = :login and password = :oldpassword and enabled;");
143 q.bindValue(
":userid", userId());
144 q.bindValue(
":login", login);
145 q.bindValue(
":newpassword", md5new);
146 q.bindValue(
":oldpassword", md5old);
152QList<Dbt::Users> DatabasePluginPostgres::authenticate(
const QString& login,
const QString& password) {
153 QString md5 = QString::fromUtf8(QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex());
154 QList<Dbt::Users> list;
156 q.prepare(
"select \"user\", login, name, lang, enabled, admin from users where login = :login and password = :password and enabled;");
157 q.bindValue(
":login", login);
158 q.bindValue(
":password", md5);
163 x.user = q.value(i++).toInt();
164 x.login = q.value(i++).toString();
165 x.name = q.value(i++).toString();
166 x.lang = q.value(i++).toString();
167 x.enabled = q.value(i++).toBool();
168 x.admin = q.value(i++).toBool();
176QList<Dbt::Users> DatabasePluginPostgres::users(
int id) {
177 QList<Dbt::Users> list;
180 select "user", login, name, lang, enabled, admin
182 where (:id1 <= 0 or :id2 = "user");
184 q.bindValue(":id1",
id);
185 q.bindValue(
":id2",
id);
190 x.user = q.value(i++).toInt();
191 x.login = q.value(i++).toString();
192 x.name = q.value(i++).toString();
193 x.lang = q.value(i++).toString();
194 x.enabled = q.value(i++).toBool();
195 x.admin = q.value(i++).toBool();
205 q.prepare(R
"'(delete from categories where category = :id;)'");
206 q.bindValue(":id",
id.category);
211QVariant DatabasePluginPostgres::currval(
const QString& sequence) {
214 q.prepare(R
"'(select currval(:sequence);)'");
215 q.bindValue(":sequence", sequence);
227 QVariant parent_category = (data.parent_category.toInt() > 0)
228 ? data.parent_category.toInt()
231 QVariant category = (data.category.toInt() > 0)
232 ? data.category.toInt()
233 : QVariant(QVariant::Int);
235 if (parent_category.toInt() == category.toInt()) {
236 parent_category = QVariant();
243 q.prepare(R
"'(select 1 from categories where category = :category;)'");
244 q.bindValue(":category", data.category.toInt());
246 bool exists = q.next();
247 if (exists && ! parent_category.isNull()) {
249 update categories set
255 q.bindValue(0, parent_category);
256 q.bindValue(1, data.description);
257 q.bindValue(2, data.price);
258 q.bindValue(3, category.toInt());
262 if (exists && parent_category.isNull()) {
264 update categories set
269 q.bindValue(0, data.description);
270 q.bindValue(1, data.price);
271 q.bindValue(2, category.toInt());
277 insert into categories (parent_category, description, price) values (?, ?, ?);
279 q.bindValue(0, parent_category);
280 q.bindValue(1, data.description);
281 q.bindValue(2, data.price);
283 category = currval("categories_category_seq");
286 insert into users_categories ("user", category) values (?, ?) on conflict do nothing;
288 q.bindValue(0, userId());
289 q.bindValue(1, category);
295 if (!data.users.isEmpty()) {
296 q.prepare(R
"'(delete from users_categories where category = ?;)'");
297 q.bindValue(0, category);
301 for (
int i=0; i<data.users.size(); i++) {
303 insert into users_categories ("user", category) values (?, ?) on conflict do nothing;
305 q.bindValue(0, data.users[i].toInt());
306 q.bindValue(1, category);
316QList<Dbt::ClientSettings> DatabasePluginPostgres::clientSettings() {
317 QList<Dbt::ClientSettings> list;
321 select multiple_timesheets, show_price, can_change_category, edit_categories,
322 show_multiple_timesheets, show_show_price, show_can_change_category, show_edit_categories
323 from client_settings;
329 x.multiple_timesheets = q.value(i++).toBool();
330 x.show_price = q.value(i++).toBool();
331 x.can_change_category = q.value(i++).toBool();
332 x.edit_categories = q.value(i++).toBool();
333 x.show_multiple_timesheets = q.value(i++).toBool();
334 x.show_show_price = q.value(i++).toBool();
335 x.show_can_change_category = q.value(i++).toBool();
336 x.show_edit_categories = q.value(i++).toBool();
343QList<Dbt::ServerInfo> DatabasePluginPostgres::serverInfo() {
344 QList<Dbt::ServerInfo> list;
347 select name, description from server_info;
353 x.name = q.value(i++).toString();
354 x.description = q.value(i++).toString();
365 update server_info set
367 description = :description
370 q.bindValue(":name", data.name);
371 q.bindValue(
":description", data.description);
378QVariantList pgArrayToVariantList(
const QVariant& input) {
379 QStringList x = input.toString().replace(
"{",
"").replace(
"}",
"").split(
",");
381 for (
int i=0; i<x.size(); i++) {
388QList<Dbt::Categories> DatabasePluginPostgres::categories(
const QString&
id) {
389 QList<Dbt::Categories> list;
393 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
394 from categories c, users u
395 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
396 where (:id1 <= 0 or :id2 = c.category)
397 and u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
400 q.bindValue(":user", userId());
401 q.bindValue(
":id1",
id.toInt());
402 q.bindValue(
":id2",
id.toInt());
407 x.category = q.value(i++).toString();
408 x.parent_category = null(q.value(i++).toString());
409 x.description = q.value(i++).toString();
410 x.price = q.value(i++).toDouble();
411 x.users = pgArrayToVariantList(q.value(i++));
412 x.description_tree = q.value(i++).toString();
420QList<Dbt::Categories> DatabasePluginPostgres::categoriesToRoot(
const QString&
id) {
421 QList<Dbt::Categories> list;
423 int xid =
id.toInt();
426 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
427 from categories c, users u
428 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
429 where :id = c.category
430 and u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
434 q.bindValue(
":user", userId());
435 q.bindValue(
":id", xid);
437 bool found = q.next();
438 PDEBUG << xid << found;
439 if (!found) {
return list; }
442 x.category = q.value(i++).toString();
443 x.parent_category = null(q.value(i++).toString());
444 x.description = q.value(i++).toString();
445 x.price = q.value(i++).toDouble();
446 x.users = pgArrayToVariantList(q.value(i++));
447 x.description_tree = q.value(i++).toString();
449 xid = x.parent_category.toInt();
456QList<Dbt::Categories> DatabasePluginPostgres::subcategories(
const QString&
id) {
457 QList<Dbt::Categories> list;
461 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
462 from categories c, users u
463 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
464 where ((:id1 <= 0 and c.parent_category is null) or :id2 = c.parent_category)
465 and u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
468 q.bindValue(":user", userId());
469 q.bindValue(
":id1",
id.toInt());
470 q.bindValue(
":id2",
id.toInt());
475 x.category = q.value(i++).toString();
476 x.parent_category = null(q.value(i++).toString());
477 x.description = q.value(i++).toString();
478 x.price = q.value(i++).toDouble();
479 x.users = pgArrayToVariantList(q.value(i++));
480 x.description_tree = q.value(i++).toString();
488QList<Dbt::Categories> DatabasePluginPostgres::siblingcategories(
const QString&
id) {
489 QList<Dbt::Categories> list;
493 with recursive tree as (
494 select category, parent_category
495 from categories where category = :id
497 select c.category, c.parent_category
498 from tree t, categories c
500 t.category = c.parent_category
502 select c.category, c.parent_category, c.description, c.price, ux.users, categories_tree_description(c.category) as description_tree
503 from categories c, users u
504 left join lateral (select array_agg("user") as users from users_categories where category = c.category) ux on (true)
505 where u."user" = :user and (u.admin or u."user" in (select "user" from users_categories uc where uc.category = c.category))
506 and c.category not in (select category from tree)
509 q.bindValue(":user", userId());
510 q.bindValue(
":id",
id.toInt());
515 x.category = q.value(i++).toString();
516 x.parent_category = null(q.value(i++).toString());
517 x.description = q.value(i++).toString();
518 x.price = q.value(i++).toDouble();
519 x.users = pgArrayToVariantList(q.value(i++));
520 x.description_tree = q.value(i++).toString();
528void DatabasePluginPostgres::remove(
const Dbt::Users&
id) {
531 q.prepare(R
"'(select 1 from users where "user" = :id and admin and enabled;)'");
532 q.bindValue(":id", userId());
534 if (!q.next()) {
return; }
536 q.prepare(R
"'(delete from users where "user" = :id;)'");
537 q.bindValue(":id",
id.user);
542QVariant DatabasePluginPostgres::save(
const Dbt::Users& data) {
545 q.prepare(R
"'(select 1 from users where "user" = :id and admin and enabled;)'");
546 q.bindValue(":id", userId());
548 if (!q.next()) {
return QVariant(); }
551 q.prepare(R
"'(select 1 from users where "user" = :id;)'");
552 q.bindValue(":id", data.user);
564 q.bindValue(":id1", data.user);
565 q.bindValue(
":login", data.login);
566 q.bindValue(
":name", data.name);
567 q.bindValue(
":lang", data.lang);
568 q.bindValue(
":enabled", data.enabled);
569 q.bindValue(
":admin", data.admin);
571 return QVariant(data.user);
575 insert into users (login, name, lang, enabled, admin)
576 select :login, :name, :lang, :enabled, :admin
577 where not exists (select 1 from users where "user" = :id1);
579 q.bindValue(":id1", data.user);
580 q.bindValue(
":login", data.login);
581 q.bindValue(
":name", data.name);
582 q.bindValue(
":lang", data.lang);
583 q.bindValue(
":enabled", data.enabled);
584 q.bindValue(
":admin", data.admin);
587 return currval(
"users_user_seq");
595QList<Dbt::StatusOrder> DatabasePluginPostgres::statusOrder(
const QString&
id) {
596 QList<Dbt::StatusOrder> list;
600 select id, category, previous_status, next_status from status_order
601 where (:id1 <= 0 or :id2 = id);
603 q.bindValue(":id1",
id.toInt());
604 q.bindValue(
":id2",
id.toInt());
609 x.id = q.value(i++).toInt();
610 x.category = q.value(i++);
611 x.previous_status = q.value(i++);
612 x.next_status = q.value(i++);
623 q.prepare(R
"'(delete from status_order where id = :id;)'");
624 q.bindValue(":id",
id.
id);
632 q.prepare(R
"'(select 1 from status_order where id = :id;)'");
633 q.bindValue(":id", data.id);
637 update status_order set
638 category = :category,
639 previous_status = :previous_status,
640 next_status = :next_status
643 q.bindValue(":category", data.category);
644 q.bindValue(
":previous_status", data.previous_status);
645 q.bindValue(
":next_status", data.next_status);
646 q.bindValue(
":id", data.id);
648 return QVariant(data.id);
653 insert into status_order (category, previous_status, next_status)
654 select :category, :previous_status, :next_status
656 q.bindValue(":category", data.category);
657 q.bindValue(
":previous_status", data.previous_status);
658 q.bindValue(
":next_status", data.next_status);
659 q.bindValue(
":id", data.id);
661 return currval(
"status_order_id_seq");
669void DatabasePluginPostgres::createTemporaryTableTickets(
int ticket,
bool all) {
670 if (m_temporaryTableTicketsCreated) {
return; }
671 m_temporaryTableTicketsCreated =
true;
674 create temporary table temporary_tickets (
677 date timestamp with time zone,
685 PDEBUG <<
"Vybiram VSE" << ticket;
687 insert into temporary_tickets (ticket, category, date, price, description, "user")
688 select t.ticket, t.category, t.date, t.price, t.description, t."user"
689 from tickets t, users u
690 where (t.category in (select category from users_categories where "user" = u."user") or u.admin = true)
692 and (:ticket1 <= 0 or :ticket2 = t.ticket)
696 PDEBUG <<
"Vybiram pouze otevrene" << ticket;
700 select status from statuses where closed
702 ticket_last_status as (
703 select t.ticket, tl.status
705 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)
708 select distinct ts.ticket from ticket_last_status ts, ending_status es where ts.status = es.status
711 select t1.ticket from tickets t1 where t1.ticket not in (select ticket from closed_tickets)
714 insert into temporary_tickets (ticket, category, date, price, description, "user")
715 select t.ticket, t.category, t.date, t.price, t.description, t."user"
716 from tickets t, users_categories uc
717 where t.ticket in (select ticket from active_tickets)
718 and uc."user" = :user
719 and t.category = uc.category
720 and (:ticket1 <= 0 or :ticket2 = t.ticket)
724 q.bindValue(":user", userId());
725 q.bindValue(
":ticket1", ticket);
726 q.bindValue(
":ticket2", ticket);
729 q.exec(
"select count(1) from temporary_tickets;");
731 PDEBUG <<
"Vybranych vet" << q.value(0).toInt();
736QList<Dbt::Tickets> DatabasePluginPostgres::tickets(
bool all) {
737 return tickets(-1, all);
741QList<Dbt::Tickets> DatabasePluginPostgres::tickets(
int ticket,
bool all) {
742 createTemporaryTableTickets(ticket, all);
743 QList<Dbt::Tickets> list;
747 select ticket, category, date, price, description, "user"
748 from temporary_tickets ;
754 x.ticket = q.value(i++);
755 x.category = q.value(i++);
756 x.date = q.value(i++).toDateTime();
757 x.price = q.value(i++).toDouble();
758 x.description = q.value(i++).toString();
759 x.user = q.value(i++).toInt();
766QList<Dbt::Tickets> DatabasePluginPostgres::tickets(
const Dbt::Categories& category,
bool all) {
767 createTemporaryTableTickets(-1, all);
768 QList<Dbt::Tickets> list;
772 select ticket, category, date, price, description, "user"
773 from temporary_tickets
774 where category = :category;
776 q.bindValue(":category", category.category);
781 x.ticket = q.value(i++);
782 x.category = q.value(i++);
783 x.date = q.value(i++).toDateTime();
784 x.price = q.value(i++).toDouble();
785 x.description = q.value(i++).toString();
786 x.user = q.value(i++).toInt();
794QList<Dbt::TicketsVw> DatabasePluginPostgres::ticketsVw(
bool all) {
795 return ticketsVw(-1, all);
799QList<Dbt::TicketsVw> DatabasePluginPostgres::ticketsVw(
int ticket,
bool all) {
800 QList<Dbt::Tickets> list1 = tickets(ticket,all);
801 QList<Dbt::TicketsVw> list;
803 for (
int i=0; i<list1.size(); i++) {
805 x.timesheets = ticketTimesheets(list1[i].ticket.toInt(), all);
806 x.statuses = ticketStatus(list1[i].ticket.toInt(), all);
807 x.values = ticketValues(list1[i].ticket.toInt(), all);
808 x.files = ticketFiles(list1[i].ticket.toInt(), all);
816QList<Dbt::TicketsVw> DatabasePluginPostgres::ticketsVw(
const Dbt::Categories& category,
bool all) {
817 QList<Dbt::Tickets> list1 = tickets(category, all);
818 QList<Dbt::TicketsVw> list;
820 for (
int i=0; i<list1.size(); i++) {
822 x.timesheets = ticketTimesheets(list1[i].ticket.toInt(),
true);
823 x.statuses = ticketStatus(list1[i].ticket.toInt(),
true);
824 x.values = ticketValues(list1[i].ticket.toInt(),
true);
825 x.files = ticketFiles(list1[i].ticket.toInt(),
true);
833void DatabasePluginPostgres::remove(
const Dbt::Tickets&
id) {
835 q.prepare(R
"'(delete from tickets where ticket = :id;)'");
836 q.bindValue(":id",
id.ticket);
841QVariant DatabasePluginPostgres::save(
const Dbt::Tickets& data) {
846 q.prepare(R
"'(select 1 from tickets where ticket = :ticket;)'");
847 q.bindValue(":ticket", data.ticket);
852 if (!data.created && found) {
855 category = :category,
858 description = :description,
860 where ticket = :ticket
862 q.bindValue(":category", data.category);
863 q.bindValue(
":date", data.date);
864 q.bindValue(
":price", data.price);
865 q.bindValue(
":description", data.description);
866 q.bindValue(
":user", data.user);
867 q.bindValue(
":ticket", data.ticket);
869 return QVariant(data.ticket);
872 if (data.created || !found) {
874 insert into tickets (category, date, price, description, "user")
875 values (:category, :date, :price, :description, :user);
877 q.bindValue(":category", data.category);
878 q.bindValue(
":date", data.date);
879 q.bindValue(
":price", data.price);
880 q.bindValue(
":description", data.description);
881 q.bindValue(
":user", data.user);
883 return currval(
"tickets_ticket_seq");
887 qFatal(
"Should not happen");
894QList<T> remapTicket(
const QList<T>& input,
int ticket) {
896 QListIterator<T> iterator(input);
897 while (iterator.hasNext()) {
898 T x = iterator.next();
900 if (ticket <= 0) { x.id = 0; }
907QVariant DatabasePluginPostgres::save(
const Dbt::TicketsVw& data) {
910 int ticket = save(
dynamic_cast<const Dbt::Tickets&
>(data)).toInt();
911 removeTicketsDetails(ticket);
912 save(remapTicket(data.timesheets, ticket));
913 save(remapTicket(data.statuses, ticket));
914 save(remapTicket(data.values, ticket));
915 save(remapTicket(data.files, ticket));
922void DatabasePluginPostgres::removeTicketsDetails(
int ticket) {
924 q.exec(QString(R
"'(delete from ticket_timesheets where ticket = %1;)'").arg(ticket));
925 q.exec(QString(R"'(delete from ticket_status where ticket = %1;)'").arg(ticket));
926 q.exec(QString(R"'(delete from ticket_values where ticket = %1;)'").arg(ticket));
927 q.exec(QString(R"'(delete from ticket_files where ticket = %1;)'").arg(ticket));
931QList<Dbt::TicketStatus> DatabasePluginPostgres::ticketStatus(int ticket,
bool all) {
932 createTemporaryTableTickets(ticket, all);
933 QList<Dbt::TicketStatus> list;
937 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
938 from temporary_tickets t, ticket_status ts, statuses s
939 where t.ticket = ts.ticket
940 and ts.status = s.status
941 and t.ticket = :ticket
943 q.bindValue(":ticket", ticket);
948 x.id = q.value(i++).toInt();
949 x.ticket = q.value(i++).toInt();
950 x.user = q.value(i++).toInt();
951 x.date = q.value(i++).toDateTime();
952 x.description = q.value(i++).toString();
953 x.status = q.value(i++).toString();
954 x.status_color = q.value(i++).toString();
955 x.status_description = q.value(i++).toString();
956 x.status_closed = q.value(i++).toBool();
957 x.status_can_be_run = q.value(i++).toBool();
958 x.status_ignored = q.value(i++).toBool();
965QList<Dbt::TicketStatus> DatabasePluginPostgres::ticketStatus(
bool all) {
966 return ticketStatus(-1, all);
970QList<Dbt::TicketStatus> DatabasePluginPostgres::ticketStatus(
int id) {
971 QList<Dbt::TicketStatus> list;
975 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
976 from ticket_status ts, users u, tickets t, users_categories uc, statuses s
978 and t.ticket = ts.ticket
979 and t.category = uc.category
980 and u."user" = ts."user"
981 and ts.status = s.status
983 q.bindValue(":id",
id);
984 q.bindValue(
":user", userId());
989 x.id = q.value(i++).toInt();
990 x.ticket = q.value(i++).toInt();
991 x.user = q.value(i++).toInt();
992 x.date = q.value(i++).toDateTime();
993 x.description = q.value(i++).toString();
994 x.status = q.value(i++).toString();
995 x.status_color = q.value(i++).toString();
996 x.status_description = q.value(i++).toString();
997 x.status_closed = q.value(i++).toBool();
998 x.status_can_be_run = q.value(i++).toBool();
999 x.status_ignored = q.value(i++).toBool();
1009 q.prepare(R
"'(delete from ticket_status where id = :id;)'");
1010 q.bindValue(":id",
id.
id);
1019 if (!data.created) {
1020 q.prepare(R
"'(select 1 from ticket_status where id = :id;)'");
1021 q.bindValue(":id", data.id);
1026 if (!data.created && found) {
1028 update ticket_status set
1032 description = :description,
1036 q.bindValue(":id", data.id);
1037 q.bindValue(
":user", data.user);
1038 q.bindValue(
":ticket", data.ticket);
1039 q.bindValue(
":date", data.date);
1040 q.bindValue(
":description", data.description);
1041 q.bindValue(
":status", data.status);
1043 return QVariant(data.id);
1046 if (data.created || !found) {
1048 insert into ticket_status (ticket, "user", date, description, status)
1049 values (:ticket, :user, :date, :description, :status)
1051 q.bindValue(":user", data.user);
1052 q.bindValue(
":ticket", data.ticket);
1053 q.bindValue(
":date", data.date);
1054 q.bindValue(
":description", data.description);
1055 q.bindValue(
":status", data.status);
1058 return currval(
"ticket_status_id_seq");
1062 qFatal(
"Should not happen");
1068 PDEBUG << data.toMap();
1071 QStringList statusesL;
1076 statusesX = statusesL.join(
",");
1078 QString categoriesX;
1079 QStringList categoriesL;
1080 for (
int i=0; i<data.
categories.size(); i++) {
1081 categoriesL <<
"'" + data.
categories[i].toString() +
"'";
1084 categoriesX = categoriesL.join(
",");
1087 q.prepare(QString(R
"'(
1092 select * from statuses
1093 where status in (%1)
1097 -- select new status, if possible
1100 from statuses s, status_order o
1101 where status = :status
1102 and s.status = o.next_status
1103 and o.previous_status in (select status from x_statuses)
1106 -- select current user
1110 where "user" = :userid
1113 -- select valid categories
1116 from categories c, x_users u
1117 where c.category in (%2)
1119 or c.category in (select category from users_categories where "user" = u."user")
1123 -- select tickets with statuses, user, categories
1127 join x_categories using (category)
1128 left join lateral (select ts.status
1129 from ticket_status ts,
1131 where t.ticket = ts.ticket
1132 and s.status = ts.status
1135 limit 1) ls on (true)
1136 where ls.status in (select status from x_statuses)
1139 insert into ticket_status (ticket, "user", description, status)
1140 select t.ticket, u."user", :description, n.status
1141 from x_tickets t, x_users u, x_newstatuses n
1143 )'").arg(statusesX).arg(categoriesX));
1144 q.bindValue(":user", data.
status);
1145 q.bindValue(
":status", data.
status);
1147 q.bindValue(
":userid", userId());
1148 PDEBUG << q.lastBoundQuery();
1155QList<Dbt::TicketTimesheets> DatabasePluginPostgres::ticketTimesheets(
int ticket,
bool all) {
1156 createTemporaryTableTickets(ticket, all);
1157 QList<Dbt::TicketTimesheets> list;
1160 select tt.id, tt.ticket, tt."user", tt.date_from, tt.date_to
1161 from temporary_tickets t, ticket_timesheets tt
1162 where t.ticket = tt.ticket
1163 and t.ticket = :ticket
1164 order by tt.date_from -- must be sorted!
1167 q.bindValue(":ticket", ticket);
1172 x.id = q.value(i++).toInt();
1173 x.ticket = q.value(i++).toInt();
1174 x.user = q.value(i++).toInt();
1175 x.date_from = q.value(i++).toDateTime();
1176 x.date_to = q.value(i++).toDateTime();
1183QList<Dbt::TicketTimesheets> DatabasePluginPostgres::ticketTimesheets(
int id) {
1184 QList<Dbt::TicketTimesheets> list;
1187 select tt.id, tt.ticket, tt."user", tt.date_from, tt.date_to
1188 from ticket_timesheets tt, tickets t, users_categories uc
1189 where t.ticket = tt.ticket
1190 and t.category = uc.category
1191 and uc."user" = :user
1193 order by tt.date_from
1196 q.bindValue(":user", userId());
1197 q.bindValue(
":id",
id);
1202 x.id = q.value(i++).toInt();
1203 x.ticket = q.value(i++).toInt();
1204 x.user = q.value(i++).toInt();
1205 x.date_from = q.value(i++).toDateTime();
1206 x.date_to = q.value(i++).toDateTime();
1213QList<Dbt::TicketTimesheets> DatabasePluginPostgres::ticketTimesheets(
bool all) {
1214 return ticketTimesheets(-1, all);
1218QList<Dbt::TicketTimesheets> DatabasePluginPostgres::runningTimesheets(
int ticket) {
1219 QList<Dbt::TicketTimesheets> list;
1222 select tt.id, tt.ticket, tt."user", tt.date_from, tt.date_to
1223 from ticket_timesheets tt, tickets t, users_categories uc
1224 where t.ticket = tt.ticket
1225 and t.category = uc.category
1226 and uc."user" = :user
1227 and tt.date_to is null
1228 and (:ticket1 = tt.ticket or :ticket2 <= 0)
1231 q.bindValue(":user", userId());
1232 q.bindValue(
":ticket1", ticket);
1233 q.bindValue(
":ticket2", ticket);
1238 x.id = q.value(i++).toInt();
1239 x.ticket = q.value(i++).toInt();
1240 x.user = q.value(i++).toInt();
1241 x.date_from = q.value(i++).toDateTime();
1242 x.date_to = q.value(i++).toDateTime();
1249QList<Dbt::TicketTimesheets> DatabasePluginPostgres::startTimesheet(
int ticket) {
1250 QList<Dbt::TicketTimesheets> list;
1256 from ticket_timesheets tt, tickets t, users_categories uc
1257 where t.ticket = tt.ticket
1258 and t.category = uc.category
1259 and uc."user" = :user
1260 and :ticket = tt.ticket
1261 and tt.date_to is null
1264 q.bindValue(":user", userId());
1265 q.bindValue(
":ticket", ticket);
1274 from tickets t, users_categories uc
1275 where t.category = uc.category
1276 and uc."user" = :user
1277 and :ticket = t.ticket
1280 q.bindValue(":user", userId());
1281 q.bindValue(
":ticket", ticket);
1289 insert into ticket_timesheets (ticket, "user", date_from)
1290 values (:ticket, :user, now())
1292 q.bindValue(":user", userId());
1293 q.bindValue(
":ticket", ticket);
1295 QVariant
id = currval(
"ticket_timesheets_id_seq").toInt();
1296 if (
id.isNull() || !
id.isValid()) {
1300 list = ticketTimesheets(
id.toInt());
1305QList<Dbt::TicketTimesheets> DatabasePluginPostgres::stopTimesheet(
int ticket) {
1306 QList<Dbt::TicketTimesheets> list;
1313 from ticket_timesheets tt, tickets t, users_categories uc
1314 where t.ticket = tt.ticket
1315 and t.category = uc.category
1316 and uc."user" = :user
1317 and :ticket = tt.ticket
1318 and tt.date_to is null
1321 q.bindValue(":user", userId());
1322 q.bindValue(
":ticket", ticket);
1323 q.setForwardOnly(
false);
1325 if (q.size() != 1) {
1330 int id = q.value(0).toInt();
1334 update ticket_timesheets set date_to = now() where id = :id;
1336 q.bindValue(":id",
id);
1339 list = ticketTimesheets(ticket,
true);
1341 int remove_secs = 10;
1342 int round2_mins = 5;
1343 int round5_mins = 5;
1345 int remove_singles_mins = 3;
1348 for (
int i=list.size()-1; i>=0; i--) {
1350 if (x.date_from.secsTo(x.date_to) > remove_secs) {
continue; }
1355 for (
int i=list.size()-1; i>=0; i--) {
1358 secs = x.date_from.secsTo(x.date_to);
1359 secs = secs / (round2_mins * 60) + ( (secs % (round2_mins*60)) ? 1 : 0);
1360 secs = secs * (round2_mins * 60);
1361 x.date_to = x.date_from.addSecs(secs);
1365 for (
int i=list.size()-1; i>=1; i--) {
1369 if (x0.date_to < x1.date_from) {
continue; }
1370 x0.date_to = x1.date_to;
1375 for (
int i=list.size()-1; i>=1; i--) {
1378 if (x0.date_to.addSecs(join_mins*60) < x1.date_from) {
continue; }
1379 x0.date_to = x1.date_to;
1384 for (
int i=list.size()-1; i>=0; i--) {
1387 secs = x.date_from.secsTo(x.date_to);
1388 secs = secs + (round5_mins * 60) -1;
1389 secs = secs / (round5_mins * 60);
1390 secs = secs * (round5_mins * 60);
1391 x.date_to = x.date_from.addSecs(secs);
1395 for (
int i=list.size()-1; i>=0; i--) {
1397 int secs = x.date_from.secsTo(x.date_to);
1398 if (secs <= 0) {
continue; }
1399 if (secs >= remove_singles_mins * 60) {
continue; }
1403 q.prepare(R
"'(delete from ticket_timesheets where ticket = :ticket;)'");
1404 q.bindValue(":ticket", ticket);
1407 q.prepare(R
"'(insert into ticket_timesheets (ticket, "user", date_from, date_to)
1408 values (:ticket, :user, :date_from, :date_to);
1410 for (
int i=0; i<list.size(); i++) {
1412 q.bindValue(
":ticket", ticket);
1413 q.bindValue(
":user", userId());
1414 q.bindValue(
":date_from", x.date_from);
1415 q.bindValue(
":date_to", x.date_to);
1420 return list.mid(list.size()-1);
1424QList<Dbt::TicketTimesheets> DatabasePluginPostgres::toggleTimesheet(
int ticket) {
1425 QList<Dbt::TicketTimesheets> list;
1431 from tickets t, users_categories uc
1432 where t.category = uc.category
1433 and uc."user" = :user
1434 and :ticket = t.ticket
1437 q.bindValue(":user", userId());
1438 q.bindValue(
":ticket", ticket);
1447 from ticket_timesheets tt, tickets t, users_categories uc
1448 where t.ticket = tt.ticket
1449 and t.category = uc.category
1450 and uc."user" = :user
1451 and :ticket = tt.ticket
1452 and tt.date_to is null
1455 q.bindValue(":user", userId());
1456 q.bindValue(
":ticket", ticket);
1457 q.setForwardOnly(
false);
1459 bool found = (q.size() == 1);
1460 int id = (found) ? q.next(), q.value(0).toInt() : 0;
1465 insert into ticket_timesheets (ticket, "user", date_from)
1466 values (:ticket, :user, now())
1468 q.bindValue(":user", userId());
1469 q.bindValue(
":ticket", ticket);
1471 QVariant newid = currval(
"ticket_timesheets_id_seq");
1472 if (newid.isNull() || !newid.isValid()) {
1481 update ticket_timesheets set date_to = now() where id = :id;
1483 q.bindValue(":id",
id);
1487 list = ticketTimesheets(
id);
1496 if (!data.created) {
1497 q.prepare(R
"'(select 1 from ticket_timesheets where id = :id;)'");
1498 q.bindValue(":id", data.id);
1503 if (!data.created && found) {
1505 update ticket_timesheets set
1508 date_from = :date_from,
1512 q.bindValue(":id", data.id);
1513 q.bindValue(
":user", data.user);
1514 q.bindValue(
":ticket", data.ticket);
1515 q.bindValue(
":date_from", data.date_from);
1516 q.bindValue(
":date_to", data.date_to);
1518 return QVariant(data.id);
1521 if (data.created || !found) {
1523 insert into ticket_timesheets (ticket, "user", date_from, date_to)
1524 values (:ticket, :user, :date_from, :date_to)
1526 q.bindValue(":user", data.user);
1527 q.bindValue(
":ticket", data.ticket);
1528 q.bindValue(
":date_from", data.date_from);
1529 q.bindValue(
":date_to", data.date_to);
1532 return currval(
"ticket_timesheets_id_seq");
1542 q.prepare(R
"'(delete from ticket_timesheets where id = :id;)'");
1543 q.bindValue(":id",
id.
id);
1548QList<Dbt::TicketValues> DatabasePluginPostgres::ticketValues(
int ticket,
bool all) {
1549 createTemporaryTableTickets(ticket, all);
1550 QList<Dbt::TicketValues> list;
1553 select tv.id, tt.ticket, tv.name, tv.value, tv."user", tv.date
1554 from temporary_tickets tt, ticket_values tv
1555 where tt.ticket = tv.ticket
1562 x.id = q.value(i++).toInt();
1563 x.ticket = q.value(i++).toInt();
1564 x.name = q.value(i++).toString();
1565 x.value =
JSON::data(q.value(i++).toByteArray());
1566 x.user = q.value(i++).toInt();
1567 x.date = q.value(i++).toDateTime();
1574QList<Dbt::TicketValues> DatabasePluginPostgres::ticketValues(
int id) {
1575 QList<Dbt::TicketValues> list;
1578 select tv.id, tv.ticket, tv.date, tv.name, tv.value, tv."user"
1579 from tickets tt, ticket_values tv, users_categories uc
1580 where tt.ticket = tv.ticket
1581 and tt.category = uc.category
1586 q.bindValue(":id",
id);
1587 q.bindValue(
":user", userId());
1592 x.id = q.value(i++).toInt();
1593 x.ticket = q.value(i++).toInt();
1594 x.date = q.value(i++).toDateTime();
1595 x.name = q.value(i++).toString();
1596 x.value =
JSON::data(q.value(i++).toByteArray());
1597 x.user = q.value(i++).toInt();
1604QList<Dbt::TicketValues> DatabasePluginPostgres::ticketValues(
bool all) {
1605 return ticketValues(-1, all);
1613 if (!data.created) {
1614 q.prepare(R
"'(select 1 from ticket_values where id = :id;)'");
1615 q.bindValue(":id", data.id);
1620 if (!data.created && found) {
1621 q.prepare(QString(R
"'(
1622 update ticket_values set
1629 )'").arg(QString::fromUtf8(JSON::json(data.value))));
1630 q.bindValue(":id", data.id);
1631 q.bindValue(
":ticket", data.ticket);
1632 q.bindValue(
":user", data.user);
1633 q.bindValue(
":date", data.date);
1634 q.bindValue(
":name", data.name);
1637 return QVariant(data.id);
1640 if (data.created || !found) {
1641 q.prepare(QString(R
"'(
1642 insert into ticket_values (ticket, "user", date, name, value)
1643 select :ticket, :user, :date, :name, '%1'
1644 where not exists (select 1 from ticket_values where id = :id);
1645 )'").arg(QString::fromUtf8(JSON::json(data.value))));
1646 q.bindValue(":id", data.id);
1647 q.bindValue(
":ticket", data.ticket);
1648 q.bindValue(
":user", data.user);
1649 q.bindValue(
":date", data.date);
1650 q.bindValue(
":name", data.name);
1654 return currval(
"ticket_values_id_seq");
1658 qFatal(
"Should not happen");
1665 q.prepare(R
"'(delete from ticket_values where id = :id;)'");
1666 q.bindValue(":id",
id.
id);
1671QList<Dbt::Statuses> DatabasePluginPostgres::statuses(
const QString&
id) {
1672 QList<Dbt::Statuses> list;
1674 if (
id.isEmpty() ||
id ==
"") {
1676 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored,
1677 n.status, n.description, n.abbreviation, n.color, n.closed, n.can_be_run, n.ignored
1679 left join status_order o on (s.status = o.previous_status)
1680 left join statuses n on (o.next_status = n.status)
1681 order by s.status, n.status
1685 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored,
1686 n.status, n.description, n.abbreviation, n.color, n.closed, n.can_be_run, n.ignored
1688 left join status_order o on (s.status = o.previous_status)
1689 left join statuses n on (o.next_status = n.status)
1690 where (:id = s.status)
1691 order by s.status, n.status
1694 q.bindValue(":id",
id);
1699 if (!x.status.isEmpty() && x.status != q.value(0).toString()) {
1703 x.status = q.value(i++).toString();
1704 x.description = q.value(i++).toString();
1705 x.abbreviation = q.value(i++).toString();
1706 x.color = q.value(i++).toString();
1707 x.closed = q.value(i++).toBool();
1708 x.can_be_run = q.value(i++).toBool();
1709 x.ignored = q.value(i++).toBool();
1710 x.can_have_next =
true;
1711 if (!q.value(i).isNull()) {
1713 n.status = q.value(i++).toString();
1714 n.description = q.value(i++).toString();
1715 n.abbreviation = q.value(i++).toString();
1716 n.color = q.value(i++).toString();
1717 n.closed = q.value(i++).toBool();
1718 n.can_be_run = q.value(i++).toBool();
1719 n.ignored = q.value(i++).toBool();
1723 if (!x.status.isEmpty()) {
1730QList<Dbt::Statuses> DatabasePluginPostgres::statuses(
const QString& category,
const QString& previousStatus) {
1731 if ((category.isEmpty() || category ==
"") &&
1732 (previousStatus.isEmpty() || previousStatus ==
"")) {
return statuses(QString()); }
1733 int categoryi = category.toInt();
1735 QString ps = (previousStatus.isEmpty()) ?
"0" : previousStatus;
1736 auto results = [&q]() {
1737 QList<Dbt::Statuses> list;
1742 x.status = q.value(i++).toString();
1743 x.description = q.value(i++).toString();
1744 x.abbreviation = q.value(i++).toString();
1745 x.color = q.value(i++).toString();
1746 x.closed = q.value(i++).toBool();
1747 x.can_be_run = q.value(i++).toBool();
1748 x.ignored = q.value(i++).toBool();
1754 bool findNullCategory = (category.isEmpty() || category ==
"");
1755 if (!findNullCategory) {
1756 q.prepare(R
"'(select 1 from status_order where category = :category;)'");
1757 q.bindValue(":category", categoryi);
1759 findNullCategory = !q.next();
1762 if (findNullCategory && (previousStatus.isEmpty() || previousStatus ==
"")) {
1764 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1765 from statuses s, status_order o
1766 where s.status = o.next_status
1767 and (o.previous_status is null or o.previous_status = '')
1768 and (o.category is null);
1773 if (findNullCategory) {
1775 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1776 from statuses s, status_order o
1777 where s.status = o.next_status
1778 and o.previous_status = :previous_status
1779 and (o.category is null);
1781 q.bindValue(":previous_status", ps);
1785 if (!findNullCategory && (previousStatus.isEmpty() || previousStatus ==
"")) {
1787 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1788 from statuses s, status_order o
1789 where s.status = o.next_status
1790 and (o.previous_status is null or o.previous_status = '')
1791 and o.category = :category;
1793 q.bindValue(":category", categoryi);
1798 if (!findNullCategory) {
1800 select s.status, s.description, s.abbreviation, s.color, s.closed, s.can_be_run, s.ignored
1801 from statuses s, status_order o
1802 where s.status = o.next_status
1803 and o.previous_status = :previous_status
1804 and o.category = :category;
1806 q.bindValue(":previous_status", ps);
1807 q.bindValue(
":category", categoryi);
1811 return QList<Dbt::Statuses>();
1816void DatabasePluginPostgres::remove(
const Dbt::Statuses&
id) {
1818 q.prepare(R
"'(delete from statuses where status = :id;)'");
1819 q.bindValue(":id",
id.status);
1824QVariant DatabasePluginPostgres::save(
const Dbt::Statuses& data) {
1830 description = :description,
1831 abbreviation = :abbreviation,
1834 can_be_run = :can_be_run,
1836 where status = :status
1838 q.bindValue(":description", data.description);
1839 q.bindValue(
":abbreviation", data.abbreviation);
1840 q.bindValue(
":color", data.color);
1841 q.bindValue(
":closed", data.closed);
1842 q.bindValue(
":can_be_run", data.can_be_run);
1843 q.bindValue(
":ignored", data.ignored);
1844 q.bindValue(
":status", data.status);
1848 insert into statuses (status, description, abbreviation, color, closed, can_be_run, ignored)
1849 select :status1, :description, :abbreviation, :color, :closed, :can_be_run, :ignored
1850 where not exists (select 1 from statuses where status = :status2);
1852 q.bindValue(":status1", data.status);
1853 q.bindValue(
":description", data.description);
1854 q.bindValue(
":abbreviation", data.abbreviation);
1855 q.bindValue(
":color", data.color);
1856 q.bindValue(
":closed", data.closed);
1857 q.bindValue(
":can_be_run", data.can_be_run);
1858 q.bindValue(
":ignored", data.ignored);
1859 q.bindValue(
":status2", data.status);
1863 delete from status_order
1864 where previous_status = :status
1866 q.bindValue(":status", data.status);
1869 for (
int i=0; i<data.next.size(); i++) {
1871 insert into status_order (previous_status, next_status)
1872 values (:previous_status, :next_status);
1874 q.bindValue(":previous_status", data.status);
1875 q.bindValue(
":next_status", data.next[i].status);
1881 return QVariant(data.status);
1885QList<Dbt::StatusTemplates> DatabasePluginPostgres::statusTemplates(
int id) {
1888 QList<Dbt::StatusTemplates> list;
1893QList<Dbt::TicketFiles> DatabasePluginPostgres::ticketFiles(
int ticket,
bool all) {
1894 createTemporaryTableTickets(ticket, all);
1895 QList<Dbt::TicketFiles> list;
1899 select f.id, f."user", f.date, f.ticket, f.name, f.type, f.content
1900 from temporary_tickets t, ticket_files f
1901 where t.ticket = f.ticket
1908 x.id = q.value(i++).toInt();
1909 x.user = q.value(i++).toInt();
1910 x.date = q.value(i++).toDateTime();
1911 x.ticket = q.value(i++).toInt();
1912 x.name = q.value(i++).toString();
1913 x.type = q.value(i++).toString();
1914 x.content = q.value(i++).toByteArray();
1922QList<Dbt::TicketFiles> DatabasePluginPostgres::ticketFiles(
int id) {
1923 QList<Dbt::TicketFiles> list;
1927 select f.id, f."user", f.date, f.ticket, f.name, f.type, f.content
1928 from ticket_files f, users u, tickets t, users_categories uc
1930 and t.ticket = f.ticket
1931 and t.category = uc.category
1932 and u."user" = f."user"
1935 q.bindValue(":id",
id);
1936 q.bindValue(
":user", userId());
1941 x.id = q.value(i++).toInt();
1942 x.user = q.value(i++).toInt();
1943 x.date = q.value(i++).toDateTime();
1944 x.ticket = q.value(i++).toInt();
1945 x.name = q.value(i++).toString();
1946 x.type = q.value(i++).toString();
1947 x.content = q.value(i++).toByteArray();
1955QList<Dbt::TicketFiles> DatabasePluginPostgres::ticketFiles(
bool all) {
1956 return ticketFiles(-1, all);
1964 if (!data.created) {
1965 q.prepare(R
"'(select 1 from ticket_files where id = :id;)'");
1966 q.bindValue(":id", data.id);
1971 if (!data.created && found) {
1973 update ticket_files set
1982 q.bindValue(":id", data.id);
1983 q.bindValue(
":ticket", data.ticket);
1984 q.bindValue(
":user", data.user);
1985 q.bindValue(
":date", data.date);
1986 q.bindValue(
":name", data.name);
1987 q.bindValue(
":type", data.type);
1988 q.bindValue(
":content", data.content);
1990 return QVariant(data.id);
1993 if (data.created || !found) {
1995 insert into ticket_files (ticket, "user", date, name, type, content)
1996 values (:ticket, :user, :date, :name, :type, :content);
1998 q.bindValue(":id", data.id);
1999 q.bindValue(
":ticket", data.ticket);
2000 q.bindValue(
":user", data.user);
2001 q.bindValue(
":date", data.date);
2002 q.bindValue(
":name", data.name);
2003 q.bindValue(
":type", data.type);
2004 q.bindValue(
":content", data.content);
2006 return currval(
"ticket_files_id_seq");
2016 q.prepare(R
"'(delete from ticket_files where id = :id;)'");
2017 q.bindValue(":id",
id.
id);
2022QList<Dbt::UsersCategories> DatabasePluginPostgres::usersCategories(
int id,
int user,
const QString& category) {
2023 QList<Dbt::UsersCategories> list;
2025 auto retvals = [&]() {
2030 x.id = q.value(i++).toInt();
2031 x.user = q.value(i++).toInt();
2032 x.category = q.value(i++).toString();
2039 q.prepare(R
"'(select id, "user", category from users_categories where id = :id)'");
2040 q.bindValue(":id",
id);
2044 if (
id <= 0 && category.isEmpty() && user > 0) {
2045 q.prepare(R
"'(select id, "user", category from users_categories where "user" = :user)'");
2046 q.bindValue(":user", user);
2050 if (
id <= 0 && category.isEmpty() && user <= 0) {
2051 q.prepare(R
"'(select id, "user", category from users_categories)'");
2055 if (
id <= 0 && !category.isEmpty() && user > 0) {
2056 q.prepare(R
"'(select id, "user", category from users_categories where "user" = :user and category = :category)'");
2057 q.bindValue(":user", user);
2058 q.bindValue(
":category", category);
2062 if (
id <= 0 && !category.isEmpty() && user <= 0) {
2063 q.prepare(R
"'(select id, "user", category from users_categories where category = :category)'");
2064 q.bindValue(":category", category);
2073QList<Dbt::CategoriesOverview> DatabasePluginPostgres::categoriesOverview(
const QStringList& statuses) {
2075 QStringList statusesL;
2076 for (
int i=0; i<statuses.size(); i++) {
2077 statusesL <<
"'" + statuses[i] +
"'";
2080 statusesX = statusesL.join(
",");
2082 QList<Dbt::CategoriesOverview> list;
2084 q.prepare(QString(R
"'(
2085 with recursive tree as (
2086 select category, parent_category, description, 0::int as depth, format('%04s', category) as ordering
2087 from categories where parent_category is null
2089 select c.category, c.parent_category, c.description, t.depth + 1 as depth, t.ordering || format('%04s', c.category) as ordering
2090 from tree t, categories c
2092 t.category = c.parent_category
2097 where "user" = :userid
2102 where status in (%1)
2106 select t.*, ls.*, tts.*
2108 left join lateral (select ts.status
2109 from ticket_status ts,
2111 where t.ticket = ts.ticket
2112 and s.status = ts.status
2115 limit 1) ls on (true)
2116 left join lateral (select to_hours(sum(tt.date_to - coalesce(tt.date_from, now()))) as time
2117 from ticket_timesheets tt,
2119 where tt.ticket = t.ticket
2120 -- and (tt."user" = u."user" or u.admin = true)
2121 group by tt.ticket) tts on (true)
2122 where ls.status in (select status from x_statuses)
2126 select c.depth, c.category, c.description, x.price, x.time, x.tickets_count, c.ordering
2127 into temporary table xxx
2129 join x_users u on true
2130 left join users_categories uc on (uc.category = c.category and uc."user" = u."user")
2131 left join (select category, count(1) as tickets_count, sum(time) as time, round(sum(price*time)) as price
2134 ) x on (c.category = x.category)
2135 where (uc.category is not null or u.admin = true)
2138 )'").arg(statusesX));
2139 q.bindValue(":userid", userId());
2142 select 'DETAIL' as type, x.depth, x.category, x.description, x.price, x.time, x.tickets_count, x.ordering
2145 select 'SUM', null, null, 'Total', sum(price), sum(time), sum(tickets_count), null
2153 x.type = q.value(i++).toString();
2154 x.depth = q.value(i++).toInt();
2155 x.category = q.value(i++).toString();
2156 x.description = q.value(i++).toString();
2157 x.price = q.value(i++).toDouble();
2158 x.time = q.value(i++).toDouble();
2159 x.tickets_count = q.value(i++).toInt();
2160 x.ordering = q.value(i++).toString();
2167QList<Dbt::Overview> DatabasePluginPostgres::overview(
const QString& category,
const QStringList& statuses) {
2169 QStringList statusesL;
2170 for (
int i=0; i<statuses.size(); i++) {
2171 statusesL <<
"'" + statuses[i] +
"'";
2174 statusesX =
"array[" +statusesL.join(
",") +
"]";
2176 QList<Dbt::Overview> list;
2179 select key, category, statuses
2180 from overview_params
2181 where category = :category
2182 and statuses = )'" + statusesX );
2183 q.bindValue(":category", category);
2186 list = overview(q.value(0).toString());
2190 quint64 rnd = QRandomGenerator::global()->generate64();
2191 QString rnds = QString::number(rnd, 36).toUpper();
2192 q.prepare(
"insert into overview_params (key, category, statuses) values (:key, :category, " + statusesX +
");");
2193 q.bindValue(
":key", rnds);
2194 q.bindValue(
":category", category);
2196 list = overview(rnds);
2202QList<Dbt::Overview> DatabasePluginPostgres::overview(
const QString& overviewId) {
2203 QList<Dbt::Overview> list;
2206 overview.id = overviewId.toUpper();
2209 select c.category, c.parent_category, c.description, c.price
2210 from categories c, overview_params p
2211 where c.category = p.category
2214 q.bindValue(":key", overviewId.toUpper());
2216 if (!q.next()) {
return list; }
2217 overview.category.category = q.value(0).toString();
2218 overview.category.parent_category = null(q.value(1).toString());
2219 overview.category.description = q.value(2).toString();
2220 overview.category.price = q.value(3).toDouble();
2222 q.exec(R
"'(create temporary table overview_categories_tmp(category int);)'");
2223 q.exec(R"'(create temporary table overview_statuses_tmp(status text);)'");
2226 with recursive tree(category, parent_category, depth) as (
2229 select category, parent_category, 0 as depth
2231 where category in (select category from overview_params where key = :key)
2235 select c.category, c.parent_category, t.depth+1 as depth
2237 left join categories c on (c.parent_category = t.category)
2239 and c.category is not null
2242 insert into overview_categories_tmp (category)
2247 q.bindValue(":key", overviewId.toUpper());
2250 q.prepare(R
"'(insert into overview_statuses_tmp select unnest(statuses) from overview_params where key = :key;)'");
2251 q.bindValue(":key", overviewId.toUpper());
2256 ticket_last_status as not materialized (
2257 select t.ticket, tl.status, st.description
2259 left join lateral (select tn.ticket, tn.status
2260 from ticket_status tn, statuses s
2261 where tn.ticket = t.ticket
2262 and tn.status = s.status
2264 order by ticket, date desc
2267 left join statuses st on (st.status = tl.status)
2269 ticket_timesheets_sum as not materialized (
2270 select ticket, "user", sum(date_to - date_from) as duration
2271 from ticket_timesheets
2272 group by ticket, "user"
2276 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
2278 left join ticket_last_status ls using (ticket)
2279 left join ticket_timesheets_sum ts using (ticket, "user")
2280 left join users u using ("user")
2281 where ls.status in (select status from overview_statuses_tmp)
2282 and category in (select category from overview_categories_tmp)
2285 -- Součet za jednotlivé tickety
2287 select g.ticket, g.description, -1, null, null, g.duration, g.price, st.description
2289 select t.ticket, t.description, -1, null, null, to_hours(sum(ts.duration)) as duration, sum(to_hours(ts.duration) * t.price) as price
2291 left join ticket_timesheets_sum ts using (ticket, "user")
2292 left join ticket_last_status ls using (ticket)
2293 where ls.status in (select status from overview_statuses_tmp)
2294 and category in (select category from overview_categories_tmp)
2297 left join ticket_last_status st using(ticket)
2300 -- Součet za jednotlivé statusy
2301 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
2302 from ticket_last_status ls
2303 left join tickets t using (ticket)
2304 left join ticket_timesheets_sum ts using (ticket)
2305 where ls.status in (select status from overview_statuses_tmp)
2306 and category in (select category from overview_categories_tmp)
2307 group by ls.description
2310 -- Součet za jednotlivé lidi
2312 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
2313 from ticket_last_status ls
2314 left join tickets t using (ticket)
2315 left join users u using ("user")
2316 left join ticket_timesheets_sum ts using (ticket)
2317 where ls.status in (select status from overview_statuses_tmp)
2318 and category in (select category from overview_categories_tmp)
2319 group by t."user", u.name
2323 if (q.value(2).toInt() == -1) {
2325 s.ticket = q.value(0).toInt();
2326 s.description = q.value(1).toString();
2327 s.duration = q.value(5).toDouble();
2328 s.price = q.value(6).toDouble();
2329 s.status = q.value(7).toString();
2330 overview.ticketsSum << s;
2333 if (q.value(2).toInt() == -2) {
2335 s.duration = q.value(5).toDouble();
2336 s.price = q.value(6).toDouble();
2337 s.status = q.value(7).toString();
2338 overview.statusSum << s;
2341 if (q.value(2).toInt() == -3) {
2343 s.user_name = q.value(3).toString();
2344 s.duration = q.value(5).toDouble();
2345 s.price = q.value(6).toDouble();
2346 overview.userSum << s;
2352 t.ticket = q.value(i++).toInt();
2353 t.description = q.value(i++).toString();
2354 t.user = q.value(i++).toInt();
2355 t.user_name = q.value(i++).toString();
2356 t.hour_price = q.value(i++).toDouble();
2357 t.duration = q.value(i++).toDouble();
2358 t.price = q.value(i++).toDouble();
2359 t.status = q.value(i++).toString();
2360 overview.tickets << t;
2365 ticket_last_status as not materialized (
2366 select t.ticket, tl.status
2368 left join lateral (select tn.ticket, tn.status
2369 from ticket_status tn, statuses s
2370 where tn.ticket = t.ticket
2371 and tn.status = s.status
2373 order by ticket, date desc
2377 ticket_timesheets_sum as not materialized (
2378 select ticket, "user", date_to::date as date, sum(date_to - date_from) as duration
2379 from ticket_timesheets
2380 group by ticket, "user", date_to::date
2383 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)
2385 left join users u using ("user")
2386 left join ticket_last_status ls using (ticket)
2387 left join ticket_timesheets_sum ts using (ticket, "user")
2388 left join categories c using (category)
2389 where ls.status in (select status from overview_statuses_tmp)
2390 and category in (select category from overview_categories_tmp)
2394 select null, null, -1, null, null, null, to_hours(sum(duration)), sum(round(to_hours(ts.duration) * t.price))
2396 left join ticket_last_status ls using (ticket)
2397 left join ticket_timesheets_sum ts using (ticket, "user")
2398 where ls.status in (select status from overview_statuses_tmp)
2399 and category in (select category from overview_categories_tmp)
2403 if (q.value(2).toInt() == -1) {
2404 overview.sum.duration = q.value(6).toDouble();
2405 overview.sum.price = q.value(7).toDouble();
2410 t.ticket = q.value(i++).toInt();
2411 t.description = q.value(i++).toString();
2412 t.user = q.value(i++).toInt();
2413 t.user_name = q.value(i++).toString();
2414 t.hour_price = q.value(i++).toDouble();
2415 t.date = q.value(i++).toDateTime();
2416 t.duration = q.value(i++).toDouble();
2417 t.price = q.value(i++).toDouble();
2426QList<Dbt::OverviewList> DatabasePluginPostgres::overviewList() {
2428 QList<Dbt::OverviewList> list;
2430 QList<Dbt::Statuses> statusesList = statuses(QString());
2431 QHash<QString, Dbt::Statuses> statusesHash;
2432 for (
int i=0; i<statusesList.size(); i++) {
2434 statusesHash[item.status] = item;
2438 select o.key, o.statuses, c.category, c.parent_category, c.description, c.price
2439 from overview_params o
2440 left join categories c using (category)
2445 x.key = q.value(i++).toString();
2447 QStringList statuses = q.value(i++).toString().replace(
"{",
"").replace(
"}",
"").split(
",");
2448 for (
int i=0; i<statuses.size(); i++) {
2449 x.statuses << statusesHash[statuses[i]];
2453 c.category = q.value(i++).toString();
2454 c.parent_category = q.value(i++).toString();
2455 c.description = q.value(i++).toString();
2456 c.price = q.value(i++).toDouble();
2467 q.prepare(R
"'(delete from overview_params where key = :key;)'");
2468 q.bindValue(":key", x.key);
2473QList<Dbt::Departments> DatabasePluginPostgres::departments(
int department) {
2475 QList<Dbt::Departments> list;
2476 q.prepare(R
"'(select department, abbr, description from departments where department = :key1 or 0 = :key2;)'");
2477 q.bindValue(":key1", department);
2478 q.bindValue(
":key2", department);
2483 x.department = q.value(i++).toInt();
2484 x.abbr = q.value(i++).toString();
2485 x.description = q.value(i++).toString();
2492QList<Dbt::Doors> DatabasePluginPostgres::doors(
int door) {
2494 QList<Dbt::Doors> list;
2495 q.prepare(R
"'(select door, description from departments where department = :key1 or 0 = :key2;)'");
2496 q.bindValue(":key1", door);
2497 q.bindValue(
":key2", door);
2502 x.door = q.value(i++).toInt();
2503 x.description = q.value(i++).toString();
2509QList<Dbt::Employees> DatabasePluginPostgres::employess(
int employee) {
2511 QList<Dbt::Employees> list;
2512 q.prepare(R
"'(select employee, firstname, surname, active where employee = :key1 or 0 = :key2;)'");
2513 q.bindValue(":key1", employee);
2514 q.bindValue(
":key2", employee);
2519 x.employee = q.value(i++).toInt();
2520 x.firstname = q.value(i++).toString();
2521 x.surname = q.value(i++).toString();
2522 x.active = q.value(i++).toBool();
2528QList<Dbt::EventTypes> DatabasePluginPostgres::eventTypes(
const QString& eventType) {
2530 QList<Dbt::EventTypes> list;
2531 q.prepare(R
"'(select event_type, description,
2532 end_state, passage, arrival, vacation, sick_leave, compensatory_leave, business_trip, break_time, unpaid_leave, sick_care
2533 where event_type = :key1 or "" = :key2;
2535 q.bindValue(":key1", eventType);
2536 q.bindValue(
":key2", eventType);
2541 x.event_type = q.value(i++).toString();
2542 x.description = q.value(i++).toString();
2543 x.end_state = q.value(i++).toBool();
2544 x.passage = q.value(i++).toBool();
2545 x.arrival = q.value(i++).toBool();
2546 x.vacation = q.value(i++).toBool();
2547 x.sick_leave = q.value(i++).toBool();
2548 x.compensatory_leave = q.value(i++).toBool();
2549 x.business_trip = q.value(i++).toBool();
2550 x.break_time = q.value(i++).toBool();
2551 x.unpaid_leave = q.value(i++).toBool();
2552 x.sick_care = q.value(i++).toBool();
2558QList<Dbt::Events> DatabasePluginPostgres::events(
int event) {
2560 QList<Dbt::Events> list;
2561 q.prepare(R
"'(select event, date, event_type, employee, valid, user_edited from events where event = :key1 or 0 = :key2;)'");
2562 q.bindValue(":key1", event);
2563 q.bindValue(
":key2", event);
2568 x.event = q.value(i++).toInt();
2569 x.date = q.value(i++).toDateTime();
2570 x.event_type = q.value(i++).toString();
2571 x.employee = q.value(i++).toInt();
2572 x.valid = q.value(i++).toBool();
2573 x.user_edited = q.value(i++).toInt();
2581 QList<Dbt::DepartmentHasManager> list;
2582 q.prepare(R
"'(select department, "user" from department_has_member
2583 where (department = ::key11 or 0 = :key12)
2584 and ("user" = :key21 or 0 == :key22);
2586 q.bindValue(":key11", p.department);
2587 q.bindValue(
":key12", p.department);
2588 q.bindValue(
":key21", p.user);
2589 q.bindValue(
":key22", p.user);
2594 x.department = q.value(i++).toInt();
2595 x.user = q.value(i++).toInt();
2603 QList<Dbt::DepartmentHasMember> list;
2604 q.prepare(R
"'(select department, employee from department_has_member
2605 where (department = ::key11 or 0 = :key12)
2606 and (employee = :key21 or 0 == :key22);
2608 q.bindValue(":key11", p.department);
2609 q.bindValue(
":key12", p.department);
2610 q.bindValue(
":key21", p.employee);
2611 q.bindValue(
":key22", p.employee);
2616 x.department = q.value(i++).toInt();
2617 x.employee = q.value(i++).toInt();
2625 QList<Dbt::EmployeeCanOpenDoor> list;
2626 q.prepare(R
"'(select door, employee from employee_can_open_door
2627 where (door = ::key11 or 0 = :key12)
2628 and (employee = :key21 or 0 == :key22);
2630 q.bindValue(":key11", p.door);
2631 q.bindValue(
":key12", p.door);
2632 q.bindValue(
":key21", p.employee);
2633 q.bindValue(
":key22", p.employee);
2638 x.door = q.value(i++).toInt();
2639 x.employee = q.value(i++).toInt();
2645QList<Dbt::EmployeeHasRfid> DatabasePluginPostgres::employeeHasRfid(
const Dbt::EmployeeHasRfid& p) {
2647 QList<Dbt::EmployeeHasRfid> list;
2648 q.prepare(R
"'(select employee, rfid from employee_has_rfid
2649 where (employee = ::key11 or 0 = :key12)
2650 and (rfid = :key21 or 0 == :key22);
2652 q.bindValue(":key11", p.employee);
2653 q.bindValue(
":key12", p.employee);
2654 q.bindValue(
":key21", p.rfid);
2655 q.bindValue(
":key22", p.rfid);
2660 x.employee = q.value(i++).toInt();
2661 x.rfid = q.value(i++).toInt();
2669 q.prepare(R
"'(delete from departments where department = :key;)'");
2670 q.bindValue(":key", data.department);
2673void DatabasePluginPostgres::remove(
const Dbt::Doors& data) {
2675 q.prepare(R
"'(delete from doors where door = :key;)'");
2676 q.bindValue(":key", data.door);
2681 q.prepare(R
"'(delete from employees where employee = :key;)'");
2682 q.bindValue(":key", data.employee);
2687 q.prepare(R
"'(delete from event_types where event_type = :key;)'");
2688 q.bindValue(":key", data.event_type);
2691void DatabasePluginPostgres::remove(
const Dbt::Events& data) {
2693 q.prepare(R
"'(delete from event where event = :key;)'");
2694 q.bindValue(":key", data.event);
2699 q.prepare(R
"'(delete from departments_has_manager where department = :key1 and "user" = :key2;)'");
2700 q.bindValue(":key1", data.department);
2701 q.bindValue(
":key2", data.user);
2706 q.prepare(R
"'(delete from departments_has_member where department = :key and employee = :key2;)'");
2707 q.bindValue(":key1", data.department);
2708 q.bindValue(
":key2", data.employee);
2713 q.prepare(R
"'(delete from employee_can_open_door where employee = :key1 and door = :key2;)'");
2714 q.bindValue(":key1", data.employee);
2715 q.bindValue(
":key2", data.door);
2720 q.prepare(R
"'(delete from employee_has_rfid where employee = :key1 and rfid = :key2;)'");
2721 q.bindValue(":key1", data.employee);
2722 q.bindValue(
":key2", data.rfid);
Vlastní rozšíření QSqlQuery o primitivní transakce a logování
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