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 @@
#include <QModelIndexList>
#include <QSqlError>
#include <QSqlIndex>
#include <QSqlRelationalDelegate>
#include <QHeaderView>
......@@ -239,28 +240,34 @@ MainWindow::MainWindow(QWidget *parent) :
clear_stockDiffTemp();
QList<int> inventoryCheckDisabled;
inventoryCheckDisabled << 0 << 1 << 2 << 4;
inventoryCheckDisabled << 0 << 1 << 2 << 3 << 4 << 6;
DisablingModel* inventoryModel = new DisablingModel(inventoryCheckDisabled);
inventoryModel->setTable("stock_diff_temp");
inventoryModel->setTable("diff_view");
inventoryModel->setHeaderData(0, Qt::Horizontal, "id");
inventoryModel->setHeaderData(1, Qt::Horizontal, "Namn");
inventoryModel->setHeaderData(2, Qt::Horizontal, "Förväntat");
inventoryModel->setHeaderData(3, Qt::Horizontal, "Faktiskt");
inventoryModel->setHeaderData(4, Qt::Horizontal, "Diff");
inventoryModel->setHeaderData(1, Qt::Horizontal, "product id");
inventoryModel->setHeaderData(2, Qt::Horizontal, "sale status");
inventoryModel->setHeaderData(3, Qt::Horizontal, "Namn");
inventoryModel->setHeaderData(4, Qt::Horizontal, "Förväntat");
inventoryModel->setHeaderData(5, Qt::Horizontal, "Faktiskt");
inventoryModel->setHeaderData(6, Qt::Horizontal, "Diff");
inventoryModel->setEditStrategy(QSqlTableModel::OnFieldChange);
QObject::connect(inventoryModel, &QAbstractItemModel::dataChanged,
this, &MainWindow::on_inventoryModel_dataChanged);
inventoryModel->setRelation(1, QSqlRelation("products", "id", "name"));
inventoryModel->select();
ui->inventoryCheckView->setModel(inventoryModel);
ui->inventoryCheckView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->inventoryCheckView->setEditTriggers(QAbstractItemView::AllEditTriggers);
QIntOrNullValidator* intOrNullVal = new QIntOrNullValidator(INT_MIN, INT_MAX);
MaybeValidatorDelegate* maybeValDel = new MaybeValidatorDelegate(intOrNullVal);
ui->inventoryCheckView->setItemDelegateForColumn(3, maybeValDel);
ui->inventoryCheckView->setItemDelegateForColumn(5, maybeValDel);
ui->inventoryCheckView->hideColumn(0);
ui->inventoryCheckView->hideColumn(1);
ui->inventoryCheckView->hideColumn(2);
ui->quickInventoryCheckBox->setChecked(true);
// --------------------------------------------------
......@@ -318,17 +325,30 @@ MainWindow::~MainWindow()
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*/)
{
if (topLeft.column() != 3) return;
QSqlTableModel* model = (QSqlTableModel*) ui->inventoryCheckView->model();
DisablingModel* model = (DisablingModel*) ui->inventoryCheckView->model();
int row = topLeft.row();
QModelIndex index = model->index(row, 2);
int expected = model->data(index).toInt();
int actual = model->data(topLeft).toInt();
int diff = actual - expected;
model->setData(model->index(row, 4), diff);
if (model->isDirty()) {
int row = topLeft.row();
bool number;
int expected = model->data(model->index(row, 4)).toInt();
int actual = model->data(model->index(row, 5)).toInt(&number);
model->setData(model->index(row, 6),
number ? actual - expected
: QVariant());
}
}
/*
......@@ -715,7 +735,7 @@ void MainWindow::on_diffDrawer_textChanged(const QString &arg1)
void MainWindow::on_submitStockDiff_clicked()
{
QSqlQuery("DELETE FROM stock_diff_temp");
QSqlQuery("DELETE FROM diff_view");
((QSqlRelationalTableModel*) ui->inventoryCheckView->model())->select();
}
......@@ -752,23 +772,19 @@ void MainWindow::open_till()
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()
{
clear_stockDiffTemp();
// TODO TODO this doesn't update the ui for some reason!
if (!((QSqlRelationalTableModel*) ui->stockView->model())->select())
qDebug() << __LINE__;
((QSqlTableModel*) ui->inventoryCheckView->model())->select();
}
// 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()
{
// TODO transaction
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'");
}
......@@ -864,3 +880,22 @@ void MainWindow::on_productListSearch_textChanged(const QString &arg1)
{
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:
void on_productListSearch_textChanged(const QString &arg1);
void on_quickInventoryCheckBox_toggled(bool checked);
void on_moneyDiffReload_clicked();
private:
Ui::MainWindow *ui;
QSqlTableModel* model;
......
......@@ -280,6 +280,20 @@
</item>
<item>
<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>
<widget class="QPushButton" name="stockDiffReset">
<property name="sizePolicy">
......@@ -406,6 +420,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="moneyDiffReload">
<property name="text">
<string>Ladda om</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="diffSubmit">
<property name="text">
......
......@@ -102,21 +102,10 @@ CREATE TABLE stock_diff (
FOREIGN KEY (product_id) REFERENCES products(id)
);
/*
* 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 (
CREATE TABLE diff_help (
id INTEGER PRIMARY KEY NOT NULL,
product_id INTEGER NOT NULL,
expected_amount INTEGER NOT NULL,
actual_amount INTEGER,
diff INTEGER NOT NULL,
amount INTEGER,
FOREIGN KEY (product_id) REFERENCES products(id)
);
......@@ -212,6 +201,21 @@ WHERE datetime(log.time, 'localtime') BETWEEN
AND datetime('now', 'localtime', '-12 hours', 'start of day', '+36 hours')
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 ===============================
-- some of these would benefit from being FOR EACH STATEMENT instead of
......@@ -279,45 +283,40 @@ BEGIN
SELECT id, price FROM current_products WHERE id = OLD.product_id;
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
WHEN (SELECT value = 1 AND NOT (OLD.actual_amount IS NULL)
FROM my_db_settings WHERE name = 'stock_diff_temp_transfer')
WHEN (SELECT count (1) = 0 FROM diff_help)
BEGIN
UPDATE stock SET active = OLD.actual_amount WHERE product_id = OLD.product_id;
INSERT INTO stock_diff (product_id, expected, actual)
SELECT OLD.product_id, OLD.expected_amount, OLD.actual_amount;
INSERT INTO diff_help (product_id)
SELECT id FROM products p;
END;
CREATE TRIGGER after_stock_diff_temp_delete_last
AFTER DELETE ON stock_diff_temp
FOR EACH ROW
WHEN (SELECT count (1) = 0 FROM stock_diff_temp)
CREATE TRIGGER diff_view_transfer
INSTEAD OF DELETE ON diff_view
BEGIN
INSERT INTO stock_diff_temp (product_id, expected_amount, diff)
SELECT p.id, s.active, - s.active
FROM products p INNER JOIN stock s ON p.id = s.product_id
WHERE p.sale_status IN (0, 1);
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;
/*
* This trigger is NOT loaded due to updates in the db
* 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
CREATE TRIGGER update_view_trigger
INSTEAD OF UPDATE ON diff_view
BEGIN
UPDATE stock_diff_temp
SET diff = NEW.actual_amount - NEW.expected_amount
WHERE id = NEW.id;
UPDATE diff_help SET amount = NEW.actual WHERE id = NEW.id;
END;
*/
CREATE TRIGGER before_money_transfer
BEFORE INSERT ON money_transfers
......@@ -359,12 +358,16 @@ INSERT INTO money (name)
VALUES ("Cash Drawer"), ("Vault");
INSERT INTO my_db_settings (name, value)
VALUES ("stock_diff_temp_transfer", 1),
("big_buy_transfer", 1),
VALUES ("big_buy_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.
INSERT INTO stock_diff_temp (product_id, expected_amount, diff)
SELECT id, 0, 0 FROM products LIMIT 1;
DELETE FROM stock_diff_temp;
INSERT INTO diff_help (product_id)
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