diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d9c235..ead4efc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,10 +17,14 @@ set(TS_FILES beerlog_ru_RU.ts)
set(PROJECT_SOURCES
main.cpp
qml/qml.qrc
- models/abstractmodel.h models/abstractmodel.cpp
- models/summarymodel.h models/summarymodel.cpp
+ models/basemodel.h models/basemodel.cpp
+ models/ordersmodel.h models/ordersmodel.cpp
models/usersmodel.h models/usersmodel.cpp
viewmodels/usersviewmodel.h viewmodels/usersviewmodel.cpp
+ viewmodels/storesviewmodel.h viewmodels/storesviewmodel.cpp
+ viewmodels/productsviewmodel.h viewmodels/productsviewmodel.cpp
+ viewmodels/ordersviewmodel.h viewmodels/ordersviewmodel.cpp
+ viewmodels/settingsviewmodel.h viewmodels/settingsviewmodel.cpp
services/beerservice.h services/beerservice.cpp
services/settingsservice.h services/settingsservice.cpp
${TS_FILES}
@@ -55,8 +59,6 @@ else()
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()
-qt_add_translations(beerlog TS_FILES beerlog_ru_RU.ts)
-
target_link_libraries(beerlog
PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::WebSockets)
diff --git a/beerlog_ru_RU.ts b/beerlog_ru_RU.ts
index 2dd95d2..81c3c85 100644
--- a/beerlog_ru_RU.ts
+++ b/beerlog_ru_RU.ts
@@ -14,6 +14,27 @@
Не в сети
+
+ OrdersView
+
+
+ Summary: %1
+
+
+
+
+ ProductsView
+
+
+ Summary: %1
+
+
+
+
+ Order
+
+
+
SettingsView
@@ -40,24 +61,29 @@
-
+
BeerLog v0.1
BeerLog v1.0.0
-
+
+ Orders
+
+
+
+
Settings
Настройки
-
-
+
+
Quit
Выход
-
+
Realy quit the application?
Действительно выйти из приложения?
diff --git a/main.cpp b/main.cpp
index d0be815..1e2b52e 100644
--- a/main.cpp
+++ b/main.cpp
@@ -6,6 +6,11 @@
#include
#include "viewmodels/usersviewmodel.h"
+#include "viewmodels/productsviewmodel.h"
+#include "viewmodels/ordersviewmodel.h"
+#include "viewmodels/storesviewmodel.h"
+#include "viewmodels/settingsviewmodel.h"
+
#include "services/beerservice.h"
#include "services/settingsservice.h"
@@ -17,11 +22,6 @@ int main(int argc, char *argv[])
QGuiApplication app(argc, argv);
QTranslator translator;
-
- if (!translator.load("qt_ru.qm", "qrc:/")) {
- qWarning() << "Cant load";
- }
-
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = "beerlog_" + QLocale(locale).name();
@@ -45,6 +45,10 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("settingsService", SettingsService::instance());
qmlRegisterType("ru.ded.beerlog", 1, 0, "UsersViewModel");
+ qmlRegisterType("ru.ded.beerlog", 1, 0, "ProductsViewModel");
+ qmlRegisterType("ru.ded.beerlog", 1, 0, "OrdersViewModel");
+ qmlRegisterType("ru.ded.beerlog", 1, 0, "StoresViewModel");
+ qmlRegisterType("ru.ded.beerlog", 1, 0, "SettingsViewModel");
engine.load(url);
diff --git a/models/abstractmodel.cpp b/models/abstractmodel.cpp
deleted file mode 100644
index f73ccce..0000000
--- a/models/abstractmodel.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "abstractmodel.h"
-
-#include "services/beerservice.h"
-
-AbstractModel::AbstractModel(QObject *parent)
- : QObject{parent}
-{
-}
-
-void AbstractModel::created(const QVariant &data)
-{
- modified(data);
-}
-
-void AbstractModel::modified(const QVariant &data)
-{
- QVariantMap d = data.toMap();
- m_data[d.value("id").toString()] = d;
-
- emit dataChanged();
-}
-
-void AbstractModel::deleted(const QVariant &data)
-{
- QString id = data.toString();
- m_data.remove(id);
-
- emit dataChanged();
-}
-
-void AbstractModel::received(const QVariant &data)
-{
- m_data = data.toMap();
-
- emit dataChanged();
-}
-
-BeerService *AbstractModel::service() const
-{
- return BeerService::instance();
-}
diff --git a/models/abstractmodel.h b/models/abstractmodel.h
deleted file mode 100644
index b41ae67..0000000
--- a/models/abstractmodel.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef ABSTRACTMODEL_H
-#define ABSTRACTMODEL_H
-
-#include
-#include
-
-class BeerService;
-class AbstractModel : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QString entity READ entity CONSTANT)
-
-public:
- explicit AbstractModel(QObject *parent = nullptr);
-
- virtual QString entity() const = 0;
-
-public slots:
- void created(const QVariant &data);
- void modified(const QVariant &data);
- void deleted(const QVariant &data);
- void received(const QVariant &data);
-
-signals:
- void dataChanged();
-
-protected:
- BeerService *service() const;
-
- QVariantMap m_data;
-};
-
-#endif // ABSTRACTMODEL_H
diff --git a/models/basemodel.cpp b/models/basemodel.cpp
new file mode 100644
index 0000000..14a9f68
--- /dev/null
+++ b/models/basemodel.cpp
@@ -0,0 +1,90 @@
+#include "basemodel.h"
+
+#include "services/beerservice.h"
+
+BaseModel::BaseModel(const QString &entity, QObject *parent) : QObject{parent},
+ m_entity(entity)
+{
+ Q_ASSERT(!m_entity.isEmpty());
+
+ service()->connectListener(this);
+ service()->sendCommand(m_entity, BeerService::ActionGet);
+}
+
+BaseModel::~BaseModel()
+{
+ service()->removeListener(this);
+}
+
+QString BaseModel::entity() const
+{
+ return m_entity;
+}
+
+QVariantList BaseModel::items() const
+{
+ return m_data.values();
+}
+
+QVariantMap BaseModel::item(const QString &itemId) const
+{
+ return m_data.value(itemId).toMap();
+}
+
+QVariant BaseModel::itemProperty(const QString &itemId, const QString &propertyName, const QVariant &def) const
+{
+ return item(itemId).value(propertyName, def);
+}
+
+void BaseModel::addItem(const QVariantMap &item) const
+{
+ service()->sendCommand(entity(), BeerService::ActionAdd, item);
+}
+
+void BaseModel::deleteItem(const QString &itemId) const
+{
+ service()->sendCommand(entity(), BeerService::ActionDelete, QVariantMap { { "id", itemId } });
+}
+
+void BaseModel::modifyItem(const QString &itemId, const QVariantMap &properties) const
+{
+ QVariantMap item = this->item(itemId);
+ for (auto it = properties.constBegin(); it != properties.constEnd(); ++it) {
+ item[it.key()] = it.value();
+ }
+
+ service()->sendCommand(entity(), BeerService::ActionModify, item);
+}
+
+void BaseModel::created(const QVariant &data)
+{
+ modified(data);
+}
+
+void BaseModel::modified(const QVariant &data)
+{
+ QVariantMap d = data.toMap();
+ m_data[d.value("id").toString()] = d;
+
+ emit dataChanged();
+}
+
+void BaseModel::deleted(const QVariant &data)
+{
+ QString id = data.toString();
+ m_data.remove(id);
+
+ emit dataChanged();
+}
+
+void BaseModel::received(const QVariant &data)
+{
+ m_data = data.toMap();
+
+ emit dataChanged();
+}
+
+BeerService *BaseModel::service() const
+{
+ return BeerService::instance();
+}
diff --git a/models/basemodel.h b/models/basemodel.h
new file mode 100644
index 0000000..0c50afb
--- /dev/null
+++ b/models/basemodel.h
@@ -0,0 +1,44 @@
+#ifndef BASEMODEL_H
+#define BASEMODEL_H
+
+#include
+#include
+
+class BeerService;
+class BaseModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString entity READ entity CONSTANT)
+
+public:
+ explicit BaseModel(const QString &entity, QObject *parent = nullptr);
+ virtual ~BaseModel();
+
+ QString entity() const;
+
+ QVariantList items() const;
+ QVariantMap item(const QString &itemId) const;
+ QVariant itemProperty(const QString &itemId, const QString &propertyName, const QVariant &def = QVariant{}) const;
+
+ void addItem(const QVariantMap &item) const;
+ void deleteItem(const QString &itemId) const;
+ void modifyItem(const QString &itemId, const QVariantMap &properties) const;
+
+public slots:
+ void created(const QVariant &data);
+ void modified(const QVariant &data);
+ void deleted(const QVariant &data);
+ void received(const QVariant &data);
+
+signals:
+ void dataChanged();
+
+private:
+ BeerService *service() const;
+
+ QVariantMap m_data;
+ QString m_entity;
+};
+
+#endif // BASEMODEL_H
diff --git a/models/ordersmodel.cpp b/models/ordersmodel.cpp
new file mode 100644
index 0000000..aa51830
--- /dev/null
+++ b/models/ordersmodel.cpp
@@ -0,0 +1,15 @@
+#include "ordersmodel.h"
+
+OrdersModel::OrdersModel(QObject *parent) : BaseModel { "orders", parent }
+{
+}
+
+QVariantList OrdersModel::orders() const
+{
+ return items();
+}
+
+void OrdersModel::submitOrder(const QVariantMap &order) const
+{
+ addItem(order);
+}
diff --git a/models/ordersmodel.h b/models/ordersmodel.h
new file mode 100644
index 0000000..84f26ae
--- /dev/null
+++ b/models/ordersmodel.h
@@ -0,0 +1,17 @@
+#ifndef ORDERSMODEL_H
+#define ORDERSMODEL_H
+
+#include "basemodel.h"
+
+class OrdersModel : public BaseModel
+{
+ Q_OBJECT
+
+public:
+ explicit OrdersModel(QObject *parent = nullptr);
+
+ QVariantList orders() const;
+ void submitOrder(const QVariantMap &order) const;
+};
+
+#endif // ORDERSMODEL_H
diff --git a/models/summarymodel.cpp b/models/summarymodel.cpp
deleted file mode 100644
index 71c26be..0000000
--- a/models/summarymodel.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "summarymodel.h"
-
-QVariantList SummaryModel::items() const
-{
- return m_items.values();
-}
-
-float SummaryModel::sum() const
-{
- float res = 0.0;
- for (auto it = m_items.constBegin(); it != m_items.constEnd(); ++it) {
- QVariantMap item = it.value().toMap();
- int count = item.value("count", 0).toInt();
- float price = item.value("cost", 0.0).toFloat();
- res += count * price;
- }
- return res;
-}
-
-void SummaryModel::setItemCount(QVariantMap item, int count)
-{
- QString id = item.value("id", QString()).toString();
-
- if (count) {
- item["count"] = count;
- m_items[id] = item;
- } else {
- m_items.remove(id);
- }
-
- emit itemsChanged();
-}
-
-void SummaryModel::clear()
-{
- m_items.clear();
-
- emit itemsChanged();
-}
diff --git a/models/summarymodel.h b/models/summarymodel.h
deleted file mode 100644
index 8dd6add..0000000
--- a/models/summarymodel.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef SUMMARYMODEL_H
-#define SUMMARYMODEL_H
-
-#include
-#include
-
-class SummaryModel : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QVariantList items READ items NOTIFY itemsChanged)
- Q_PROPERTY(float sum READ sum NOTIFY itemsChanged)
-
-public:
- QVariantList items() const;
- float sum() const;
-
- Q_INVOKABLE void setItemCount(QVariantMap item, int count);
- Q_INVOKABLE void clear();
-
-signals:
- void itemsChanged();
-
-private:
- QVariantMap m_items;
-};
-
-#endif // SUMMARYMODEL_H
diff --git a/models/usersmodel.cpp b/models/usersmodel.cpp
index 197bb9d..f8f799e 100644
--- a/models/usersmodel.cpp
+++ b/models/usersmodel.cpp
@@ -1,24 +1,13 @@
#include "usersmodel.h"
-#include "services/beerservice.h"
-
namespace Keys {
-constexpr auto Users = "users";
constexpr auto Name = "name";
}
-UsersModel::UsersModel(QObject *parent)
- : AbstractModel{parent}
+UsersModel::UsersModel(QObject *parent) : BaseModel{ "users", parent }
{
- service()->connectListener(this);
- service()->sendCommand(Keys::Users, "get");
-}
-
-QString UsersModel::entity() const
-{
- return Keys::Users;
}
void UsersModel::connected(const QVariant &data)
@@ -33,10 +22,5 @@ void UsersModel::disconnected(const QVariant &data)
QVariantList UsersModel::users() const
{
- return m_data.values();
-}
-
-QString UsersModel::userName(const QString &userId) const
-{
- return m_data.value(userId).toMap().value(Keys::Name).toString();
+ return items();
}
diff --git a/models/usersmodel.h b/models/usersmodel.h
index 382de44..8c14b48 100644
--- a/models/usersmodel.h
+++ b/models/usersmodel.h
@@ -4,19 +4,16 @@
#include
#include
-#include "models/abstractmodel.h"
+#include "models/basemodel.h"
-class UsersModel : public AbstractModel
+class UsersModel : public BaseModel
{
Q_OBJECT
public:
explicit UsersModel(QObject *parent = nullptr);
- QString entity() const override;
-
QVariantList users() const;
- QString userName(const QString &userId) const;
public slots:
void connected(const QVariant &data);
diff --git a/qml/Views/OrdersView.qml b/qml/Views/OrdersView.qml
index 9dec0d1..9370cac 100644
--- a/qml/Views/OrdersView.qml
+++ b/qml/Views/OrdersView.qml
@@ -1,12 +1,63 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+import ru.ded.beerlog 1.0
Page {
- Label {
- anchors.centerIn: parent
+ title: qsTr("Orders")
- text: "Orders"
+ OrdersViewModel {
+ id: ordersModel
}
+ ListView {
+ id: ordersList
+
+ anchors.fill: parent
+ anchors.margins: 10
+
+ model: ordersModel
+
+ section.criteria: ViewSection.FullString
+ section.property: "date"
+ section.delegate: Label {
+ text: section
+ font.bold: true
+ }
+
+ delegate: Column {
+ width: ordersList.width
+ height: productsList.heigt
+
+ ListView {
+ id: productsList
+
+ width: ordersList.width
+ height: contentHeight
+
+ model: products
+
+ header: Label {
+ padding: 10
+ font.bold: true
+ text: "%1 (%2), %3".arg(userName).arg(storeName).arg(time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat))
+ }
+
+ delegate: Label {
+ width: ordersList.width
+ leftPadding: 20
+
+ text: "%1 x%2, %3".arg(modelData.product).arg(modelData.quantity.toLocaleString(Qt.locale())).arg(modelData.price.toLocaleCurrencyString(Qt.locale()))
+ }
+
+ footer: Label {
+ padding: 10
+ font.bold: true
+ text: qsTr("Summary: %1").arg(amount.toLocaleCurrencyString(Qt.locale()))
+ }
+ }
+ }
+ }
}
diff --git a/qml/Views/ProductsView.qml b/qml/Views/ProductsView.qml
new file mode 100644
index 0000000..a40e38c
--- /dev/null
+++ b/qml/Views/ProductsView.qml
@@ -0,0 +1,106 @@
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+import ru.ded.beerlog 1.0
+
+Page {
+
+ ProductsViewModel {
+ id: productsModel
+ }
+
+ RowLayout {
+ anchors.fill: parent
+
+ ListView {
+ id: productsList
+
+ Layout.fillWidth: true
+ Layout.preferredHeight: parent.height
+
+ model: productsModel.products
+
+ delegate: ItemDelegate {
+ width: productsList.width
+
+ text: modelData.name
+ height: spinbox.height
+
+ SpinBox {
+ id: spinbox
+
+ width: 150
+ from: 0
+ to: 100 * 100
+ stepSize: 50
+ anchors.right: parent.right
+ editable: true
+
+ property int decimals: 1
+ property real realValue: value / 100
+
+ validator: DoubleValidator {
+ bottom: Math.min(spinbox.from, spinbox.to)
+ top: Math.max(spinbox.from, spinbox.to)
+ }
+
+ textFromValue: function(value, locale) {
+ return value === 0 ? "" : Number(value / 100).toLocaleString(locale, 'f', spinbox.decimals)
+ }
+
+ valueFromText: function(text, locale) {
+ return Number.fromLocaleString(locale, text) * 100
+ }
+
+ onRealValueChanged: productsModel.setOrderValue(modelData.id, realValue)
+ }
+ }
+
+ function reload() {
+ model = undefined
+ model = productsModel.products
+ }
+ }
+
+ ListView {
+ id: orderList
+
+ Layout.minimumWidth: parent.width * 0.3
+ Layout.preferredHeight: parent.height
+
+ model: productsModel.order
+
+ delegate: ItemDelegate {
+ width: orderList.width
+
+ text: "%1 x%2".arg(modelData.name).arg(modelData.count.toLocaleString(Qt.locale()))
+ }
+
+ footer: Column {
+ width: orderList.width
+
+ Label {
+ anchors.right: parent.right
+ anchors.margins: 10
+
+ text: qsTr("Summary: %1").arg(productsModel.orderSum.toLocaleCurrencyString(Qt.locale()))
+ }
+
+ Button {
+ anchors.right: parent.right
+ anchors.margins: 10
+
+ text: qsTr("Order")
+
+ enabled: productsModel.orderSum > 0
+
+ onClicked: {
+ productsModel.submitOrder()
+ productsList.reload()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/qml/Views/SettingsView.qml b/qml/Views/SettingsView.qml
index ff23454..629ea37 100644
--- a/qml/Views/SettingsView.qml
+++ b/qml/Views/SettingsView.qml
@@ -1,24 +1,20 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import Components 1.0
+import ru.ded.beerlog 1.0
Page {
id: root
title: qsTr("Settings")
- ListModel {
+ property var controls: {
+ "text": textComponent,
+ "choice": choiceComponent
+ }
+
+ SettingsViewModel {
id: settingsModel
-
- ListElement {
- title: qsTr("BeerLog service address")
- name: "serverAddress"
- }
-
- ListElement {
- title: qsTr("Selected user id")
- name: "selectedUserId"
- }
}
ListView {
@@ -35,6 +31,8 @@ Page {
onClicked: {
inputDialog.title = model.title
inputDialog.name = model.name
+ inputDialog.control = controls[model.control]
+ inputDialog.choiceModel = model.choiceModel
inputDialog.open()
}
}
@@ -44,6 +42,8 @@ Page {
id: inputDialog
property string name: ""
+ property var choiceModel: undefined
+ property alias control: editor.sourceComponent
width: root.width * 0.8
@@ -53,22 +53,54 @@ Page {
modal: true
standardButtons: Dialog.Ok | Dialog.Cancel
- Column {
- spacing: 20
+ Loader {
+ id: editor
+
+ property var value: item && item.value
+
anchors.fill: parent
-
- TextField {
- id: textField
-
- width: parent.width
- inputMethodHints: Qt.ImhNoAutoUppercase
- placeholderText: inputDialog.title
- text: settingsService[inputDialog.name] || ""
- }
}
onAccepted: {
- settingsService[inputDialog.name] = textField.text
+ settingsService[inputDialog.name] = editor.value
+ }
+ }
+
+ Component {
+ id: textComponent
+
+ TextField {
+ id: textField
+
+ property alias value: textField.text
+
+ inputMethodHints: Qt.ImhNoAutoUppercase
+ text: settingsService[inputDialog.name] || ""
+ }
+ }
+
+ ButtonGroup {
+ id: group
+ }
+
+ Component {
+ id: choiceComponent
+
+ Column {
+
+ property var value: group.checkedButton && group.checkedButton.valueId
+
+ Repeater {
+ model: inputDialog.choiceModel
+
+ delegate: RadioDelegate {
+ property var valueId: modelData.id
+ text: modelData.name
+ width: parent.width
+ checked: settingsService[inputDialog.name] === valueId
+ ButtonGroup.group: group
+ }
+ }
}
}
}
diff --git a/qml/main.qml b/qml/main.qml
index a273d35..a320826 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -26,6 +26,7 @@ ApplicationWindow {
Label {
text: stackView.currentItem.title || usersModel.selectedUserName
Layout.fillWidth: true
+ Layout.minimumWidth: 100
horizontalAlignment: Qt.AlignCenter
MouseArea {
@@ -55,24 +56,46 @@ ApplicationWindow {
}
}
}
+
+ Menu {
+ id: storesMenu
+
+ StoresViewModel {
+ id: storesModel
+ }
+
+ Repeater {
+ model: storesModel.stores
+
+ MenuItem {
+ text: modelData.name
+
+ onClicked: {
+ storesModel.selectedStore = modelData.id
+ }
+ }
+ }
+ }
}
MainMenu {
id: mainMenu
readonly property var actions: {
+ "orders": () => { stackView.openPage("Views/OrdersView.qml") },
"settings": () => { stackView.openPage("Views/SettingsView.qml") },
"quit": () => { Qt.quit() }
}
- width: parent.width * 0.66
- height: parent.height
-
logo: "qrc:/logo.png"
appName: qsTr("BeerLog v0.1")
connected: beerService.connected
model: ListModel {
+ ListElement {
+ title: qsTr("Orders")
+ action: "orders"
+ }
ListElement {
title: qsTr("Settings")
action: "settings"
@@ -88,7 +111,7 @@ ApplicationWindow {
StackView {
id: stackView
- initialItem: "Views/OrdersView.qml"
+ initialItem: "Views/ProductsView.qml"
anchors.fill: parent
function openPage(page) {
diff --git a/qml/qml.qrc b/qml/qml.qrc
index ca1e243..52af8c3 100644
--- a/qml/qml.qrc
+++ b/qml/qml.qrc
@@ -11,5 +11,6 @@
Components/SubtitledItemDelegate.qml
Components/qmldir
qt_ru.qm
+ Views/ProductsView.qml
diff --git a/services/beerservice.cpp b/services/beerservice.cpp
index 823fcbb..b36c328 100644
--- a/services/beerservice.cpp
+++ b/services/beerservice.cpp
@@ -12,6 +12,13 @@
BeerService::BeerService()
: QObject{nullptr}
{
+ m_actions = {
+ { ActionGet, "get" },
+ { ActionAdd, "add" },
+ { ActionDelete, "del" },
+ { ActionModify, "mod" }
+ };
+
restoreStash();
connect(&m_socket, &QWebSocket::textMessageReceived, this, [this](QString message) {
@@ -31,7 +38,7 @@ BeerService::BeerService()
}
});
- connect(&m_socket, QOverload::of(&QWebSocket::error), this, [this](QAbstractSocket::SocketError error) {
+ connect(&m_socket, &QWebSocket::errorOccurred, this, [this](QAbstractSocket::SocketError error) {
qInfo() << error << m_socket.errorString();
});
@@ -60,11 +67,13 @@ SettingsService *BeerService::settings() const
return SettingsService::instance();
}
-void BeerService::sendCommand(const QString &entity, const QString &action, const QVariantMap &data)
+void BeerService::sendCommand(const QString &entity, Action action, const QVariantMap &data)
{
+ Q_ASSERT(action != ActionUndefined);
+
sendCommand(QVariantMap {
{ "entity", entity },
- { "action", action },
+ { "action", m_actions[action] },
{ "data", data }
});
}
@@ -75,6 +84,12 @@ void BeerService::connectListener(QObject *listener)
m_listeners.insert(entity, listener);
}
+void BeerService::removeListener(QObject *listener)
+{
+ QString entity = listener->property("entity").toString();
+ m_listeners.remove(entity, listener);
+}
+
QString BeerService::stashFileName() const
{
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/command.stash";
@@ -102,6 +117,7 @@ void BeerService::restoreStash()
QJsonDocument doc = QJsonDocument::fromJson(stash.readAll());
m_commandStash = doc.array().toVariantList();
stash.close();
+ stash.remove();
} else {
qWarning() << stash.errorString();
}
diff --git a/services/beerservice.h b/services/beerservice.h
index 6eb506f..d0ae8d3 100644
--- a/services/beerservice.h
+++ b/services/beerservice.h
@@ -18,8 +18,18 @@ public:
return &i;
}
- void sendCommand(const QString &entity, const QString &action, const QVariantMap &data = QVariantMap());
+ enum Action
+ {
+ ActionUndefined = 0,
+ ActionGet,
+ ActionAdd,
+ ActionDelete,
+ ActionModify
+ };
+
+ void sendCommand(const QString &entity, Action action, const QVariantMap &data = QVariantMap());
void connectListener(QObject *listener);
+ void removeListener(QObject *listener);
signals:
void connectedChanged();
@@ -42,6 +52,7 @@ private:
QWebSocket m_socket;
QVariantList m_commandStash;
+ QMap m_actions;
};
#endif // BEERSERVICE_H
diff --git a/services/settingsservice.cpp b/services/settingsservice.cpp
index f0ba617..0a76a39 100644
--- a/services/settingsservice.cpp
+++ b/services/settingsservice.cpp
@@ -11,6 +11,7 @@ namespace Keys {
constexpr auto ServerAddress = "server_address";
constexpr auto SelectedUser = "selected_user";
+constexpr auto SelectedStore = "selected_store";
}
@@ -26,22 +27,45 @@ void SettingsService::setValue(const QString &key, const QVariant &value)
QString SettingsService::serverAddress() const
{
- return m_settings.value(Keys::ServerAddress, Defaults::ServerAddress).toString();
+ return value(Keys::ServerAddress, Defaults::ServerAddress).toString();
}
void SettingsService::setServerAddress(const QString &address)
{
- m_settings.setValue(Keys::ServerAddress, address);
+ if (serverAddress() == address) {
+ return;
+ }
+
+ setValue(Keys::ServerAddress, address);
emit serverAddressChanged();
}
QString SettingsService::selectedUserId() const
{
- return m_settings.value(Keys::SelectedUser, Defaults::GuestUserId).toString();
+ return value(Keys::SelectedUser, Defaults::GuestUserId).toString();
}
void SettingsService::setSelectedUserId(const QString &userId)
{
- m_settings.setValue(Keys::SelectedUser, userId);
+ if (selectedUserId() == userId) {
+ return;
+ }
+
+ setValue(Keys::SelectedUser, userId);
emit selectedUserIdChanged();
}
+
+QString SettingsService::selectedStoreId() const
+{
+ return value(Keys::SelectedStore).toString();
+}
+
+void SettingsService::setSelectedStoreId(const QString &storeId)
+{
+ if (selectedStoreId() == storeId) {
+ return;
+ }
+
+ setValue(Keys::SelectedStore, storeId);
+ emit selectedStoreIdChanged();
+}
diff --git a/services/settingsservice.h b/services/settingsservice.h
index b0eac76..d2b40ec 100644
--- a/services/settingsservice.h
+++ b/services/settingsservice.h
@@ -10,6 +10,7 @@ class SettingsService : public QObject
Q_PROPERTY(QString serverAddress READ serverAddress WRITE setServerAddress NOTIFY serverAddressChanged)
Q_PROPERTY(QString selectedUserId READ selectedUserId WRITE setSelectedUserId NOTIFY selectedUserIdChanged)
+ Q_PROPERTY(QString selectedStoreId READ selectedStoreId WRITE setSelectedStoreId NOTIFY selectedStoreIdChanged)
public:
static SettingsService *instance()
@@ -18,20 +19,24 @@ public:
return &i;
}
- QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
- void setValue(const QString &key, const QVariant &value);
-
QString serverAddress() const;
void setServerAddress(const QString &address);
QString selectedUserId() const;
void setSelectedUserId(const QString &userId);
+ QString selectedStoreId() const;
+ void setSelectedStoreId(const QString &storeId);
+
signals:
void serverAddressChanged();
void selectedUserIdChanged();
+ void selectedStoreIdChanged();
private:
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant{}) const;
+ void setValue(const QString &key, const QVariant &value);
+
SettingsService() = default;
~SettingsService() = default;
diff --git a/viewmodels/ordersviewmodel.cpp b/viewmodels/ordersviewmodel.cpp
new file mode 100644
index 0000000..aa100bd
--- /dev/null
+++ b/viewmodels/ordersviewmodel.cpp
@@ -0,0 +1,72 @@
+#include "ordersviewmodel.h"
+
+#include
+
+OrdersViewModel::OrdersViewModel(QObject *parent)
+ : QAbstractListModel{parent}
+{
+ connect(&m_ordersModel, &BaseModel::dataChanged, this, &OrdersViewModel::reload);
+ connect(&m_usersModel, &BaseModel::dataChanged, this, &OrdersViewModel::reload);
+ connect(&m_productsModel, &BaseModel::dataChanged, this, &OrdersViewModel::reload);
+ connect(&m_storesModel, &BaseModel::dataChanged, this, &OrdersViewModel::reload);
+
+ reload();
+}
+
+int OrdersViewModel::rowCount(const QModelIndex &) const
+{
+ return m_model.count();
+}
+
+QVariant OrdersViewModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return {};
+ }
+
+ return m_model.at(index.row()).toMap()[roleNames().value(role)];
+}
+
+QHash OrdersViewModel::roleNames() const
+{
+ return QHash {
+ { Roles::UserName, "userName" },
+ { Roles::StoreName, "storeName" },
+ { Roles::Date, "date" },
+ { Roles::Time, "time"},
+ { Roles::Amount, "amount" },
+ { Roles::Products, "products" }
+ };
+}
+
+void OrdersViewModel::reload()
+{
+ beginResetModel();
+
+ m_model.clear();
+
+ for (const QVariant &vOrder : m_ordersModel.orders()) {
+ QVariantMap order = vOrder.toMap();
+ QDateTime orderTime = QDateTime::fromSecsSinceEpoch(order.value("ts", 0).toDouble());
+ order["date"] = orderTime.date();
+ order["time"] = orderTime.time();
+ order["userName"] = m_usersModel.itemProperty(order["userId"].toString(), "name").toString();
+ order["storeName"] = m_storesModel.itemProperty(order["storeId"].toString(), "name").toString();
+
+ QVariantList prodModel;
+ for (const QVariant &prod : order["products"].toList()) {
+ QVariantMap product = prod.toMap();
+ product["product"] = m_productsModel.itemProperty(product.value("productId").toString(), "name");
+ prodModel << product;
+ }
+ order["products"] = prodModel;
+
+ m_model << order;
+ }
+
+ std::sort(m_model.begin(), m_model.end(), [](const QVariant &l, const QVariant &r) {
+ return l.toMap().value("ts").toDouble() < r.toMap().value("ts").toDouble();
+ });
+
+ endResetModel();
+}
diff --git a/viewmodels/ordersviewmodel.h b/viewmodels/ordersviewmodel.h
new file mode 100644
index 0000000..0d666ba
--- /dev/null
+++ b/viewmodels/ordersviewmodel.h
@@ -0,0 +1,43 @@
+#ifndef ORDERSVIEWMODEL_H
+#define ORDERSVIEWMODEL_H
+
+#include
+
+#include "models/ordersmodel.h"
+#include "models/usersmodel.h"
+
+class OrdersViewModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit OrdersViewModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash roleNames() const override;
+
+signals:
+ void ordersChanged();
+
+private:
+ enum Roles {
+ UserName = Qt::UserRole + 1,
+ StoreName,
+ Date,
+ Time,
+ Amount,
+ Products
+ };
+
+ void reload();
+
+ OrdersModel m_ordersModel;
+ UsersModel m_usersModel;
+ BaseModel m_productsModel = BaseModel("products", this);
+ BaseModel m_storesModel = BaseModel("stores", this);
+
+ QVariantList m_model;
+};
+
+#endif // ORDERSVIEWMODEL_H
diff --git a/viewmodels/productsviewmodel.cpp b/viewmodels/productsviewmodel.cpp
new file mode 100644
index 0000000..f1c6336
--- /dev/null
+++ b/viewmodels/productsviewmodel.cpp
@@ -0,0 +1,78 @@
+#include "productsviewmodel.h"
+
+#include "services/settingsservice.h"
+#include "models/ordersmodel.h"
+
+ProductsViewModel::ProductsViewModel(QObject *parent)
+ : QObject{parent}
+{
+ connect(&m_productsModel, &BaseModel::dataChanged, this, &ProductsViewModel::productsChanged);
+}
+
+QVariantList ProductsViewModel::products() const
+{
+ return m_productsModel.items();
+}
+
+QVariantList ProductsViewModel::order() const
+{
+ QVariantList res;
+
+ for (auto it = m_order.constBegin(); it != m_order.constEnd(); ++it) {
+ QVariantMap product = m_productsModel.item(it.key());
+ product["count"] = it.value().toMap().value("quantity").toDouble();
+ res << product;
+ }
+
+ return res;
+}
+
+float ProductsViewModel::orderSum() const
+{
+ float res = 0.0;
+
+ for (auto it = m_order.constBegin(); it != m_order.constEnd(); ++it) {
+ QVariantMap product = m_productsModel.item(it.key());
+ float price = product.value("price", 0.0).toFloat();
+ float quantity = it.value().toMap().value("quantity").toDouble();
+ res += quantity * price;
+ }
+
+ return res;
+}
+
+void ProductsViewModel::setOrderValue(const QString &productId, float value)
+{
+ if (value) {
+ float price = m_productsModel.itemProperty(productId, "price", 0.0).toFloat();
+ m_order[productId] = QVariantMap {
+ { "productId", productId },
+ { "quantity", value},
+ { "price", price }
+ };
+ } else {
+ m_order.remove(productId);
+ }
+
+ emit orderChanged();
+}
+
+void ProductsViewModel::submitOrder()
+{
+ OrdersModel model;
+ model.submitOrder(QVariantMap {
+ { "userId", settings()->selectedUserId() },
+ { "storeId", settings()->selectedStoreId() },
+ { "products", m_order.values() },
+ { "amount", orderSum() }
+ });
+
+ m_order.clear();
+
+ emit orderChanged();
+}
+
+SettingsService *ProductsViewModel::settings() const
+{
+ return SettingsService::instance();
+}
diff --git a/viewmodels/productsviewmodel.h b/viewmodels/productsviewmodel.h
new file mode 100644
index 0000000..4e92fbf
--- /dev/null
+++ b/viewmodels/productsviewmodel.h
@@ -0,0 +1,38 @@
+#ifndef PRODUCTSVIEWMODEL_H
+#define PRODUCTSVIEWMODEL_H
+
+#include
+
+#include "models/basemodel.h"
+
+class SettingsService;
+class ProductsViewModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariantList products READ products NOTIFY productsChanged)
+ Q_PROPERTY(QVariantList order READ order NOTIFY orderChanged)
+ Q_PROPERTY(float orderSum READ orderSum NOTIFY orderChanged)
+
+public:
+ explicit ProductsViewModel(QObject *parent = nullptr);
+
+ QVariantList products() const;
+ QVariantList order() const;
+ float orderSum() const;
+
+ Q_INVOKABLE void setOrderValue(const QString &productId, float value);
+ Q_INVOKABLE void submitOrder();
+
+signals:
+ void productsChanged();
+ void orderChanged();
+
+private:
+ SettingsService *settings() const;
+
+ BaseModel m_productsModel = BaseModel("products", this);
+ QVariantMap m_order;
+};
+
+#endif // PRODUCTSVIEWMODEL_H
diff --git a/viewmodels/settingsviewmodel.cpp b/viewmodels/settingsviewmodel.cpp
new file mode 100644
index 0000000..443f6e4
--- /dev/null
+++ b/viewmodels/settingsviewmodel.cpp
@@ -0,0 +1,57 @@
+#include "settingsviewmodel.h"
+
+SettingsViewModel::SettingsViewModel(QObject *parent)
+ : QAbstractListModel{parent}
+{
+ m_items << SettingItem { tr("BeerLog service address"), "serverAddress", "text" }
+ << SettingItem { tr("Selected user id"), "selectedUserId", "choice", "users" }
+ << SettingItem { tr("Selected store"), "selectedStoreId", "choice", "stores" };
+
+ m_models["users"] = new BaseModel("users", this);
+ m_models["stores"] = new BaseModel("stores", this);
+}
+
+
+int SettingsViewModel::rowCount(const QModelIndex &) const
+{
+ return m_items.count();
+}
+
+QVariant SettingsViewModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return {};
+ }
+
+ SettingItem item = m_items.at(index.row());
+
+ switch (role) {
+ case Roles::Title: return item.title;
+ case Roles::PropertyName: return item.propertyName;
+ case Roles::ControlType: return item.controlType;
+ case Roles::Model: return model(item.modelName);
+ default:
+ break;
+ }
+
+ return {};
+}
+
+QHash SettingsViewModel::roleNames() const
+{
+ return QHash {
+ { Roles::Title, "title" },
+ { Roles::PropertyName, "name" },
+ { Roles::ControlType, "control" },
+ { Roles::Model, "choiceModel" }
+ };
+}
+
+QVariant SettingsViewModel::model(const QString &modelName) const
+{
+ if (!m_models.contains(modelName)) {
+ return {};
+ }
+
+ return m_models[modelName]->items();
+}
diff --git a/viewmodels/settingsviewmodel.h b/viewmodels/settingsviewmodel.h
new file mode 100644
index 0000000..c35cf24
--- /dev/null
+++ b/viewmodels/settingsviewmodel.h
@@ -0,0 +1,47 @@
+#ifndef SETTINGSVIEWMODEL_H
+#define SETTINGSVIEWMODEL_H
+
+#include
+
+#include "models/basemodel.h"
+
+class SettingsViewModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsViewModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash roleNames() const override;
+
+private:
+ enum Roles {
+ Title = Qt::UserRole + 1,
+ PropertyName,
+ ControlType,
+ Model
+ };
+
+ struct SettingItem
+ {
+ QString title;
+ QString propertyName;
+ QString controlType;
+ QString modelName;
+
+ SettingItem(const QString &title, const QString &propertyName, const QString &controlType, const QString &modelName = {}) :
+ title(title),
+ propertyName(propertyName),
+ controlType(controlType),
+ modelName(modelName) {}
+ };
+
+ QVariant model(const QString &modelName) const;
+
+ QList m_items;
+ QMap m_models;
+};
+
+#endif // SETTINGSVIEWMODEL_H
diff --git a/viewmodels/storesviewmodel.cpp b/viewmodels/storesviewmodel.cpp
new file mode 100644
index 0000000..6d1b23c
--- /dev/null
+++ b/viewmodels/storesviewmodel.cpp
@@ -0,0 +1,38 @@
+#include "storesviewmodel.h"
+
+#include "services/settingsservice.h"
+
+StoresViewModel::StoresViewModel(QObject *parent)
+ : QObject{parent}
+{
+ connect(&m_storesModel, &BaseModel::dataChanged, this, &StoresViewModel::storesChanged);
+ connect(&m_storesModel, &BaseModel::dataChanged, this, &StoresViewModel::selectedStoreNameChanged);
+
+ connect(settings(), &SettingsService::selectedStoreIdChanged, this, &StoresViewModel::selectedStoreChanged);
+ connect(settings(), &SettingsService::selectedStoreIdChanged, this, &StoresViewModel::selectedStoreNameChanged);
+}
+
+QVariantList StoresViewModel::stores() const
+{
+ return m_storesModel.items();
+}
+
+QString StoresViewModel::selectedStore() const
+{
+ return settings()->selectedStoreId();
+}
+
+void StoresViewModel::setSelectedStore(const QString &newSelectedStore)
+{
+ settings()->setSelectedStoreId(newSelectedStore);
+}
+
+QString StoresViewModel::selectedStoreName() const
+{
+ return m_storesModel.itemProperty(selectedStore(), "name").toString();
+}
+
+SettingsService *StoresViewModel::settings() const
+{
+ return SettingsService::instance();
+}
diff --git a/viewmodels/storesviewmodel.h b/viewmodels/storesviewmodel.h
new file mode 100644
index 0000000..ea77794
--- /dev/null
+++ b/viewmodels/storesviewmodel.h
@@ -0,0 +1,36 @@
+#ifndef STORESVIEWMODEL_H
+#define STORESVIEWMODEL_H
+
+#include
+
+#include "models/basemodel.h"
+
+class SettingsService;
+class StoresViewModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariantList stores READ stores NOTIFY storesChanged)
+ Q_PROPERTY(QString selectedStore READ selectedStore WRITE setSelectedStore NOTIFY selectedStoreChanged)
+ Q_PROPERTY(QString selectedStoreName READ selectedStoreName NOTIFY selectedStoreNameChanged)
+
+public:
+ explicit StoresViewModel(QObject *parent = nullptr);
+
+ QVariantList stores() const;
+ QString selectedStore() const;
+ void setSelectedStore(const QString &newSelectedStore);
+ QString selectedStoreName() const;
+
+signals:
+ void storesChanged();
+ void selectedStoreChanged();
+ void selectedStoreNameChanged();
+
+private:
+ SettingsService *settings() const;
+
+ BaseModel m_storesModel = BaseModel("stores", this);
+};
+
+#endif // STORESVIEWMODEL_H
diff --git a/viewmodels/usersviewmodel.cpp b/viewmodels/usersviewmodel.cpp
index 4eabb6a..e26c528 100644
--- a/viewmodels/usersviewmodel.cpp
+++ b/viewmodels/usersviewmodel.cpp
@@ -5,8 +5,8 @@
UsersViewModel::UsersViewModel(QObject *parent)
: QObject{parent}
{
- connect(&m_usersModel, &AbstractModel::dataChanged, this, &UsersViewModel::usersChanged);
- connect(&m_usersModel, &AbstractModel::dataChanged, this, &UsersViewModel::selectedUserNameChanged);
+ connect(&m_usersModel, &BaseModel::dataChanged, this, &UsersViewModel::usersChanged);
+ connect(&m_usersModel, &BaseModel::dataChanged, this, &UsersViewModel::selectedUserNameChanged);
connect(settings(), &SettingsService::selectedUserIdChanged, this, &UsersViewModel::selectedUserChanged);
connect(settings(), &SettingsService::selectedUserIdChanged, this, &UsersViewModel::selectedUserNameChanged);
@@ -24,16 +24,12 @@ QString UsersViewModel::selectedUser() const
void UsersViewModel::setSelectedUser(const QString &newSelectedUser)
{
- if (selectedUser() == newSelectedUser) {
- return;
- }
-
settings()->setSelectedUserId(newSelectedUser);
}
QString UsersViewModel::selectedUserName() const
{
- return m_usersModel.userName(selectedUser());
+ return m_usersModel.itemProperty(selectedUser(), "name").toString();
}
SettingsService *UsersViewModel::settings() const