{*******************************************************} { } { Responsive Software http://www.responsive.co.nz } { } { Copyright (c) 2003-2006 Responsive Software Limited } { } {*******************************************************} unit SalesManagerUnit; interface uses DatabaseObjects; const POSSalesFileName = 'POSSALES'; type TSalesManager = class LastTime : TDateTime; ErrorOccurred : boolean; function CreateNewFile : boolean; function PostNextSale : boolean; public SaleCount : integer; constructor Create; destructor Destroy; override; function Verify : boolean; procedure AddSaleToQueue (Sale : TSale); procedure PostSales; end; implementation uses Classes, SysUtils, DateUtils, Windows, Forms, Controls, CommunicationsManager, Utilities, GeneralUtilities, Globals, POSMain; constructor TSalesManager.Create; begin end; destructor TSalesManager.Destroy; begin end; function TSalesManager.CreateNewFile : boolean; var POSSalesFileStream : TFileStream; HeadPointer : integer; HeadPointerPosition : integer; begin POSSalesFileStream := OpenFileStream(AppendBackslash(POSConfiguration.POSDataDirectory) + POSSalesFileName,false); try if POSSalesFileStream <> nil then begin WriteStrToStream(ProgramVersion,POSSalesFileStream); SaleCount := 0; HeadPointer := 0; POSSalesFileStream.Write(SaleCount,SizeOf(SaleCount)); HeadPointerPosition := POSSalesFileStream.Position; POSSalesFileStream.Write(HeadPointer,SizeOf(HeadPointer)); HeadPointer := POSSalesFileStream.Position; POSSalesFileStream.Seek(HeadPointerPosition,soFromBeginning); POSSalesFileStream.Write(HeadPointer,SizeOf(HeadPointer)); Result := true; end else Result := false; finally POSSalesFileStream.Free; end; end; function TSalesManager.Verify : boolean; var POSSalesFileStream : TFileStream; VersionStr : string; begin POSSalesFileStream := OpenFileStream(AppendBackslash(POSConfiguration.POSDataDirectory) + POSSalesFileName,true); try // if file not found then create one if POSSalesFileStream = nil then Result := CreateNewFile else begin VersionStr := ReadStrFromStream(POSSalesFileStream); POSSalesFileStream.Read(SaleCount,SizeOf(SaleCount)); if (VersionStr <> ProgramVersion) and (SaleCount > 0) then Result := false else Result := true; end; finally POSSalesFileStream.Free; end; end; procedure TSalesManager.AddSaleToQueue (Sale : TSale); var POSSalesFileStream : TFileStream; VersionStr : string; SaleCountPosition : integer; HeadPointer : integer; HeadPointerPosition : integer; begin POSSalesFileStream := OpenFileStream(AppendBackslash(POSConfiguration.POSDataDirectory) + POSSalesFileName,false); try // if file not opened then raise exception if POSSalesFileStream = nil then raise Exception.Create('Error occurred accessing POSSALES file'); // read version and sale counter VersionStr := ReadStrFromStream(POSSalesFileStream); SaleCountPosition := POSSalesFileStream.Position; POSSalesFileStream.Read(SaleCount,SizeOf(SaleCount)); // if there are sales in the queue then check versions are compatible if (VersionStr <> ProgramVersion) and (SaleCount > 0) then raise Exception.Create('Different program versions accessing the same POSSALES file'); // if no sales then write program version etc again from the beginning if SaleCount = 0 then begin POSSalesFileStream.Seek(0,soFromBeginning); SetEndOfFile(POSSalesFileStream.Handle); WriteStrToStream(ProgramVersion,POSSalesFileStream); SaleCountPosition := POSSalesFileStream.Position; HeadPointer := 0; POSSalesFileStream.Write(SaleCount,SizeOf(SaleCount)); HeadPointerPosition := POSSalesFileStream.Position; POSSalesFileStream.Write(HeadPointer,SizeOf(HeadPointer)); HeadPointer := POSSalesFileStream.Position; POSSalesFileStream.Seek(HeadPointerPosition,soFromBeginning); POSSalesFileStream.Write(HeadPointer,SizeOf(HeadPointer)); end; // increment sale counter Inc(SaleCount); POSSalesFileStream.Seek(SaleCountPosition,soFromBeginning); POSSalesFileStream.Write(SaleCount,SizeOf(SaleCount)); // save sale at end of file POSSalesFileStream.Seek(0,soFromEnd); Sale.SaveToStream(POSSalesFileStream); Sale.SaveDetailsToStream(POSSalesFileStream); finally POSSalesFileStream.Free; end; end; function TSalesManager.PostNextSale : boolean; var Sale : TSale; POSSalesFileStream : TFileStream; VersionStr : string; SaleCountPosition : integer; HeadPointer : integer; HeadPointerPosition : integer; ZeroHeadPointerPosition : integer; begin Result := false; // this procedure will post the next sale in the queue // to the database and increment the head pointer // it will return true if there are still more sales in the queue POSSalesFileStream := OpenFileStream(AppendBackslash(POSConfiguration.POSDataDirectory) + POSSalesFileName,false); try // if file not opened then raise exception if POSSalesFileStream = nil then begin ErrorOccurred := true; raise Exception.Create('Error occurred accessing POSSALES file'); end; // read version and sale counter VersionStr := ReadStrFromStream(POSSalesFileStream); SaleCountPosition := POSSalesFileStream.Position; POSSalesFileStream.Read(SaleCount,SizeOf(SaleCount)); // if there are sales in the queue then check versions are compatible if (VersionStr <> ProgramVersion) and (SaleCount > 0) then begin ErrorOccurred := true; raise Exception.Create('Different program versions accessing the same POSSALES file'); end; // if no sales then return if SaleCount = 0 then Exit; try // read head pointer HeadPointerPosition := POSSalesFileStream.Position; POSSalesFileStream.Read(HeadPointer,SizeOf(HeadPointer)); // record zero head pointer position ZeroHeadPointerPosition := POSSalesFileStream.Position; // find head position POSSalesFileStream.Seek(HeadPointer,soFromBeginning); // load sale Sale := TSale.Create; try Sale.LoadFromStream(POSSalesFileStream); Sale.LoadDetailsFromStream(POSSalesFileStream); // save sale to database Sale.FullSaveToDatabase(true); // decrement sale counter Dec(SaleCount); // record new head pointer truncating if no more sales if SaleCount = 0 then begin POSSalesFileStream.Seek(ZeroHeadPointerPosition,soFromBeginning); SetEndOfFile(POSSalesFileStream.Handle); end; HeadPointer := POSSalesFileStream.Position; POSSalesFileStream.Seek(HeadPointerPosition,soFromBeginning); POSSalesFileStream.Write(HeadPointer,SizeOf(HeadPointer)); POSSalesFileStream.Seek(SaleCountPosition,soFromBeginning); POSSalesFileStream.Write(SaleCount,SizeOf(SaleCount)); Result := SaleCount > 0; // notify all workstations of new sale UpdateDatabaseObjectOnLoggedOnWorkstations(TSale,Sale); // update general ledger if GlobalConfiguration.Accounts then UpdateSaleToLedger(Sale); finally Sale.Free; end; except ErrorOccurred := true; // set to offline mode if error occurs while trying to post // sale to database Offline := true; raise; end; finally POSSalesFileStream.Free; end; end; procedure TSalesManager.PostSales; var Cursor : TCursor; begin // check that enough time has elapsed since the last time if Now <= IncMillisecond(LastTime,POSSalesDataPostTime) then Exit; // don't continue posting once an error has occurred if ErrorOccurred then Exit; // verify file to get latest sale count Verify; // post all outstanding sales in queue if online if not Offline then begin Cursor := Screen.Cursor; Screen.Cursor := crHourGlass; try repeat POSMainForm.UpdateLabels; POSMainForm.Repaint; until not PostNextSale; finally Screen.Cursor := Cursor; end; end; // record current time LastTime := Now; end; end.