企业经营如逆水行舟不进则退。每一个努力发展的企业都明白,先进管理的重要作用,引进先进管理系统使企业经营变得规范、合理。进销存管理系统无论是在功能设计还是业务流程上都尽可能做到满足经营管理运作流程的需求,并且操作方便、功能强大,即使操作者对计算机知识一窍不通也能一用就会。它强化库存管理,规范业务流程,提高资金管理的透明度,加快商品资金周转,是企业经营管理中数据分析中必不可少的管理工具。
根据市场的需求,要求系统具有以下功能:
实现商品入库、入库退货操作。
实现商品销售、销售退货操作。
实现库存管理(调货、盘点)操作。
实现信息的查询、打印功能。
准确地进行账款记录、账款查询。
根据需求分析,设计系统框架。进销存管理系统由等8部分组成。设计各部分具体功能如下:
基础信息模块
基础信息模块包括药品信息、员工信息、供应商信息和客户信息4部分。
销售模块
销售模块由销售登记、销售退货、销售查询、销售退货查询4部分。
入库模块
入库模块主要由入库登记、入库退货、入库查询、入库退货查询4组成。
调货模块
调货模块包含调货登记和调货查询两部分。
库存模块
库存模块由库存盘点、库存查询、仓库管理3部分组成。
结账模块
结账模块主要包括销售结账、销售退货结账、入库结账、入库退货结账。
财务模块
财务模块由日结、月结、供应商往来账、客户往来账4部分组成。
系统管理模块
系统管理模块由修改用户密码、修改用户权限、修改用户3部分组成。
CPU:300MHz以上的处理器。
内存:128MB,推荐256MB。
硬盘:150MB以上剩余空间。
显示像素:最低800*600,最佳效果1024*768。
操作系统:Windows2000/NT/XP/CE。
数据库:SQLServer2000。
本系统采用SQLServer2000数据库,系统数据库名为ypgl,中共包含46个表,其中作为临时表的有20个,作为数据存储表的有26个。
临时表:lsdhinfo0、lsdhinfo1、lsgys0、lsgys1、lskcquery0、lskcquery1、lskh0、lskh1、lsrkquery0、lsrkquery1、lsrkthinfo0、lsrkthinfo1、lsxsquery0、lsxsquery1、lsxsthinfo0、lsxsthinfo1、lsyg0、lsyg1、lsypinfo0、lsypinfo1。
数据存储表:tabbf、tabck、tabdhdj、tabdhph、tabgys、tabjsfs、tabkc、tabkcpddj、tabkcpdph、tabkh、tabpurview、tabpurviewctrl、tabrkdj、tabrkjz、tabrkph、tabrkthdj、tabrkthjz、tabrkthph、tabxsdj、tabxsjz、tabxsph、tabxsthdj、tabxsthjz、tabxsthph、tabyginfo、tabypinfo。
图2所示的即为本系统中数据库的数据表结构图,该数据表结构图包含系统所有数据表。可以清晰地反应数据库信息。
数据库中的数据表请参见附录B。
进销存管理系统主窗口由菜单、工具栏、客户区域和状态栏四部分组成,效果如图3所示。
图3进销存管理系统主窗口
(1)在工具栏中单击按钮,或者从菜单中选择“View”/“Workspace”项,这时会弹出如图4所示的工作区窗口(Workspace窗口)。在工作区窗口中,能看到该程序所使用的资源,且每种资源都有一个资源符号,主窗体也使用了一个资源符号IDD_A1_DIALOG,这是VC缺省提供的。可以在这里添加或者删除各种资源。
图4Workspace窗口
(2)在工作区窗口(Workspace窗口)右键单击“a1resources”选项,在弹出菜单中选择“Insert...”选项,将弹出“InsertResource”对话框。在该对话框中选择“Menu”选项,然后单击“New”按钮,将生成如图5所示的菜单资源。
图5编辑菜单资源
(3)右键双击菜单资源编辑器的虚线空白框,在弹出的菜单中选择“Properties”选项,将弹出“MenuItemProperties”对话框,在“MenuItemProperties”对话框的“caption”(标题)编辑框中键入:“基础信息(&I)”(符号&可以使字母I有一个下划线,而且可以通过“Alt+I”访问该菜单项。此时关闭“MenuItemProperties”对话框,将在菜单编辑器中生成主菜单“基础信息”。双击“基础信息”菜单下的虚线空白框,在弹出的“MenuItemProperties”对话框中设计“药品信息(&M)”、“员工信息(&Y)”等菜单项。
(4)同上,可以设计其他主菜单及菜单项。最后得到如图6所示的菜单界面。
图6菜单界面
在应用程序中要经常使用工具栏,它是最常用的界面元素,对应着应用程序的最常用功能。主窗口共有9个工具栏按钮,分别是“销售登记”、“销售退货”、“销售结账”、“入库登记”、“入库退货”、“入库结账”、“调货登记”、“库存登记”、“退出”工具栏按钮。创建工具栏可使用MFC类库中的CToolBarCtrl类,该类用来生成工具条。本系统主窗体的工具栏将引用MSDN提供的类CStandardBar,该类派生自CToolBarCtrl。
操作步骤如下:
(1)从基类CToolBarCtrl中派生需要的类CstandardBar。选择“Insert”/“NewClass...”菜单项,在弹出来的“NewClass”对话框中设置“ClassType”为“MFCClass”,在“ClassInfomation”中的Name编辑框中键入“CstandardBar”,然后在“BaseClass”下拉列表框中选择“CtoolBarCtrl”,最后单击“OK”按钮。
(2)需要9个按钮,每个按钮有相应的文本和图片。所以,需要添加如图7所示的图片资源,资源长为288像素,高为32像素,资源符号为:IDR_STANDARDBAR。
(3)添加字符串资源(StringTable),如表1所示。
表1字符串资源
资源符号
值
字符串资源
IDSTR_XSDJ
102
销售登记
IDSTR_XSTH
103
销售退货
IDSTR_XSJZ
104
销售结账
IDSTR_RKDJ
105
入库登记
IDSTR_RKTH
106
入库退货
IDSTR_RKJZ
107
入库结账
IDSTR_DHDJ
108
调货登记
IDSTR_KCPD
109
库存盘点
IDSTR_OUT
110
退出
(4)程序中引入资源,创建工具栏按钮。
创建工具栏按钮需要重写Create函数,该函数创建工具栏的步骤如下:
SetBitmapSize(CSize(32,32));//设置单个位图的大小
VERIFY(AddBitmap(m_nButtonCount,IDR_STANDARDBAR)!=-1);//添加位图
m_nButtonCount是指按钮图片的个数,IDR_STANDARDBAR对应着相应的图片。
m_pTBButtons=newTBBUTTON[m_nButtonCount];//用来加入到工具栏里的按钮
……
m_pTBButtons[nIndex].iString=AddStrings(pString);
m_pTBButtons[nIndex].fsState=TBSTATE_ENABLED;
m_pTBButtons[nIndex].fsStyle=TBSTYLE_BUTTON;
m_pTBButtons[nIndex].dwData=0;
m_pTBButtons[nIndex].idCommand=nIndex+IDSTR_XSDJ;//用于命令消息传递
在步骤②中,要注意如下事项:
①AddStrings(pString)返回一个字符串的基于0的编号,该值用来连接字符串到按钮上,其中的字符串参数pString需要两个结束符来表示结尾,必须将字符串写成如下形式:pString="Onlyonestringtoadd\0";CString类不能提供这样的功能,因为不可能在CString中保存超过一个结束符的字符串。所以,将CString中的字符串取出,以char定义的字符串保存,再对该字符串添加一个结束符,做法如下:
CStringstring;
string.LoadString(nIndex+IDSTR_XSDJ);//装载字符串资源
//取得字符串的长度为了添加一个结束符,给长度加1
intnStringLength=string.GetLength()+1;
TCHAR*pString=string.GetBufferSetLength(nStringLength);//按增加后的长度返回字符串
pString[nStringLength]=0;
函数GetBufferSetLength的过程分配了nStringLength+1长度的内存空间,并在加上结束符'\0'之后,复制原字符串到这个新的内存空间中,同时将原字符串的结束符也复制到新的位置,于是,该函数结束后,字符串pString已经有两个结束符了,最后一个语句略显多余或不足。但为了保证该字符串确实有两个结束符,不能省略这两个结束符。
pString[nStringLength-1]=0;
②fsState确定按钮的状态,fsStyle确定按钮的风格。若给fsStyle赋值TBSTYLE_SEP,则该按钮表现为一个间隔。dwData可以是用户自定义的数据,可以将一个指针或句柄传递给它,可以在某些消息响应函数中使用。iBitmap是表示基于0的图像列表的编号。
③idCommand为与按钮连接的命令标识,当这个按钮被按下时,这个值将被放到WM_COMMAND中发送到父窗体。如果fsStyle被设置为TBSTYLE_SEP,该值必须为0。
用Create函数创建工具栏的代码如下:
BOOLCStandardBar::Create(DWORDdwStyle,constRECT&rect,CWnd*pParentWnd,UINTnID);
{
BOOLbRet=CToolBarCtrl::Create(dwStyle,rect,pParentWnd,nID);//记录基类的返回值
m_nButtonCount=IDSTR_OUT-IDSTR_XSDJ+1;
for(intnIndex=0;nIndex //为每一个字符串再加一个'\0',用于向工具栏里加字符串 TCHAR*pString=string.GetBufferSetLength(nStringLength); VERIFY((m_pTBButtons[nIndex].iString=AddStrings(pString))!=-1);//返回字符串的编号 string.ReleaseBuffer(); } m_pTBButtons[m_nButtonCount-1].idCommand=IDOK;//用来响应退出消息 TBBUTTONsepButton;//用于分隔的按钮 sepButton.idCommand=0; sepButton.fsStyle=TBSTYLE_SEP; sepButton.fsState=TBSTATE_ENABLED; sepButton.iString=0; sepButton.iBitmap=0; sepButton.dwData=0; for(nIndex=0;nIndex VERIFY(AddButtons(1,&m_pTBButtons[nIndex]));//循环添加按钮 if(!((nIndex+1)%3)) VERIFY(AddButtons(1,&sepButton));//每3个按钮为一组,两组间有一个分隔按钮 returnbRet;//返回CToolBarCtrl::Create的返回值 (5)调用工具栏类。先在类CA1Dlg中实例化CStandardBar的对象。 CStandardBarm_StandardBar; #include"StandardBar.h" (6)增加消息WM_CREATE的响应函数,为CStandardBar对象创建相应窗口。 intCA1Dlg::OnCreate(LPCREATESTRUCTlpCreateStruct) if(CDialog::OnCreate(lpCreateStruct)==-1) return-1; m_StandardBar.Create(WS_BORDER|WS_VISIBLE|WS_CHILD |TBSTYLE_WRAPABLE|CCS_TOP|CCS_ADJUSTABLE, CRect(0,0,0,0),this,IDR_STANDARDBAR1); m_StandardBar.AutoSize();//重新计算控件的大小 return0; 工具栏到这里就创建成功了。 在此之前,定义了菜单和工具栏界面,单击他们并没有实质的内容,现在为他们添加消息处理函数。 (1)单击菜单和工具栏按钮的两种消息都是命令消息,所以,只要让他们传递相同的消息,就能执行相同的消息处理函数。从代码中可以看到对于工具栏的按钮来说,按钮的命令消息值与字符串资源符号的值相同,而且是顺序的,而对于相应的菜单项来说,消息值是随机的顺序值。为了将两者对应起来,要修改菜单项的资源符号,将其改为相应的按钮的字符串资源符号。例如:将销售登记菜单项的资源符号改为IDSTR_XSDJ,并给它定义消息响应函数:voidCA1Dlg::OnXsdj()。这样,无论是单击“销售登记”菜单项,还是单击“销售登记”按钮都会执行这个函数。同理,完成其他的菜单项与按钮的对应。 (2)还有一个问题:别忘了,工具栏中有一个“退出”按钮。这个退出按钮与谁对应呢?当用户按下〈Enter〉键或〈Esc〉键时,对话框就会退出,这里触发的两个消息分别是IDOK和IDCANCEL。如果给“退出”按钮的命令消息值赋值为IDOK,那么单击该按钮时,对话框就会退出。代码如下: ①访问应用程序对象。 CA1App*app=(CA1App*)::AfxGetApp(); //app是应用程序对象指针,可以访问应用程序对象的成员变量,例如: MessageBox(app->m_sUserName); ②访问资源字符串。 先在“Workspace”中的“ResourceView”选项中建立字符串资源,定义符号IDS_COMPANY,对应资源为“明日腾龙科技有限责任公司(www.mingrisoft.com)”。 在程序中使用如下代码: CStringstr; str.LoadString(IDS_COMPANY); str保存相应的字符串资源。 CTimet=CTime::GetCurrentTime(); CStrings=t.Format("%H:%M:%S"); ④OnTimer消息响应函数。 为实现每隔1秒刷新一次状态栏的显示内容,可以使用WM_TIMER的消息响应函数OnTimer。要创建主窗体的OnTimer函数,首先在“Workspace”工作区“ClassView”选项卡中右键单击“CA1Dlg”选项,在弹出菜单中选择“AddWindowsMessageHandle...”菜单项,将弹出“NewWindowsMessageandeventhandlesforclassCAIDlg”对话框。在该对话框中,可以选择要进行处理的消息句柄,并为其添加消息响应函数。步骤是从左边的列表框中双击 UINTSetTimer(UINTnIDEvent,UINTnElapse,void(CALLBACKEXPORT*lpfnTimer)(HWND,UINT,UINT,DWORD)); 参数说明: nIDEvent:用来标识是哪一个Timer事件。 lpfnTimer:设置回调函数,用来响应事件的发生,相当于OnTimer函数。如果将其设为NULL,那么,WM_TIMER事件由窗口类来处理,即由OnTimer函数处理。 在本程序中,设定OnTimer函数1000毫秒响应一次。 SetTimer(12,1000,NULL); ⑤状态栏类CStatusBarCtrl的使用。 CRectrect; this->GetClientRect(&rect); intindicators[3]; indicators[0]=rect.Width()/2; indicators[1]=rect.Width()*3/4; indicators[2]=rect.Width(); m_StatusBarCtrl.SetParts(3,indicators); 以上代码用来初始化状态栏,函数SetParts用来设定该状态栏由几个面板组成,每个面板的宽度。实际上,该函数用整型数组作参数,数组元素的值代表面板的宽度。 函数SetText用来设定每个面板上显示的数据,代码如下: lBOOLSetText(LPCTSTRlpszText,intnPane,intnType); lpszText:是该面板的字符串。 nPane:是面板编号(基于0)。 nType:是面板风格,该参数一般为0。 (2)创建状态栏。 创建状态栏的操作步骤如下: ②在CA1Dlg的初始化函数中初始化状态栏对象,代码如下: BOOLCA1Dlg::OnInitDialog() CDialog::OnInitDialog(); SetIcon(m_hIcon,TRUE);//Setbigicon SetIcon(m_hIcon,FALSE);//Setsmallicon //为状态栏创建窗体 m_StatusBarCtrl.Create(WS_CHILD|WS_VISIBLE|CCS_BOTTOM, rect,this,ID_STATUS_BAR_CTRL); //设置状态栏的显示区间数,及相应宽度 //显示各区间文本 m_StatusBarCtrl.SetText(str,0,0); m_StatusBarCtrl.SetText("当前操作员:"+app->m_sUserName,1,0); m_StatusBarCtrl.SetText(s,2,0); this->SetTimer(12,1000,NULL); returnTRUE;//returnTRUEunlessyousetthefocustoacontrol ③处理WM_TIMER消息的消息响应函数OnTimer,代码如下: voidCA1Dlg::OnTimer(UINTnIDEvent) this->m_StatusBarCtrl.SetText(s,2,0); CDialog::OnTimer(nIDEvent); 一个优秀的商业管理系统,不但要有实用的功能,还要有漂亮友好的界面。在本例中,设置窗体背景只需加入一个Bitmap资源,运行效果如图3.3所示,其操作步骤如下: (1)在资源对话框上放一个Picture控件。 (2)然后加入一个需要的Bitmap资源,如果图片包含的颜色超过256种,那么它会提示该图片不能在资源编辑器里编辑,不用管它,除非真的想编辑它,加进来的图片的资源符号缺省为IDB_BITMAP1。 (3)设置Picture控件属性类型为Bitmap,图像为IDB_BITMAP1。 q输入密码的控件采用文本框。密码如果输入正确,取得用户权限并进入系统,否则,将提示错误,并返回密码输入框。 q记录错误次数,录入密码错误3次将自动退出系统。 q用户按下〈Enter〉键,控制焦点的移动。 (1)增加对话框资源,设计窗体资源符号为IDD_DIALOG_LOGIN。 (3)向窗口中添加图片、编辑框、静态文本、按钮等资源,设置主要资源属性,如表2所示。 资源名称 资源对应的变量 资源属性 Bitmap JIEMIAN 无 来自于文件jiemian.bmp Picture IDC_STATIC 类型Bitmap,图像JIEMIAN Button IDOK 缺省 IDCANCEL StaticBox 标题为:请输入用户名和密码: 标题为:用户名: 标题为:密码: EditBox IDC_EDIT_NAME CEditm_editUserName 取消Border,选上【Staticedge” IDC_EDIT_PASSWORD CEditm_editPassWord 取消Border,选上【Staticedge”和【PassWord” BOOLCMedApp::InitInstance() ...... LRunSql::InitConnectPtr();//初始化COM环境,进行数据库连接 CA1Dlg*pdlg=newCA1Dlg;//创建主窗体对象 m_pMainWnd=pdlg; pdlg->DoModal();//显示主窗体 deletepdlg; pdlg=NULL; LRunSql::Close();//断开数据库连接 returnFALSE; 当用户单击“确定”按钮时,进行密码判断和次数判断。处理“确定“按钮的消息响应函数如下: voidCDlgLogOn::OnOK() BOOLbLogOn=FALSE; CStringsUserPassWord,sPurview,sUserName,sInputPassWord; LRunSqlm_runsql; CStringsql; _variant_tvalue; CStringsError; //更新数据变量 this->m_editPassWord.GetWindowText(sInputPassWord); this->m_editUserName.GetWindowText(sUserName); CA1App*App=(CA1App*)AfxGetApp(); sql.Format( "selectadmi_password,admi_purviewfromtabpurviewwhereadmi_name='%s'", sUserName); if(m_runsql.CheckSQLResult(sql)) value=m_runsql.m_recordset->GetCollect("admi_password"); if(value.vt!=VT_NULL) sUserPassWord=(char*)(_bstr_t)value; value=m_runsql.m_recordset->GetCollect("admi_purview"); sPurview=(char*)(_bstr_t)value; if(sUserPassWord==CCrypt::Encrypt(sInputPassWord,123)) App->m_sUserName=sUserName; App->m_sPurview=sPurview; bLogOn=TRUE; else sError="请重新输入密码。\n注意大小写!","密码错误"; this->m_editPassWord.SetFocus(); sError="请确认用户名大小写是否正确!","无此用户"; this->m_editUserName.SetFocus(); if(bLogOn)EndDialog(IDOK); m_iLogOnCount++; if(m_iLogOnCount>=3) this->EndDialog(0); MessageBox(sError); 进行焦点控制。定义控制焦点的函数,该函数定义了当用户单击〈Enter〉键时,焦点改变的顺序,当需要改变焦点时,调用此函数。 boolCDlgLogOn::SetTheFocus() HWNDhwnd=::GetFocus(); UINTid=::GetDlgCtrlID(hwnd); switch(id) caseIDC_EDIT_NAME: ::PostMessage(m_editPassWord.GetSafeHwnd(),WM_KEYDOWN,VK_END,0); returntrue; caseIDC_EDIT_PASSWORD: this->GetDlgItem(IDOK)->SetFocus(); caseIDOK: this->OnOK(); caseIDCANCEL: this->OnCancel(); returnfalse; 在消息预处理函数中调用此函数。 BOOLCDlgLogOn::PreTranslateMessage(MSG*pMsg) if(pMsg->message==WM_KEYDOWN&&pMsg->wParam==13) if(this->SetTheFocus()) returnCDialog::PreTranslateMessage(pMsg); //该函数返回True,表示该消息已被处理 q自动生成销售日期及销售票号。 q确定用户输入数据完毕后,可提交数据给数据库。 q自动核算销售金额。 q支持打印功能。 销售登记模块运行结果如图9所示。 图9销售登记模块运行结果 (1)新建一个窗口类,名称为CDlgXSDJ,对话框资源ID为IDD_DIALOG_XSDJ。 (3)设置主要资源属性,如表3所示。 Dialog IDD_DIALOG_XSDJ CDlgXSDJdlg(true) 标题:销售登记 字体名称:楷体_GB2312 字体大小:12 ComboBox IDC_COMBO1 CComboBoxm_comboJsfs Type:DropList IDC_COMBO2 CComboBoxm_comboJsr IDC_EDIT_KHID CGeneralEditm_editKhId 取消Border IDC_EDIT_KHNAME CGeneralEditm_editKhName IDC_EDIT1 CStringm_strPh CEditm_editPh 取消Border,选上ReadOnly IDC_EDIT_SS Type:Frame Color:Black IDC_STATIC_SS 标题为:实收: 标题为:总金额: IDC_STATIC_ZJE 标题为:0.00 IDC_STATIC_PH 标题为:销售登记票号: DateTimePicker IDC_DATETIMEPICKER1 CDateTimeCtrlm_tcRq 格式:ShortDate 选上:Staticedge IDC_BUTTON1 标题为:删除 IDC_BUTTON_OK CButtonm_btnOk 标题为:确定 IDC_BUTTON_PRINT 标题为:打印... 标题为:退出 (1)创建左上角的标题信息。 ①创建白色背景。 CWhiteFramem_whiteframe; 在OnCreate的成员函数中创建该对象对应的窗口。 intCDlgXSDJ::OnCreate(LPCREATESTRUCTlpCreateStruct) rect.left+=10; rect.right-=250; rect.top+=10; rect.bottom=200; m_whiteframe.Create("",WS_CHILD|WS_TABSTOP|WS_VISIBLE,rect,this,700); ②创建标题部分的控件。 该部分的控件都以上述的白色背景窗口为父窗口,要注意以下几点: 在这个背景上创建静态文本,且文本背景是白色。对此,同样使用CWhiteFrame类,并用该类定义一组静态文本对象,要注意创建文本的位置,使其与相应的控件相配合。例如:确保静态文本“客户编号”与相应的编辑框在同一个水平高度;日期与日期控件在同一个高度。 由于入库与销售大部分内容是相同的,所以,用一个窗体来执行这两部分功能。为此,用Bool型的变量m_bXs来表示这两种区别,并在代码中要适时修改一些属性。 改变设计时添加的控件的父窗体要用到其成员函数SetParent。方式如下: m_tcRq.SetParent(&m_whiteframe); 对于结算方式下拉列表框和经手人下拉列表框,需要从数据库中取得相应的数据来初始化这两个窗口,并在最后为这两个下拉列表框确定初值。 this->m_tcRq.SetTime(&m_dateRq); 创建和初始化标题部分的控件的代码如下: boolCDlgXSDJ::InitBaseInfo() CRectrect;CStringstr; wf=newCWhiteFrame[5]; CStringstrs[5];//该字符串保存静态文本 if(m_bXs)//判断当前是销售登记,还是入库登记 strs[0]="客户编号:"; strs[1]="客户名称:"; this->GetDlgItem(IDC_STATIC_PH)->SetWindowText("入库登记票号:"); this->SetWindowText("入库登记:"); strs[0]="供应商编号:"; strs[1]="供应商名称:"; strs[2]="结算方式:"; strs[3]="经手人:"; strs[4]="日期:"; rect=CRect(10,40,80,55);//用来确定静态文本的位置 for(inti=0;i<5;i++) {//创建静态文本 wf[i].Create(strs[i],WS_CHILD|WS_VISIBLE,rect,&m_whiteframe); wf[i].Invalidate(); rect.OffsetRect(0,28); m_tcRq.SetParent(&m_whiteframe);//改变标题控件的父窗口 m_comboJsr.SetParent(&m_whiteframe); m_editKhName.SetParent(&m_whiteframe); m_editKhId.SetParent(&m_whiteframe); m_comboJsfs.SetParent(&m_whiteframe); //inittime; //initjsr;//初始化经手人下拉列表框 sql.Format("selectyg_namefromtabyginfo"); if(!m_runsql.CheckSQLResult(sql))returnfalse; while(!m_runsql.m_recordset->adoEOF)//从数据库取值初始化员工下拉列表框 value=m_runsql.m_recordset->GetCollect("yg_name"); this->m_comboJsr.AddString((char*)(_bstr_t)value); m_runsql.m_recordset->MoveNext(); //initjsfs; sql.Format("selectjsfsfromtabjsfs"); this->m_runsql.RunSQL(sql); while(!m_runsql.m_recordset->adoEOF)//从数据库取值初始化经手人下拉列表框 value=m_runsql.m_recordset->GetCollect("jsfs"); this->m_comboJsfs.AddString((char*)(_bstr_t)value); this->m_comboJsfs.SetCurSel(0); this->m_comboJsr.SetCurSel(0); ③为客户编号和客户名称编辑框连接自动提示窗口。 对于客户信息和供应商信息,这两者信息格式大部分是相同的,并且每次是用到其中的一组数据时都需要用到自动提示窗口,所以专门作出一个类CGeneralEdit来对应着两组信息,并包含对自动提示窗口的使用。该类派生自CEdit(参看类图2.12)。在此之前,请参看关键技术中的5.2.2关于显示自动提示窗口的介绍。 功能分析: 界面上看,需要有下划线和白色背景。 当把焦点移到某个编辑框上时原有弹出的自动提示窗口消失。 当在编辑框中进行编辑时引起EN_CHANGE事件时,弹出自动提示窗口。 焦点移走时,让自动提示窗口消失;但是如果焦点移动到了自动提示窗口上,则自动提示窗口不消失。 当父窗体移动时,自动提示窗口也跟着移动。 当在编辑框中进行编辑时,按〈↑〉、〈↓〉、〈PageUp〉、〈PageDown〉键,自动提示窗口会响应这些消息,且此时焦点还在编辑框中。 当用户在其中一个编辑框中按〈Enter〉键,表示确定了选择某一个客户(编号或名称), 那么另一个编辑框中的内容也随之改变。 当用户在其中一个编辑框中单击〈Esc〉键,表示用户放弃进行编辑,取消自动弹出对话框。 为了实现功能2,写消息WM_SETFOCUS的消息响应函数OnSetFocus。 voidCGeneralEdit::OnSetFocus(CWnd*pOldWnd) ASSERT(this->m_poplist!=NULL); m_poplist->ShowWindow(SW_HIDE); CEdit::OnSetFocus(pOldWnd); 对于功能3,要写EN_CHANGE消息响应函数OnChange。 在编写EN_CHANGE消息响应函数OnChange时,需要注意: 客户编号编辑框和客户名称编辑框都是由一个类定义出来的,使用资源符号来区分这两个对象,如IDC_EDIT_KHID对应着客户编号编辑框,而IDC_EDIT_KHNAME则对应着客户名称编辑框对象。 当编辑编号时,直接用当前编辑框的字符串来刷新自动提示窗口,当编辑名称时,还需要参考编号编辑框的值,来显示自动提示窗口的内容。 如果自动提示窗口已经显示了,就没有必要重复计算显示。 代码如下: voidCGeneralEdit::OnChange() if(m_bSetValue)return; this->GetWindowText(str);//取得当前编辑框的字符串 intiID=this->GetDlgCtrlID();//取得当前编辑框对象的资源符号的值 if(iID==IDC_EDIT_KHID)//判断是编号对象,还是名称对象 this->m_poplist->RefreshListCtrlView(0,str);//刷新自动提示窗口数据 elseif(iID==IDC_EDIT_KHNAME) CStringid; CWnd*editid; editid=this->GetParent()->GetDlgItem(IDC_EDIT_KHID); editid->GetWindowText(id); m_poplist->RefreshListCtrlView(1,str,id,0);//根据两个字符串刷新自动提示窗口 if(!m_poplist->IsWindowVisible())//如果自动提示窗口已显示,就不用再设定位置、显示 this->GetWindowRect(&m_rectWindow); this->m_poplist->SetShowPosition(this->m_rectWindow,this); m_poplist->ShowListPop(); 对于功能4、6、7、8,需要编写预解释函数PreTranslateMessage(MSG*pMsg)和WM_KILLFOCUS的消息响应函数OnKillFocus,功能分析如下: 编辑框失去焦点的时候,会调用函数OnKillFocus(CWnd*pNewWnd),此时,需要判断是否焦点转移到了自动提示窗口上,pNewWnd就是焦点转移到的窗口指针,判断这个指针是谁就行了。 当用户按〈Enter〉键,缺省情况下,对话框处理这个消息并最终执行OnOk函数,关闭对话框。需要在预解释函数中处理这个消息,并把它解释为WM_KILLFOCUS,并设定一个开关变量标识这个消息,在OnKillFocus函数中收到并处理这个消息。 当用户按〈Esc〉键时,需要让自动提示窗口消失,同时将〈Enter〉键解释为无关紧要的键如〈Ctrl〉键。 当用户按〈↑〉、〈↓〉、〈PageUp〉、〈PageDown〉键时,将这个消息原封不动地发送给自动提示窗口,自动提示窗口会响应这些消息(此时焦点还在编辑框中)。 BOOLCGeneralEdit::PreTranslateMessage(MSG*pMsg) //TODO:Addyourspecializedcodehereand/orcallthebaseclass if(pMsg->message==WM_KEYDOWN)//捕捉键盘某个键按下事件 switch(pMsg->wParam) case13://捕捉〈Enter〉键 pMsg->message=WM_KILLFOCUS;//将该消息改为WM_KILLFOCUS消息 m_Kill=RETURNKILL;//设置开关变量,将在OnKillFocus中使用 break; case27://捕捉〈Esc〉键 if(m_poplist->IsWindowVisible())//如果自动提示窗口处于显示状态,隐藏它 this->m_poplist->ShowWindow(false); pMsg->wParam=VK_CONTROL;//将该消息改为〈Ctrl〉键 caseVK_UP://捕捉〈↑〉、〈↓〉、〈PageUp〉、〈PageDown〉键 caseVK_DOWN: caseVK_NEXT: caseVK_PRIOR: {//将这些消息由自动提示窗口处理 this->m_poplist->SendTheUDNPMessage(pMsg->wParam); pMsg->wParam=VK_CONTROL;//并将该消息解释为无用的键 returnCEdit::PreTranslateMessage(pMsg); 下面看看自动提示窗口是如何处理〈↑〉、〈↓〉、〈PageUp〉、〈PageDown〉键的。 voidCListCtrlPop::SendTheUDNPMessage(UINTKEY) ::SendMessage(this->m_listctrl->GetSafeHwnd(),WM_KEYDOWN,KEY,0); m_listctrl->SetHotItem(m_listctrl->GetSelectionMark()); 自动提示窗口将这些消息原封不动地发送给了其中的列表控件。 调用完消息与解释函数后,消息被预解释了,可以处理这些解释后的消息。 voidCGeneralEdit::OnKillFocus(CWnd*pNewWnd) CEdit::OnKillFocus(pNewWnd); intiID=this->GetDlgCtrlID();//取得当前编辑框对象的资源符号值 switch(m_Kill)//判断是不是经过处理的〈Enter〉键按下的消息 caseRETURNKILL://〈Enter〉键按下后 ASSERT(this->m_poplist!=NULL);//判断自动提示窗口对象指针有效 if(m_poplist->IsWindowVisible()&&m_poplist->GetSelectedMark()>=0) {//判断自动提示窗口是可视的,并且有某个记录被选择 //接下来,进行从提示对话框中取值赋值给编辑框 this->m_poplist->ShowWindow(SW_HIDE); CGeneralEdit*edit; //根据资源编号取得对象指针 edit=(CGeneralEdit*)this->GetParent()->GetDlgItem(IDC_EDIT_KHNAME); edit->SetValueUnOnChange(m_poplist->GetListCtrlSel(1)); edit->Invalidate();//刷新编辑框 edit=(CGeneralEdit*)this->GetParent()->GetDlgItem(IDC_EDIT_KHID); edit->SetValueUnOnChange(m_poplist->GetListCtrlSel(0)); edit->Invalidate(); this->m_poplist->ShowWindow(SW_HIDE);//隐藏窗口 //让对话框窗口设定焦点该由谁获得 ::PostMessage(this->GetParent()->GetParent()->GetSafeHwnd(), MYMESSAGE_XSDJ_SETTHEFOCUS,0,0); default: //此时,需要判断是否焦点转移到了自动提示窗口或自动提示窗口中的列表上,如果是,就不隐藏 //自动提示窗口,否则,隐藏自动提示窗口 if(pNewWnd!=NULL) if(pNewWnd->GetDlgCtrlID()==m_poplist->GetDlgCtrlID()|| pNewWnd->GetDlgCtrlID()==ID_POP_LISTCTRL); elsethis->m_poplist->ShowWindow(SW_HIDE); m_Kill=NORMALKILL;//恢复开关变量 //TODO:Addyourmessagehandlercodehere (2)自动产生销售编号。 CStringCDlgXSDJ::ChanShengXSDJPH() this->UpdateData(); _bstr_tsql_; CStrings_value; CStrings_date; CStringph; CTimem_tRq; this->m_tcRq.GetTime(m_tRq); this->m_tcRq.GetWindowText(s_date); CStringy_date,m_date,d_date; y_date=m_tRq.Format("%Y"); m_date.Format("%02d",m_tRq.GetMonth()); d_date.Format("%02d",m_tRq.GetDay()); ph=y_date+"-"+m_date+"-"+d_date; if(this->m_bXs)//判断是销售登记还是入库登记 ph=ph+"xsd"; //从数据库中取得最大的编号 sql.Format("selectxs_phfromtabxsphwhererq=#%s#orderbyxs_phdesc",s_date); value=m_runsql.m_recordset->GetCollect("xs_ph"); if(value.vt!=NULL) svalue=(char*)(_bstr_t)value; svalue=s_value.Mid(s_value.GetLength()-4,4); svalue="0"; ph=ph+"rkd"; sql.Format("selectrk_phfromtabrkphwhererq=#%s#orderbyrk_phdesc",s_date); value=m_runsql.m_recordset->GetCollect("rk_ph"); //格式化产生的新编号 longlvalue=atoi(s_value)+1; s_value.Format("%04d",l_value); ph=ph+s_value; returnph; (3)创建列表控件。 初始化列表控件,设置列表控件的大小、风格、列标题、可编辑的列号。读者可参看本章5.7节专题技术,其中详细介绍了功能强大的列表控件。 boolCDlgXSDJ::InitnListXSDJ() CRectrect_nlist(10,300,700,650); this->GetClientRect(&rect_nlist); rect_nlist.top=210; rect_nlist.bottom=480; this->m_plistXSDJ=newCListXSDJ(m_bXs); m_plistXSDJ->Create(WS_CHILD|WS_VISIBLE|LVS_REPORT|WS_BORDER, rect_nlist,this,ID_NEWLISTLSXSDJ); this->m_plistXSDJ->ModifyStyle(LVS_EDITLABELS,0L);//禁止标题编辑 m_plistXSDJ->ModifyStyle(0L,LVS_REPORT);//设为Report类型 m_plistXSDJ->ModifyStyle(0L,LVS_SHOWSELALWAYS);//始终高亮显示被选中的项 m_plistXSDJ->ModifyStyle(0L,LVS_NOSORTHEADER); m_plistXSDJ->ModifyStyle(LVS_OWNERDRAWFIXED,0L); m_plistXSDJ->SetExtendedStyle(LVS_EX_FULLROWSELECT|//允许整行选中 LVS_EX_GRIDLINES|//画出网格线 LVS_EX_FLATSB//扁平风格的滚动条 ); //1、先产生numTitle列 inti=0; m_plistXSDJ->InsertColumn(i,"药品编号"); m_plistXSDJ->InsertColumn(1,"药品名称"); m_plistXSDJ->InsertColumn(2,"仓库名称"); m_plistXSDJ->InsertColumn(4,"数量"); m_plistXSDJ->InsertColumn(5,"总金额"); if(m_bXs) m_plistXSDJ->InsertColumn(3,"单价"); m_plistXSDJ->InsertColumn(6,"实收"); m_plistXSDJ->InsertColumn(3,"进价"); m_plistXSDJ->InsertColumn(6,"实付"); //2、插入1行 m_plistXSDJ->InsertItem(0,""); //3、调整列宽 RECTrect2; m_plistXSDJ->GetWindowRect(&rect2); intwid=rect2.right-rect2.left; for(i=0;i<7;i++) m_plistXSDJ->SetItemText(0,i,""); m_plistXSDJ->SetColumnWidth(i,wid/7); m_plistXSDJ->SetSubItemCanEdited(0); m_plistXSDJ->SetSubItemCanEdited(1); m_plistXSDJ->SetSubItemCanEdited(2); m_plistXSDJ->SetSubItemCanEdited(3); m_plistXSDJ->SetSubItemCanEdited(4); m_plistXSDJ->SetSubItemCanEdited(5); m_plistXSDJ->SetSubItemCanEdited(6); m_plistXSDJ->Invalidate(false); q确定用户输入数据完毕后,可提交给数据库。 q自动核算入库金额,支持打印功能。 q删除当前指针所对应的记录信息。 入库登记模块运行结果如图10所示。 图10入库登记模块运行结果 (1)新建一个对话框,对话框资源ID为IDD_DIALOG_RKDJ。 (3)设置对话框标题属性为“入库登记”,其他控件属性保持默认设置。 定义bool类型的变量m_bXs,在该类的构造函数中对这两个模块加以区分。 CDlgXSDJ::CDlgXSDJ(boolisxs,CWnd*pParent/*=NULL*/) :CDialog(CDlgXSDJ::IDD,pParent) this->m_bXs=isxs; (2)创建左上角的标题信息。 ①显示各种文本内容。 ②为供应商编号和供应商名称编辑框连接自动提示窗口。 boolCDlgXSDJ::InitStringLists() m_strlistKh=newCStringList[2]; m_strlistKh[0].AddTail("客户编号"); m_strlistKh[1].AddTail("客户全称"); sql.Format("selectDISTINCTkh_id,kh_namefromtabkhorderbykh_idasc"); if(!this->m_runsql.CheckSQLResult(sql))returnfalse; while(!m_runsql.m_recordset->adoEOF) value=m_runsql.m_recordset->GetCollect("kh_id"); this->m_strlistKh[0].AddTail((char*)(_bstr_t)value); value=m_runsql.m_recordset->GetCollect("kh_name"); this->m_strlistKh[1].AddTail((char*)(_bstr_t)value); m_strlistKh[0].AddTail("供应商编号"); m_strlistKh[1].AddTail("供应商全称"); sql.Format("selectDISTINCTgys_id,gys_namefromtabgysorderbygys_idasc"); value=m_runsql.m_recordset->GetCollect("gys_id"); value=m_runsql.m_recordset->GetCollect("gys_name"); this->m_popList.Create(this); m_popList.SelectStringList(2,this->m_strlistKh); this->m_editKhId.SelectPopList(&m_popList); this->m_editKhName.SelectPopList(&m_popList); (3)自动产生入库票号。 sql.Format("selectxs_phfromtabxsphwhererq=’%s’orderbyxs_phdesc",s_date); sql.Format("selectrk_phfromtabrkphwhererq=’%s’orderbyrk_phdesc",s_date); longlvalue=atoi(s_value)+1;//格式化产生的新编号 (4)创建列表控件。 对于列表控件,需要注意第3列标题不同。 (5)在对话框的初始化函数中进行数据初始化。 BOOLCDlgXSDJ::OnInitDialog() InitStringLists(); InitnListXSDJ(); if(!this->InitBaseInfo()) MessageBox("初始化基本数据失败"); this->m_sXsPh=m_strPh=this->ChanShengXSDJPH(); this->UpdateData(false); m_editKhId.SetFocus(); returnFALSE;//returnTRUEunlessyousetthefocustoacontrol //EXCEPTION:OCXPropertyPagesshouldreturnFALSE }//返回值是FALSE表示焦点已被确定 (6)实现按钮功能。 入库登记模块有4个功能按钮:确定、打印、删除、退出。其中,打印、退出按钮与销售模块相同,这里不再重复。 boolCListXSDJ::DeleteSelected() if(this->m_poplist->IsWindowVisible())//隐藏不必要的窗口 if(this->m_edit.IsWindowVisible()) m_edit.ShowWindow(SW_HIDE); LVITEM*item=newLVITEM; intnumber=this->GetSelectedCount(); for(inti=this->GetItemCount()-1;i>=0;i--)//从最后一行开始判断是否被选上 this->m_nItem=i; item->iItem=i; item->mask=LVIF_STATE; item->stateMask=LVIS_SELECTED; GetItem(item); if(item->state==LVIS_SELECTED) CStrings_sl=this->GetItemText(i,4); longl_sl=atoi(s_sl); if(m_iYpKcSelected[i]<0||l_sl==0)continue; longkc_number =atoi(m_pstrlistKc[3].GetAt(this->m_pstrlistKc[3].FindIndex(this->m_iYpKcSelected[i]))); CStringleft; left.Format("%d",kc_number+l_sl); left.Format("%d",kc_number-l_sl); this->m_pstrlistKc[3].SetAt(this->m_pstrlistKc[3].FindIndex(this->m_iYpKcSelected[i]),left); DeleteItem(i); this->m_iTheItemState[i]=NONE; if(GetItemCount()==0) InsertItem(0,""); this->m_nItem=0; Invalidate(); 实现“”确定按钮,该按钮完成两个内容:检查用户输入数据是否有效及以事务形式提交数据库。提交数据库时,刷新的数据表不同,且刷新库存表时,算法不同。 voidCDlgXSDJ::OnButtonOk() if(!CheckKh()) this->m_editKhId.SetFocus(); MessageBox("请输入客户信息","注意"); MessageBox("请输入供应商信息","注意"); return; elseif(!this->m_plistXSDJ->CheckAllItem()) MessageBox("请输入完整客户登记信息","注意"); MessageBox("请输入完整供应商登记信息","注意"); this->m_plistXSDJ->m_edit.SetFocus(); CA1App*app=(CA1App*)AfxGetApp(); this->BeginWaitCursor(); CStringxs_ph=this->m_sXsPh;inti=0; CStringkh_id;this->m_editKhId.GetWindowText(kh_id); CStringkh_name;this->m_editKhName.GetWindowText(kh_name); CStringczy=app->m_sUserName; CStringjsr;this->m_comboJsr.GetWindowText(jsr); CStringrq;this->m_tcRq.GetWindowText(rq); CStringjsfs;this->m_comboJsfs.GetWindowText(jsfs); CStringyp_id,yp_name,s_sl,ck_name,s_dj,s_je,sql; longl_sl=0; doubled_dj=0,d_je=0,ys=0,ss=0,ws=0; longpzs=0; boolis_jq=false; CStringListslist; intn=m_plistXSDJ->GetItemCount(); if(n<=0) MessageBox("请输入登记信息","注意"); this->m_plistXSDJ->m_iEditedIndex=-1; ::PostMessage(m_plistXSDJ->GetSafeHwnd(),WM_SETFOCUS,0,0); ::PostMessage(m_plistXSDJ->GetSafeHwnd(),SETNEXTITEMPHFOCUS,0,0); try{ m_runsql.m_recordset->Close(); LRunSql::BeginTrans(); catch(_com_errore) MessageBox(e.ErrorMessage()); for(i=0;i yp_id=this->m_plistXSDJ->GetItemText(i,0); yp_name=this->m_plistXSDJ->GetItemText(i,1); ck_name=this->m_plistXSDJ->GetItemText(i,2); s_dj=this->m_plistXSDJ->GetItemText(i,3); d_dj=atof(s_dj); s_sl=this->m_plistXSDJ->GetItemText(i,4); l_sl=atoi(s_sl); s_je=this->m_plistXSDJ->GetItemText(i,5); d_je=atof(s_je); ys=d_je+ys; ss=ss+atof(m_plistXSDJ->GetItemText(i,6)); //savedj; if(slist.Find(yp_id)==NULL)slist.AddTail(yp_id); doublejinjia=0;doubleprofit=0; m_plistXSDJ->m_mapYpToJj.Lookup(yp_id,jinjia);profit=(d_dj-jinjia)*l_sl; sql.Format("INSERTINTOtabxsdj(xs_ph,yp_id,ck_name,sl,dj,je,profit)\ VALUES('%s','%s','%s',%d,%f,%f,%f)" ,xs_ph,yp_id,ck_name,l_sl,d_dj,d_je,profit); sql.Format("INSERTINTOtabrkdj(rk_ph,yp_id,ck_name,sl,dj,je)\ VALUES('%s','%s','%s',%d,%f,%f)" ,xs_ph,yp_id,ck_name,l_sl,d_dj,d_je); sql.Format("updatetabkcsetkc_number=kc_number-%dwhereyp_id='%s'andck_name=\ '%s'",l_sl,yp_id,ck_name); sql.Format("updatetabkcsetkc_number=kc_number+%dwhereyp_id='%s'andck_name=\ ws=ys-ss; if(ws<=0.0) ws=0.0; is_jq=true; pzs=slist.GetCount(); if(!m_bXs) sql.Format("INSERTINTOtabrkph(rk_ph,gys_id,pzs,yf,sf,wf,is_jq,rq,czy,jsr,jsfs)\ VALUES('%s','%s',%d,%f,%f,%f,%d,#%s#,'%s','%s','%s')" ,xs_ph,kh_id,pzs,ys,ss,ws,is_jq,rq,czy,jsr,jsfs); sql.Format("INSERTINTOtabxsph(xs_ph,kh_id,pzs,ys,ss,ws,is_jq,rq,czy,jsr,jsfs)\ this->EndWaitCursor(); if(!LRunSql::CommitTrans()) MessageBox("数据库事务提交错误","医药管理系统"); MessageBox("OK","医药管理系统"); (7)实现弹出提示窗口。 入库登记模块与销售登记模块实现弹出提示窗口的不同点是:修改临时库存的算法不同。主要表现在编辑框的EN_CHANGE消息处理函数上,当用户修改数量编辑框中数据时,要对临时数据库进行修改。 voidCListXSDJ::OnNewEditChange(constint&item,constint&sub_item) switch(sub_item) {…… case4://数量 l_left=m_edit.kc_number-atoi(str)+m_edit.m_dSetFocusValue;//-curshownumber+setfocusvalue=left l_left=m_edit.kc_number+atoi(str)-m_edit.m_dSetFocusValue;//+curshownumber-setfocusvalue=left 1.《VisualBasic精彩编程200例》机械工业出版社赛奎春、高春艳等 2003年1月 2.《VisualBasic数据库开发实例解析》机械工业出版社刘志铭、高春艳等 2003年8月 3.《VisualFoxPro数据库开发实例解析》机械工业出版社王晶莹、王国辉等 2003年9月 4.《PowerBuilder数据库开发实例解析》机械工业出版社华传铭、张振坤等 5.《Delphi数据库开发实例解析》机械工业出版社赛奎春、郑骁鹏等 2004年2月 6.《PowerBuilder精彩编程200例》机械工业出版社张振坤、李文立等 2004年9月 7.《VisualFoxPro精彩编程200例》机械工业出版社王国辉、董韶华等 8.《ASP数据库开发实例解析》机械工业出版社李严、于亚芳、王国辉2004年12月 9.《Delphi工程应用与项目实践》机械工业出版社宋坤、赵智勇等 2005年1月 10.《VisualBasic工程应用与项目实践》机械工业出版社高春艳、李俊民等 11.《VisualC++工程应用与项目实践》机械工业出版社张雨、阮伟良等 12.《JSP工程应用与项目实践》机械工业出版社陈威、白伟明、李楠 2005年2月 13.《ASP工程应用与项目实践》机械工业出版社王国辉、牛强、李南南 2005年4月 2005年7月 17.《Visualfoxpro数据库开发关键技术与实例应用》人民邮电出版社周桓、张雨、王国辉 2004年5月 18.《PowerBuilder数据库开发关键技术与实例应用》人民邮电出版社刘志铭、张振坤、冯文萃2004年5月 19.《Delphi数据库开发关键技术与实例应用》人民邮电出版社赛奎春、陈紫鸿、宋昆 20.《Visualbasic数据库开发关键技术与实例应用》人民邮电出版社高春艳、李艳 21.《VisualC++管理信息系统完整项目实例剖析》人民邮电出版社明日科技 22.《VisualBasic管理信息系统完整项目实例剖析》人民邮电出版社明日科技 23.《PowerBuilder管理信息系统完整项目实例剖析》人民邮电出版社明日科技 24.《VisualFoxPro管理信息系统完整项目实例剖析》人民邮电出版社明日科技 25.《SQLServer数据库开发实例解析》机械工业出版社宋昆、李严等 2006年1月 26.《Access数据库开发实例解析》机械工业出版社李俊民、高春燕等 27.《VisualBasic数据库系统开发完全手册》人民邮电出版社明日科技王春才、高春艳、 李俊民2006年3月 28.《VisualC++数据库系统开发完全手册》人民邮电出版社明日科技王端、于速、张雨 2006年3月 29.《Delphi数据库系统开发完全手册》人民邮电出版社明日科技宋坤、邹天思 30.《JSP数据库系统开发完全手册》人民邮电出版社明日科技王国辉、李文立、杨亮 刘彬彬2006年5月 2006年5月 由于篇幅有限,只给出部分数据表。 下面分别介绍以下各个表的结构。(临时表是为了实现在查询结果中进行查询,在这里不做具体介绍)。 qtabdhdj表 tabdhdj表用于保存调货登记信息。tabdhdj表的结构如表4所示。 表4tabdhdj表的结构 字段名称 数据类型 字段大小 可否为空 说明 dh_ph 文本 20 NotNull 调货票号(主键) out_ck 调出的库(外健) in_ck 调入的库(外健) yp_id 10 药品编号(外健) sl 长整型 Null 数量 qtabpurview表 tabpurview表用于保存权限信息。tabpurview表的结构如表5所示。