ASP.NET小型企业办公耗材库存管理软件的设计与实现

摘 要

库存管理系统是将企业的存和转等企业的经营业务有机的结合起来,达到数据共享、降低成本、提高效率。本系统前台采用**.NET**,后台数据库采用SQL Server 2000,语言采用C#。本系统实现的主要功能有库存查询、进货作业、领料作业、库存盘点、损坏处理等。在系统完成后对工作进行了总结,对系统功能进行了测试,并指出了系统中的问题和需要改进的地方。

关键词 **:**B/S,C#,库存管理系统,.NET

2 系统需求分析

本系统目的是让管理者能够对仓库存储进行更有效,更经济,更方便地管理而开发的。

2.1 用户身份级别要求

即根据身份的不同对系统的使用权限有所不同。从权限大小大致可以分为:系统管理员、一般注册用户、未注册用户。系统提供灵活的角色设定和权限的分配功能。

  1. 系统管理员:最高级权限的用户,拥有库存管理系统的所有权限。主要负责对库存数据的添加,修改,删除等操作,对一般注册用户下放权限,对系统进行日常的维护和管理,排除故障。
  2. 一般注册用户:权限被限制用户。只具有访问系统中某些功能的操作(如查询。
  3. 未注册用户:未具有任何权限的用户。只能通过注册才能进入库存管理系统。

2.2 系统功能模块设置

  1. 用户注册:未注册用户需注册后才能进入系统。
  • 操作对象:所有未注册用户。
  • 浏览对象:所有未注册用户。
  • 具体操作:注册用户在需填写用户名和密码才能注册。
  1. 用户管理:对注册用户和管理员的基本信息进行操作。对用户权限进行设置。
  • 操作对象:系统管理员
  • 浏览对象:系统管理员
  1. 库存查询:合法用户可以查看库存的所有信息,可以查询总库存和各分仓库的库存,用户可以根据种类名和货物名查找匹配的物品。
  • 操作对象:注册用户、管理员
  • 浏览对象:注册用户、管理员
  1. 进货作业:管理员通过记录每次进货的物品的详细信息,系统在进货表中产生一条进货记录。
  • 操作对象:管理员
  • 浏览对象:管理员
  • 具体操作:管理员需将物品的货物号,种类号,种类名,货物名,数量,进货价格,仓库号,厂家号依次填入对应的栏目中,最后点击确定即可。
  1. 领料作业:管理员输入每次出货物品的信息,系统在出货单中产生一条出货记录。
  • 操作对象:管理员
  • 浏览对象:管理员
  • 具体操作:管理员在下拉菜单中选择相应的货物种类和货物名,在对应的空白栏中输入出货货物的厂家、数量、单价、领料人的名字,最后单击确定即完成该操作。
  1. 损坏处理:由于功能与领料作业类似,所以属于领料作业的子功能模块。
  • 操作对象:管理员
  • 浏览对象:管理员
  • 具体操作:与领料作业类似。
  1. 库存转库:用于将一个仓库的商品转移到另一个仓库。内容包括编号、源部门、目的部门、数量、货物号、经手人、调货日期等。由于是小型企业,所以同种类的商品都分在同个仓库中,需要调库时需将所有同种类的货物进行转库,不再对同种类不同货物名的商品进行细分。
  • 操作对象:管理员
  • 浏览对象:管理员
  • 具体操作:管理员在下拉菜单中选择该种类货物对应的货物种类、数量和源仓库号,填入目的仓库和经手人即可。
  1. 库存盘点:用于管理企业的库存盘点工作,将实际盘存的商品数量输入计算机,计算机自动与数据库中的库存数量进行核对,并产生盘盈盘亏统计信息。
  • 操作对象:管理员
  • 浏览对象:管理员
  • 具体操作:管理员选择盘点的货物名,输入该货物的厂家和数量,点击确定后即在该页面的下放显示出盘盈盘亏的信息。
  1. 历史查询:用于查询各种转库、盘点、进货、出货的历史数据。
  • 操作对象:注册用户、管理员
  • 浏览对象:注册用户、管理员
  1. 退出登录:让所有合法用户在操作完后顺利的退出系统。
  • 操作对象:所有用户

4 库存管理系统功能模块的具体实现

4.1 系统主要功能模块的实现及部分代码

4.1.1 用户注册和登录界面

在用户注册时首先检测该用户注册的用户名是否可用,即检查数据库该用户名是否已经存在,代码如下:

ConnectionSQL sqlcon1 = new ConnectionSQL();

string User;

User = TextBox1.Text.Trim();

SqlDataReader UserReader = sqlcon1.SqldataReader("select *from ADMIN_LIST where UserId='" + User + "'");

if (UserReader.Read())

{

Label5.Text = "该用户名已经被使用,请重新注册";

}

else

{

Label5.Text = "该用户名可以使用";

}

检测完后,再判断注册用户输入的密码是否为空,若为空,则弹出"密码不能为空"的对话框;若不为空,则检测注册用户输入的密码与确认密码是否吻合,代码如下:

if (Password =="")

{

HttpContext.Current.Response.Write("<script>alert('密码不能为空!');window.location.href='Registration.aspx'</script>");

}

else

{

if (Password != Surepassword)

{

HttpContext.Current.Response.Write("<script>alert('确认密码与密码不匹配,请重新输入!');window.location.href='Registration.aspx'</script>");

}

else

{

sqlcon.Sqldataset("insert ADMIN_LIST(UserId,Password,Jurisdiction)values('" + User + "','" + Password + "','1')");

HttpContext.Current.Response.Write("<script>alert('恭喜,用户注册成功!');window.location.href='Admin.aspx'</script>");

// Label4.Text = "恭喜,用户注册成功,请点击返回登录";

TextBox1.Text = null;

}

}

对于密码过长的限制,在前台使用了RegularExpressionValidator验证控件,通过输入.{0,10}表达式,将密码长度限制在10个字符以下。

在用户登录界面,除了对用户名与密码的合法性验证外,还要将管理员权限的值赋给Session["Right"]这个全局变量,目的是为了以后方便对用户权限的判断。具体代码如下:

User = TextBox1.Text.Trim();

PassWord = TextBox2.Text.Trim();

SqlDataReader UserReader = skycon1.SqldataReader("select * from ADMIN_LIST where UserId='" + User + "' and Password='" + PassWord + "'");

if (UserReader.Read())

{

if (UserReader.GetString(2).Trim()=="2")

{

Session["Right"]= "2";

}

HttpContext.Current.Response.Write("<script>alert('登录成功!');window.location.href='Admin.aspx'</script>");

UserReader.Close();

}

else

{

Label3.Text = "你输入的用户名和密码不正确,请重新输入";

UserReader.Close();

}

4.1.2 进货作业

进货作业是库存管理系统中最为重要的模块之一,上面的模块设计中已经分析了进货作业中存在的几种情况,除此之外,系统还要判断管理员是否出现输入有误现象。如输入的种类号与种类名不相符时,货物号与货物名不相符合时,系统都应该报错,但这两种出现的几率不是很大,为了减轻服务器负担,并没有把这些判断加入到系统中,只是对管理员输入时进行默认,认为输入的对应的货物种类和名称都是相符合的。当输入的仓库号与货物所属仓库不一致时(同种类的货物都存放在一个仓库中),由于此类错误出现的几率很大,所以在系统中加入了判断,如出现错误,则系统弹出"您输入的仓库号与物品仓库号不符合,请重新输入"的对话框。对与管理员输入的进货数量,进货价的合法性判断都采用了RegularExpressionValidator验证控件进行判断,以减少后台的操作。具体代码如下:

DataSet dt1 = skycon1.Sqldataset("select *from GOODS_DETAIL where HuoWuId='" + HuoWuId + "' and BusinessId='" + BusinessId + "'");

if (dt1.Tables[0].Rows.Count != 0)//当该物品在明细表中已经存在时:

{

DataSet dt2 = skycon1.Sqldataset("select *from GOODS_LIST where TypeId='" + TypeId+"'");//GooddataRow["TypeId"].ToString()

if (dt2.Tables[0].Rows.Count != 0)

{

DataRow GooddataRow1 = dt2.Tables[0].Rows[0];

//判断仓库号

if (StoreHouseId != GooddataRow1["StoreHouseId"].ToString().Trim())

{

HttpContext.Current.Response.Write("<script>alert('您输入的仓库号与物品仓库号不符合,请重新输入!');window.location.href='Enter.aspx'</script>");

}

else

{

skycon1.SqlExcute("insert JINHUO_LIST(HuoWuId,HuoWuName,TypeId,JinHuoSiQty,EnterPrice,EnterTotalPrice,StoreHouseId,BusinessId,DateTime)values('" + HuoWuId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + "," + EnterTotalprice.ToString() + ",'" + StoreHouseId + "','" + BusinessId + "','" + Datetime + "')");

DataRow GooddataRow = dt1.Tables[0].Rows[0];

SiQtyG = (int)GooddataRow["SiQty"] + JinhuoSiQty;

EnterPriceG = ((decimal)GooddataRow["EnterPrice"] * (int)GooddataRow["SiQty"] + EnterTotalprice) / ((int)GooddataRow["SiQty"] + JinhuoSiQty);

skycon1.SqlExcute("update GOODS_DETAIL set SiQty=" + SiQtyG.ToString() + ",EnterPrice=" + EnterPriceG.ToString() + "where HuoWuId='" + HuoWuId + "' and BusinessId='" + BusinessId + "'");

//更新细表

int SiQtyG1 = (int)GooddataRow1["SiQty"] + JinhuoSiQty;

decimal StorePrice = (decimal)GooddataRow1["StorePrice"] + EnterTotalprice;

skycon1.SqlExcute("update GOODS_LIST set SiQty=" + SiQtyG1.ToString() + ",StorePrice=" + StorePrice.ToString() + "where TypeId='" + TypeId + "'");//GooddataRow["TypeId"].ToString()

//更新总表

HttpContext.Current.Response.Write("<script>alert('恭喜您的操作已成功!');window.location.href='Enter.aspx'</script>");

}

}

else

{

HttpContext.Current.Response.Write("<script>alert('您输入的货物种类和和货物不相符,请重新输入!');window.location.href='Enter.aspx'</script>");

}

}

else

{

//如果是同种类不同名字的或不同产家的货物则执行下列步骤;

DataSet dt4 = skycon1.Sqldataset("select *from GOODS_LIST where TypeId='" + TypeId + "'");

DataSet dt5 = skycon1.Sqldataset("select *from GOODS_DETAIL where HuoWuId='" + HuoWuId + "'");

if(dt4.Tables[0].Rows.Count!=0)

{

if (dt5.Tables[0].Rows.Count != 0)//同种类同名但没有该厂家的货物

{

DataRow GooddataRow = dt4.Tables[0].Rows[0];

int SiQtyG2 = (int)GooddataRow["SiQty"] + JinhuoSiQty;

decimal StorePrice1 = (decimal)GooddataRow["StorePrice"] + EnterTotalprice;

if (StoreHouseId != GooddataRow["StoreHouseId"].ToString().Trim())//判断仓库号

{

HttpContext.Current.Response.Write("<script>alert('您输入的仓库号与物品仓库号不符合,请重新输入!');window.location.href='Enter.aspx'</script>");

}

else

{

skycon1.SqlExcute("insert JINHUO_LIST(HuoWuId,HuoWuName,TypeId,JinHuoSiQty,EnterPrice,EnterTotalPrice,StoreHouseId,BusinessId,DateTime)values('" + HuoWuId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + "," + EnterTotalprice.ToString() + ",'" + StoreHouseId + "','" + BusinessId + "','" + Datetime + "')");

skycon1.SqlExcute("insert GOODS_DETAIL(HuoWuId,BusinessId,HuoWuName,TypeId,SiQty,EnterPrice)values('" + HuoWuId + "','" + BusinessId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + ")");

skycon1.SqlExcute("update GOODS_LIST set SiQty=" + SiQtyG2.ToString() + ",StorePrice=" + StorePrice1.ToString() + "where TypeId='" + TypeId + "'");

HttpContext.Current.Response.Write("<script>alert('恭喜您的操作已成功!');window.location.href='Enter.aspx'</script>");

}

}

else//同种类没有该名字的货物

{

DataRow GooddataRow = dt4.Tables[0].Rows[0];

int SiQtyG3 = (int)GooddataRow["SiQty"] + JinhuoSiQty;

decimal StorePrice1 = (decimal)GooddataRow["StorePrice"] + EnterTotalprice;

if (StoreHouseId != GooddataRow["StoreHouseId"].ToString().Trim())//判断仓库号

{

HttpContext.Current.Response.Write("<script>alert('您输入的仓库号与物品仓库号不符合,请重新输入!');window.location.href='Enter.aspx'</script>");

}

else

{

skycon1.SqlExcute("insert JINHUO_LIST(HuoWuId,HuoWuName,TypeId,JinHuoSiQty,EnterPrice,EnterTotalPrice,StoreHouseId,BusinessId,DateTime)values('" + HuoWuId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + "," + EnterTotalprice.ToString() + ",'" + StoreHouseId + "','" + BusinessId + "','" + Datetime + "')");

skycon1.SqlExcute("insert GOODS_DETAIL(HuoWuId,BusinessId,HuoWuName,TypeId,SiQty,EnterPrice)values('" + HuoWuId + "','" + BusinessId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + ")");

skycon1.SqlExcute("insert GOODSNAME_LIST(HuoWuId,HuoWuName)values('" + HuoWuId + "','" + HuoWuName + "')");

skycon1.SqlExcute("update GOODS_LIST set SiQty=" + SiQtyG3.ToString() + ",StorePrice=" + StorePrice1.ToString() + "where TypeId='" + TypeId + "'");

HttpContext.Current.Response.Write("<script>alert('恭喜您的操作已成功!');window.location.href='Enter.aspx'</script>");

}

}

}

else  //如果是新种类的货物,则执行下列步骤;

{

skycon1.SqlExcute("insert JINHUO_LIST(HuoWuId,HuoWuName,TypeId,JinHuoSiQty,EnterPrice,EnterTotalPrice,StoreHouseId,BusinessId,DateTime)values('" + HuoWuId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + "," + EnterTotalprice.ToString() + ",'" + StoreHouseId + "','" + BusinessId + "','" + Datetime + "')");

skycon1.SqlExcute("insert GOODS_LIST(TypeName,TypeId,SiQty,StoreHouseId,StorePrice)values('"+TypeName+"','"+TypeId+"','"+JinhuoSiQty.ToString()+"','"+StoreHouseId+"','"+EnterTotalprice.ToString()+"')");

skycon1.SqlExcute("insert GOODS_DETAIL(HuoWuId,BusinessId,HuoWuName,TypeId,SiQty,EnterPrice)values('" + HuoWuId + "','" + BusinessId + "','" + HuoWuName + "','" + TypeId + "'," + JinhuoSiQty.ToString() + "," + EnterPrice.ToString() + ")");

skycon1.SqlExcute("insert GOODS_TYPES(TypeId,TypeName)values('" + TypeId + "','" + TypeName + "')");

skycon1.SqlExcute("insert GOODSNAME_LIST(HuoWuId,HuoWuName)values('" + HuoWuId + "','" + HuoWuName + "')");

HttpContext.Current.Response.Write("<script>alert('恭喜您的操作已成功!');window.location.href='Enter.aspx'</script>");

}

}

4.1.3 库存盘点

库存盘点是库存管理中相对较为关键的一环,由于专业知识所限,这个模块功能不是很全面,盘盈和盘亏的计算是通过将实际盘存的货物数量与数据库中的库存数量进行相减,得到正值即为盘盈,负值为盘亏。代码如下:

if (PanDianSiQty >= StoreSiQty)

{

PanY = OutPrice * (PanDianSiQty - (int)DataGoodDetail["SiQty"]);

sqlcon1.SqlExcute("insert PanDian_LIST(HuoWuId,HuoWuName,BusinessId,PanDianSiQty,StoreSiQty,OutPrice,PanY,DateTime)values('" + HuoWuId +

"','" + HuoWuName + "','" + BusinessId + "'," + PanDianSiQty.ToString() + "," + StoreSiQty.ToString() + "," + OutPrice.ToString() + "," + PanY + ",'" + Datetime + "')");

TextBox3.Text = PanY.ToString();

TextBox4.Text = null;

}

else

{

PanK = OutPrice * ((int)DataGoodDetail["SiQty"] - PanDianSiQty);

sqlcon1.SqlExcute("insert PanDian_LIST(HuoWuId,HuoWuName,BusinessId,PanDianSiQty,StoreSiQty,OutPrice,PanK,DateTime)values('" + HuoWuId +

"','" + HuoWuName + "','" + BusinessId + "'," + PanDianSiQty.ToString() + "," + StoreSiQty.ToString() + "," + OutPrice.ToString() + "," + PanK + ",'" + Datetime + "')");

TextBox4.Text = PanK.ToString();

TextBox3.Text = null;

}

4.2系统实现的关键技术

4.2.1 数据库的连接技术

过去,通过ADO的数据存取采用了两层的基于连接的编程模型。随着多层应用的需求不但增加,程序员需要一个无连接的模型。ADO.NET就应运而生了。ADO.NET的Managed Provider就是一个多层结构的无连接的一致的编程模型。Managed Provider提供了DataSet和数据中心(如MS SQL)之间的联系。Managed Provider包含了存取数据中心(数据库)的一系列接口。主要有三个部件:(1)连接对象Connection、命令对象Command、参数对象Parameter提供了数据源和DataSet之间的接口。DataSetCommand接口定义了数据列和表映射,并最终取回一个DataSet。(2)数据流提供了高性能的、前向的数据存取机制。通过IdataReader,你可以轻松而高效地访问数。(3)更底层的对象允许你连接到数据库,然后执行数据库系统一级的特定命令。

过去,数据处理主要依赖于两层结构,并且是基于连接的。连接断开,数据就不能再存取。现在,数据处理被延伸到三层以上的结构,相应地,程序员需要切换到无连接的应用模型。这样,DataSetCommand就在ADO.NET中扮演了极其重要的角色。它可以取回一个DataSet,并维护一个数据源和DataSet之间的"桥",以便于数据访问和修改、保存。DataSetCommand自动将数据的各种操作变换到数据源相关的合适的SQL语句。从图上可以看出,四个Command对象:SelectCommand、InsertCommand、UpdateCommand、DeleteCommand分别代替了数据库的查询、插入、更新、删除操作。

为了方便系统调用数据库连接,减少后台代码量,提高连接效率。我们把几个常用的方法都封装在ConnectionSQL这个类里面,以下是连接数据库代码:

public class ConnectionSQL:System.IDisposable

{

private SqlConnection sqlcon1=null;

public ConnectionSQL()

{

sqlcon1=new SqlConnection("server=localhost;uid=sa;pwd=;database=KuCunManage");

// // TODO: 在此处添加构造函数逻辑 //

}

public int SqlExcute(string s1) // 返回一个BOOL值 //

{

try

{

int i;

if(sqlcon1.State==ConnectionState.Closed)

sqlcon1.Open();

SqlCommand com1 = new SqlCommand(s1,sqlcon1);

i=com1.ExecuteNonQuery();

return i;

}

finally

{

sqlcon1.Close();

}

}

public SqlDataReader SqldataReader(string s1)

{

try

{

if (sqlcon1.State == ConnectionState.Closed)

sqlcon1.Open();

SqlDataReader read1;

SqlCommand com1 = new SqlCommand(s1, sqlcon1);

read1 = com1.ExecuteReader();

return read1;

}

finally

{

}

}

public DataSet Sqldataset(string s1) // 返回第一张表 //

{

try

{

if (sqlcon1.State == ConnectionState.Closed)

sqlcon1.Open();

SqlDataAdapter Sqlada1 = new SqlDataAdapter(s1, sqlcon1);

DataSet dt1 = new DataSet();

Sqlada1.Fill(dt1, "table1");

return dt1;

}

finally

{

sqlcon1.Close();

}

}

//销毁对象;

public virtual void Dispose(bool flag)

{

if (flag == true)

{

sqlcon1.Close();

}

sqlcon1 = null;

}

public void Dispose()

{

GC.SuppressFinalize(true);

this.Dispose(true);

}

}

相关推荐
跟着珅聪学java33 分钟前
spring boot +Elment UI 上传文件教程
java·spring boot·后端·ui·elementui·vue
徐小黑ACG2 小时前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
·薯条大王4 小时前
MySQL联合查询
数据库·mysql
战族狼魂4 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
杉之6 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
morris1316 小时前
【redis】redis实现分布式锁
数据库·redis·缓存·分布式锁
hycccccch6 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
bobz9657 小时前
k8s 怎么提供虚拟机更好
后端
这个懒人7 小时前
深入解析Translog机制:Elasticsearch的数据守护者
数据库·elasticsearch·nosql·translog
Yan-英杰7 小时前
【百日精通JAVA | SQL篇 | 第二篇】数据库操作
服务器·数据库·sql