仓库管理系统————QT+SQLite实现

1.概述

非常感谢hello_monster博主的分享,在他的基础上完善和新增的一些功能,使功能相对更加完整。
本文较为基础,本人也是小白,文中有不合适和不对的地方欢迎各位留言指正。
希望能对你有所帮助。

1.1 开发环境及开发工具

  • SQLite3 + QT 5.12.10 SQLite是一个轻量级的数据库,不需要部署即可使用。 qt绘制图表比较简单,并且Qt5可以直接使用SQLite;同时QT支持夸平台,这样不同的操作系统也不需要修改源码,只需要重新编译即可。
  • QT Creator + Navicat QT使用官方的开发工具QT Creator; Navicat Premium是一款数据库可视化工具。

1.2 功能描述及实现方式

1.2.1 数据库功能
  • 数据库中需存在四张表(入库表、出库表、库存表、收益表);
    仓库管理系统————QT+SQLite实现
  • 当入库/出库表数据发生变化时,同时修改入库表/收益表(触发器实现)。
; 1.2.2 软件功能
  • 界面实现数据的增、删、改、查;
  • 库存余量不足提示(重写QSqlTableModel的data函数);
  • 绘制一年中各个月份的成本和利润的柱状图(QtCharts)。

2.数据库功能的实现

2.1 设计表

2.1.1 SQLite3中的数据类型

SQLite3不同于MySql等数据库,他的数据类型只有4种。本次主要用到的类型如下:

  • TEXT(字符型)
  • Date(日期型)
  • INTEGER(整型)
  • REAL(浮点型)

参考链接:https://www.runoob.com/sqlite/sqlite-data-types.html

2.1.2 创建表

创建表可以选择Sql语言或者图形界面操作。本文采用Sql语言来创建。

--入库表
CREATE TABLE in_stock
(
    name TEXT(30),
        prod_date date,
    in_price real,
    in_num integer,
    in_total real
);
--出库表
CREATE TABLE out_prod
(
  name TEXT(30),
  out_date date,
  client TEXT(10),
  sale_price real,
  out_num integer,
  out_total real,
  finalpay TEXT(2)
);
--库存表
CREATE TABLE stock
(
  name TEXT(30),
  remain integer,
  alarm integer
);
--收益表
CREATE TABLE earn
(
  name TEXT(30),
  earn_date date,
  earn real
);
2.1.3 每月的成本和利润表

​ 为了界面中统计表的数据获取更加简单,可以多创建两张表,随后直接从这两张表中获取数据。

--每月的成本表
CREATE TABLE cost
(
  month_cost TEXT,
  cost real
);
--每月的利润表
CREATE TABLE report
(
  month_earn TEXT,
  earn real
);

2.2 触发器

2.2.1 触发器设计

​ 数据主要产生变化的表就是入库和出库两张表,可能新增、修改、删除等操作,首先想到用触发器实现。

​ 设想一下,当入库表有新增操作时,库存表和成本表都需要跟着变化;有删除操作时,库存表和成本表也都需要变化;但是有修改操作时,可能只有库存表变化,也可能只有成本表变化,也可能都有变化。出库表也是同样。

​ 在产生修改操作的时候合理的触发器实现较为复杂,本案例采用了较为简单易懂的实现方式:无论你的数据如何变化,只要产生了变化,我们就把库存表、成本表、收益表,重新填入一遍数据。覆盖掉之前的数据。这样无论你做出什么样的修改,其他的所有的表只需要做两件事:

​ 1.删除原来的数据;2.求出最新的库存、收益、成本等数据。

2.2.2 创建触发器

仓库管理系统————QT+SQLite实现

本案例需要使用6个触发器,由上图蓝色标记。

--创建触发器
--(创建在in_stock表产生insert操作after的触发器,名为tr_replace_stock)
CREATE TRIGGER tr_replace_stock
AFTER INSERT
ON in_stock
--触发成功开始执行
BEGIN
--结束
END

--in_stock删除之后触发器
CREATE TRIGGER tr_del_in_stock
AFTER DELETE
ON in_stock
BEGIN
--触发器功能代码
END

--in_stock更新之后触发器
CREATE TRIGGER tr_update_stock
AFTER UPDATE OF name,
in_price,
in_num,
in_total,
prod_date
ON in_stock
BEGIN
--
END

--剩下三个改变表名和字段名即可
2.2.3 触发器功能
--in_stock插入触发器--
CREATE TRIGGER tr_replace_stock
AFTER INSERT
ON in_stock
--触发成功开始执行
BEGIN
--删除stock表中所有的数据
DELETE FROM stock;
--REPLACE INTO (如果新表中存在就更新如果不存在就插入)
--分别将in_stock、out_prod表中以name分类求in_num、out——num的和;
--当in_stock和out_prod表中的name相同时,计算in_num - out_prod;如果out_num为空时,以0带入计算。
REPLACE INTO stock(name,remain)
    SELECT i.name,SUM(i.in_num) - IFNULL((SELECT SUM(o.out_num) FROM out_prod AS o
    WHERE i.name = o.name
        GROUP BY o.name
        ) ,0)FROM in_stock AS i GROUP BY i.name;
--结束
END

这样入库表有数据插入,就可以自动完成库存表的数据更新。

随后当入库表插入时还需要更新收益表、按月统计的成本、利润表。这时只需要在BEGIN和END之间添加代码即可。

BEIGIN
--每日售出利润表
DELETE FROM earn;
    REPLACE INTO earn(name,earn_date,earn)
    SELECT
        o.name,o.out_date,
        o.out_num * (o.sale_price- (SELECT i.in_price FROM in_stock AS i WHERE i.name = o.name))     AS earn
    FROM out_prod AS o;
--按月统计收益表
DELETE FROM report;
    REPLACE INTO report(month_earn,earn)
    SELECT
        STRFTIME( '%Y-%m', e.earn_date ) AS month,
        SUM( e.earn )
    FROM
        earn AS e
        --按earn_date中的年-月归组
    GROUP BY
        STRFTIME( '%Y-%m', e.earn_date );
--按月统计成本表
DELETE FROM cost;
    REPLACE INTO cost(month_cost,cost)
    SELECT
        STRFTIME( '%Y-%m', i.prod_date ),
        i.in_total
    FROM
        in_stock AS i
    GROUP BY
        STRFTIME( '%Y-%m', i.prod_date );
END

之前设计的时候提到,只要原始表发生任何变化,其他的表全部重新做,所以剩下的工作只是更改触发器触发条件,触发器内容全部一致。(这种设计固然是不好的。数据非常大的时候,会浪费很多资源。)

3.客户端

3.1 Ui界面

3.1.1 入库登记、出库登记、收益明细界面

​ 客户端全局UI具体代码可见[hello_monster]的博客:点击《Qt实战笔记-从零开始搭建一套库存管理系统-(三)UI框架搭建-02》了解详情。

仓库管理系统————QT+SQLite实现
仓库管理系统————QT+SQLite实现
仓库管理系统————QT+SQLite实现
; 3.1.1 库存管理界面

​ 库存管理界面设计到库存余量不足提醒功能。需要对QSqlTableModel类中的Data()方法进行重写,从而实现红色突出显示。

仓库管理系统————QT+SQLite实现

#include
class MySqlTableModel : public QSqlTableModel
{
  public:
    MySqlTableModel(QObject * parent = 0,QSqlDatabase db = QSqlDatabase());
    ~MySqlTableModel();
    QVariant data(const QModelIndex &index,int role = Qt::DisplayRole)const;
}

#include "MySqlTableModel.h"
#include
MySqlTableModel::MySqlTableModel(QObject * parent, QSqlDatabase db) : QSqlTableModel(parent,db)
{

}

MySqlTableModel::~MySqlTableModel()
{

}
QVariant MySqlTableModel::data(const QModelIndex &index, int role) const
{
   bool flag =false;
  if(index.column() == 1)
    {
      QVariant value = QSqlTableModel::data(index,Qt::DisplayRole);
      int r = index.row();
      int c = 2;
      QModelIndex index1 = this->index(r,c,QModelIndex());
      QVariant value1 = QSqlTableModel::data(index1,Qt::DisplayRole);
      int stock = value.toInt();
      int alarm = value1.toInt();
      if(stock<alarm)
        {
          flag = true;
        }
    }
  if(role == Qt::BackgroundColorRole && flag)
  {
    return QVariant(QColor(255,60,0));
  }
  return QSqlTableModel::data(index,role);;
}

3.1.3 收益报表

仓库管理系统————QT+SQLite实现

QWidget* MainWindow::creatReport()
{
  QWidget *reportPage = new QWidget;
  QLabel *titleLable = new QLabel("选择年份:");
  reportEdit = new QLineEdit;
  QHBoxLayout *titleLayout = new QHBoxLayout;
  titleLayout->addWidget(titleLable);
  titleLayout->addWidget(reportEdit);
  titleLayout->addWidget(reportBtn);
  titleLayout->addStretch();
  QBarSet *set0 = new QBarSet("成本");
    QBarSet *set1 = new QBarSet("利润");
   set0 = new QBarSet("成本");
   set1 = new QBarSet("利润");
   int year = getYear();
   SelectYearData(year);
   bool bFlagEarn;

   for(int i =1;i<13;i++)
     {
       bFlagEarn = false;
       for(int j = 0;j<vcearn.size();j++)
         {
           if(vcearn.at(j).month == i)

             {
               *set1<<vcearn.at(j).earn;
               bFlagEarn = true;
               break;
             }
         }
       if(!bFlagEarn)
         {
           *set1<<0;
         }
     }
   bool bFlagCost;
   for(int i =1;i<13;i++)
     {
       bFlagCost = false;
       for(int j = 0;j<vccost.size();j++)
         {
           if(vccost.at(j).month == i)

             {
               *set0<<vccost.at(j).cost;
               bFlagCost = true;
               break;
             }

         }
       if(!bFlagCost)
         {
           *set0<<0;
         }
     }

   QBarSeries *series = new QBarSeries();

   series->append(set0);
   series->append(set1);

   QChart *chart = new QChart();
   chart->addSeries(series);
   chart->setTitle("全年进销表");
   chart->setAnimationOptions(QChart::SeriesAnimations);

   QStringList categories;
   categories << "一月" << "二月" << "三月" << "四月" << "五月" << "六月"<<"七月"<<"八月"<<"九月"<<"十月"<<"十一月"<<"十二月";
   QBarCategoryAxis *axisX = new QBarCategoryAxis();
   axisX->append(categories);
   chart->addAxis(axisX, Qt::AlignBottom);
   series->attachAxis(axisX);

   QValueAxis *axisY = new QValueAxis();
   axisY->setRange(0,100);
   axisY->setTitleText("金额/千元");
   axisY->setMinorTickCount(4);
   axisY->setTickCount(5);
   chart->addAxis(axisY, Qt::AlignLeft);
   series->attachAxis(axisY);

   chart->legend()->setVisible(true);
   chart->legend()->setAlignment(Qt::AlignBottom);

   QChartView *chartView = new QChartView(chart);
   chartView->setRenderHint(QPainter::Antialiasing);
   QVBoxLayout *layout = new QVBoxLayout;
   layout->addLayout(titleLayout);
   layout->addWidget(chartView);
    reportPage->setLayout(layout);
    return reportPage;
}

3.2 显示数据库中数据

3.2.1 数据库的连接
bool mySqlite::connectDB()
{
    QSqlDatabase myDB = QSqlDatabase::addDatabase("QSQLITE");
    myDB.setDatabaseName(QApplication::applicationDirPath() + "/Database/"+"myWMS.db");
    if(!myDB.open())
        return false;
    return true;
}
3.2.2 将表中数据显示到界面(Model于View)

QT中将数据层(Model)和表示层(View)进行了分离,这里我们使用QSqlModel类中的方法与数据源通信,用QTableView将Model中数据以图表的形式显示出来。

3.2.2.1Model关联原始数据
  • 函数原型
explicit QSqlTableModel(QObject *parent = nullptr, QSqlDatabase db = QSqlDatabase());
  • 例子

QWidget *supplierPage = new QWidget;
mySqlite *mysql;
QSqlTableModel *supplierModel;

supplierModel = new QSqlTableModel(supplierPage,mysql->myDB);
supplierModel->setTable("in_stock");
supplierModel->select();
3.2.2.2 表示层显示Model数据

QTableView *tableView;

tableView = new QTableView(this);
tableView->setModel(outModel);
tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);

3.3 操作数据库

  QSqlRecord record = supplierModel->record();
        record.setValue(0,goodsName);
        record.setValue(1,goodsDate);
        record.setValue(2,goodsPrice);
        record.setValue(3,goodsNum);
        record.setValue(4,tatal);
        supplierModel->insertRecord(supplierModel->rowCount(), record);
        supplierModel->submitAll();

 int curRow = tableView->currentIndex().row();
      supplierModel->removeRow(curRow);
      int ok = QMessageBox::warning(this,tr("删除当前行!"),
                                        tr("确定删除当前行吗"),
                                      QMessageBox::Yes,QMessageBox::No);
          if(ok == QMessageBox::No)
              supplierModel->revertAll();
          else
              supplierModel->submitAll();
void MainWindow::modifySupplierData()
{
  QString goodsName = nameEdit->text();
  QDate currentDate = QDateTime::currentDateTime().date();
  QString goodsDate = currentDate.toString("yyyy-MM-dd");
  float goodsPrice = (priceEdit->text()).toFloat();
  int goodsNum = (numEdit->text()).toInt();
  float tatal = goodsPrice * goodsNum;
  QString goodsTotal = QString::number(tatal, 'f', 2);
  if(goodsName.isEmpty() || tatal  0)
    {
    QMessageBox::information(this,"提示","修改失败,数据为空");
    return;
    }

  int curRow = tableView->currentIndex().row();
  QSqlRecord record = supplierModel->record(curRow);
  record.setValue(0,goodsName);
  record.setValue(1,goodsDate);
  record.setValue(2,goodsPrice);
  record.setValue(3,goodsNum);
  record.setValue(4,tatal);

  if(supplierModel->setRecord(curRow, record))
  {
      supplierModel->submitAll();
  }
}

void MainWindow::searchSupplierData()
{
    QString name =  QString("name = '%1'").arg(nameEdit->text());

    QString strFilter = "";
    if(!nameEdit->text().isEmpty())
    {
        strFilter.append(name);
    }

    supplierModel->setFilter(strFilter);
    supplierModel->select();
}

3.4 图表更新

实现效果:

仓库管理系统————QT+SQLite实现

struct stEarn
{
  int month;
  float earn;
};
struct stCost
{
  int month;
  float cost;
};

     QBarSet *set0;
     QBarSet *set1;
     QVector<stEarn>vcearn;
     QVector<stCost>vccost;

void MainWindow::searchYearBtn()
{
  int year = getYear();
  SelectYearData(year);
  bool bCost,bEarn;
  for(int i = 0;i<12;i++)
    {
      bCost = false;
      bEarn = false;
      set0->replace(i,0);
      set1->replace(i,0);
      for(int j = 0;j<vccost.size();j++)
       {
          if(vccost.at(j).month == i+1)
            {
              set0->replace(i,vccost.at(j).cost);
              break;
            }
        }
      for(int j = 0;j<vcearn.size();j++)
       {
          if(vcearn.at(j).month == i+1)
            {
              set1->replace(i,vcearn.at(j).earn);
              break;
            }
        }
    }
}

int MainWindow::getYear()
{
  int year = reportEdit->text().toInt();
  if(year == 0)
    {
      QDate date = QDate::currentDate();
      year = date.year();
    }

  return year;
}

void MainWindow::SelectYearData(int year)
{
  vcearn.clear();
  vccost.clear();
  QSqlQuery query;
  QString str;
  str.sprintf("SELECT * FROM cost AS c WHERE SUBSTR(c.month_cost,0,5) = '%d'",year);
  query.prepare(str);
  query.exec();
  while(query.next())
    {
      stCost costTemp;
      QString month = query.value("month_cost").toString();
      costTemp.month = month.right(2).toInt();
      QString earn = query.value("cost").toString();
      costTemp.cost = earn.toFloat() / 1000;
      vccost.push_back(costTemp);
    }
  str.sprintf("SELECT * FROM report AS e WHERE SUBSTR(e.month_earn,0,5) = '%d'",year);
  query.prepare(str);
  query.exec();
  while(query.next())
    {
      stEarn earnTemp;
      QString month = query.value("month_earn").toString();
      earnTemp.month = month.right(2).toInt();
      QString earn = query.value("earn").toString();
      earnTemp.earn = earn.toFloat() / 1000;
      vcearn.push_back(earnTemp);
    }
}

4.发布

在一台没有装过QT开发环境的电脑上运行本程序,可能会出现各种各样的错误。我们只要使用Q T自带的windeployqt工具即可完成打包。

参考:《Qt程序打包发布方法(使用官方提供的windeployqt工具)》——lxj434368832

总结:在windeployqt中先cd到exe执行文件目录下,随后执行windeployqt Manger.exe,等待完成即可。

Original: https://blog.csdn.net/weixin_45623223/article/details/115275328
Author: -zhaocan-
Title: 仓库管理系统————QT+SQLite实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/817033/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球