Commit c67a1efd authored by Hugo Hörnquist's avatar Hugo Hörnquist

Merge branch 'diff'

This commit replaces how the diff system for products works under the
hood. The final saved diffs are the exact same as before, and the GUI
has simple received a "quick mode" checkbox.

The major update is that the temp diff table stores a lot less data,
and is accessable as a view, allowing for much easier subquerying in it.

This was also the (I think) last place the database didn't operate on
it's own, but was rather dependent on the GUI. Which makes this really
nice.

==== INSTALLATION ====

Apply the changes in diff.sql to the already existing database, which
creates all new tables, and sets the required data. Note that it doesn't
clean up the old tables. I will probably fix that in the future.
parents 1539df10 a928d4a6
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <QModelIndexList> #include <QModelIndexList>
#include <QSqlError> #include <QSqlError>
#include <QSqlIndex>
#include <QSqlRelationalDelegate> #include <QSqlRelationalDelegate>
#include <QHeaderView> #include <QHeaderView>
...@@ -239,28 +240,34 @@ MainWindow::MainWindow(QWidget *parent) : ...@@ -239,28 +240,34 @@ MainWindow::MainWindow(QWidget *parent) :
clear_stockDiffTemp(); clear_stockDiffTemp();
QList<int> inventoryCheckDisabled; QList<int> inventoryCheckDisabled;
inventoryCheckDisabled << 0 << 1 << 2 << 4; inventoryCheckDisabled << 0 << 1 << 2 << 3 << 4 << 6;
DisablingModel* inventoryModel = new DisablingModel(inventoryCheckDisabled); DisablingModel* inventoryModel = new DisablingModel(inventoryCheckDisabled);
inventoryModel->setTable("stock_diff_temp"); inventoryModel->setTable("diff_view");
inventoryModel->setHeaderData(0, Qt::Horizontal, "id"); inventoryModel->setHeaderData(0, Qt::Horizontal, "id");
inventoryModel->setHeaderData(1, Qt::Horizontal, "Namn"); inventoryModel->setHeaderData(1, Qt::Horizontal, "product id");
inventoryModel->setHeaderData(2, Qt::Horizontal, "Förväntat"); inventoryModel->setHeaderData(2, Qt::Horizontal, "sale status");
inventoryModel->setHeaderData(3, Qt::Horizontal, "Faktiskt"); inventoryModel->setHeaderData(3, Qt::Horizontal, "Namn");
inventoryModel->setHeaderData(4, Qt::Horizontal, "Diff"); inventoryModel->setHeaderData(4, Qt::Horizontal, "Förväntat");
inventoryModel->setHeaderData(5, Qt::Horizontal, "Faktiskt");
inventoryModel->setHeaderData(6, Qt::Horizontal, "Diff");
inventoryModel->setEditStrategy(QSqlTableModel::OnFieldChange); inventoryModel->setEditStrategy(QSqlTableModel::OnFieldChange);
QObject::connect(inventoryModel, &QAbstractItemModel::dataChanged, QObject::connect(inventoryModel, &QAbstractItemModel::dataChanged,
this, &MainWindow::on_inventoryModel_dataChanged); this, &MainWindow::on_inventoryModel_dataChanged);
inventoryModel->setRelation(1, QSqlRelation("products", "id", "name"));
inventoryModel->select(); inventoryModel->select();
ui->inventoryCheckView->setModel(inventoryModel); ui->inventoryCheckView->setModel(inventoryModel);
ui->inventoryCheckView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->inventoryCheckView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->inventoryCheckView->setEditTriggers(QAbstractItemView::AllEditTriggers); ui->inventoryCheckView->setEditTriggers(QAbstractItemView::AllEditTriggers);
QIntOrNullValidator* intOrNullVal = new QIntOrNullValidator(INT_MIN, INT_MAX); QIntOrNullValidator* intOrNullVal = new QIntOrNullValidator(INT_MIN, INT_MAX);
MaybeValidatorDelegate* maybeValDel = new MaybeValidatorDelegate(intOrNullVal); MaybeValidatorDelegate* maybeValDel = new MaybeValidatorDelegate(intOrNullVal);
ui->inventoryCheckView->setItemDelegateForColumn(3, maybeValDel); ui->inventoryCheckView->setItemDelegateForColumn(5, maybeValDel);
ui->inventoryCheckView->hideColumn(0); ui->inventoryCheckView->hideColumn(0);
ui->inventoryCheckView->hideColumn(1);
ui->inventoryCheckView->hideColumn(2);
ui->quickInventoryCheckBox->setChecked(true);
// -------------------------------------------------- // --------------------------------------------------
...@@ -318,17 +325,30 @@ MainWindow::~MainWindow() ...@@ -318,17 +325,30 @@ MainWindow::~MainWindow()
delete ui; delete ui;
} }
/*
* This updates the QSqlTableModel to contain the same data as the database.
* This is needed since it looks at a view, and can therefore not find the
* primary key. Requiring all the fields in the front-end and back-end database
* to have the exact same fields.
*
* So, the calculations here are also done in the database, and those are what
* should be used when doing further stuff with the data.
*/
void MainWindow::on_inventoryModel_dataChanged(const QModelIndex &topLeft, const QModelIndex& /*bottomRight*/, const QVector<int>& /*roles*/) void MainWindow::on_inventoryModel_dataChanged(const QModelIndex &topLeft, const QModelIndex& /*bottomRight*/, const QVector<int>& /*roles*/)
{ {
if (topLeft.column() != 3) return; QSqlTableModel* model = (QSqlTableModel*) ui->inventoryCheckView->model();
DisablingModel* model = (DisablingModel*) ui->inventoryCheckView->model(); if (model->isDirty()) {
int row = topLeft.row(); int row = topLeft.row();
QModelIndex index = model->index(row, 2);
int expected = model->data(index).toInt(); bool number;
int actual = model->data(topLeft).toInt(); int expected = model->data(model->index(row, 4)).toInt();
int diff = actual - expected; int actual = model->data(model->index(row, 5)).toInt(&number);
model->setData(model->index(row, 4), diff);
model->setData(model->index(row, 6),
number ? actual - expected
: QVariant());
}
} }
/* /*
...@@ -715,7 +735,7 @@ void MainWindow::on_diffDrawer_textChanged(const QString &arg1) ...@@ -715,7 +735,7 @@ void MainWindow::on_diffDrawer_textChanged(const QString &arg1)
void MainWindow::on_submitStockDiff_clicked() void MainWindow::on_submitStockDiff_clicked()
{ {
QSqlQuery("DELETE FROM stock_diff_temp"); QSqlQuery("DELETE FROM diff_view");
((QSqlRelationalTableModel*) ui->inventoryCheckView->model())->select(); ((QSqlRelationalTableModel*) ui->inventoryCheckView->model())->select();
} }
...@@ -752,23 +772,19 @@ void MainWindow::open_till() ...@@ -752,23 +772,19 @@ void MainWindow::open_till()
port_write(port_name, '1'); port_write(port_name, '1');
} }
// TODO this function does what it should on the db layer
// but doesn't update the ui as expected
void MainWindow::on_stockDiffReset_clicked() void MainWindow::on_stockDiffReset_clicked()
{ {
clear_stockDiffTemp(); clear_stockDiffTemp();
// TODO TODO this doesn't update the ui for some reason! ((QSqlTableModel*) ui->inventoryCheckView->model())->select();
if (!((QSqlRelationalTableModel*) ui->stockView->model())->select())
qDebug() << __LINE__;
} }
// this also inserts the new needed data into stock_diff_temp // this also inserts the new needed data into diff_help
void MainWindow::clear_stockDiffTemp() void MainWindow::clear_stockDiffTemp()
{ {
// TODO transaction // TODO transaction
QSqlQuery("UPDATE my_db_settings SET value = 0 WHERE name = 'stock_diff_temp_transfer'"); QSqlQuery("UPDATE my_db_settings SET value = 0 WHERE name = 'stock_diff_temp_transfer'");
QSqlQuery("DELETE FROM stock_diff_temp"); QSqlQuery("DELETE FROM diff_help");
QSqlQuery("UPDATE my_db_settings SET value = 1 WHERE name = 'stock_diff_temp_transfer'"); QSqlQuery("UPDATE my_db_settings SET value = 1 WHERE name = 'stock_diff_temp_transfer'");
} }
...@@ -864,3 +880,22 @@ void MainWindow::on_productListSearch_textChanged(const QString &arg1) ...@@ -864,3 +880,22 @@ void MainWindow::on_productListSearch_textChanged(const QString &arg1)
{ {
setProductFilter(); setProductFilter();
} }
/*
* Note that the hidden rows can still contain data that WILL be
* submitted.
* TODO Possibly have some form of warning when it comes to that.
*/
void MainWindow::on_quickInventoryCheckBox_toggled(bool checked)
{
((QSqlRelationalTableModel*) ui->inventoryCheckView->model())
->setFilter( checked
? "sale_status = 0 or expected != 0"
: "sale_status in (0, 1)");
}
void MainWindow::on_moneyDiffReload_clicked()
{
ui->diffDrawer->setText("");
setMoneyDiffLabels();
}
...@@ -75,6 +75,10 @@ private slots: ...@@ -75,6 +75,10 @@ private slots:
void on_productListSearch_textChanged(const QString &arg1); void on_productListSearch_textChanged(const QString &arg1);
void on_quickInventoryCheckBox_toggled(bool checked);
void on_moneyDiffReload_clicked();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
QSqlTableModel* model; QSqlTableModel* model;
......
...@@ -280,6 +280,20 @@ ...@@ -280,6 +280,20 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QCheckBox" name="quickInventoryCheckBox">
<property name="text">
<string>Snabbläge</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="stockDiffReset"> <widget class="QPushButton" name="stockDiffReset">
<property name="sizePolicy"> <property name="sizePolicy">
...@@ -406,6 +420,13 @@ ...@@ -406,6 +420,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="moneyDiffReload">
<property name="text">
<string>Ladda om</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="diffSubmit"> <widget class="QPushButton" name="diffSubmit">
<property name="text"> <property name="text">
......
...@@ -102,21 +102,10 @@ CREATE TABLE stock_diff ( ...@@ -102,21 +102,10 @@ CREATE TABLE stock_diff (
FOREIGN KEY (product_id) REFERENCES products(id) FOREIGN KEY (product_id) REFERENCES products(id)
); );
/* CREATE TABLE diff_help (
* Table used in frontend
* Which is the reason for the redundant `diff` field.
*
* Note that the diff field is currently only updated in the frontent
*
* The expected_amount field should be nullable if null stock entries
* are to exist.
*/
CREATE TABLE stock_diff_temp (
id INTEGER PRIMARY KEY NOT NULL, id INTEGER PRIMARY KEY NOT NULL,
product_id INTEGER NOT NULL, product_id INTEGER NOT NULL,
expected_amount INTEGER NOT NULL, amount INTEGER,
actual_amount INTEGER,
diff INTEGER NOT NULL,
FOREIGN KEY (product_id) REFERENCES products(id) FOREIGN KEY (product_id) REFERENCES products(id)
); );
...@@ -212,6 +201,21 @@ WHERE datetime(log.time, 'localtime') BETWEEN ...@@ -212,6 +201,21 @@ WHERE datetime(log.time, 'localtime') BETWEEN
AND datetime('now', 'localtime', '-12 hours', 'start of day', '+36 hours') AND datetime('now', 'localtime', '-12 hours', 'start of day', '+36 hours')
ORDER BY log.time; ORDER BY log.time;
CREATE VIEW diff_view AS
SELECT d.id AS id,
p.id AS product_id,
p.sale_status AS sale_status,
-- visible border
p.name AS name,
s.active AS expected,
d.amount AS actual,
d.amount - s.active AS diff
FROM products p
INNER JOIN stock s ON p.id = s.product_id
INNER JOIN diff_help d ON p.id = d.product_id;
-- WHERE p.sale_status = 0 OR s.active != 0;
-- where p.sale_status in (0, 1);
-- ============================== Triggers =============================== -- ============================== Triggers ===============================
-- some of these would benefit from being FOR EACH STATEMENT instead of -- some of these would benefit from being FOR EACH STATEMENT instead of
...@@ -279,45 +283,40 @@ BEGIN ...@@ -279,45 +283,40 @@ BEGIN
SELECT id, price FROM current_products WHERE id = OLD.product_id; SELECT id, price FROM current_products WHERE id = OLD.product_id;
END; END;
CREATE TRIGGER after_stock_diff_temp_delete /*
AFTER DELETE ON stock_diff_temp * delete from the view to transfer,
* delete from the underlying table (diff_help) if
* a transfer is not desired.
*/
CREATE TRIGGER after_diff_help_delete_last
AFTER DELETE ON diff_help
FOR EACH ROW FOR EACH ROW
WHEN (SELECT value = 1 AND NOT (OLD.actual_amount IS NULL) WHEN (SELECT count (1) = 0 FROM diff_help)
FROM my_db_settings WHERE name = 'stock_diff_temp_transfer')
BEGIN BEGIN
UPDATE stock SET active = OLD.actual_amount WHERE product_id = OLD.product_id; INSERT INTO diff_help (product_id)
INSERT INTO stock_diff (product_id, expected, actual) SELECT id FROM products p;
SELECT OLD.product_id, OLD.expected_amount, OLD.actual_amount;
END; END;
CREATE TRIGGER after_stock_diff_temp_delete_last CREATE TRIGGER diff_view_transfer
AFTER DELETE ON stock_diff_temp INSTEAD OF DELETE ON diff_view
FOR EACH ROW
WHEN (SELECT count (1) = 0 FROM stock_diff_temp)
BEGIN BEGIN
INSERT INTO stock_diff_temp (product_id, expected_amount, diff) UPDATE stock SET active = OLD.actual
SELECT p.id, s.active, - s.active WHERE OLD.actual IS NOT NULL
FROM products p INNER JOIN stock s ON p.id = s.product_id AND product_id = OLD.product_id;
WHERE p.sale_status IN (0, 1);
INSERT INTO stock_diff (product_id, expected, actual)
SELECT OLD.product_id, OLD.expected, OLD.actual
WHERE OLD.actual IS NOT NULL;
DELETE FROM diff_help WHERE id = OLD.id;
END; END;
/* CREATE TRIGGER update_view_trigger
* This trigger is NOT loaded due to updates in the db INSTEAD OF UPDATE ON diff_view
* not causing GUI updates.
* The exact same functionality is currently present in
* the GUI logic.
*/
/*
CREATE TRIGGER stock_diff_temp_diff
AFTER UPDATE OF expected_amount, actual_amount
ON stock_diff_temp
FOR EACH ROW
BEGIN BEGIN
UPDATE stock_diff_temp UPDATE diff_help SET amount = NEW.actual WHERE id = NEW.id;
SET diff = NEW.actual_amount - NEW.expected_amount
WHERE id = NEW.id;
END; END;
*/
CREATE TRIGGER before_money_transfer CREATE TRIGGER before_money_transfer
BEFORE INSERT ON money_transfers BEFORE INSERT ON money_transfers
...@@ -359,12 +358,16 @@ INSERT INTO money (name) ...@@ -359,12 +358,16 @@ INSERT INTO money (name)
VALUES ("Cash Drawer"), ("Vault"); VALUES ("Cash Drawer"), ("Vault");
INSERT INTO my_db_settings (name, value) INSERT INTO my_db_settings (name, value)
VALUES ("stock_diff_temp_transfer", 1), VALUES ("big_buy_transfer", 1),
("big_buy_transfer", 1),
("acquisitions_temp_transfer", 1); ("acquisitions_temp_transfer", 1);
-- stock_diff_temp gets it's data when the last record is deleted, -- This is mostly so that diff_help have something to insert
INSERT INTO products (name)
VALUES ("Default Product");
-- diff_help gets it's data when the last record is deleted,
-- so insert *any* data and delete it. -- so insert *any* data and delete it.
INSERT INTO stock_diff_temp (product_id, expected_amount, diff)
SELECT id, 0, 0 FROM products LIMIT 1; INSERT INTO diff_help (product_id)
DELETE FROM stock_diff_temp; SELECT id FROM products LIMIT 1;
DELETE FROM diff_help;
/*
* This file is to patch up an already existing SQLite file
* to use the new product diffing system
*/
CREATE TABLE diff_help (
id INTEGER PRIMARY KEY NOT NULL,
product_id INTEGER NOT NULL,
amount INTEGER,
FOREIGN KEY (product_id) REFERENCES products(id)
);
CREATE VIEW diff_view AS
SELECT d.id AS id,
p.id AS product_id,
p.sale_status AS sale_status,
-- visible border
p.name AS name,
s.active AS expected,
d.amount AS actual,
d.amount - s.active AS diff
FROM products p
INNER JOIN stock s ON p.id = s.product_id
INNER JOIN diff_help d ON p.id = d.product_id;
CREATE TRIGGER after_diff_help_delete_last
AFTER DELETE ON diff_help
FOR EACH ROW
WHEN (SELECT count (1) = 0 FROM diff_help)
BEGIN
INSERT INTO diff_help (product_id)
SELECT id FROM products p;
END;
CREATE TRIGGER diff_view_transfer
INSTEAD OF DELETE ON diff_view
BEGIN
UPDATE stock SET active = OLD.actual
WHERE OLD.actual IS NOT NULL
AND product_id = OLD.product_id;
INSERT INTO stock_diff (product_id, expected, actual)
SELECT OLD.product_id, OLD.expected, OLD.actual
WHERE OLD.actual IS NOT NULL;
DELETE FROM diff_help WHERE id = OLD.id;
END;
CREATE TRIGGER update_view_trigger
INSTEAD OF UPDATE ON diff_view
BEGIN
UPDATE diff_help SET amount = NEW.actual WHERE id = NEW.id;
END;
INSERT INTO diff_help (product_id)
SELECT id FROM products LIMIT 1;
DELETE FROM diff_help;
https://github.com/openwebos/qt/blob/92fde5feca3d792dfd775348ca59127204ab4ac0/src/sql/models/qsqltablemodel.cpp#L529
https://stackoverflow.com/questions/45209701/qt-updating-database-model-through-view
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment