unit PZ;
interface
/////////////////////CNPZGrid控件与数据连接演示程序 ///////////////////////////////////////////////////////////////////////////////*
// //
// 常有网友问CNPZGrid控件如何连接数据,所在做了这个演示程序,程序在D7+WIN2000 //
//+CNPZGrid控件V2.0试用版下调试成功。 //
//1)、ACCOUNT数据库由会计科目编码表CODE和凭证表PZ组成,用户数据库如果自行设计//
// 请注意主键与索引(FYEAR+FPERIOD+FGROUP+FNUM)与本数据库保持一致,否则不应//
// 直接应用本示例代码 //
//2)、读取数据库的关键函数为LoadPZ //
//3)、存入数据库的关键函数为SavePZ //
// //
//谢谢你对CNPZGrid控件的关注与支持 //
// 作者 杨日强 中国.厦门 yang6130@163.com 2005.11.9 //
/////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// ///*
//用户需自行完成功能 //
//1)、不同类弄凭证合法性检查,如"收"字凭证借方必须为"现金"或"银行存款" //
//2)、凭证过账与反过账、结账操作 //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, CNPZGridProXControl1_TLB, Menus, ToolWin, ComCtrls,
ImgList, StdCtrls, jpeg, ExtCtrls;
const
DEFAULTROWCOUNT=7; //默认行数
DEFAULTPZNO=1; //默认凭证号
_EXP=0;
_ACCT=1;
_CY=2;
_EXCHRATE=3;
_FCY=4;
_DR=5;
_CR=6;
MAXPZType=10;
type
TfrmPZ = class(TForm)
MM: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
tb: TToolBar;
tbSave: TToolButton;
ImageList1: TImageList;
tbRestore: TToolButton;
tbFirst: TToolButton;
tbPrior: TToolButton;
tbNext: TToolButton;
tbEnd: TToolButton;
tbFind: TToolButton;
tbDelete: TToolButton;
tbcopy: TToolButton;
tbPaste: TToolButton;
tbShift: TToolButton;
tbClose: TToolButton;
sb: TStatusBar;
Panel1: TPanel;
edtAdmin: TEdit;
lblAccount: TLabel;
PZ: TCNPZGridPro;
dtPDate: TDateTimePicker;
lblDate: TLabel;
lblPZ: TLabel;
lblPZNO: TLabel;
edtPZNO: TEdit;
cbPZType: TComboBox;
lblPZType: TLabel;
Image1: TImage;
tbPrint: TToolButton;
tbPrintView: TToolButton;
ToolButton1: TToolButton;
ToolButton2: TToolButton;
ToolButton3: TToolButton;
btnDeleteRec: TButton;
ImageList2: TImageList;
procedure tbCloseClick(Sender: TObject);
procedure tbShiftClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure PZInplaceEditClick(Sender: TObject);
procedure PZ_GetAccText(Sender: TObject; const sAccID: WideString;
var sResult: WideString);
procedure PZDblClick(Sender: TObject);
procedure tbDeleteClick(Sender: TObject);
procedure tbcopyClick(Sender: TObject);
procedure tbPasteClick(Sender: TObject);
procedure tbSaveClick(Sender: TObject);
procedure PZKeyDown_(ASender: TObject; Key, Shift: Integer);
procedure tbFindClick(Sender: TObject);
procedure tbRestoreClick(Sender: TObject);
procedure cbPZTypeChange(Sender: TObject);
procedure btnDeleteRecClick(Sender: TObject);
procedure dtPDateChange(Sender: TObject);
procedure edtPZNOChange(Sender: TObject);
procedure tbFirstClick(Sender: TObject);
procedure tbPriorClick(Sender: TObject);
procedure tbNextClick(Sender: TObject);
procedure tbEndClick(Sender: TObject);
private
{ Private declarations }
bModified:boolean; //凭证数据修改标志,用于凭证保存
bNewPZ:boolean; //用于判断当前凭证是新增的,还是载入已有的
bError:boolean; //凭证(会计科目代码)错误标志
bCloseWarning:boolean; //是否关闭错误提示,何时关闭用户自己决定;
procedure PZClear;//清空PZGrid内容
function SavePZ:boolean; //保存凭证,只保存新凭证;若要保存已修改的凭证,可先调用DeletePZ删除凭证,再保存
function DeletePZ(sYear,sMonth,sPZType,sPZNO:string):boolean;
function LoadPZ(sYear,sMonth,sPZType,sPZNO:string):boolean;
function ACCTTextToACCTID(sAcctText:string):string;
//转换会计科目文本为编号,如'1101 现金'->'1101'
function ACCTIDToACCTText(sAcctID:string;var sAcctText:string):boolean;
//转换会计科目编号为科目名称,如'1101'->'1101 现金'
//function ACCTIDExists(sAddtID:string):boolean; //未完成功能,用户自己实现;
//查询科目编号sAddtID是否存在,用于检查PZ数据表数据合法性
public
{ Public declarations }
FYear,FMonth:integer;//当前会计年度,当前会计期间,实务中根据用户建账时间及结账操作确定,这里简单当前时间代替
//FPZNO:array[0..MAXPZType] of integer; //各类凭证的当前凭证号
//FPZType:array[0..MAXPZType] of string; //各类凭证字,如'记','收','付','转'
function GetNEXTPZNO(sPZType:string):integer;//指定凭证类型sPZType如存在断号则提供最小断号;如为连续则提供后继最小号码
end;
var
frmPZ: TfrmPZ;
implementation
uses DM, CODE,DateUtils, PZFind;
{$R *.dfm}
procedure TfrmPZ.PZClear;
var
iRow,iCol:integer;
begin
for iCol:=0 to pz.ColCount -1 do
for iRow:=pz.RowCount -2 downto 1 do begin //1..RowCount -2为数据区
pz.Col:=iCol; //定位焦点
pz.Row:=iRow;
pz.setCell(iCol,iRow,''); //清空
end;
// pz.Col:=0; //重新定位于第1行,0列
// pz.Row:=1;
pz.RowCount :=DEFAULTROWCOUNT;
end;
function TfrmPZ.SavePZ:boolean;
var
i,iLastRow:integer; //iValue 当前分录金额
bISDr:boolean;//当前分录是否有借方发生额
begin
result:=false;
if bError then exit;
for i:=PZ.RowCount -2 downto 1 do //数据区为1..RowCount-2
if trim(PZ.Getcell(1,i))<>'' then break;//找到最后一行数据
iLastRow:=i;
if not Pz.canCellInput(1,i+1) then begin
MessageBox(handle,'凭证输入不完整,不能保存','警告',MB_OK or MB_ICONWARNING);
exit;
end;
if PZ.DrSum <>PZ.CrSum then begin
MessageBox(handle,'凭证借贷方不平,不能保存','警告',MB_OK or MB_ICONWARNING);
exit;
end;
if PZ.DrSum=0.00 then begin
MessageBox(handle,'凭证合计为0,不能保存','警告',MB_OK or MB_ICONWARNING);
exit;
end;
result:=true;
with DataM.ADOTPZ do begin
for i:=1 to iLastRow do begin //一条会计分录保存为一条记录,数据库设计参考现有成熟软件
Append; //添加一条记录
//凭证数据表PZ以 会计年度+会计期间+凭证字+凭证号+分录号 排序
//会计年度+会计期间+凭证字+凭证号 可以唯一定位每一张记账凭证
//会计年度+会计期间+凭证字+凭证号+分录号 可以唯一定位每一条记录
if PZ.getCell(_DR,i)<>'' then bisDr:=true else bIsDr:=false;//判断是否有借方发生额
FieldByName('FDATE').Value :=dtPDate.Date ; //日期
FieldByName('FYEAR').Value :=Yearof(dtPDate.Date );//会计年度
FieldByName('FPERIOD').Value :=Monthof(dtPDate.Date);//会计期间
FieldByName('FGROUP').Value :=cbPZType.Text;//凭证字
FieldByName('FNUM').Value :=edtPZNO.Text;//凭证号
FieldByName('FENTRYID').Value :=i-1;//分录号
FieldByName('FEXP').Value :=PZ.getCell(_EXP,i);//摘要
FieldByName('FACCTID').Value :=ACCTTextToACCTID(PZ.getCell(_ACCT,i));//科目代码,去掉空格后部分
FieldByName('FCYID').Value :=PZ.getCell(_CY,i);//币别
FieldByName('FEXCHRATE').Value :=strtofloatdef(PZ.getCell(_EXCHRATE,i),0);//汇率
FieldByName('FFCYAMT').Value :=strtofloatdef(PZ.getCell(_FCY,i),0); //原币
if bIsDr then FieldByName('FDC').Value:='D' else //借方发生额
FieldByName('FDC').Value:='C'; //贷方发生额
FieldByName('FDEBIT').Value :=strtofloatdef(PZ.getCell(_DR,i),0);//借方金额
FieldByName('FCREDIT').Value :=strtofloatdef(PZ.getCell(_CR,i),0);//贷方金额
FieldByName('FPREPARE').Value :=edtAdmin.Text ; //记账员姓名
post;//提交记录
end;
end;
end;
function TfrmPZ.DeletePZ(sYear, sMonth, sPZType, sPZNO: string): boolean;
begin
result:=false;//未找到凭证
with DataM.ADOTPZ do begin
First;
while Locate('FYEAR;FPERIOD;FGROUP;FNUM',VarArrayOf([sYear,sMonth,sPZType,sPZNO]),[]) do
begin
delete;
result:=true;//找到并删除
end;
end;
end ;
function TfrmPZ.LoadPZ(sYear,sMonth,sPZType,sPZNO: string): boolean;
var
i:integer;
tmp,s:string;
begin
result:=false;
with DataM.ADOTPZ do begin
First;
if Locate('FYEAR;FPERIOD;FGROUP;FNUM',VarArrayOf([sYear,sMonth,sPZType,sPZNO]),[]) then
begin
PZClear;
result:=true;
dtPDate.Date :=FieldByName('FDATE').Value;
cbPZType.Text:=FieldByName('FGROUP').Value;
edtPZNO.Text:=FieldByName('FNUM').Value;
edtAdmin.Text := FieldByName('FPREPARE').Value ;
PZ.Col:=0;PZ.Row:=1;i:=1;
PZ.DrSum:=0;PZ.CrSum:=0;//正式版可去除该语句^_^
repeat
begin
if i+2>DEFAULTROWCOUNT then PZ.RowCount:=i+2;
s:=FieldByName('ID').AsString ;
PZ.setCell(_EXP,i,FieldByName('FEXP').Value);
ACCTIDTOACCTText(FieldByName('FACCTID').Value,tmp);
PZ.setCell(_ACCT,i,tmp);
PZ.setCell(_CY,i,FieldByName('FCYID').Value);
if FieldByName('FCYID').Value<>'RMB' then begin
PZ.setCell(_CY,i,FieldByName('FCYID').AsString );
PZ.setCell(_EXCHRATE,i,FieldByName('FEXCHRATE').AsString);
PZ.setCell(_FCY,i,FieldByName('FFCYAMT').AsString);
end; //if FieldByName('FCYID')
PZ.setCell(_DR,i,FieldByName('FDEBIT').AsString );
PZ.setCell(_CR,i,FieldByName('FCREDIT').AsString );
i:=i+1;
NEXT;
end; //while (not Eof) and
until Eof or (FieldByName('FENTRYID').AsInteger =0);
if PZ.DrSum <>PZ.CrSum then
messagebox(handle,'载入凭证借贷不平衡,请先修复数据库!','提示',MB_OK or MB_ICONINFORMATION);
end; //if Locate('FYEAR;FPERIO
end; // with DataM.ADOTPZ do begin
end;
function TfrmPZ.ACCTTextToACCTID(sAcctText: string): string;
var
i:integer;
begin
result:=sAcctText;
i:=pos(' ',sAcctText);
if i>0 then result:=copy(sAcctText,1,i-1); //去除空格后文本
end;
function TfrmPZ.ACCTIDToACCTText(sAcctID: string;
var sAcctText: string): boolean;
begin
result:=false;
if DataM.ADOTCode.Locate('CODE',trim(sAcctID),[]) then //找对应科目
begin
sAcctText:=sAcctID+' '+ DataM.ADOTCode.fieldbyName('NAME').AsString;
result:=true;
end;
end;
procedure TfrmPZ.tbCloseClick(Sender: TObject);
begin
if not bModified then //凭证已修改
close;
if messagebox(handle,'当前凭证未保存,要保存吗?','提示',MB_YESNO or MB_ICONINFORMATION)=IDNO then
close;
end;
procedure TfrmPZ.tbShiftClick(Sender: TObject);
begin
if PZ.Status=0 then PZ.Expand else
PZ.collapse ;
end;
procedure TfrmPZ.FormCreate(Sender: TObject);
begin
PZ.InplaceAddItem('提取现金'); //增加常用摘要,实务中一般从摘要库文本文本中导入
PZ.InplaceAddItem('支付工资');
PZ.InplaceAddItem('支付差旅费');
PZ.InplaceAddItem('收货款');
PZ.InplaceAddItem('购买原材料');
PZ.InplaceAddItem('销售产品');
PZ.collapse; //↓禁用最大化按钮
bModified:=false; bError:=false;bCloseWarning:=false;bNewPZ:=true;
setwindowlong(handle,gwl_style,getwindowlong(handle,gwl_style) and not ws_maximizebox);
left:=(screen.Width -width) div 2;
top:=(screen.Height -height) div 2;
PZ.setCell(2,0,'币别');
FYear:=Yearof(dtPDate.Date );
FMonth:=Monthof(dtPDate.date); //实务中根据用户建账时间及结账操作确定,这里简单当前时间代替
end;
procedure TfrmPZ.PZInplaceEditClick(Sender: TObject);
begin
if (frmCODE.ShowModal=mrOK) then begin //弹出会计科目表
if frmCODE.tvCODE.Selected <>nil then
PZ.setCell(PZ.Col,PZ.Row,frmCODE.tvCODE.Selected.Text);
end;
end;
procedure TfrmPZ.PZ_GetAccText(Sender: TObject; const sAccID: WideString;
var sResult: WideString);
var
s,tmp:string;
begin
if sAccID='' then begin sResult:=''; exit;end;
s:=ACCTTextToACCTID(sAccID); //转换成科目编号
if ACCTIDToACCTText(s,tmp) then //找对应科目名称
begin
sResult:=tmp;
bError:=false;
end
else
begin
if not bCloseWarning then begin //必要时可关闭错误提示,何时关闭用户自己决定;
sResult:='ERROR'; //没找到
bError:=true; //发生会计科目错误
PZ.SetEditStyle_(1);//设置会计科目列风格
end;
end;
end;
procedure TfrmPZ.PZDblClick(Sender: TObject);
begin
if (PZ.Col =1) and PZ.canCellInput(PZ.Col,PZ.Row) then //双击调出科目代码表
self.PZInplaceEditClick(self);
end;
procedure TfrmPZ.tbDeleteClick(Sender: TObject);
begin
pz.deleteRow_(pz.Row ); //删除一行会计分录
bModified:=true;
end;
procedure TfrmPZ.tbcopyClick(Sender: TObject);
begin
pz.copyRow(pz.Row );//拷贝一行会计分录
tbPaste.Enabled :=true;
end;
procedure TfrmPZ.tbPasteClick(Sender: TObject);
begin
pz.PasteRow(pz.Row ); //粘贴一行会计分录
bModified:=true;
end;
procedure TfrmPZ.tbSaveClick(Sender: TObject);
var
sYear,sMonth:string;
begin
sYear:=inttostr(FYEAR);
sMonth:=inttostr(FMonth);
if DataM.ADOTPZ.Locate('FYEAR;FPERIOD;FGROUP;FNUM',VarArrayOf([sYear,sMonth,cbPZType.Text,edtPZNO.Text]),[]) then
begin
if bNewPZ then begin //不允许覆盖已有凭证
messagebox(self.handle,PChar(sYear+'年'+sMonth+'月'+cbPZType.Text+'字第'+edtPZNO.Text+'号凭证已存在,请先更改凭证号(建议:'+inttostr(GetNextPZNO(cbPZType.Text))+'号)!'),PChar('提示'),MB_OK or MB_ICONINFORMATION);
exit;
end else DeletePZ(sYear,sMonth,cbPZType.Text,edtPZNO.Text);//先删除再保存
end;
pz.Col:=0; //重新定位于第1行,0列
if not SavePZ then //调用凭证保存函数
exit;
bModified:=false; //置修改标志为false
tbPaste.Enabled:=false;
if bNewPZ then begin //如果本次保存的凭证是新增的则再新增一张凭证;否则只保存修改;
edtPZNO.Text :=inttostr(GetNextPZNO(cbPZType.Text));
self.PZClear;//清空数据
pz.Col:=0;
pz.Row:=1;//重新定位于第1行,0列
end;
bError:=false;
end;
procedure TfrmPZ.PZKeyDown_(ASender: TObject; Key, Shift: Integer);
begin
inherited;
bModified:=true;
if (Key=VK_F7) and (PZ.Col=1) then //F7调出会计科目表
self.PZInplaceEditClick(self);
//可根据情况对热键做不同处理,如Col=0,则调出摘要库;
//Col=1,则调出科目库;Col=5或6,则调出计算器;
if PZ.col=0 then //如果用户有摘要栏输入‘//’则取第一条分录摘要。
begin
with PZ do
begin
if trim(getCell(Col,row))='//' then
setCell(Col,Row,getCell(0,1))
else //如果用户有摘要栏输入‘..’则取上一条分录摘要
if (trim(getCell(Col,row))='..') and (Row>1) then
setCell(Col,Row,getCell(0,Row-1));
end;
end;
end;
procedure TfrmPZ.tbFindClick(Sender: TObject);
begin
if bModified then //凭证已修改
if messagebox(handle,'当前凭证未保存,要保存吗?','提示',MB_YESNO or MB_ICONINFORMATION)=IDYES then exit;
with frmPZFind do
if ShowModal =mrOK then
if not loadPZ(edtYear.Text,cbMonth.Text,cbPZType.Text,edtPZNO.Text) then
messagebox(self.handle,PChar(edtYear.Text+'年'+cbMonth.Text+'月'+cbPZType.Text+'字第'+edtPZNO.Text+'号凭证不存在'),PChar('提示'),MB_OK or MB_ICONINFORMATION)
else begin
bNewPZ:=false;
bModified:=false;
end;
end;
procedure TfrmPZ.tbRestoreClick(Sender: TObject);
begin
//凭证还原,指定凭证不存在,则清为空;已存在则调用LOADPZ重新显示
if not loadPZ(inttostr(FYear),inttostr(FMonth),cbPZType.Text,edtPZNO.Text) then PZClear;
bMOdified:=false;
bNewPZ:=false;
end;
function TfrmPZ.GetNextPZNO(sPZType: string): integer;
var
s:string;
oldNum,j:integer;
begin
result:=DEFAULTPZNO;
with DataM.ADOTPZ do begin
First;
if Locate('FYEAR;FPERIOD;FGROUP;FNUM',VarArrayOf([inttostr(FYear),inttostr(FMonth),sPZType,'1']),[]) then
begin
s:=FieldByName('FGROUP').AsString ;
while (FieldByName('FGROUP').AsString =s) and (not eof) do begin
oldNum:=FieldByName('FNUM').AsInteger;
Next;
J:=FieldByName('FNUM').AsInteger;
if j-oldNum>1 then break;//发现一个断号
//ADOTPZ.indexFieldNames 必须是 FYEAR;FPERIOED;FGROUP;FNUM,否则可能不能得到正确结果
end;
prior;
result:=FieldByName('FNUM').AsInteger+1;
end;
end;
end;
procedure TfrmPZ.cbPZTypeChange(Sender: TObject);
begin
PZClear;
edtPZNO.Text :=inttostr(GETNextPZNO(cbPZType.Text ));
bModified:=false;
bNewPZ:=true;
end;
procedure TfrmPZ.btnDeleteRecClick(Sender: TObject);
begin
if messagebox(handle,'当前凭证即将被删除?','警告',MB_YESNO or MB_ICONWARNING)=IDYES then
begin
deletePZ(inttostr(FYear),inttostr(FMonth),cbPZType.Text ,edtPZNO.Text );
PZClear;
end;
end;
procedure TfrmPZ.dtPDateChange(Sender: TObject);
begin
FYear:=Yearof(dtPDate.Date ); //用户改变日期,需重设相关信息
FMonth:=Monthof(dtPDate.Date); //实务中是不允许用户把日期设至结账月份之前的,这点请用户自己处理
edtPZNO.Text :=inttostr(GetNextPZNO(cbPZType.Text));
end;
procedure TfrmPZ.edtPZNOChange(Sender: TObject);
begin
bNewPZ:=true;//用户修改了凭证号
end;
procedure TfrmPZ.tbFirstClick(Sender: TObject);
begin
if bModified then exit;
if LoadPZ(inttostr(FYear),inttostr(FMonth),cbPZType.Text,'1') then begin
bNewPZ:=false;bModified:=false;
end;
end;
procedure TfrmPZ.tbPriorClick(Sender: TObject);
var
i:integer;
begin
i:=strtointdef(edtPZNO.text,2)-1;if i<1 then i:=1;
if bModified then exit;
if LoadPZ(inttostr(FYear),inttostr(FMonth),cbPZType.Text,inttostr(i)) then begin
bNewPZ:=false;bModified:=false;
end;
end;
procedure TfrmPZ.tbNextClick(Sender: TObject);
var
i:integer;
begin
if bModified then exit;
i:=strtointdef(edtPZNO.text,0)+1;
if LoadPZ(inttostr(FYear),inttostr(FMonth),cbPZType.Text,inttostr(i)) then begin
bNewPZ:=false;bModified:=false;
end;
end;
procedure TfrmPZ.tbEndClick(Sender: TObject);
var
i:integer;
begin //如果编号连续则移到最后,否则定位于断号处GetNextPZNO(cbPZType.Text),用户可根需要修改
if bModified then exit;
i:=GetNextPZNO(cbPZType.Text)-1;if i<1 then i:=1;
if LoadPZ(inttostr(FYear),inttostr(FMonth),cbPZType.Text,inttostr(i)) then begin
bNewPZ:=false;bModified:=false;
end;
end;
end.