资源描述
论文题目 中小型超市管理系统规划
学生姓名 陈锐
学 号 1033001469589
指导教师 李志军
专 业 计算机信息管理
年 级 2012级
学 校 浙江电大常山分校
目 录
一 需求分析 1
(一) 行业概念的理解 1
(二) 业务流程说明 2
(三)问题解决方案 2
(四) 数据流图 3
二 系统设计 3
二 系统设计 4
(一)系统模块的划分 4
(二) 数据字典 4
三、程序的设计与编码 8
(一) POS系统的开发 8
1. 功能及实现方法 8
2. 问题讨论:交易流水号的确定 9
(二)管理信息系统的设计 9
1.界面设计 9
2. 各个功能模块实现的要点和技术 10
2.1 类继承与重载 10
2.2 windows消息机制 11
2.3 关于权限设置 12
2.4 应用Delphi显式事务控制 13
2.5 存储过程的使用 13
参考书录 20
中小型超市管理系统规划
内容摘要:《中小型超市管理信息系统》是一个比较复杂和全面的管理系统,可以完成中小型超市的大部分业务。总的来说包括进货管理,物价管理,库存管理,销售管理,供货厂商管理,POS收款机子系统等。该系统界面朴素一致,但不失美观大方;操作方便,查询功能强大;数据全面、准确。系统采用了目前比较流行而技术非常成熟地客户机/服务器结构(C/S),前台应用程序用Delphi开发,后台数据库是Microsoft SQL Server 2000。
关键字: 面向对象的程序设计,继承,WINDOWS消息,存储过程,事务
一 需求分析
(一) 行业概念的理解
POS: Point Of Sells , 电子收款机系统,也就是直接与顾客打交道的收银机系统。
定单状态:用来描述商品进货方式,一般分为定单、日配、自采等。
定单:表示该商品进货必须先向供货商发出定单,供货商按定单上的商品明细发货,超市则按定单上的商品明细收货。
日配:指那些每天都要进货的商品或者补货期较短的商品,例如水果,生鲜蔬菜等。如果按定单,时间不允许而且工作重复烦琐。
自采:顾名思义,超市自己负责商品的进货。
销售状态:用来描述某种商品当前的销售状态,一般定义为正常、促销、折扣等,正常表示商品处于正常的售价状态,即按正常的售价销售。
促销:表示超市把某种商品让利销售,以此来吸引跟多的消费者。一般低于正常售价甚至成本价。
折扣:表示某种商品销售情况不好,或者商品有瑕疵,或者保质期快到等情况发生时,使尽快商品销售出去的一种手段。
库存调整:当非因正常销售发生而实物数量变动时,通过调整库存使其帐面数量与实物数量相符。
日处理:当一天的销售结束后,开始做日处理。把当天的销售数据汇总,调整商品库存。
损益:商品出现损坏,变质,被盗等情况发生时,商品发生损益。损益通过调整库存得到体现。
商品变价:商品变价与促销/折扣调整价格是两个不同的概念。商品变价是指修改商品的正常售价,调整后在一定的时间内不会在发生改变。价格可能是升高也可能是降低,一般都是由于进价变动引起的。
促销/折扣期: 每一种商品促销/折扣都有一定的期限,即有一定的时间限制。一种商品不可能无期限的促销/折扣。当期限超过,商品自动恢复正常售价。
毛利、毛利百分比: 毛利=商品售价-商品进价 毛利百分比=(商品售价-商品进价)÷商品售价
商品进价及成本核算: 在成本核算中,商品成本价的确定一般有最后进价,移动加权平均,按商品批次定价等几种方式,由于后两种核算方式较复杂,本系统采用简单的最后进价方式。
(二) 业务流程说明
在实际管理中,有三种信息流动,即资金流,单据流和商品实物流。系统围绕实物流展开,对商品从供应商那里进货到商品销售到顾客手中整个流程进行管理。简单来说,就是商品的进销存管理。每个环节通过相应的单据来连接,同时资金发生流动。在商品管理方面,我们提出部门和商品分类的概念,这种分级管理方式简洁高效。所谓部门,就是按照商品的基本属性进行分类管理。例如,超市一般可分为食品部,生鲜部,日用品部等。部门下面再分类,所谓商品分类,指按商品的具体属性划分,例如,食品部下面可以有糖果饼干类,饮料类等,每个部门和每个商品类别都有相应的负责人负责。《中小型超市管理信息系统》完全体现了这种管理思想,商品的销售和商品库存都可以按部门级和分类级完成查询等其他操作。作为一个超市的管理人员,及时了解销售情况是非常必要的,以便及时调整进货或者安排营销策略,把握商机。而《中小型超市管理信息系统》正好满足了这一要求,可以实时查询前台的销售情况,并且数据准确可信。
(三)问题解决方案
超市系统在运行过程中数据流量是非常巨大的。有时候会因数据流量过大而使网络发生阻塞,使POS系统发生断网,直接影响销售活动的进行。这种情况是应该坚决避免的,应该尽量减少网络的数据流量。这一思想在本系统中的直接体现就是采用日处理和运用本地缓存技术。日处理的主要目的就是把当天的销售数据整理汇总,更新商品库存表,为第二天的销售做准备。与日处理不同的另一种处理方式就是每次商品销售完成以后就立即更新库存。但这样会使得服务器的负荷非常的重,经常会使POS系统发生断网。POS系统销售商品时所取的商品信息是服务器上的数据,有效的保证了数据的一致性和集中控制。虽然商品信息来自服务器,但我们使用了本地缓存。POS系统会把取得的数据暂时的存放在本地的内存,当要读取商品信息时,会先在本地缓存里查找,如果成功的话,就不必访问服务器。这样一来就有效的减少了网络的数据流量和服务器的负荷。此外,系统充分利用了客户机/服务器结构的特点,比较复杂的查询更新等操作都用存储过程来实现。存储过程在服务器端执行完毕仅返回执行结果,能有效的减少网络的通讯流量。
(四) 数据流图
对整个业务活动进行分析归纳后,我得出如下的数据流图:
数 据 流 程 图
审核、打印
采购定单
商品入库
供应商
定单信息
定单
商品入库单
验收审核
结算
应付帐款
入库商品
库存变动
商品库存
商品信息
销售分析
销售数据
成本
毛利
财务核算
商品销售
销售明细
销售数量
POS销售
采购信息
采购定单
库存盘点
报损、报益
采购信息
二 系统设计
(一)系统模块的划分
根据前面所作的需求分析,我把整个系统分为如下几个模块:
管理信息系统
系 统 管 理
收 银 台 管 理
销 售 管 理
进 货 管 理
商 品 管 理
供 应 商 管 理
其 他 管 理
库 存 管 理
收 银 台 销 售
(二) 数据字典
商品入库单明细ENTER_GOODS_DETAIL
字段名称
数据类型
描述
ENTER_DAN_ID
int
入库单ID
GOODS_ID
int
商品ID
QUANTITY
numeric(18, 2)
入库数量
AMOUNT
numeric(18, 2)
入库金额
STATUS
char(4)
审核状态
WORK_PERSON
smallint
操作人员ID
WORK_DATE
datetime
记帐日期
商品信息GOODS_INFO
字段名称
数据类型
描述
GOODS_ID
int
商品ID
BAR_ID
int
商品条形码
GUIGE
char(4)
规格
COST_PRICE
money
进价
BUY_PRICE1
money
正常售价
BUY_PRICE2
money
促销/折扣销售价格
LARGE_TYPE
smallint
所属部门号
SMALL_TYPE
smallint
所属类别
MAX_STORE
numeric(18, 2)
最大库存
MIN_STORE
numeric(18, 2)
最小库存
GYS_CODE
int
供应商代码
STATUS
char(4)
商品进货状态
FLAG
char(4)
促销/折扣标记
BUYER
int
采购员ID
VAR_DATE
datetime
登记日期
供应商信息GYS_INFO
字段名称
数据类型
描述
GYS_CODE
int
厂商代码
NAME
varchar(20)
企业全称
ADDRESS
varchar(20)
地址
TEL
char(20)
电话号码
RELATION
char(10)
联系人
TYPE
char(4)
类型
BANK
char(4)
开户行
ACCOUNT
varchar(20)
帐号
MENO
varchar(20)
备注
部门信息LARGE_TYPE
字段名称
数据类型
描述
LARGE_TYPE_ID
int
部门代码
NAME
varchar(10)
部门名称
商品分类信息SMALL_TYPE
字段名称
数据类型
描述
SMALL_TYPE_ID
int
商品分类代码
NAME
varChar(10)
分类名称
人员信息PERSON_INFO
字段名称
数据类型
描述
PERSON_ID
int
人员代码
NAME
varchar(10)
姓名
SEX
char(2)
性别
AGE
smallint
年龄
DUTY
char(8)
职务
DEGREE
char(8)
学历
LOGIN
varbinary(20)
口令
MENO
varchar(20)
备注
前台销售表POS_SALE
字段名称
数据类型
描述
GOODS_ID
int
商品ID
BAR_ID
varchar(10)
条形码
QUANTITY
numeric(18, 2)
销售数量
AMOUNT
money
销售金额
CASHER
smallint
收银员ID
P_NO
int
交易流水号
收银台设置POS_SET
字段名称
数据类型
描述
POS_ID
smallint
收银台号
WORK_PERSON
smallint
收银员ID
商品变价记录表PRICE_ADJUST
字段名称
数据类型
描述
GOODS_ID
smallint
商品ID
COST_PRICE
smallint
商品进价
LAST_PRICE1
money
当前售价一
LAST_PRICE2
money
当前售价二
NOW_PRICE1
money
所调售价一
NOW_PRICE2
money
所调售价二
ADJUST_REASON
varchar(20)
变价原因
WORK_PERSON
int
操作员ID
WORK_DATE
datatime
变价日期
库存调整记录表STORE_ADJUST
字段名称
数据类型
描述
WORK_DATE
datatime
记帐日期
GOODS_ID
int
商品ID
QUANTITY
numeric(18,2)
调整前数量
AMOUNT
money
调整前金额
ADJUST_QUANTITY
numeric(18,2)
调整数量
DJUST_AMOUNT
Money
调整金额
ADJUST_REASON
int
调整原因ID
WORK_PERSON
int
操作员ID
日处理历史状态DIALY_DAN
库存调整原因表STORE_ADJUST_REASON
字段名称
数据类型
描述
ADJUST_ID
int
代号
NAME
smallint
摘要
商品库存表STORE_DETAIL
字段名称
数据类型
描述
GOODS_ID
int
商品ID
QUANTITY
numeric(18,2)
数量
AMOUNT
money
金额
MENO
varchar(10)
备注
商品促销/折扣记录GOODS_PROMOTE_LOG
字段名称
数据类型
描述
WORK_DATE
datatime
记帐日期
GOODS_ID
int
商品ID
CURRENT_PRICE
Money
当前售价
PROMOTE_PRICE
money
促销价
BEGIN_DATE
datetime
开始日期
END_DATE
datetime
结束日期
WORK_PERSON
int
操作员
权限表MENU_FLAG
字段名称
数据类型
描述
ID
smallint
代号
N1…42
smallint
标志
字段名称
数据类型
描述
WORK_DATE
smalldatetime
日处理时间
STATUS
char (4)
状态
WORK_PERSON
int
操作员ID
三、程序的设计与编码
有了前面的工作,我们选择就进入编码阶段了。
(一) POS系统的开发
1. 功能及实现方法
POS系统是作为一个单独的程序开发的,主要在POS机上运行,完成商品的交易任务。销售的商品信息存储在POS_SALE表中。我们把一次交易放在一个事务提交,保证了数据的完整性和数据的正确性。界面以灰色调为主,使得收银员长时间工作眼睛不感到疲劳。收银的操作也很简单方便。交易流水号自动增加。
当某种商品处于促销或折扣销售状态时,POS系统自动取商品的促销或折扣售价,无需人工干预,并且促销或折扣期结束时,系统能自动识别恢复正常售价。这一个功能的实现是通过商品视图来完成的。请看商品视图源码。
前台POS销售时取商品信息时用到的视图:
CREATE VIEW V_SPXX
AS
SELECT NAME , A.GOODS_ID,A.BAR_ID,A.GUIGE,
BUY_PRICE= CASE
WHEN (GETDATE()>B.END_DATE AND GETDATE()<B.BEGIN_DATE) OR A.FLAG='正常' THEN A.BUY_PRICE1
WHEN (GETDATE()<B.END_DATE AND GETDATE()>B.BEGIN_DATE) AND A.FLAG='促销' THEN A.BUY_PRICE2
WHEN (GETDATE()<B.END_DATE AND GETDATE()>B.BEGIN_DATE) AND A.FLAG='折扣' THEN A.BUY_PRICE2
ELSE A.BUY_PRICE1
END
FROM GOODS_INFO A ,GOODS_PROMOTE_LOG B
WHERE A.GOODS_ID*=B.GOODS_ID
交易是在一个事务里完成的,请看交易完成数据提交的代码:
if form_pos.Database1.InTransaction then
try
form_pos.query1.ApplyUpdates;把本地缓存修改的操作提交服务器
form_pos.Database1.Commit; 事务提交
form_pos.query1.CommitUpdates;清空本地缓存
if form_pos.Database1.InTransaction=false then
form_pos.Database1.StartTransaction;
except
form_pos.Database1.Rollback; 事务回滚
end;
2. 问题讨论:交易流水号的确定
在实际运用中,可能十几台甚至几十台POS机同时工作,也就是存在大量的数据库并发操作。怎样使交易流水号唯一呢?我是这样实现的,在每次事务提交之前,比较本机的流水号是否在后台数据库交易流水号中已经存在,如果存在,也就是说已经被别人占用,则重新取出最大交易流水号,然后提交整个事务。事务提交成功以后还要更新本地交易流水号。这是取最大流水号select max(P_NO) as p_no from pos_sale的SQL语句。
(二)管理信息系统的设计
1.界面设计
本系统采用了带导航栏的多文档窗体设计风格,使操作变的简单直观。当使用某一个功能模块时,只显示相应的菜单,使人不会觉得眼花缭乱。系统完全摈弃那种界面花哨,色彩绚丽等华而不实做法,但和传统的风格有有所区别,传统的风格显得过于呆板。本系统的界面色调一致,简单朴素,但不失美观大方。窗体界面一致,操作也大同小异,丝毫没有杂乱无章的感觉。
我首先定义了几个重要的窗体基类,其他的窗体基本上这几个基类的派生类。在使用时在对窗体进行重载。所有的窗体都是在运行时动态加载的。需要运行那个窗体时,程序才加载该窗体,用完后立即释放,这样使用户计算机系统资源得以最少得占用。请看一个窗体得创建例子:
enter_dan:= Tenter_dan.Create(Self); 入库单录入窗体得创建
form_show(enter_dan,555 ,360,10,2); form_show为自定义函数
对所有子窗体显示我自定义了一个函数form_show,减少了工作量和代码量。
procedure form_show(form:TForm;fwidth ,fheight,fleft,ftop :integer);
begin
with form do
begin
height:=fheight;
width:=fwidth;
Left:=fleft;
top:=ftop;
Show;
end;
end;
窗体的释放实现,例如释放入库单录入界面窗体:
procedure Tenter_dan.FormClose(Sender: TObject; var Action: TCloseAction);
begin
action:=cafree; 在内存中释放掉窗体
end;
2. 各个功能模块实现的要点和技术
这里我只把我在编码过程中比较重要或者比较有代表性的技术和部分源码阐述一下。详情查看系统的源代码。
2.1 类继承与重载
在编码过程中,我充分利用了面向对象编程的特定——代码的重用,具体实现起来依靠的是重载和继承。把具有相同或相似属性的类抽象出来作为一个基类。我定义了几个重要的窗体基类,基类窗体包含了需要的公有控件和共有事件方法。其他的窗体基本上这几个基类的派生类。在使用时在对窗体的控件的属性和方法进行继承或者重载。
关于在DELPHI方法的重载,例如:
procedure Action7Execute(Sender: TObject); OVERRIDE;
procedure Tenter_goods_info.Action7Execute(Sender: TObject);
begin
try
if (dbedit1.Text<>'') and (dbedit2.Text<>'') and (dbedit3.Text<>'')and (dbedit4.Text<>'')then
begin
query1.Post;
addbutton.Enabled:=true;
delbutton.Enabled:=true;
savebutton.Enabled:=false;
end
else
showmessage('你的输入不完整!');
except
showmessage('代码重复!');
end;
end;
2.2 windows消息机制
当查询窗体要返回结果时,我们用到了windows消息机制。
首先定义一个全局的自定义消息常量。
Const My_WM_USER=WM_USER+100; //自定义消息
向指定的窗体发送自定义消息,传递查询结果。例如:
procedure Tgoods_info_search.searchbuttonClick(Sender: TObject);
begin
try
price_adjust.Query1.Close; price_adjust.Query1.SQL.Clear;
price_adjust.Query1.SQL:=query1.SQL;
sendmessage(price_adjust.Handle,My_WM_USER,0,0);
except
beep;
end;
close;
end;
接收消息,并且处理它,例如:
定义消息响应的方法
procedure my_wm_user100(var msg:Tmessage); message My_WM_USER;
处理消息:
procedure Tgoods_promote.my_wm_user100(var msg:Tmessage) ;
begin
with query1 do
close;
sql.clear;
sql.add(sql语句);
open;
end;
2.3 关于权限设置
权限设置是一个管理系统非常重要的一部分,直接关系到公司业务和财务安全性。因此,本系统在这方面的功能是较完善的。操作人员的权限可以定义到每级子菜单。对没有赋权限的菜单项不予显示。也就是说,系统能根据登陆人员的权限自动显示具有权限的功能菜单。我们把人员的权限信息存放在MENU_FLAG表中,在系统启动时加载相关的权限设置信息。权限设置这一功能只有高级管理人员和系统管理员才能使用。
根据权限显示菜单算法
with damo.query1 do //query1关联MENU_FLAG表
begin
close;
sql.Clear;
sql.Add('select * from menu_flag where id=:id ');
ParamByName('id').AsInteger:=person_id;
open;
end;
for i:=1 to 42 do
begin
flag:=damo.query1.fields[i].AsInteger;//flag为每项菜单的权限标志
for j:=0 to main_form.ComponentCount-1 do
begin
if (main_form.Components[j] is TMenuItem) and ((main_form.Components[j] as TMenuItem).Tag=i-1 ) then
if flag = 0 then
(main_form.Components[j] as TMenuItem).Visible:=false
else
(main_form.Components[j] as TMenuItem).Visible:=true;
end;
end;
2.4 应用Delphi显式事务控制
事务控制是一种能够把数据库的一组修改作为整体提交给数据库以保证数据的一致性和完整性的机制。如果其中有一个操作失败,则所有操作失败。
2.5 存储过程的使用
在程序设计中,系统主要的复杂的操作我都都是通过存储过程来实现的。
例如:使用库存调整操作时,我们用P_ADJUST_STORE来完成。
procedure Tadjust_store.okbuttonClick(Sender: TObject);
begin
if not damo.Database1.InTransaction then
damo.Database1.StartTransaction; 开始一个事务
with P_ADJUST_STORE do //P_ADJUST_STORE为存储过程
begin
Params[1].AsInteger:=strtoint(edit_id.Text);
Params[2].AsFloat:=strtofloat(edit_adjust.Text);
Params[3].AsInteger:= table1.Fields[0].AsInteger ;
Params[4].AsInteger:= person_id ;// person_id为全局变量人员ID
Prepare;
execproc;
end;
try
damo.Database1.Commit; 事务提交
except
begin
damo.Database1.Rollback; 事务回滚
showmessage('数据提交失败! ');
end;
end;
以下是几种典型存储过程。
日处理存储过程:
CREATE PROCEDURE P_DIALY_DO
@dialy_date char(12),
@person_id smallint
AS
DECLARE @goods_id int
DECLARE @quantity float
DECLARE @amount money
DECLARE temp_cursor CURSOR FOR
SELECT A.GOODS_ID,SUM(A.QUANTITY),SUM(A.QUANTITY*B.COST_PRICE)
FROM POS_SALE A, GOODS_INFO B
WHERE convert(char(12),WORK_DATE,102)=@dialy_date
AND A.GOODS_ID=B.GOODS_ID GROUP BY A.GOODS_ID
OPEN temp_cursor
FETCH NEXT FROM temp_cursor INTO @goods_id, @quantity, @amount
WHILE (@@FETCH_STATUS=0)
BEGIN
if EXISTS(SELECT * FROM STORE_DETAIL WHERE GOODS_ID = @goods_id)
BEGIN
UPDATE STORE_DETAIL
SET QUANTITY=QUANTITY-@quantity , AMOUNT=AMOUNT-@amount
WHERE GOODS_ID = @goods_id
END
FETCH NEXT FROM temp_cursor INTO @goods_id,@quantity,@amount
END
INSERT DIALY_DAN
VALUES( @dialy_date,'已做',@person_id)
CLOSE temp_cursor
DEALLOCATE temp_cursor
查询每日商品销售汇总的存储过程:
CREATE PROCEDURE P_DIALY_SALE
AS
CREATE TABLE #temp_table
(
amount float,
work_date char(12),
sale_cost float,
gain float
)
INSERT INTO #temp_table
SELECT
a.amount,convert(char(12),a.work_date,102) AS work_date,
(c.cost_price*a.quantity) AS sale_cost,
(a.amount-c.cost_price*a.quantity) AS gain
FROM pos_sale a,small_type b,goods_info c
WHERE a.goods_id=c.goods_id
AND c.small_type=b.small_type_id
SELECT work_date, sum(amount) AS sum_amount , sum(sale_cost) AS sum_sale_cost , sum( gain ) AS sum_gain
FROM #temp_table
GROUP BY work_date, ORDER BY work_date
商品审核入库存储过程
CREATE PROCEDURE P_GOODS_ENTER
@enter_dan_id int
AS
DECLARE @goods_id int
DECLARE @quantity float
DECLARE @amount money
DECLARE temp_cursor CURSOR FOR
SELECT GOODS_ID,QUANTITY,AMOUNT FROM ENTER_GOODS_DETAIL
WHERE ENTER_DAN_ID=@enter_dan_id
OPEN temp_cursor
FETCH NEXT FROM temp_cursor INTO @goods_id,@quantity,@amount
WHILE (@@FETCH_STATUS=0)
BEGIN
if EXISTS(SELECT * FROM STORE_DETAIL WHERE GOODS_ID = @goods_id)
BEGIN
UPDATE STORE_DETAIL
SET QUANTITY=QUANTITY+@quantity ,
AMOUNT=AMOUNT+@amount
WHERE GOODS_ID = @goods_id
END
ELSE
BEGIN
INSERT STORE_DETAIL
VALUES(@goods_id,@quantity,@amount,getdate())
END
FETCH NEXT FROM temp_cursor INTO @goods_id,@quantity,@amount
END
CLOSE temp_cursor
DEALLOCATE temp_cursor
查询部门销售汇总的存储过程
CREATE PROCEDURE P_SEARCH_DEP_SALE
AS
CREATE TABLE #temp_table
(
large_type_id int,
dep_name char(10),
quantity float,
amount float,
work_date char(12),
sale_cost float,
gain float
)
INSERT INTO #temp_table
SELECT
b.large_type_id,b.name AS dep_name,
a.quantity,a.amount,convert(char(12),a.work_date,102) AS work_date,
(c.cost_price*a.quantity) AS sale_cost,
(a.amount-c.cost_price*a.quantity) AS gain
FORM pos_sale a,large_type b,goods_info c
WHERE a.goods_id=c.goods_id
AND c.large_type=b.large_type_id
SELECT work_date, large_type_id ,dep_name,
sum( quantity) AS sum_quantity , sum(amount) AS sum_amount , sum(sale_cost) AS sum_sale_cost , sum( gain ) AS sum_gain
FROM #temp_table
GROUP BY work_date, large_type_id ,dep_name ORDER BY work_date
查询商品分类汇总的存储过程
CREATE PROCEDURE P_SEARCH_KIND_SALE
AS
CREATE TABLE #
展开阅读全文