{*******************************************************} { } { Responsive Software http://www.responsive.co.nz } { } { Copyright (c) 2003-2006 Responsive Software Limited } { } {*******************************************************} unit DatabaseObjects; interface uses DB, DBTables, IBTable, IBQuery, IBDatabase, Classes, GeneralUtilities, Grids; type {***** Forward class declarations *********************************************} TDatabaseObject = class; TDatabaseObjectCollection = class; TAttachment = class; {***** System classes *********************************************************} // this class is used to manage a field containing a binary file // where the file is stored in a separate file from the database // and linked via a TAttachment object TAttachmentManager = class private FOwner : TDatabaseObject; FFieldNumber : integer; FAttachment : TAttachment; FFileName : string; FFileAttribute : integer; FFileDateTime : TDateTime; FFileContents : string; function AttachmentFileName : string; function AttachmentFileAttribute : integer; function AttachmentFileDateTime : TDateTime; function AttachmentFileContents : string; function AttachmentFileSize : integer; function SelectionString : string; function Changed : boolean; procedure LoadAttachment; public constructor Create (Owner : TDatabaseObject; FieldNumber : integer); destructor Destroy; override; procedure Assign (AttachmentManager : TAttachmentManager); procedure Reload; function FileName : string; function FileAttribute : integer; function FileDateTime : TDateTime; function FileContents : string; function FileSize : integer; function SetFile (FileName : string) : boolean; function GetFile (FileName : string) : boolean; procedure LocalSave; procedure Save; procedure Delete; procedure LoadFromStream (Stream : TStream); procedure SaveToStream (Stream : TStream); function AttachmentId : int64; end; // this class is used to manage a variable length string field // rather than using the blob or memo fields provided by the database // all the data is stored in the Notes table as TNotes objects TNotesManager = class private FOwner : TDatabaseObject; FFieldNumber : integer; FNotes : TDatabaseObjectCollection; FString : string; function NotesAsString : string; function SelectionString : string; function OrderedSelectionString : string; function Changed : boolean; public constructor Create (Owner : TDatabaseObject; FieldNumber : integer); destructor Destroy; override; procedure Assign (NotesManager : TNotesManager); procedure Reload; function AsString : string; procedure SetString (Str : string); procedure Save; // this will delete the notes from the database but retain // any information in FNotes and FString procedure Delete; procedure LoadFromStream (Stream : TStream); procedure SaveToStream (Stream : TStream); end; // classes are used to represent entities stored in the database // they have a common ancestor class which provides virtual // maintenance functions to allow for generic handling TDatabaseObject = class Id : int64; // functions used to reference and create database tables class function TableName : string; virtual; abstract; class procedure CreateTable; class procedure AddNewTableFields (Table : TDataset); virtual; class procedure UpdateTable; virtual; abstract; class procedure ConvertTable; // functions used to move object to and from a table/query/stream procedure LoadFromTable (Table : TDataset); virtual; procedure SaveToTable (Table : TDataset); virtual; function InsertSQLStrColumnNames : string; virtual; function InsertSQLStrValues : string; virtual; function InsertSQLStr : string; procedure LoadFromStream (Stream : TStream); virtual; procedure SaveToStream (Stream : TStream); virtual; // constructor constructor Create; virtual; // assignment procedure Assign (DatabaseObject : TDatabaseObject); // create a new TTable object and open it (thread-safe) class function OpenTable (Exclusive : boolean) : TTable; // create a new TIBTable object and open it class function OpenIBTable : TIBTable; // create a new TQuery object and open it (thread-safe) class function OpenQuery (SelectionString : string) : TQuery; // create a new TIBQuery object and open it class function OpenIBQuery (SelectionString : string) : TIBQuery; // elemental database update functions (thread-safe) procedure SaveToDatabase (NotifyWorkstations : boolean); procedure DeleteFromDatabase (NotifyWorkstations : boolean); // complex database update functions (thread-safe) procedure FullSaveToDatabase (IncludeDetails : boolean; NewObject : boolean = false); procedure SaveDetailsToDatabase; virtual; procedure DeleteDetailsFromDatabase (BeforeSave : boolean); virtual; procedure LoadDetailsFromStream (Stream : TStream); virtual; procedure SaveDetailsToStream (Stream : TStream); virtual; // complex database update functions (not thread-safe) procedure SetNewEntryValues; virtual; procedure FullDeleteFromDatabase (IncludeDetails : boolean); // determine if object is referred to by other records in database function HasReferences : boolean; virtual; // functions used by find screen class procedure SetupFindStringGrid (StringGrid : TStringGrid); virtual; class function FindFormCaption : string; virtual; abstract; class function FindSelectionString : string; virtual; function FindStringGridText (ACol : integer) : string; virtual; // functions used by maintain screen class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); virtual; class function MaintainFormCaption : string; virtual; abstract; class function MaintainSelectionString : string; virtual; function MaintainStringGridDrawText (ACol : integer) : string; virtual; function MaintainStringGridGetEditText (ACol : integer) : string; virtual; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); virtual; function MaintainStringGridDblClick (ACol : integer) : boolean; virtual; // functions used by communications manager to handle // updates/deletes notified by other workstations procedure ProcessUpdate; virtual; class procedure ProcessDelete (Id : int64); virtual; // functions used on listing report class procedure PrintAllOnListing; overload; class procedure PrintAllOnListing (SelectionString : string); overload; class function HeadingString : string; virtual; abstract; class function ColumnHeadingsString : string; virtual; function DetailsString : string; virtual; class function CSVHeadingsString : string; virtual; function CSVDetailsString : string; virtual; // miscellaneous functions function ComboBoxDisplayString : string; virtual; function ListBoxDisplayString : string; virtual; end; TDatabaseObjectClass = class of TDatabaseObject; // this is used to manage a collection of database objects TDatabaseObjectCollection = class private FObjects : TList; FOwned : boolean; function GetCount : integer; function GetObject (i : integer) : TDatabaseObject; function GetObjectById (Id : int64) : TDatabaseObject; public constructor Create; destructor Destroy; override; procedure Add (DatabaseObject : TDatabaseObject); function AddUnique (DatabaseObject : TDatabaseObject) : boolean; procedure Update (DatabaseObject : TDatabaseObject); procedure Insert (Index : integer; DatabaseObject : TDatabaseObject); function IndexOf (DatabaseObject : TDatabaseObject) : integer; procedure Delete (Index : integer); procedure DeleteById (Id : int64); procedure Clear; procedure LoadAllObjects (DatabaseObjectClass : TDatabaseObjectClass); procedure LoadSomeObjects (DatabaseObjectClass : TDatabaseObjectClass; SelectionString : string); procedure SaveToDatabase (DatabaseObjectClass : TDatabaseObjectClass; NotifyWorkstations : boolean); procedure DeleteFromDatabase (DatabaseObjectClass : TDatabaseObjectClass; NotifyWorkstations : boolean); procedure UpdateDatabase (DatabaseObjectClass : TDatabaseObjectClass); procedure LoadFromStream (Stream : TStream); procedure SaveToStream (Stream : TStream); function GetIdFromComboBoxDisplayString (ComboBoxDisplayString : string) : int64; function GetIdFromListBoxDisplayString (ListBoxDisplayString : string) : int64; procedure Sort (Compare : TListSortCompare); property Count : integer read GetCount; property Objects[i : integer] : TDatabaseObject read GetObject; default; property ObjectsById[Id : int64] : TDatabaseObject read GetObjectById; property Owned : boolean read FOwned write FOwned; end; {***** General database classes ***********************************************} // this table contains a single field used to generate unique ids for // other database objects and must be locked while accessing TIdGenerator = class(TDatabaseObject) NextId : int64; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; class function FindFormCaption : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; class function HeadingString : string; override; function GenerateId (Increment : integer) : int64; end; // this table contains configuration settings which apply to all workstations TGlobalConfiguration = class(TDatabaseObject) CompanyName : string; ExpiryDate : TDateTime; NoOfWorkstations : integer; Unlimited : boolean; Accounts : boolean; POS : boolean; Documents : boolean; RegistrationCode1 : string; RegistrationCode2 : string; RegistrationCode3 : string; RegistrationCode4 : string; PortNumber : integer; // port number used by communicator class HTTPPortNumber : integer; // port number used by HTTP communicator class HTTPPageColor : integer; SalesAccountAbbreviation : string; BankAccountAbbreviation : string; AttachmentDirectory : string; WebsiteContentDirectory : string; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; class function FindFormCaption : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; class function HeadingString : string; override; procedure SetDefaults; function CheckRegistrationCode : boolean; end; // this table contains configuration settings which apply to current workstation TWorkstationConfiguration = class(TDatabaseObject) ComputerName : string; LoggedOn : boolean; IPAddress : string; ControlColor : integer; MaximiseOnStart : boolean; CacheAttachments : boolean; ReportsPicture : string; QRPFileDirectory : string; AttachmentDirectory : string; CompanyId : int64; RecentEntryFirst : boolean; UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime; ReportLayouts : TNotesManager; // contains the collection of report layouts as a string // stored in the Notes table NoOfDaysSales : integer; FSalesReportParameters : TSalesReportParameters; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; constructor Create; override; destructor Destroy; override; procedure SaveDetailsToDatabase; override; procedure DeleteDetailsFromDatabase (BeforeSave : boolean); override; procedure LoadDetailsFromStream (Stream : TStream); override; procedure SaveDetailsToStream (Stream : TStream); override; class function FindFormCaption : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; procedure SetDefaults; function AccountingPeriodString : string; function SalesReportParameters : TSalesReportParameters; end; // this object represents a single line in a note field TNote = class(TDatabaseObject) ObjectId : int64; FieldNumber : integer; SequenceNumber : integer; Text : string; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; class function FindFormCaption : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; end; // this object represents a binary attachment stored in a separate file TAttachment = class(TDatabaseObject) ObjectId : int64; FieldNumber : integer; FileName : string; FileAttribute : integer; FileDateTime : TDateTime; FileSize : integer; FFileContents : string; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; procedure SaveDetailsToDatabase; override; procedure DeleteDetailsFromDatabase (BeforeSave : boolean); override; procedure LoadDetailsFromStream (Stream : TStream); override; procedure SaveDetailsToStream (Stream : TStream); override; class function FindFormCaption : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; procedure DeleteFileContents; procedure SaveFileContents; procedure SaveWorkstationFileContents; function FileContents : string; procedure SetFileContents (Str : string); end; // this object represents a user in client/server mode // it is not used in standard mode TUser = class(TDatabaseObject) Name : string; Password : string; // in encrypted form Administrator : boolean; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; procedure SetNewEntryValues; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function UnencryptedPassword : string; end; { // this object represents a ? T? = class(TDatabaseObject) Name : string; Abbreviation : string; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function ComboBoxDisplayString : string; override; end; } {***** Accounting system utility class ****************************************} // this class is used to manage a group of balanced (double) account entries // in the accounting subsystem // it is not a database object itself but is only used to manage the entries // it should not be used in the server as the database access is not thread-safe TCombinedEntry = class Id : int64; Date : TDateTime; FEntries : TDatabaseObjectCollection; procedure SaveToStream (Stream : TStream); procedure LoadFromStream (Stream : TStream); destructor Destroy; override; procedure Assign (CombinedEntry : TCombinedEntry); procedure LoadEntries; function Entries : TDatabaseObjectCollection; procedure UpdateDatabase (NotifyWorkstations : boolean); procedure DeleteFromDatabase (NotifyWorkstations : boolean); function TotalDebits : int64; function TotalCredits : int64; function Balanced : boolean; function SplitCompany : boolean; function PairedEntries : boolean; function MissingAccountIndex : integer; procedure SetDefaults; end; {***** Forward class declarations *********************************************} TCompany = class; TAccount = class; TEntry = class; TCashbook = class; TCashbookEntry = class; TDocument = class; {***** Forward class declarations *********************************************} TItem = class; TLookupCode = class; TSalesperson = class; TPaymentType = class; TSale = class; TSaleItem = class; TPaymentItem = class; {***** Accounting system database classes *************************************} // NOTE: These classes are still declared and compiled when the symbol ACCOUNTS // is not defined. However they are not registered which means no database // tables are created for them and any attempt to load or save from the // database will cause an exception // this object represents a company in the accounting subsystem TCompany = class(TDatabaseObject) Name : string; Abbreviation : string; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; function HasReferences : boolean; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function ComboBoxDisplayString : string; override; end; // this is used to indicate the type of account TAccountType = (atAsset,atLiability,atEquity,atIncome,atExpense); // this object represents an account in the accounting subsystem TAccount = class(TDatabaseObject) Name : string; Abbreviation : string; Description : string; CompanyId : int64; AccountType : TAccountType; FEntries : TDatabaseObjectCollection; FUseBeginPeriod : boolean; FBeginPeriodDate : TDateTime; FUseEndPeriod : boolean; FEndPeriodDate : TDateTime; FEntriesInPeriod : TDatabaseObjectCollection; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; function HasReferences : boolean; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function ComboBoxDisplayString : string; override; procedure LoadEntries; function Entries : TDatabaseObjectCollection; function EntriesCount : integer; function CompanyName : string; function CompanyAbbreviation : string; function CombinedAbbreviation : string; function AccountTypeString : string; function AccountTypeDescription : string; function Debit : boolean; function Credit : boolean; function IncomeStatement : boolean; function BalanceSheet : boolean; function TotalBalance : int64; // note that these balances relate to the period set // using SetPeriod function PeriodBalance : int64; function BeginBalance : int64; function EndBalance : int64; // these functions allows any arbitrary balance to be calculated function Balance (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime) : int64; function BalanceAsAt (AsAtDate : TDateTime) : int64; function AccountingPeriodBalanceString : string; procedure SortEntries; procedure SetPeriod (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime); procedure LoadEntriesInPeriod; function EntriesInPeriod : TDatabaseObjectCollection; procedure Print; procedure Email; procedure PrintSummary; function EarliestDate : TDateTime; function LatestDate : TDateTime; procedure Purge (PurgeDate : TDateTime); function FindNextEntryInPeriod (FindText : string; CurrentEntry : TEntry; Earlier : boolean) : int64; end; // this object represents an account entry in the accounting subsystem TEntry = class(TDatabaseObject) CombinedEntryId : int64; // used to match groups of balanced (double) entries AccountId : int64; Description : string; Date : TDateTime; Amount : int64; OtherAccountId : int64; // used to store id of other account in combined entry // if there are multiple accounts then this is set to -1 // note that this is a redundant field because the information // is accessible through the combined entry // it is used to speed access when displaying a list of entries FCombinedEntry : TCombinedEntry; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function CombinedEntry : TCombinedEntry; function AccountName : string; function AccountAbbreviation : string; function AccountCombinedAbbreviation : string; function OtherAccountName : string; function OtherAccountAbbreviation : string; function AbsoluteAmount : int64; function Debit : boolean; function Credit : boolean; function Account : TAccount; function TextInDescription (Text : string) : boolean; end; TCashbook = class(TDatabaseObject) Name : string; Abbreviation : string; Description : string; AccountId : int64; FCashbookEntries : TDatabaseObjectCollection; FUseBeginPeriod : boolean; FBeginPeriodDate : TDateTime; FUseEndPeriod : boolean; FEndPeriodDate : TDateTime; FCashbookEntriesInPeriod : TDatabaseObjectCollection; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; function HasReferences : boolean; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; function ComboBoxDisplayString : string; override; procedure LoadCashbookEntries; function CashbookEntries : TDatabaseObjectCollection; function CashbookEntriesCount : integer; function CombinedAbbreviation : string; function TotalBalance (StatementOnly : boolean) : int64; // note that these balances relate to the period set // using SetPeriod function PeriodBalance (StatementOnly : boolean) : int64; function BeginBalance (StatementOnly : boolean) : int64; function EndBalance (StatementOnly : boolean) : int64; // these functions allows any arbitrary balance to be calculated function Balance (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime; StatementOnly : boolean) : int64; function BalanceAsAt (AsAtDate : TDateTime; StatementOnly : boolean) : int64; function AccountingPeriodBalanceString (StatementOnly : boolean) : string; procedure SortCashbookEntries; procedure SetPeriod (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime); procedure LoadCashbookEntriesInPeriod; function CashbookEntriesInPeriod : TDatabaseObjectCollection; procedure AddCashbookEntry (CashbookEntry : TCashbookEntry); procedure DeleteCashbookEntry (CashbookEntry : TCashbookEntry); procedure Print; procedure Email; function Account : TAccount; function CachedAccount : TAccount; // use this if we want up to date financial data function FindNextCashbookEntryInPeriod (FindText : string; CurrentCashbookEntry : TCashbookEntry; Earlier : boolean) : int64; end; // this object represents a cash book entry in the accounting subsystem // it may or may not have corresponding entries in the ledger TCashbookEntry = class(TDatabaseObject) CashbookId : int64; OtherAccountId : int64; // the other account in the combined ledger entry CombinedEntryId : int64; // the combined entry id in the ledger Description : string; Date : TDateTime; Amount : int64; OnStatement : boolean; FCombinedEntry : TCombinedEntry; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; class function FindFormCaption : string; override; class function MaintainFormCaption : string; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function CombinedEntry : TCombinedEntry; procedure UpdateCombinedEntry; procedure DeleteCombinedEntry; function CashbookName : string; function CashbookAbbreviation : string; function OtherAccountName : string; function OtherAccountAbbreviation : string; function OtherAccountComboBoxDisplayString : string; function AbsoluteAmount : int64; function Debit : boolean; function Credit : boolean; function Cashbook : TCashbook; function OtherAccount : TAccount; function TextInDescription (Text : string) : boolean; end; {***** POS system database classes ********************************************} // NOTE: These classes are still declared and compiled when the symbol POS // is not defined. However they are not registered which means no database // tables are created for them and any attempt to load or save from the // database will cause an exception TItem = class(TDatabaseObject) Name : string; Price : double; Active : boolean; Picture : TAttachmentManager; FLookupCodes : TDatabaseObjectCollection; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; procedure SetNewEntryValues; override; constructor Create; override; destructor Destroy; override; procedure SaveDetailsToDatabase; override; procedure DeleteDetailsFromDatabase (BeforeSave : boolean); override; procedure LoadDetailsFromStream (Stream : TStream); override; procedure SaveDetailsToStream (Stream : TStream); override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; procedure ReloadLookupCodes; function LookupCodes : TDatabaseObjectCollection; end; TLookupCode = class(TDatabaseObject) ItemId : int64; Code : string; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; end; TSalesperson = class(TDatabaseObject) Name : string; Password : string; Active : boolean; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; procedure SetNewEntryValues; override; function HasReferences : boolean; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function UnencryptedPassword : string; end; TPaymentType = class(TDatabaseObject) Name : string; KeyboardShortcut : char; OpenCashDrawer : boolean; EFT : boolean; PromptCashOut : boolean; PromptDetails : boolean; Active : boolean; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; procedure SetNewEntryValues; override; function HasReferences : boolean; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; function ComboBoxDisplayString : string; override; end; TSale = class(TDatabaseObject) DateTime : TDateTime; SalespersonId : int64; CompanyId : int64; // current defaults to zero FSaleItems : TDatabaseObjectCollection; FPaymentItems : TDatabaseObjectCollection; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; procedure SaveDetailsToDatabase; override; procedure DeleteDetailsFromDatabase (BeforeSave : boolean); override; procedure LoadDetailsFromStream (Stream : TStream); override; procedure SaveDetailsToStream (Stream : TStream); override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; procedure ProcessUpdate; override; class procedure ProcessDelete (Id : int64); override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; class procedure ShowHeadingsOnSalesReport (ReportData : TReportData); procedure ShowOnSalesReport (ReportData : TReportData); procedure LoadDetails; function SaleItems : TDatabaseObjectCollection; function PaymentItems : TDatabaseObjectCollection; function TotalPrice : int64; function TotalPayment : int64; function Change : int64; procedure OpenCashDrawer; procedure PrintReceipt (Preview : boolean); function SaleItemsAsString : string; function PaymentItemsAsString : string; function IncludesItem (ItemId : int64) : boolean; function IncludesPaymentType (PaymentTypeId : int64) : boolean; function Date : TDateTime; function Time : TDateTime; end; TSaleItem = class(TDatabaseObject) SaleId : int64; ItemId : int64; Quantity : double; FullPrice : int64; // full price will be negative to indicate a credit Discount : int64; FSale : TSale; FSaleOwned : boolean; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; class procedure ShowHeadingsOnSalesReport (ReportData : TReportData); procedure ShowOnSalesReport (ReportData : TReportData); function NetPrice : int64; procedure SetSale (Sale : TSale); function Sale : TSale; end; TPaymentItem = class(TDatabaseObject) SaleId : int64; PaymentTypeId : int64; Amount : int64; Details : string; FSale : TSale; FSaleOwned : boolean; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; destructor Destroy; override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; class procedure ShowHeadingsOnSalesReport (ReportData : TReportData); procedure ShowOnSalesReport (ReportData : TReportData); procedure SetSale (Sale : TSale); function Sale : TSale; function PaymentType : TPaymentType; end; {***** Document management system database classes ****************************} // NOTE: These classes are still declared and compiled when the symbol DOCUMENTS // is not defined. However they are not registered which means no database // tables are created for them and any attempt to load or save from the // database will cause an exception // this object represents a document/file in the document management system TDocument = class(TDatabaseObject) Name : string; EntryDate : TDateTime; Notes : TNotesManager; Attachment : TAttachmentManager; class function TableName : string; override; class procedure AddNewTableFields (Table : TDataset); override; class procedure UpdateTable; override; procedure LoadFromTable (Table : TDataset); override; procedure SaveToTable (Table : TDataset); override; function InsertSQLStrColumnNames : string; override; function InsertSQLStrValues : string; override; procedure LoadFromStream (Stream : TStream); override; procedure SaveToStream (Stream : TStream); override; procedure SetNewEntryValues; override; constructor Create; override; destructor Destroy; override; procedure SaveDetailsToDatabase; override; procedure DeleteDetailsFromDatabase (BeforeSave : boolean); override; procedure LoadDetailsFromStream (Stream : TStream); override; procedure SaveDetailsToStream (Stream : TStream); override; class procedure SetupFindStringGrid (StringGrid : TStringGrid); override; class function FindFormCaption : string; override; class function FindSelectionString : string; override; function FindStringGridText (ACol : integer) : string; override; class procedure SetupMaintainStringGrid (StringGrid : TStringGrid); override; class function MaintainFormCaption : string; override; class function MaintainSelectionString : string; override; function MaintainStringGridDrawText (ACol : integer) : string; override; function MaintainStringGridGetEditText (ACol : integer) : string; override; procedure MaintainStringGridSetEditText (ACol : integer; Value : string); override; function MaintainStringGridDblClick (ACol : integer) : boolean; override; class function HeadingString : string; override; class function ColumnHeadingsString : string; override; function DetailsString : string; override; end; {******************************************************************************} procedure RegisterDatabaseClasses; procedure CreateTables; procedure UpdateTables; procedure ConvertTables; function DatabaseStatusString : string; function ConvertDatabaseObjectClassToInteger (DatabaseObjectClass : TDatabaseObjectClass) : integer; function ConvertIntegerToDatabaseObjectClass (Number : integer) : TDatabaseObjectClass; // calling this procedure will create a collection and populate // it with all database objects of the specified class // it will then be kept up-to-date with all database changes // made on this or any other workstation procedure CreateGlobalMaintainedCollection (var DatabaseObjectCollection : TDatabaseObjectCollection; DatabaseObjectClass : TDatabaseObjectClass); const NoOfDatabaseClasses = 19; var DatabaseObjectCollections : array[1..NoOfDatabaseClasses] of TDatabaseObjectCollection; implementation uses Globals, Utilities, DatabaseManager, Progress, Forms, CommunicationsManager, PromptAccountType, Main, IB, IBErrorCodes, SysUtils, Windows, Dialogs, Controls; var DatabaseClasses : array[1..NoOfDatabaseClasses] of TDatabaseObjectClass; // record all classes which have corresponding database tables // in the DatabaseClasses array allowing for easy reference procedure RegisterDatabaseClasses; var i : integer; begin i := 0; Inc(i); DatabaseClasses[i] := TIdGenerator; Inc(i); DatabaseClasses[i] := TGlobalConfiguration; Inc(i); DatabaseClasses[i] := TWorkstationConfiguration; Inc(i); DatabaseClasses[i] := TNote; Inc(i); DatabaseClasses[i] := TAttachment; Inc(i); DatabaseClasses[i] := TUser; {$IFDEF ACCOUNTS} Inc(i); DatabaseClasses[i] := TCompany; Inc(i); DatabaseClasses[i] := TAccount; Inc(i); DatabaseClasses[i] := TEntry; Inc(i); DatabaseClasses[i] := TCashbook; Inc(i); DatabaseClasses[i] := TCashbookEntry; {$ENDIF} {$IFDEF POS} Inc(i); DatabaseClasses[i] := TItem; Inc(i); DatabaseClasses[i] := TLookupCode; Inc(i); DatabaseClasses[i] := TSalesperson; Inc(i); DatabaseClasses[i] := TPaymentType; Inc(i); DatabaseClasses[i] := TSale; Inc(i); DatabaseClasses[i] := TSaleItem; Inc(i); DatabaseClasses[i] := TPaymentItem; {$ENDIF} {$IFDEF DOCUMENTS} Inc(i); DatabaseClasses[i] := TDocument; {$ENDIF} // Inc(i); DatabaseClasses[i] := T?; end; // this will create any tables which do not exist procedure CreateTables; var i : integer; begin for i := Low(DatabaseClasses) to High(DatabaseClasses) do if DatabaseClasses[i] <> nil then DatabaseClasses[i].CreateTable; end; // this procedure will apply any restructuring needed to existing tables procedure UpdateTables; var i : integer; begin for i := Low(DatabaseClasses) to High(DatabaseClasses) do if DatabaseClasses[i] <> nil then DatabaseClasses[i].UpdateTable; end; // this procedure will copy the entire contents of the database // tables from the BDE database to the Firebird database procedure ConvertTables; var i : integer; begin for i := Low(DatabaseClasses) to High(DatabaseClasses) do if DatabaseClasses[i] <> nil then DatabaseClasses[i].ConvertTable; end; function DatabaseStatusString : string; begin // if offline then we don't know what the database status is if Offline then begin Result := '[Offline]'; Exit; end; // if client mode then get information direct from server if ClientMode then begin if not ClientCommunicator.Connected then Result := '[Not connected]' else Result := ClientCommunicator.DatabaseStatusString; Exit; end; Result := '['; // add code here to show database status info Result := Result + ']'; end; function ConvertDatabaseObjectClassToInteger (DatabaseObjectClass : TDatabaseObjectClass) : integer; var i : integer; begin Result := 0; for i := Low(DatabaseClasses) to High(DatabaseClasses) do if DatabaseClasses[i] = DatabaseObjectClass then begin Result := i; break; end; end; function ConvertIntegerToDatabaseObjectClass (Number : integer) : TDatabaseObjectClass; begin Result := TDatabaseObject; if (Number >= Low(DatabaseClasses)) and (Number <= High(DatabaseClasses)) then Result := DatabaseClasses[Number]; end; procedure CreateGlobalMaintainedCollection (var DatabaseObjectCollection : TDatabaseObjectCollection; DatabaseObjectClass : TDatabaseObjectClass); begin LoadAllDatabaseObjects(DatabaseObjectCollection,DatabaseObjectClass); DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)] := DatabaseObjectCollection; end; function CompareEntries (Item1, Item2 : pointer) : integer; var Entry1, Entry2 : TEntry; begin Entry1 := TEntry(Item1); Entry2 := TEntry(Item2); if (Entry1 = nil) and (Entry2 = nil) then Result := 0 else if (Entry1 = nil) and (Entry2 <> nil) then Result := -1 else if (Entry1 <> nil) and (Entry2 = nil) then Result := 1 else begin // compare on date Result := Trunc(Entry1.Date - Entry2.Date); // if same then use id if Result = 0 then Result := Entry1.Id - Entry2.Id; end; end; function CompareCashbookEntries (Item1, Item2 : pointer) : integer; var CashbookEntry1, CashbookEntry2 : TCashbookEntry; begin CashbookEntry1 := TCashbookEntry(Item1); CashbookEntry2 := TCashbookEntry(Item2); if (CashbookEntry1 = nil) and (CashbookEntry2 = nil) then Result := 0 else if (CashbookEntry1 = nil) and (CashbookEntry2 <> nil) then Result := -1 else if (CashbookEntry1 <> nil) and (CashbookEntry2 = nil) then Result := 1 else begin // compare on date Result := Trunc(CashbookEntry1.Date - CashbookEntry2.Date); // if same then use id if Result = 0 then Result := CashbookEntry1.Id - CashbookEntry2.Id; end; end; {***** TAttachmentManager methods *********************************************} function TAttachmentManager.AttachmentFileName : string; begin LoadAttachment; if FAttachment = nil then Result := '' else Result := FAttachment.FileName; end; function TAttachmentManager.AttachmentFileAttribute : integer; begin LoadAttachment; if FAttachment = nil then Result := 0 else Result := FAttachment.FileAttribute; end; function TAttachmentManager.AttachmentFileDateTime : TDateTime; begin LoadAttachment; if FAttachment = nil then Result := 0 else Result := FAttachment.FileDateTime; end; function TAttachmentManager.AttachmentFileContents : string; begin LoadAttachment; if FAttachment = nil then Result := '' else Result := FAttachment.FileContents; end; function TAttachmentManager.AttachmentFileSize : integer; begin LoadAttachment; if FAttachment = nil then Result := 0 else Result := FAttachment.FileSize; end; function TAttachmentManager.SelectionString : string; begin // set up selection string Result := 'WHERE ' + DelimitSQLFieldName('ObjectId') + ' = ' + IntToStr(FOwner.Id) + ' AND ' + DelimitSQLFieldName('FieldNumber') + ' = ' + IntToStr(FFieldNumber) end; function TAttachmentManager.Changed : boolean; begin LoadAttachment; if FAttachment = nil then Result := (FFileName <> '') or (FFileAttribute <> 0) or (FFileDateTime <> 0) or (FFileContents <> '') else Result := ((FFileName <> '') and (FFileName <> AttachmentFileName)) or ((FFileAttribute <> 0) and (FFileAttribute <> AttachmentFileAttribute)) or ((FFileDateTime <> 0) and (FFileDateTime <> AttachmentFileDateTime)) or ((FFileContents <> '') and (FFileContents <> AttachmentFileContents)); end; procedure TAttachmentManager.LoadAttachment; begin if (FAttachment = nil) and (FOwner.Id <> 0) then FAttachment := TAttachment(LoadDatabaseObject(TAttachment,SelectionString)); end; constructor TAttachmentManager.Create (Owner : TDatabaseObject; FieldNumber : integer); begin inherited Create; FOwner := Owner; FFieldNumber := FieldNumber; // don't load attachment until it is needed FAttachment := nil; FFileName := ''; FFileAttribute := 0; FFileDateTime := 0; FFileContents := ''; end; destructor TAttachmentManager.Destroy; begin FAttachment.Free; inherited; end; procedure TAttachmentManager.Assign (AttachmentManager : TAttachmentManager); begin FFileName := AttachmentManager.FFileName; FFileAttribute := AttachmentManager.FFileAttribute; FFileDateTime := AttachmentManager.FFileDateTime; FFileContents := AttachmentManager.FFileContents; if FAttachment = nil then FAttachment := TAttachment.Create; FAttachment.Assign(AttachmentManager.FAttachment); end; procedure TAttachmentManager.Reload; begin // force attachment to be reloaded FAttachment.Free; FAttachment := nil; FFileName := ''; FFileAttribute := 0; FFileDateTime := 0; FFileContents := ''; end; function TAttachmentManager.FileName : string; begin if FFileName <> '' then Result := FFileName else Result := AttachmentFileName; end; function TAttachmentManager.FileAttribute : integer; begin if FFileAttribute <> 0 then Result := FFileAttribute else Result := AttachmentFileAttribute; end; function TAttachmentManager.FileDateTime : TDateTime; begin if FFileDateTime <> 0 then Result := FFileDateTime else Result := AttachmentFileDateTime; end; function TAttachmentManager.FileContents : string; begin if FFileContents <> '' then Result := FFileContents else Result := AttachmentFileContents; end; function TAttachmentManager.FileSize : integer; begin if FFileContents <> '' then Result := Length(FFileContents) else Result := AttachmentFileSize; end; function TAttachmentManager.SetFile (FileName : string) : boolean; begin if not FileExists(FileName) then begin Result := false; Exit; end; // record file attributes FFileName := ExtractFileName(FileName); {$WARNINGS OFF} FFileAttribute := FileGetAttr(FileName); {$WARNINGS ON} FFileDateTime := FileDateToDateTime(FileAge(FileName)); FFileContents := GetFileContents(FileName); Result := true; end; function TAttachmentManager.GetFile (FileName : string) : boolean; begin if FileContents <> '' then begin SaveFileContents(FileName,FileContents,DateTimeToFileDate(FileDateTime)); {$WARNINGS OFF} FileSetAttr(FileName,FileAttribute); {$WARNINGS ON} Result := true; end else Result := false; end; procedure TAttachmentManager.LocalSave; begin // this function should be called for each attachment // after a full save to ensure a copy is saved in // the local attachment cache // just ignore if not in client mode if not ClientMode then Exit; // can't save attachment if owner does not have an id if FOwner.Id = 0 then Exit; // load attachment LoadAttachment; if (FAttachment <> nil) and (FFileContents <> '') then begin // populate attachment contents FAttachment.SetFileContents(FFileContents); // save attachment to local cache FAttachment.SaveWorkstationFileContents; end; end; procedure TAttachmentManager.Save; begin // can't save attachment if owner does not have an id if FOwner.Id = 0 then Exit; // if attachment has been changed then save it if Changed then begin // set attachment to match details if FAttachment = nil then begin FAttachment := TAttachment.Create; FAttachment.ObjectId := FOwner.Id; FAttachment.FieldNumber := FFieldNumber; end; FAttachment.FileName := FFileName; FAttachment.FileAttribute := FFileAttribute; FAttachment.FileDateTime := FFileDateTime; FAttachment.SetFileContents(FFileContents); // save attachment to database FAttachment.FullSaveToDatabase(true); end; end; procedure TAttachmentManager.Delete; begin LoadAttachment; if FAttachment <> nil then FAttachment.FullDeleteFromDatabase(true); end; procedure TAttachmentManager.LoadFromStream (Stream : TStream); var Changed : boolean; begin Stream.Read(Changed,SizeOf(Changed)); if Changed then begin FFileName := ReadStrFromStream(Stream); Stream.Read(FFileAttribute,SizeOf(FFileAttribute)); Stream.Read(FFileDateTime,SizeOf(FFileDateTime)); FFileContents := DecompressString(ReadStrFromStream(Stream)); end; end; procedure TAttachmentManager.SaveToStream (Stream : TStream); var Changed : boolean; begin // record whether change has been made since loading Changed := Self.Changed; Stream.Write(Changed,SizeOf(Changed)); // only write details if change has occurred if Changed then begin WriteStrToStream(FFileName,Stream); Stream.Write(FFileAttribute,SizeOf(FFileAttribute)); Stream.Write(FFileDateTime,SizeOf(FFileDateTime)); WriteStrToStream(CompressString(FFileContents),Stream); end; end; function TAttachmentManager.AttachmentId : int64; begin LoadAttachment; if FAttachment <> nil then Result := FAttachment.Id else Result := 0; end; {***** TNotesManager methods **************************************************} function TNotesManager.NotesAsString : string; var i : integer; begin // convert to single string Result := ''; if FNotes = nil then Exit; for i := 0 to FNotes.Count - 1 do Result := Result + TNote(FNotes[i]).Text; Result := RestoreNullsEtc(Result); end; function TNotesManager.SelectionString : string; begin // set up selection string Result := 'WHERE ' + DelimitSQLFieldName('ObjectId') + ' = ' + IntToStr(FOwner.Id) + ' AND ' + DelimitSQLFieldName('FieldNumber') + ' = ' + IntToStr(FFieldNumber) end; function TNotesManager.OrderedSelectionString : string; begin Result := SelectionString + ' ORDER BY ' + DelimitSQLFieldName('SequenceNumber'); end; function TNotesManager.Changed : boolean; begin Result := FString <> NotesAsString; end; constructor TNotesManager.Create (Owner : TDatabaseObject; FieldNumber : integer); begin inherited Create; FOwner := Owner; FFieldNumber := FieldNumber; // don't load notes yet until they are needed FNotes := nil; FString := ''; end; destructor TNotesManager.Destroy; begin FNotes.Free; inherited; end; procedure TNotesManager.Assign (NotesManager : TNotesManager); var i : integer; OriginalNote : TNote; NewNote : TNote; begin FString := NotesManager.FString; if FNotes = nil then begin FNotes := TDatabaseObjectCollection.Create; FNotes.Owned := true; end else FNotes.Clear; for i := 0 to NotesManager.FNotes.Count - 1 do begin OriginalNote := TNote(NotesManager.FNotes[i]); NewNote := TNote.Create; NewNote.ObjectId := FOwner.Id; NewNote.FieldNumber := FFieldNumber; NewNote.SequenceNumber := OriginalNote.SequenceNumber; NewNote.Text := OriginalNote.Text; FNotes.Add(NewNote); end; end; procedure TNotesManager.Reload; begin // force notes to be reloaded from database FNotes.Free; FNotes := nil; FString := ''; end; function TNotesManager.AsString : string; begin if (FNotes = nil) and (FString = '') and (FOwner.Id <> 0) then begin // load notes for this field from database LoadSomeDatabaseObjects(FNotes,TNote,OrderedSelectionString); // convert to single string FString := NotesAsString; end; Result := FString; end; procedure TNotesManager.SetString (Str : string); begin FString := Str; end; procedure TNotesManager.Save; var WorkStr : string; Text : string; Note : TNote; SequenceNumber : integer; i : integer; begin // call AsString to make sure that data is loaded from database AsString; // can't save notes if owner does not have an id if FOwner.Id = 0 then Exit; // if notes have changed then recreate collection if Changed then begin if FNotes = nil then begin FNotes := TDatabaseObjectCollection.Create; FNotes.Owned := true; end else FNotes.Clear; // split up Notes string into chunks and add to collection // as TNote objects WorkStr := ReplaceNullsEtc(FString); SequenceNumber := 0; while WorkStr <> '' do begin Text := Copy(WorkStr,1,NoteTextLength); System.Delete(WorkStr,1,NoteTextLength); Note := TNote.Create; Note.ObjectId := FOwner.Id; Note.FieldNumber := FFieldNumber; Note.SequenceNumber := SequenceNumber; Note.Text := Text; Inc(SequenceNumber); FNotes.Add(Note); end; end; // if nothing changed and there is no collection // then don't do anything if FNotes = nil then Exit; // delete any pre-existing notes from database DeleteSomeDatabaseObjects(TNote,SelectionString); // assign object id for i := 0 to FNotes.Count - 1 do if TNote(FNotes[i]).ObjectId = 0 then TNote(FNotes[i]).ObjectId := FOwner.Id; // now save the current notes to the database FNotes.SaveToDatabase(TNote,false); end; procedure TNotesManager.Delete; begin // call AsString to make sure that data is loaded from database AsString; // can't delete notes if owner does not have an id if FOwner.Id = 0 then Exit; // delete existing notes from database DeleteSomeDatabaseObjects(TNote,SelectionString); end; procedure TNotesManager.LoadFromStream (Stream : TStream); var Changed : boolean; Note : TNote; begin Stream.Read(Changed,SizeOf(Changed)); if Changed then begin FString := ReadStrFromStream(Stream); // if it is a null string and there is // no notes collection then create // one to avoid it being // reloaded from the database if (FString = '') and (FNotes = nil) then begin FNotes := TDatabaseObjectCollection.Create; FNotes.Owned := true; Note := TNote.Create; Note.ObjectId := FOwner.Id; Note.FieldNumber := FFieldNumber; Note.Text := 'x'; FNotes.Add(Note); end; end else AsString; end; procedure TNotesManager.SaveToStream (Stream : TStream); var Changed : boolean; begin // call AsString to make sure that data is loaded from database AsString; // record whether change has been made since loading Changed := Self.Changed; Stream.Write(Changed,SizeOf(Changed)); // only write string if change has occurred if Changed then WriteStrToStream(FString,Stream); end; {***** TDatabaseObject methods ************************************************} class procedure TDatabaseObject.CreateTable; var Table : TTable; IBTable : TIBTable; Transaction : TIBTransaction; TableExists : boolean; begin if Firebird then begin Transaction := TIBTransaction.Create(nil); Transaction.DefaultDatabase := TVFirebirdDatabase; Transaction.StartTransaction; IBTable := TIBTable.Create(nil); IBTable.Database := TVFirebirdDatabase; IBTable.Transaction := Transaction; IBTable.TableName := TableName; TableExists := IBTable.Exists; // if table doesn't exist then create it if not TableExists then begin IBTable.IndexDefs.Clear; IBTable.FieldDefs.Clear; AddNewTableFields(IBTable); IBTable.CreateTable; end; IBTable.Free; if Transaction.InTransaction then Transaction.Commit; Transaction.Free; end else begin Table := TTable.Create(nil); Table.DatabaseName := BDEDatabaseName; Table.TableName := TableName; TableExists := Table.Exists; // if table doesn't exist then create it if not TableExists then begin Table.IndexDefs.Clear; Table.FieldDefs.Clear; AddNewTableFields(Table); Table.CreateTable; end; Table.Free; end; end; class procedure TDatabaseObject.ConvertTable; var DatabaseObject : TDatabaseObject; Table : TTable; IBQuery : TIBQuery; Transaction : TIBTransaction; begin ProgressForm.SetPosition(0); ProgressForm.SetCaption('Converting ' + TableName + '. Please wait...'); ProgressForm.Show; try // create the Firebird table Firebird := true; CreateTable; Firebird := false; Table := OpenTable(false); Transaction := TIBTransaction.Create(nil); Transaction.DefaultDatabase := TVFirebirdDatabase; Transaction.StartTransaction; IBQuery := TIBQuery.Create(nil); IBQuery.Database := TVFirebirdDatabase; IBQuery.Transaction := Transaction; try while not Table.EOF do begin DatabaseObject := Create; DatabaseObject.LoadFromTable(Table); Firebird := true; IBQuery.SQL.Clear; IBQuery.SQL.Add(DatabaseObject.InsertSQLStr); IBQuery.ExecSQL; Firebird := false; DatabaseObject.Free; Table.Next; ProgressForm.SetPosition(Table.RecNo * 100 div Table.RecordCount); // do a commit every 100 records if Table.RecNo mod 100 = 0 then begin if Transaction.InTransaction then begin Transaction.Commit; Transaction.StartTransaction; end; end; end; finally Table.Active := false; Table.Free; IBQuery.Free; if Transaction.InTransaction then Transaction.Commit; Transaction.Free; end; finally ProgressForm.Hide; end; end; class procedure TDatabaseObject.AddNewTableFields (Table : TDataset); begin Table.FieldDefs.Add('Id',IdFieldType,0,false); if Firebird then TIBTable(Table).IndexDefs.Add(TableName + 'Id','Id',[]) else TTable(Table).IndexDefs.Add('Id','Id',[ixPrimary]) end; procedure TDatabaseObject.LoadFromTable (Table : TDataset); begin Id := IdFieldValue(Table.FieldByName('Id')); end; procedure TDatabaseObject.SaveToTable (Table : TDataset); begin SetIdFieldValue(Table.FieldByName('Id'),Id); end; function TDatabaseObject.InsertSQLStrColumnNames : string; begin Result := DelimitSQLFieldName('Id'); end; function TDatabaseObject.InsertSQLStrValues : string; begin Result := IntToStr(Id); end; function TDatabaseObject.InsertSQLStr : string; begin Result := 'INSERT INTO "'; Result := Result + TableName; Result := Result + '" ('; Result := Result + InsertSQLStrColumnNames; Result := Result + ') VALUES('; Result := Result + InsertSQLStrValues; Result := Result + ')'; end; procedure TDatabaseObject.LoadFromStream (Stream : TStream); begin Stream.Read(Id,SizeOf(Id)); end; procedure TDatabaseObject.SaveToStream (Stream : TStream); begin Stream.Write(Id,SizeOf(Id)); end; constructor TDatabaseObject.Create; begin inherited; end; procedure TDatabaseObject.Assign (DatabaseObject : TDatabaseObject); var StringStream : TStringStream; begin StringStream := TStringStream.Create(''); DatabaseObject.SaveToStream(StringStream); StringStream.Position := 0; Self.LoadFromStream(StringStream); StringStream.Free; end; class function TDatabaseObject.OpenTable (Exclusive : boolean) : TTable; var Opened : boolean; Retries : integer; begin // create a new table object Result := TTable.Create(nil); Result.DatabaseName := BDEDatabaseName; Result.TableName := TableName; Result.SessionName := TVSessionName; Result.Exclusive := Exclusive; Opened := false; Retries := 0; while not Opened do begin try Result.Active := true; Opened := true; except on E:Exception do begin // keep trying until table is opened // but count retries and show error message when limit is reached // but only if we are not multi-threading Inc(Retries); if ServerMode and (TVSessionName <> '') then begin if Retries > NoOfDatabaseRetries * 2 then begin if E is EDBEngineError then MessageLog.Log(FormatDBEngineError(EDBEngineError(E))) else MessageLog.Log(E.Message); raise; end; end else begin if Retries > NoOfDatabaseRetries then begin if E is EDBEngineError then ShowMessage(FormatDBEngineError(EDBEngineError(E))) else ShowMessage(E.Message); if MessageDlg('Do you wish to retry the operation that caused the error?', mtConfirmation, [mbYes, mbNo], 0) = mrNo then raise else Retries := 0; end; end; Sleep(PauseBetweenRetries); end; end; end; end; class function TDatabaseObject.OpenIBTable : TIBTable; var Transaction : TIBTransaction; begin Transaction := TIBTransaction.Create(nil); Transaction.DefaultDatabase := TVFirebirdDatabase; Transaction.StartTransaction; Result := TIBTable.Create(nil); Result.Database := TVFirebirdDatabase; Result.Transaction := Transaction; Result.TableName := TableName; Result.Open; // Result.FetchAll; end; class function TDatabaseObject.OpenQuery (SelectionString : string) : TQuery; var SQLStr : string; Opened : boolean; Retries : integer; begin if Pos('SELECT',SelectionString) <> 0 then SQLStr := SelectionString else begin SQLStr := 'SELECT * FROM "'; SQLStr := SQLStr + TableName; SQLStr := SQLStr + '" '; SQLStr := SQLStr + SelectionString; end; // create a new query object Result := TQuery.Create(nil); Result.DatabaseName := BDEDatabaseName; Result.SessionName := TVSessionName; Result.SQL.Clear; Result.SQL.Add(SQLStr); Opened := false; Retries := 0; while not Opened do begin try Result.Active := true; Opened := true; except on E:Exception do begin // keep trying until table is opened // but count retries and show error message when limit is reached // but only if we are not multi-threading Inc(Retries); if ServerMode and (TVSessionName <> '') then begin if Retries > NoOfDatabaseRetries * 2 then begin if E is EDBEngineError then MessageLog.Log(FormatDBEngineError(EDBEngineError(E))) else MessageLog.Log(E.Message); raise; end; end else begin if Retries > NoOfDatabaseRetries then begin if E is EDBEngineError then ShowMessage(FormatDBEngineError(EDBEngineError(E))) else ShowMessage(E.Message); if MessageDlg('Do you wish to retry the operation that caused the error?', mtConfirmation, [mbYes, mbNo], 0) = mrNo then raise else Retries := 0; end; end; Sleep(PauseBetweenRetries); end; end; end; end; class function TDatabaseObject.OpenIBQuery (SelectionString : string) : TIBQuery; var Transaction : TIBTransaction; SQLStr : string; begin if Pos('SELECT',SelectionString) <> 0 then SQLStr := SelectionString else begin SQLStr := 'SELECT * FROM "'; SQLStr := SQLStr + TableName; SQLStr := SQLStr + '" '; SQLStr := SQLStr + SelectionString; end; Transaction := TIBTransaction.Create(nil); Transaction.DefaultDatabase := TVFirebirdDatabase; Transaction.StartTransaction; Result := TIBQuery.Create(nil); Result.Database := TVFirebirdDatabase; Result.Transaction := Transaction; Result.SQL.Clear; Result.SQL.Add(SQLStr); Result.Open; Result.FetchAll; end; // this will save the object to the database and assign // an id if it does not already have one // note that this will not delete any object with the same id // this should be done first using delete from database procedure TDatabaseObject.SaveToDatabase (NotifyWorkstations : boolean); var IBQuery : TIBQuery; Transaction : TIBTransaction; Query : TQuery; begin // if no id assigned then do that first if Id = 0 then begin Id := GenerateId(1); SetNewEntryValues; end; if ClientMode then begin if not ClientCommunicator.SaveDatabaseObject(TDatabaseObjectClass(ClassType),Self) then raise Exception.Create('Server confirmation not received when saving object to database'); end else begin // if firebird then use SQL to add new record if Firebird then begin Transaction := TIBTransaction.Create(nil); Transaction.DefaultDatabase := TVFirebirdDatabase; Transaction.StartTransaction; IBQuery := TIBQuery.Create(nil); IBQuery.Database := TVFirebirdDatabase; IBQuery.Transaction := Transaction; try IBQuery.SQL.Clear; IBQuery.SQL.Add(InsertSQLStr); IBQuery.ExecSQL; finally IBQuery.Free; if Transaction.InTransaction then Transaction.Commit; Transaction.Free; end; // open table and post object to table end else begin Query := TQuery.Create(nil); Query.DatabaseName := BDEDatabaseName; Query.SessionName := TVSessionName; try Query.SQL.Clear; Query.SQL.Add(InsertSQLStr); Query.ExecSQL; finally Query.Free; end; end; end; if ServerMode then begin // update global collection if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))].Update(Self); end else if NotifyWorkstations then begin // notify all workstations to update their global collections if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then UpdateDatabaseObjectOnLoggedOnWorkstations(TDatabaseObjectClass(ClassType),Self); end; end; procedure TDatabaseObject.DeleteFromDatabase (NotifyWorkstations : boolean); var SelectionString : string; begin if Id = 0 then Exit; if ClientMode then begin if not ClientCommunicator.DeleteDatabaseObject(TDatabaseObjectClass(ClassType),Id) then raise Exception.Create('Server confirmation not received when deleting object from database'); end else begin SelectionString := 'WHERE ' + DelimitSQLFieldName('Id') + ' = ' + IntToStr(Id); DeleteSomeDatabaseObjects(TDatabaseObjectClass(ClassType),SelectionString) end; if ServerMode then begin // update global collection if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))].DeleteById(Id); end else if NotifyWorkstations then begin // notify all workstations to update their global collections if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then DeleteDatabaseObjectFromLoggedOnWorkstations(TDatabaseObjectClass(ClassType),Id); end; end; // this will save the entire object to the database INCLUDING // any detail information // it will also delete any existing information in the database // it will also acquire a complete lock on the database while // this is occuring so that no other changes can be made while // the update is in progress procedure TDatabaseObject.FullSaveToDatabase (IncludeDetails : boolean; NewObject : boolean); var Cursor : TCursor; begin // determine if it is a new object or an existing one from the id ignoring // the NewObject parameter unless we are in server mode when the id will // have already been assigned to a new object and its fields initialised by // the client if not ServerMode then begin NewObject := (Id = 0); // if it is a new one then initialise fields and generate id if NewObject then begin Id := GenerateId(1); SetNewEntryValues; end; end; if ClientMode then begin Cursor := Screen.Cursor; Screen.Cursor := crHourGlass; try if not ClientCommunicator.FullSaveDatabaseObject(TDatabaseObjectClass(ClassType),Self,IncludeDetails,NewObject) then raise Exception.Create('Server confirmation not received when attempting full save of object to database'); finally Screen.Cursor := Cursor; end; // notify all workstations to update their global collections if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then UpdateDatabaseObjectOnLoggedOnWorkstations(TDatabaseObjectClass(ClassType),Self); Exit; end; if not ServerMode then AcquireDatabaseCriticalUpdate; try // if it is not a new object then delete the existing object if not NewObject then begin // delete details first if IncludeDetails then DeleteDetailsFromDatabase(true); // then delete object itself DeleteFromDatabase(false); end; // save object itself SaveToDatabase(false); // save details if IncludeDetails then SaveDetailsToDatabase; finally if not ServerMode then ReleaseDatabaseCriticalUpdate; end; if ServerMode then begin // update global collection if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))].Update(Self); end else begin // notify all workstations to update their global collections if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(ClassType))] <> nil then UpdateDatabaseObjectOnLoggedOnWorkstations(TDatabaseObjectClass(ClassType),Self); end; end; procedure TDatabaseObject.SaveDetailsToDatabase; begin // by default don't do anything end; procedure TDatabaseObject.DeleteDetailsFromDatabase (BeforeSave : boolean); begin // by default don't do anything end; procedure TDatabaseObject.LoadDetailsFromStream (Stream : TStream); begin // by default don't do anything end; procedure TDatabaseObject.SaveDetailsToStream (Stream : TStream); begin // by default don't do anything end; procedure TDatabaseObject.SetNewEntryValues; begin // by default don't do anything end; procedure TDatabaseObject.FullDeleteFromDatabase (IncludeDetails : boolean); begin AcquireDatabaseCriticalUpdate; try // delete details first if IncludeDetails then DeleteDetailsFromDatabase(false); // then delete object itself DeleteFromDatabase(true); finally ReleaseDatabaseCriticalUpdate; end; end; function TDatabaseObject.HasReferences : boolean; begin Result := false; end; class procedure TDatabaseObject.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'ID' ],[ 10 // ID ]); end; class function TDatabaseObject.FindSelectionString : string; begin Result := ''; end; function TDatabaseObject.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(Id) else Result := ''; end; class procedure TDatabaseObject.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TDatabaseObject.MaintainSelectionString : string; begin Result := ''; end; function TDatabaseObject.MaintainStringGridDrawText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(Id) else Result := ''; end; function TDatabaseObject.MaintainStringGridGetEditText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(Id) else Result := ''; end; procedure TDatabaseObject.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then begin try Id := StrToInt64(Value); except // ignore any exception end; end; end; function TDatabaseObject.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; procedure TDatabaseObject.ProcessUpdate; begin // by default don't do anything end; class procedure TDatabaseObject.ProcessDelete (Id : int64); begin // by default don't do anything end; class procedure TDatabaseObject.PrintAllOnListing; begin if CheckQuickReportOpen then Exit; PrintListing(Self,''); end; class procedure TDatabaseObject.PrintAllOnListing (SelectionString : string); begin if CheckQuickReportOpen then Exit; PrintListing(Self,SelectionString); end; class function TDatabaseObject.ColumnHeadingsString : string; begin Result := 'Id ' end; function TDatabaseObject.DetailsString : string; begin Result := Format('%-7d ',[Id]); end; class function TDatabaseObject.CSVHeadingsString : string; begin Result := 'Id'; end; function TDatabaseObject.CSVDetailsString : string; begin Result := IntToStr(Id); end; function TDatabaseObject.ComboBoxDisplayString : string; begin Result := 'Object from ' + TableName + ' with ID = ' + IntToStr(Id); end; function TDatabaseObject.ListBoxDisplayString : string; begin Result := 'Object from ' + TableName + ' with ID = ' + IntToStr(Id); end; {***** TDatabaseObjectCollection methods **************************************} // return number of objects in collection function TDatabaseObjectCollection.GetCount : integer; begin Result := FObjects.Count; end; function TDatabaseObjectCollection.GetObject (i : integer) : TDatabaseObject; begin if (i >= 0) and (i < FObjects.Count) then Result := TDatabaseObject(FObjects.Items[i]) else Result := nil; end; function TDatabaseObjectCollection.GetObjectById (Id : int64) : TDatabaseObject; var i : integer; begin for i := 0 to FObjects.Count - 1 do if TDatabaseObject(FObjects.Items[i]).Id = Id then begin Result := TDatabaseObject(FObjects.Items[i]); Exit; end; Result := nil; end; // create a new empty database object collection constructor TDatabaseObjectCollection.Create; begin inherited Create; FObjects := TList.Create; end; destructor TDatabaseObjectCollection.Destroy; var i : integer; begin if FOwned then begin for i := 0 to FObjects.Count - 1 do TDatabaseObject(FObjects.Items[i]).Free; end; FObjects.Free; inherited; end; // add a database object to the collection procedure TDatabaseObjectCollection.Add (DatabaseObject : TDatabaseObject); begin FObjects.Add(DatabaseObject); end; // add a database object to the collection but only if it is // not already there // return true if added, false if not function TDatabaseObjectCollection.AddUnique (DatabaseObject : TDatabaseObject) : boolean; begin if GetObjectById(DatabaseObject.Id) = nil then begin FObjects.Add(DatabaseObject); Result := true; end else Result := false; end; // update the details of the existing object if it is in the collection // otherwise add a copy to the collection // the ownership of the DatabaseObject is left with the caller procedure TDatabaseObjectCollection.Update (DatabaseObject : TDatabaseObject); var ExistingDatabaseObject : TDatabaseObject; begin // if this is not an owned collection then don't do anything if not FOwned then Exit; ExistingDatabaseObject := GetObjectById(DatabaseObject.Id); if ExistingDatabaseObject = nil then begin ExistingDatabaseObject := TDatabaseObjectClass(DatabaseObject.ClassType).Create; FObjects.Add(ExistingDatabaseObject); end; ExistingDatabaseObject.Assign(DatabaseObject); end; // insert a database object into the collection in the given position procedure TDatabaseObjectCollection.Insert (Index : integer; DatabaseObject : TDatabaseObject); begin FObjects.Insert(Index,DatabaseObject); end; // obtain the position of a database object in the collection function TDatabaseObjectCollection.IndexOf (DatabaseObject : TDatabaseObject) : integer; begin Result := FObjects.IndexOf(DatabaseObject); end; // delete a database object from the collection in the given position procedure TDatabaseObjectCollection.Delete (Index : integer); begin if FOwned then TDatabaseObject(FObjects.Items[Index]).Free; FObjects.Delete(Index); end; // delete a database object from the collection with the specified id procedure TDatabaseObjectCollection.DeleteById (Id : int64); var i : integer; begin for i := 0 to FObjects.Count - 1 do if TDatabaseObject(FObjects.Items[i]).Id = Id then begin Delete(i); Exit; end; end; // remove all objects from collection procedure TDatabaseObjectCollection.Clear; var i : integer; begin if FOwned then begin for i := 0 to FObjects.Count - 1 do TDatabaseObject(FObjects.Items[i]).Free; end; FObjects.Clear; end; // load all objects of specified type from database into collection procedure TDatabaseObjectCollection.LoadAllObjects (DatabaseObjectClass : TDatabaseObjectClass); var DatabaseObject : TDatabaseObject; Table : TDataset; begin // if client mode then get objects from server if ClientMode then begin ClientCommunicator.LoadAllDatabaseObjects(Self,DatabaseObjectClass); Exit; end; if Firebird then Table := DatabaseObjectClass.OpenIBTable else Table := DatabaseObjectClass.OpenTable(false); try while not Table.EOF do begin DatabaseObject := DatabaseObjectClass.Create; DatabaseObject.LoadFromTable(Table); FObjects.Add(DatabaseObject); Table.Next; // give other threads a chance if ServerMode then Sleep(0); end; finally Table.Active := false; if Firebird then begin if TIBTable(Table).Transaction.InTransaction then TIBTable(Table).Transaction.Commit; TIBTable(Table).Transaction.Free; end; Table.Free; end; end; // load some objects of specified type from database into collection procedure TDatabaseObjectCollection.LoadSomeObjects (DatabaseObjectClass : TDatabaseObjectClass; SelectionString : string); var DatabaseObject : TDatabaseObject; Query : TDataset; begin // if client mode then get objects from server if ClientMode then begin ClientCommunicator.LoadSomeDatabaseObjects(Self,DatabaseObjectClass,SelectionString); Exit; end; if Firebird then Query := DatabaseObjectClass.OpenIBQuery(SelectionString) else Query := DatabaseObjectClass.OpenQuery(SelectionString); try while not Query.EOF do begin DatabaseObject := DatabaseObjectClass.Create; DatabaseObject.LoadFromTable(Query); FObjects.Add(DatabaseObject); Query.Next; // give other threads a chance if ServerMode then Sleep(0); end; finally Query.Active := false; if Firebird then begin if TIBQuery(Query).Transaction.InTransaction then TIBQuery(Query).Transaction.Commit; TIBQuery(Query).Transaction.Free; end; Query.Free; end; end; procedure TDatabaseObjectCollection.SaveToDatabase (DatabaseObjectClass : TDatabaseObjectClass; NotifyWorkstations : boolean); var i : integer; AllZero : boolean; StartId : int64; IBQuery : TIBQuery; Transaction : TIBTransaction; Query : TQuery; begin // don't do anything if there is nothing to save if FObjects.Count = 0 then Exit; // check if all ids are zero AllZero := true; for i := 0 to FObjects.Count - 1 do if TDatabaseObject(FObjects.Items[i]).Id <> 0 then begin AllZero := false; break; end; // if so then generate in batch to save time if AllZero then begin StartId := GenerateId(FObjects.Count); for i := 0 to FObjects.Count - 1 do begin TDatabaseObject(FObjects.Items[i]).Id := StartId + i; TDatabaseObject(FObjects.Items[i]).SetNewEntryValues; end; // otherwise assign ids individually to those that don't have them end else begin for i := 0 to FObjects.Count - 1 do if TDatabaseObject(FObjects.Items[i]).Id = 0 then begin TDatabaseObject(FObjects.Items[i]).Id := GenerateId(1); TDatabaseObject(FObjects.Items[i]).SetNewEntryValues; end; end; if ClientMode then begin if not ClientCommunicator.SaveDatabaseObjects(Self,DatabaseObjectClass) then raise Exception.Create('Server confirmation not received when saving objects to database'); Exit; end else begin // if firebird then use SQL to add new records if Firebird then begin Transaction := TIBTransaction.Create(nil); Transaction.DefaultDatabase := TVFirebirdDatabase; Transaction.StartTransaction; IBQuery := TIBQuery.Create(nil); IBQuery.Database := TVFirebirdDatabase; IBQuery.Transaction := Transaction; try for i := 0 to FObjects.Count - 1 do begin IBQuery.SQL.Clear; IBQuery.SQL.Add(TDatabaseObject(FObjects.Items[i]).InsertSQLStr); IBQuery.ExecSQL; // give other threads a chance if ServerMode then Sleep(0); end; finally IBQuery.Free; if Transaction.InTransaction then Transaction.Commit; Transaction.Free; end; // open table and post objects to table end else begin Query := TQuery.Create(nil); Query.DatabaseName := BDEDatabaseName; Query.SessionName := TVSessionName; try for i := 0 to FObjects.Count - 1 do begin Query.SQL.Clear; Query.SQL.Add(TDatabaseObject(FObjects.Items[i]).InsertSQLStr); Query.ExecSQL; end; finally Query.Free; end; end; end; if ServerMode then begin // update global collections for i := 0 to FObjects.Count - 1 do if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)] <> nil then DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)].Update(TDatabaseObject(FObjects.Items[i])); end else if NotifyWorkstations then begin // notify all workstations to update their global collections for i := 0 to FObjects.Count - 1 do if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)] <> nil then UpdateDatabaseObjectOnLoggedOnWorkstations(DatabaseObjectClass,TDatabaseObject(FObjects.Items[i])); end; end; procedure TDatabaseObjectCollection.DeleteFromDatabase (DatabaseObjectClass : TDatabaseObjectClass; NotifyWorkstations : boolean); var i : integer; SelectionString : string; begin if ClientMode then begin if not ClientCommunicator.DeleteDatabaseObjects(Self,DatabaseObjectClass) then raise Exception.Create('Server confirmation not received when deleting objects from database'); Exit; end else begin SelectionString := ''; for i := 0 to FObjects.Count - 1 do begin if SelectionString = '' then SelectionString := 'WHERE ' + DelimitSQLFieldName('Id') + ' = ' + IntToStr(TDatabaseObject(FObjects.Items[i]).Id) else SelectionString := SelectionString + ' OR ' + DelimitSQLFieldName('Id') + ' = ' + IntToStr(TDatabaseObject(FObjects.Items[i]).Id); end; if SelectionString <> '' then DeleteSomeDatabaseObjects(DatabaseObjectClass,SelectionString); end; if ServerMode then begin // update global collections for i := 0 to FObjects.Count - 1 do if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)] <> nil then DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)].DeleteById(TDatabaseObject(FObjects.Items[i]).Id); end else if NotifyWorkstations then begin // notify all workstations to update their global collections for i := 0 to FObjects.Count - 1 do if DatabaseObjectCollections[ConvertDatabaseObjectClassToInteger(DatabaseObjectClass)] <> nil then DeleteDatabaseObjectFromLoggedOnWorkstations(DatabaseObjectClass,TDatabaseObject(FObjects.Items[i]).Id); end; end; procedure TDatabaseObjectCollection.UpdateDatabase (DatabaseObjectClass : TDatabaseObjectClass); begin // this will update all the objects in the collection to the database // and will acquire a total lock to prevent other changes being made AcquireDatabaseCriticalUpdate; try DeleteFromDatabase(DatabaseObjectClass,false); SaveToDatabase(DatabaseObjectClass,true); finally ReleaseDatabaseCriticalUpdate; end; end; procedure TDatabaseObjectCollection.LoadFromStream (Stream : TStream); var DatabaseObjectClassId : integer; DatabaseObjectClass : TDatabaseObjectClass; DatabaseObject : TDatabaseObject; Count : integer; i : integer; begin FOwned := true; Stream.Read(Count,SizeOf(Count)); for i := 0 to Count - 1 do begin Stream.Read(DatabaseObjectClassId,SizeOf(DatabaseObjectClassId)); DatabaseObjectClass := ConvertIntegerToDatabaseObjectClass(DatabaseObjectClassId); DatabaseObject := DatabaseObjectClass.Create; DatabaseObject.LoadFromStream(Stream); Add(DatabaseObject); end; end; procedure TDatabaseObjectCollection.SaveToStream (Stream : TStream); var i : integer; Count : integer; DatabaseObjectClassId : integer; begin Count := FObjects.Count; Stream.Write(Count,SizeOf(Count)); for i := 0 to Count - 1 do begin DatabaseObjectClassId := ConvertDatabaseObjectClassToInteger(TDatabaseObjectClass(TObject(FObjects[i]).ClassType)); Stream.Write(DatabaseObjectClassId,SizeOf(DatabaseObjectClassId)); TDatabaseObject(FObjects[i]).SaveToStream(Stream); end; end; function TDatabaseObjectCollection.GetIdFromComboBoxDisplayString (ComboBoxDisplayString : string) : int64; var i : integer; begin Result := 0; for i := 0 to FObjects.Count - 1 do if TDatabaseObject(FObjects.Items[i]).ComboBoxDisplayString = ComboBoxDisplayString then begin Result := TDatabaseObject(FObjects.Items[i]).Id; Exit; end; end; function TDatabaseObjectCollection.GetIdFromListBoxDisplayString (ListBoxDisplayString : string) : int64; var i : integer; begin Result := 0; for i := 0 to FObjects.Count - 1 do if TDatabaseObject(FObjects.Items[i]).ListBoxDisplayString = ListBoxDisplayString then begin Result := TDatabaseObject(FObjects.Items[i]).Id; Exit; end; end; procedure TDatabaseObjectCollection.Sort (Compare : TListSortCompare); begin FObjects.Sort(Compare); end; {***** TIdGenerator methods ***************************************************} class function TIdGenerator.TableName : string; begin Result := 'IdGenerator'; end; class procedure TIdGenerator.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('NextId',IdFieldType,0,false); end; class procedure TIdGenerator.UpdateTable; begin // add code to update old table structures here end; procedure TIdGenerator.LoadFromTable (Table : TDataset); begin inherited; NextId := IdFieldValue(Table.FieldByName('NextId')); end; procedure TIdGenerator.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('NextId'),NextId); end; function TIdGenerator.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('NextId'); end; function TIdGenerator.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(NextId); end; procedure TIdGenerator.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(NextId,SizeOf(NextId)); end; procedure TIdGenerator.SaveToStream (Stream : TStream); begin inherited; Stream.Write(NextId,SizeOf(NextId)); end; class function TIdGenerator.FindFormCaption : string; begin Result := 'Find Id Generator'; end; class procedure TIdGenerator.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Next ID' ],[ 10 // Next ID ]); end; class function TIdGenerator.MaintainFormCaption : string; begin Result := 'Maintain Id Generator'; end; function TIdGenerator.MaintainStringGridDrawText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(NextId) else Result := ''; end; function TIdGenerator.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TIdGenerator.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then try NextId := StrToInt64(Value); except // ignore exception end; end; class function TIdGenerator.HeadingString : string; begin Result := 'Id Generator'; end; function TIdGenerator.GenerateId (Increment : integer) : int64; begin Result := NextId; Inc(NextId,Increment); end; {***** TGlobalConfiguration methods *******************************************} class function TGlobalConfiguration.TableName : string; begin Result := 'GlobalConfiguration'; end; class procedure TGlobalConfiguration.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('CompanyName',ftString,50,false); Table.FieldDefs.Add('ExpiryDate',ftString,10,false); Table.FieldDefs.Add('NoOfWorkstations',ftInteger,0,false); Table.FieldDefs.Add('Unlimited',ftInteger,0,false); Table.FieldDefs.Add('Accounts',ftInteger,0,false); Table.FieldDefs.Add('POS',ftInteger,0,false); Table.FieldDefs.Add('Documents',ftInteger,0,false); Table.FieldDefs.Add('RegistrationCode1',ftString,4,false); Table.FieldDefs.Add('RegistrationCode2',ftString,4,false); Table.FieldDefs.Add('RegistrationCode3',ftString,4,false); Table.FieldDefs.Add('RegistrationCode4',ftString,4,false); Table.FieldDefs.Add('PortNumber',ftInteger,0,false); Table.FieldDefs.Add('HTTPPortNumber',ftInteger,0,false); Table.FieldDefs.Add('HTTPPageColor',ftInteger,0,false); Table.FieldDefs.Add('SalesAccAbbrev',ftString,15,false); Table.FieldDefs.Add('BankAccAbbrev',ftString,15,false); Table.FieldDefs.Add('AttachmentDirectory',ftString,200,false); Table.FieldDefs.Add('WebsiteContentDirectory',ftString,200,false); end; class procedure TGlobalConfiguration.UpdateTable; begin // add code to update old table structures here if not FieldExists(TableName,'WebsiteContentDirectory') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'WebsiteContentDirectory',ftString,200,''); ProgressForm.StepIt; ProgressForm.Hide; end; end; procedure TGlobalConfiguration.LoadFromTable (Table : TDataset); begin inherited; CompanyName := TStringField(Table.FieldByName('CompanyName')).Value; ExpiryDate := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('ExpiryDate')).Value); NoOfWorkstations := TIntegerField(Table.FieldByName('NoOfWorkstations')).Value; Unlimited := TIntegerField(Table.FieldByName('Unlimited')).Value <> 0; Accounts := TIntegerField(Table.FieldByName('Accounts')).Value <> 0; POS := TIntegerField(Table.FieldByName('POS')).Value <> 0; Documents := TIntegerField(Table.FieldByName('Documents')).Value <> 0; RegistrationCode1 := TStringField(Table.FieldByName('RegistrationCode1')).Value; RegistrationCode2 := TStringField(Table.FieldByName('RegistrationCode2')).Value; RegistrationCode3 := TStringField(Table.FieldByName('RegistrationCode3')).Value; RegistrationCode4 := TStringField(Table.FieldByName('RegistrationCode4')).Value; PortNumber := TIntegerField(Table.FieldByName('PortNumber')).Value; HTTPPortNumber := TIntegerField(Table.FieldByName('HTTPPortNumber')).Value; HTTPPageColor := TIntegerField(Table.FieldByName('HTTPPageColor')).Value; SalesAccountAbbreviation := TStringField(Table.FieldByName('SalesAccAbbrev')).Value; BankAccountAbbreviation := TStringField(Table.FieldByName('BankAccAbbrev')).Value; AttachmentDirectory := TStringField(Table.FieldByName('AttachmentDirectory')).Value; // initialise this if necessary if AttachmentDirectory = '' then begin if Firebird then AttachmentDirectory := ExeDirectory else AttachmentDirectory := DatabaseDirectory; end; WebsiteContentDirectory := TStringField(Table.FieldByName('WebsiteContentDirectory')).Value; end; procedure TGlobalConfiguration.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('CompanyName')).Value := CompanyName; TStringField(Table.FieldByName('ExpiryDate')).Value := ConvertDateToDatabaseString(ExpiryDate); TIntegerField(Table.FieldByName('NoOfWorkstations')).Value := NoOfWorkstations; TIntegerField(Table.FieldByName('Unlimited')).Value := integer(Unlimited); TIntegerField(Table.FieldByName('Accounts')).Value := integer(Accounts); TIntegerField(Table.FieldByName('POS')).Value := integer(POS); TIntegerField(Table.FieldByName('Documents')).Value := integer(Documents); TStringField(Table.FieldByName('RegistrationCode1')).Value := RegistrationCode1; TStringField(Table.FieldByName('RegistrationCode2')).Value := RegistrationCode2; TStringField(Table.FieldByName('RegistrationCode3')).Value := RegistrationCode3; TStringField(Table.FieldByName('RegistrationCode4')).Value := RegistrationCode4; TIntegerField(Table.FieldByName('PortNumber')).Value := PortNumber; TIntegerField(Table.FieldByName('HTTPPortNumber')).Value := HTTPPortNumber; TIntegerField(Table.FieldByName('HTTPPageColor')).Value := HTTPPageColor; TStringField(Table.FieldByName('SalesAccAbbrev')).Value := SalesAccountAbbreviation; TStringField(Table.FieldByName('BankAccAbbrev')).Value := BankAccountAbbreviation; TStringField(Table.FieldByName('AttachmentDirectory')).Value := AttachmentDirectory; TStringField(Table.FieldByName('WebsiteContentDirectory')).Value := WebsiteContentDirectory; end; function TGlobalConfiguration.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('CompanyName'); Result := Result + ',' + DelimitSQLFieldName('ExpiryDate'); Result := Result + ',' + DelimitSQLFieldName('NoOfWorkstations'); Result := Result + ',' + DelimitSQLFieldName('Unlimited'); Result := Result + ',' + DelimitSQLFieldName('Accounts'); Result := Result + ',' + DelimitSQLFieldName('POS'); Result := Result + ',' + DelimitSQLFieldName('Documents'); Result := Result + ',' + DelimitSQLFieldName('RegistrationCode1'); Result := Result + ',' + DelimitSQLFieldName('RegistrationCode2'); Result := Result + ',' + DelimitSQLFieldName('RegistrationCode3'); Result := Result + ',' + DelimitSQLFieldName('RegistrationCode4'); Result := Result + ',' + DelimitSQLFieldName('PortNumber'); Result := Result + ',' + DelimitSQLFieldName('HTTPPortNumber'); Result := Result + ',' + DelimitSQLFieldName('HTTPPageColor'); Result := Result + ',' + DelimitSQLFieldName('SalesAccAbbrev'); Result := Result + ',' + DelimitSQLFieldName('BankAccAbbrev'); Result := Result + ',' + DelimitSQLFieldName('AttachmentDirectory'); Result := Result + ',' + DelimitSQLFieldName('WebsiteContentDirectory'); end; function TGlobalConfiguration.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(CompanyName,50); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(ExpiryDate),10); Result := Result + ',' + IntToStr(NoOfWorkstations); Result := Result + ',' + IntToStr(integer(Unlimited)); Result := Result + ',' + IntToStr(integer(Accounts)); Result := Result + ',' + IntToStr(integer(POS)); Result := Result + ',' + IntToStr(integer(Documents)); Result := Result + ',' + DelimitSQLStringValue(RegistrationCode1,4); Result := Result + ',' + DelimitSQLStringValue(RegistrationCode2,4); Result := Result + ',' + DelimitSQLStringValue(RegistrationCode3,4); Result := Result + ',' + DelimitSQLStringValue(RegistrationCode4,4); Result := Result + ',' + IntToStr(PortNumber); Result := Result + ',' + IntToStr(HTTPPortNumber); Result := Result + ',' + IntToStr(HTTPPageColor); Result := Result + ',' + DelimitSQLStringValue(SalesAccountAbbreviation,15); Result := Result + ',' + DelimitSQLStringValue(BankAccountAbbreviation,15); Result := Result + ',' + DelimitSQLStringValue(AttachmentDirectory,200); Result := Result + ',' + DelimitSQLStringValue(WebsiteContentDirectory,200); end; procedure TGlobalConfiguration.LoadFromStream (Stream : TStream); begin inherited; CompanyName := ReadStrFromStream(Stream); Stream.Read(ExpiryDate,SizeOf(ExpiryDate)); Stream.Read(NoOfWorkstations,SizeOf(NoOfWorkstations)); Stream.Read(Unlimited,SizeOf(Unlimited)); Stream.Read(Accounts,SizeOf(Accounts)); Stream.Read(POS,SizeOf(POS)); Stream.Read(Documents,SizeOf(Documents)); RegistrationCode1 := ReadStrFromStream(Stream); RegistrationCode2 := ReadStrFromStream(Stream); RegistrationCode3 := ReadStrFromStream(Stream); RegistrationCode4 := ReadStrFromStream(Stream); Stream.Read(PortNumber,SizeOf(PortNumber)); Stream.Read(HTTPPortNumber,SizeOf(HTTPPortNumber)); Stream.Read(HTTPPageColor,SizeOf(HTTPPageColor)); SalesAccountAbbreviation := ReadStrFromStream(Stream); BankAccountAbbreviation := ReadStrFromStream(Stream); AttachmentDirectory := ReadStrFromStream(Stream); WebsiteContentDirectory := ReadStrFromStream(Stream); end; procedure TGlobalConfiguration.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(CompanyName,Stream); Stream.Write(ExpiryDate,SizeOf(ExpiryDate)); Stream.Write(NoOfWorkstations,SizeOf(NoOfWorkstations)); Stream.Write(Unlimited,SizeOf(Unlimited)); Stream.Write(Accounts,SizeOf(Accounts)); Stream.Write(POS,SizeOf(POS)); Stream.Write(Documents,SizeOf(Documents)); WriteStrToStream(RegistrationCode1,Stream); WriteStrToStream(RegistrationCode2,Stream); WriteStrToStream(RegistrationCode3,Stream); WriteStrToStream(RegistrationCode4,Stream); Stream.Write(PortNumber,SizeOf(PortNumber)); Stream.Write(HTTPPortNumber,SizeOf(HTTPPortNumber)); Stream.Write(HTTPPageColor,SizeOf(HTTPPageColor)); WriteStrToStream(SalesAccountAbbreviation,Stream); WriteStrToStream(BankAccountAbbreviation,Stream); WriteStrToStream(AttachmentDirectory,Stream); WriteStrToStream(WebsiteContentDirectory,Stream); end; destructor TGlobalConfiguration.Destroy; begin inherited; end; class function TGlobalConfiguration.FindFormCaption : string; begin Result := 'Find Global Configuration'; end; class procedure TGlobalConfiguration.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Company Name', 'Expiry Date', 'No Of WS', 'Unlimited', 'Accounts', 'POS', 'Documents', 'Reg Code 1', 'Reg Code 2', 'Reg Code 3', 'Reg Code 4', 'Port' ],[ 25, // Company Name 12, // Expiry Date 10, // No Of WS 10, // Unlimited 10, // Accounts 10, // POS 10, // Documents 12, // Reg Code 1 12, // Reg Code 2 12, // Reg Code 3 12, // Reg Code 4 10 // Port ]); end; class function TGlobalConfiguration.MaintainFormCaption : string; begin Result := 'Maintain Global Configuration'; end; function TGlobalConfiguration.MaintainStringGridDrawText (ACol : integer) : string; begin if ACol = 0 then Result := CompanyName else if ACol = 1 then Result := FormatDate(ExpiryDate) else if ACol = 2 then Result := IntToStr(NoOfWorkstations) else if ACol = 3 then Result := FormatBoolean(Unlimited) else if ACol = 4 then Result := FormatBoolean(Accounts) else if ACol = 5 then Result := FormatBoolean(POS) else if ACol = 6 then Result := FormatBoolean(Documents) else if ACol = 7 then Result := RegistrationCode1 else if ACol = 8 then Result := RegistrationCode2 else if ACol = 9 then Result := RegistrationCode3 else if ACol = 10 then Result := RegistrationCode4 else if ACol = 11 then Result := IntToStr(PortNumber) else Result := ''; end; function TGlobalConfiguration.MaintainStringGridGetEditText (ACol : integer) : string; begin if ACol = 1 then Result := ConvertDateToDatabaseString(ExpiryDate) else Result := MaintainStringGridDrawText(ACol); end; procedure TGlobalConfiguration.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then CompanyName := Value else if ACol = 1 then ExpiryDate := ConvertDatabaseStringToDate(Value) else if ACol = 2 then NoOfWorkstations := StrToIntDef(Value,0) else if ACol = 3 then Unlimited := (UpperCase(Value) = 'Y') else if ACol = 4 then Accounts := (UpperCase(Value) = 'Y') else if ACol = 5 then POS := (UpperCase(Value) = 'Y') else if ACol = 6 then Documents := (UpperCase(Value) = 'Y') else if ACol = 7 then RegistrationCode1 := Value else if ACol = 8 then RegistrationCode2 := Value else if ACol = 9 then RegistrationCode3 := Value else if ACol = 10 then RegistrationCode4 := Value else if ACol = 11 then PortNumber := StrToIntDef(Value,DefaultPortNumber); end; class function TGlobalConfiguration.HeadingString : string; begin Result := 'Global Configuration'; end; procedure TGlobalConfiguration.SetDefaults; begin PortNumber := DefaultPortNumber; HTTPPortNumber := DefaultHTTPPortNumber; CompanyName := DefaultCompanyName; ExpiryDate := Date + DefaultExpiryPeriod; NoOfWorkstations := DefaultNoOfWorkstations; Unlimited := false; {$IFDEF ACCOUNTS} Accounts := true; {$ELSE} Accounts := false; {$ENDIF} {$IFDEF POS} Pos := true; {$ELSE} Pos := false; {$ENDIF} {$IFDEF DOCUMENTS} Documents := true; {$ELSE} Documents := false; {$ENDIF} GenerateRegistrationCode (CompanyName, ExpiryDate, NoOfWorkstations, Unlimited, Accounts, POS, Documents, RegistrationCode1, RegistrationCode2, RegistrationCode3, RegistrationCode4); HTTPPageColor := DefaultControlColor; SalesAccountAbbreviation := 'SLS'; BankAccountAbbreviation := 'BNK'; if Firebird then AttachmentDirectory := ExeDirectory else AttachmentDirectory := DatabaseDirectory; WebsiteContentDirectory := ''; end; function TGlobalConfiguration.CheckRegistrationCode : boolean; begin Result := GeneralUtilities.CheckRegistrationCode (CompanyName, ExpiryDate, NoOfWorkstations, Unlimited, Accounts, POS, Documents, RegistrationCode1, RegistrationCode2, RegistrationCode3, RegistrationCode4); end; {***** TWorkstationConfiguration methods **************************************} class function TWorkstationConfiguration.TableName : string; begin Result := 'WorkstationConfiguration'; end; class procedure TWorkstationConfiguration.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('ComputerName',ftString,20,false); Table.FieldDefs.Add('LoggedOn',ftInteger,0,false); Table.FieldDefs.Add('IPAddress',ftString,20,false); Table.FieldDefs.Add('ControlColor',ftInteger,0,false); Table.FieldDefs.Add('MaximiseOnStart',ftInteger,0,false); Table.FieldDefs.Add('CacheAttachments',ftInteger,0,false); Table.FieldDefs.Add('ReportsPicture',ftString,100,false); Table.FieldDefs.Add('QRPFileDirectory',ftString,200,false); Table.FieldDefs.Add('AttachmentDirectory',ftString,200,false); Table.FieldDefs.Add('CompanyId',IdFieldType,0,false); Table.FieldDefs.Add('RecentEntryFirst',ftInteger,0,false); Table.FieldDefs.Add('UseBeginPeriod',ftInteger,0,false); Table.FieldDefs.Add('BeginPeriodDate',ftString,10,false); Table.FieldDefs.Add('UseEndPeriod',ftInteger,0,false); Table.FieldDefs.Add('EndPeriodDate',ftString,10,false); Table.FieldDefs.Add('NoOfDaysSales',ftInteger,0,false); Table.FieldDefs.Add('SalesReportParams',ftString,200,false); end; class procedure TWorkstationConfiguration.UpdateTable; begin // add code to update old table structures here if not FieldExists(TableName,'AttachmentDirectory') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'AttachmentDirectory',ftString,200,''); AddFieldToTable(TableName,'CacheAttachments',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; if not FieldExists(TableName,'NoOfDaysSales') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'NoOfDaysSales',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; // add code to update old table structures here if not FieldExists(TableName,'SalesReportParams') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'SalesReportParams',ftString,200,''); ProgressForm.StepIt; ProgressForm.Hide; end; end; procedure TWorkstationConfiguration.LoadFromTable (Table : TDataset); begin inherited; ComputerName := TStringField(Table.FieldByName('ComputerName')).Value; LoggedOn := TIntegerField(Table.FieldByName('LoggedOn')).Value <> 0; IPAddress := TStringField(Table.FieldByName('IPAddress')).Value; ControlColor := TIntegerField(Table.FieldByName('ControlColor')).Value; MaximiseOnStart := TIntegerField(Table.FieldByName('MaximiseOnStart')).Value <> 0; CacheAttachments := TIntegerField(Table.FieldByName('CacheAttachments')).Value <> 0; ReportsPicture := TStringField(Table.FieldByName('ReportsPicture')).Value; QRPFileDirectory := TStringField(Table.FieldByName('QRPFileDirectory')).Value; AttachmentDirectory := TStringField(Table.FieldByName('AttachmentDirectory')).Value; CompanyId := IdFieldValue(Table.FieldByName('CompanyId')); RecentEntryFirst := TIntegerField(Table.FieldByName('RecentEntryFirst')).Value <> 0; UseBeginPeriod := TIntegerField(Table.FieldByName('UseBeginPeriod')).Value <> 0; BeginPeriodDate := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('BeginPeriodDate')).Value); UseEndPeriod := TIntegerField(Table.FieldByName('UseEndPeriod')).Value <> 0; EndPeriodDate := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('EndPeriodDate')).Value); NoOfDaysSales := TIntegerField(Table.FieldByName('NoOfDaysSales')).Value; SalesReportParameters.SetText(TStringField(Table.FieldByName('SalesReportParams')).Value); end; procedure TWorkstationConfiguration.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('ComputerName')).Value := ComputerName; TIntegerField(Table.FieldByName('LoggedOn')).Value := integer(LoggedOn); TStringField(Table.FieldByName('IPAddress')).Value := IPAddress; TIntegerField(Table.FieldByName('ControlColor')).Value := ControlColor; TIntegerField(Table.FieldByName('MaximiseOnStart')).Value := integer(MaximiseOnStart); TIntegerField(Table.FieldByName('CacheAttachments')).Value := integer(CacheAttachments); TStringField(Table.FieldByName('ReportsPicture')).Value := ReportsPicture; TStringField(Table.FieldByName('QRPFileDirectory')).Value := QRPFileDirectory; TStringField(Table.FieldByName('AttachmentDirectory')).Value := AttachmentDirectory; SetIdFieldValue(Table.FieldByName('CompanyId'),CompanyId); TIntegerField(Table.FieldByName('RecentEntryFirst')).Value := integer(RecentEntryFirst); TIntegerField(Table.FieldByName('UseBeginPeriod')).Value := integer(UseBeginPeriod); TStringField(Table.FieldByName('BeginPeriodDate')).Value := ConvertDateToDatabaseString(BeginPeriodDate); TIntegerField(Table.FieldByName('UseEndPeriod')).Value := integer(UseEndPeriod); TStringField(Table.FieldByName('EndPeriodDate')).Value := ConvertDateToDatabaseString(EndPeriodDate); TIntegerField(Table.FieldByName('NoOfDaysSales')).Value := NoOfDaysSales; TStringField(Table.FieldByName('SalesReportParams')).Value := SalesReportParameters.Text; end; function TWorkstationConfiguration.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('ComputerName'); Result := Result + ',' + DelimitSQLFieldName('LoggedOn'); Result := Result + ',' + DelimitSQLFieldName('IPAddress'); Result := Result + ',' + DelimitSQLFieldName('ControlColor'); Result := Result + ',' + DelimitSQLFieldName('MaximiseOnStart'); Result := Result + ',' + DelimitSQLFieldName('CacheAttachments'); Result := Result + ',' + DelimitSQLFieldName('ReportsPicture'); Result := Result + ',' + DelimitSQLFieldName('QRPFileDirectory'); Result := Result + ',' + DelimitSQLFieldName('AttachmentDirectory'); Result := Result + ',' + DelimitSQLFieldName('CompanyId'); Result := Result + ',' + DelimitSQLFieldName('RecentEntryFirst'); Result := Result + ',' + DelimitSQLFieldName('UseBeginPeriod'); Result := Result + ',' + DelimitSQLFieldName('BeginPeriodDate'); Result := Result + ',' + DelimitSQLFieldName('UseEndPeriod'); Result := Result + ',' + DelimitSQLFieldName('EndPeriodDate'); Result := Result + ',' + DelimitSQLFieldName('NoOfDaysSales'); Result := Result + ',' + DelimitSQLFieldName('SalesReportParams'); end; function TWorkstationConfiguration.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(ComputerName,20); Result := Result + ',' + IntToStr(integer(LoggedOn)); Result := Result + ',' + DelimitSQLStringValue(IPAddress,20); Result := Result + ',' + IntToStr(ControlColor); Result := Result + ',' + IntToStr(integer(MaximiseOnStart)); Result := Result + ',' + IntToStr(integer(CacheAttachments)); Result := Result + ',' + DelimitSQLStringValue(ReportsPicture,100); Result := Result + ',' + DelimitSQLStringValue(QRPFileDirectory,200); Result := Result + ',' + DelimitSQLStringValue(AttachmentDirectory,200); Result := Result + ',' + IntToStr(CompanyId); Result := Result + ',' + IntToStr(integer(RecentEntryFirst)); Result := Result + ',' + IntToStr(integer(UseBeginPeriod)); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(BeginPeriodDate),10); Result := Result + ',' + IntToStr(integer(UseEndPeriod)); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(EndPeriodDate),10); Result := Result + ',' + IntToStr(NoOfDaysSales); Result := Result + ',' + DelimitSQLStringValue(SalesReportParameters.Text,200); end; procedure TWorkstationConfiguration.LoadFromStream (Stream : TStream); begin inherited; ComputerName := ReadStrFromStream(Stream); Stream.Read(LoggedOn,SizeOf(LoggedOn)); IPAddress := ReadStrFromStream(Stream); Stream.Read(ControlColor,SizeOf(ControlColor)); Stream.Read(MaximiseOnStart,SizeOf(MaximiseOnStart)); Stream.Read(CacheAttachments,SizeOf(CacheAttachments)); ReportsPicture := ReadStrFromStream(Stream); QRPFileDirectory := ReadStrFromStream(Stream); AttachmentDirectory := ReadStrFromStream(Stream); Stream.Read(CompanyId,SizeOf(CompanyId)); Stream.Read(RecentEntryFirst,SizeOf(RecentEntryFirst)); Stream.Read(UseBeginPeriod,SizeOf(UseBeginPeriod)); Stream.Read(BeginPeriodDate,SizeOf(BeginPeriodDate)); Stream.Read(UseEndPeriod,SizeOf(UseEndPeriod)); Stream.Read(EndPeriodDate,SizeOf(EndPeriodDate)); Stream.Read(NoOfDaysSales,SizeOf(NoOfDaysSales)); SalesReportParameters.SetText(ReadStrFromStream(Stream)); end; procedure TWorkstationConfiguration.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(ComputerName,Stream); Stream.Write(LoggedOn,SizeOf(LoggedOn)); WriteStrToStream(IPAddress,Stream); Stream.Write(ControlColor,SizeOf(ControlColor)); Stream.Write(MaximiseOnStart,SizeOf(MaximiseOnStart)); Stream.Write(CacheAttachments,SizeOf(CacheAttachments)); WriteStrToStream(ReportsPicture,Stream); WriteStrToStream(QRPFileDirectory,Stream); WriteStrToStream(AttachmentDirectory,Stream); Stream.Write(CompanyId,SizeOf(CompanyId)); Stream.Write(RecentEntryFirst,SizeOf(RecentEntryFirst)); Stream.Write(UseBeginPeriod,SizeOf(UseBeginPeriod)); Stream.Write(BeginPeriodDate,SizeOf(BeginPeriodDate)); Stream.Write(UseEndPeriod,SizeOf(UseEndPeriod)); Stream.Write(EndPeriodDate,SizeOf(EndPeriodDate)); Stream.Write(NoOfDaysSales,SizeOf(NoOfDaysSales)); WriteStrToStream(SalesReportParameters.Text,Stream); end; constructor TWorkstationConfiguration.Create; begin inherited; ReportLayouts := TNotesManager.Create(Self,1); end; destructor TWorkstationConfiguration.Destroy; begin FSalesReportParameters.Free; ReportLayouts.Free; inherited; end; procedure TWorkstationConfiguration.SaveDetailsToDatabase; begin if ReportLayouts.Changed then ReportLayouts.Save; end; procedure TWorkstationConfiguration.DeleteDetailsFromDatabase (BeforeSave : boolean); begin if BeforeSave and (not ReportLayouts.Changed) then // do nothing else ReportLayouts.Delete; end; procedure TWorkstationConfiguration.LoadDetailsFromStream (Stream : TStream); begin ReportLayouts.LoadFromStream(Stream); end; procedure TWorkstationConfiguration.SaveDetailsToStream (Stream : TStream); begin ReportLayouts.SaveToStream(Stream); end; class function TWorkstationConfiguration.FindFormCaption : string; begin Result := 'Find Workstation Configuration'; end; class procedure TWorkstationConfiguration.SetupMaintainStringGrid (StringGrid : TStringGrid); begin if not ServerMode then SetUpStringGrid(StringGrid,[ 'Computer Name', 'IP Address', 'Logged On' ],[ 20, // Computer Name 20, // IP Address 15 // Logged On ]) else SetUpStringGrid(StringGrid,[ 'Computer Name', 'IP Address' ],[ 20, // Computer Name 20 // IP Address ]); end; class function TWorkstationConfiguration.MaintainFormCaption : string; begin Result := 'Maintain Workstation Configurations'; end; function TWorkstationConfiguration.MaintainStringGridDrawText (ACol : integer) : string; begin if ACol = 0 then Result := ComputerName else if ACol = 1 then Result := IPAddress else if ACol = 2 then Result := FormatBoolean(LoggedOn) else Result := ''; end; function TWorkstationConfiguration.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TWorkstationConfiguration.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then ComputerName := Value else if ACol = 1 then IPAddress := Value else if ACol = 2 then LoggedOn := (UpperCase(Value) = 'Y'); end; function TWorkstationConfiguration.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; if ACol = 2 then begin LoggedOn := not LoggedOn; Result := true; end; end; class function TWorkstationConfiguration.HeadingString : string; begin Result := 'Workstation Configurations'; end; class function TWorkstationConfiguration.ColumnHeadingsString : string; begin Result := 'Computer Name IP Address'; end; function TWorkstationConfiguration.DetailsString : string; begin Result := Format('%-20s %-20s',[ComputerName,IPAddress]); end; function TWorkstationConfiguration.SalesReportParameters : TSalesReportParameters; begin if FSalesReportParameters = nil then FSalesReportParameters := TSalesReportParameters.Create; Result := FSalesReportParameters; end; procedure TWorkstationConfiguration.SetDefaults; begin if ClientMode then begin ComputerName := Globals.ClientUserName; LoggedOn := false; IPAddress := '' end else begin ComputerName := Globals.ComputerName; LoggedOn := true; IPAddress := ChooseIPAddress; end; ControlColor := DefaultControlColor; MaximiseOnStart := false; CacheAttachments := false; QRPFileDirectory := ExeDirectory; AttachmentDirectory := ExeDirectory; RecentEntryFirst := false; BeginPeriodDate := Date; EndPeriodDate := Date; NoOfDaysSales := 7; end; function TWorkstationConfiguration.AccountingPeriodString : string; begin if (not UseBeginPeriod) and (not UseEndPeriod) then Result := 'All entries' else if (UseBeginPeriod) and (not UseEndPeriod) then Result := FormatDate(BeginPeriodDate) + ' ----> ' else if (not UseBeginPeriod) and (UseEndPeriod) then Result := ' ----> ' + FormatDate(EndPeriodDate) else Result := FormatDate(BeginPeriodDate) + ' ----> ' + FormatDate(EndPeriodDate); end; {***** TAttachment methods ****************************************************} class function TAttachment.TableName : string; begin Result := 'Attachment'; end; class procedure TAttachment.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('ObjectId',IdFieldType,0,false); Table.FieldDefs.Add('FieldNumber',ftInteger,0,false); Table.FieldDefs.Add('FileName',ftString,100,false); Table.FieldDefs.Add('FileAttribute',ftInteger,0,false); Table.FieldDefs.Add('FileDate',ftString,10,false); Table.FieldDefs.Add('FileTime',ftString,10,false); Table.FieldDefs.Add('FileSize',ftInteger,0,false); if Firebird then TIBTable(Table).IndexDefs.Add(TableName + 'ObjectId','ObjectId',[]); end; class procedure TAttachment.UpdateTable; begin // add code to update old table structures here end; procedure TAttachment.LoadFromTable (Table : TDataset); begin inherited; ObjectId := IdFieldValue(Table.FieldByName('ObjectId')); FieldNumber := TIntegerField(Table.FieldByName('FieldNumber')).Value; FileName := TStringField(Table.FieldByName('FileName')).Value; FileAttribute := TIntegerField(Table.FieldByName('FileAttribute')).Value; FileDateTime := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('FileDate')).Value) + ConvertDatabaseStringToTime(TStringField(Table.FieldByName('FileTime')).Value); FileSize := TIntegerField(Table.FieldByName('FileSize')).Value; end; procedure TAttachment.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('ObjectId'),ObjectId); TIntegerField(Table.FieldByName('FieldNumber')).Value := FieldNumber; TStringField(Table.FieldByName('FileName')).Value := FileName; TIntegerField(Table.FieldByName('FileAttribute')).Value := FileAttribute; TStringField(Table.FieldByName('FileDate')).Value := ConvertDateToDatabaseString(FileDateTime); TStringField(Table.FieldByName('FileTime')).Value := ConvertTimeToDatabaseString(FileDateTime); TIntegerField(Table.FieldByName('FileSize')).Value := FileSize; end; function TAttachment.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('ObjectId'); Result := Result + ',' + DelimitSQLFieldName('FieldNumber'); Result := Result + ',' + DelimitSQLFieldName('FileName'); Result := Result + ',' + DelimitSQLFieldName('FileAttribute'); Result := Result + ',' + DelimitSQLFieldName('FileDate'); Result := Result + ',' + DelimitSQLFieldName('FileTime'); Result := Result + ',' + DelimitSQLFieldName('FileSize'); end; function TAttachment.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(ObjectId); Result := Result + ',' + IntToStr(FieldNumber); Result := Result + ',' + DelimitSQLStringValue(FileName,100); Result := Result + ',' + IntToStr(FileAttribute); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(FileDateTime),10); Result := Result + ',' + DelimitSQLStringValue(ConvertTimeToDatabaseString(FileDateTime),10); Result := Result + ',' + IntToStr(FileSize); end; procedure TAttachment.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(ObjectId,SizeOf(ObjectId)); Stream.Read(FieldNumber,SizeOf(FieldNumber)); FileName := ReadStrFromStream(Stream); Stream.Read(FileAttribute,SizeOf(FileAttribute)); Stream.Read(FileDateTime,SizeOf(FileDateTime)); Stream.Read(FileSize,SizeOf(FileSize)); end; procedure TAttachment.SaveToStream (Stream : TStream); begin inherited; Stream.Write(ObjectId,SizeOf(ObjectId)); Stream.Write(FieldNumber,SizeOf(FieldNumber)); WriteStrToStream(FileName,Stream); Stream.Write(FileAttribute,SizeOf(FileAttribute)); Stream.Write(FileDateTime,SizeOf(FileDateTime)); Stream.Write(FileSize,SizeOf(FileSize)); end; procedure TAttachment.SaveDetailsToDatabase; begin SaveFileContents; end; procedure TAttachment.DeleteDetailsFromDatabase (BeforeSave : boolean); begin if BeforeSave then // do nothing else DeleteFileContents; end; procedure TAttachment.LoadDetailsFromStream (Stream : TStream); begin FFileContents := DecompressString(ReadStrFromStream(Stream)); end; procedure TAttachment.SaveDetailsToStream (Stream : TStream); begin WriteStrToStream(CompressString(FFileContents),Stream); end; class function TAttachment.FindFormCaption : string; begin Result := 'Find Attachment'; end; class procedure TAttachment.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'ID', 'File Name', 'File Size', 'Attribute', 'Date', 'Time' ],[ 10, // ID 40, // File Name 12, // File Size 10, // Attribute 12, // Date 12 // Time ]); end; class function TAttachment.MaintainFormCaption : string; begin Result := 'Maintain Attachments'; end; function TAttachment.MaintainStringGridDrawText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(Id) else if ACol = 1 then Result := FileName else if ACol = 2 then Result := IntToStr(FileSize) else if ACol = 3 then Result := IntToStr(FileAttribute) else if ACol = 4 then Result := FormatDate(Trunc(FileDateTime)) else if ACol = 5 then Result := FormatTime(FileDateTime) else Result := ''; end; function TAttachment.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TAttachment.MaintainStringGridSetEditText (ACol : integer; Value : string); begin end; class function TAttachment.HeadingString : string; begin Result := 'Attachments'; end; class function TAttachment.ColumnHeadingsString : string; begin Result := (inherited ColumnHeadingsString) + 'Obj ID Fld No File Name'; end; function TAttachment.DetailsString : string; begin Result := (inherited DetailsString) + Format('%-7d %-7d %-80s ',[ObjectId,FieldNumber,FileName]); end; procedure TAttachment.DeleteFileContents; begin AttachmentCacheManager.DeleteFileContents(Id); FileSize := 0; end; procedure TAttachment.SaveFileContents; begin if FFileContents <> '' then AttachmentCacheManager.SaveFileContents(Id,FFileContents); end; procedure TAttachment.SaveWorkstationFileContents; begin if FFileContents <> '' then AttachmentCacheManager.SaveWorkstationFileContents(Id,FFileContents); end; function TAttachment.FileContents : string; begin if FFileContents = '' then FFileContents := AttachmentCacheManager.GetFileContents(Id); Result := FFileContents; end; procedure TAttachment.SetFileContents (Str : string); begin FFileContents := Str; FileSize := Length(Str); end; {***** TNote methods *************************************************************} class function TNote.TableName : string; begin Result := 'Note'; end; class procedure TNote.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('ObjectId',IdFieldType,0,false); Table.FieldDefs.Add('FieldNumber',ftInteger,0,false); Table.FieldDefs.Add('SequenceNumber',ftInteger,0,false); Table.FieldDefs.Add('Text',ftString,NoteTextLength,false); if Firebird then TIBTable(Table).IndexDefs.Add(TableName + 'ObjectId','ObjectId',[]); end; class procedure TNote.UpdateTable; begin // add code to update old table structures here end; procedure TNote.LoadFromTable (Table : TDataset); begin inherited; ObjectId := IdFieldValue(Table.FieldByName('ObjectId')); FieldNumber := TIntegerField(Table.FieldByName('FieldNumber')).Value; SequenceNumber := TIntegerField(Table.FieldByName('SequenceNumber')).Value; Text := TStringField(Table.FieldByName('Text')).Value; end; procedure TNote.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('ObjectId'),ObjectId); TIntegerField(Table.FieldByName('FieldNumber')).Value := FieldNumber; TIntegerField(Table.FieldByName('SequenceNumber')).Value := SequenceNumber; TStringField(Table.FieldByName('Text')).Value := Text; end; function TNote.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('ObjectId'); Result := Result + ',' + DelimitSQLFieldName('FieldNumber'); Result := Result + ',' + DelimitSQLFieldName('SequenceNumber'); Result := Result + ',' + DelimitSQLFieldName('Text'); end; function TNote.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(ObjectId); Result := Result + ',' + IntToStr(FieldNumber); Result := Result + ',' + IntToStr(SequenceNumber); Result := Result + ',' + DelimitSQLStringValue(Text,NoteTextLength); end; procedure TNote.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(ObjectId,SizeOf(ObjectId)); Stream.Read(FieldNumber,SizeOf(FieldNumber)); Stream.Read(SequenceNumber,SizeOf(SequenceNumber)); Text := ReadStrFromStream(Stream); end; procedure TNote.SaveToStream (Stream : TStream); begin inherited; Stream.Write(ObjectId,SizeOf(ObjectId)); Stream.Write(FieldNumber,SizeOf(FieldNumber)); Stream.Write(SequenceNumber,SizeOf(SequenceNumber)); WriteStrToStream(Text,Stream); end; class function TNote.FindFormCaption : string; begin Result := 'Find Note'; end; class procedure TNote.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Obj ID', 'Field', 'Seq', 'Text' ],[ 10, // Obj ID 10, // Field 10, // Seq 60 // Text ]); end; class function TNote.MaintainFormCaption : string; begin Result := 'Maintain Notes'; end; function TNote.MaintainStringGridDrawText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(ObjectId) else if ACol = 1 then Result := IntToStr(FieldNumber) else if ACol = 2 then Result := IntToStr(SequenceNumber) else if ACol = 3 then Result := Text else Result := ''; end; function TNote.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TNote.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then begin try ObjectId := StrToInt64(Value); except // ignore exception end; end else if ACol = 1 then begin try FieldNumber := StrToInt(Value); except // ignore exception end; end else if ACol = 2 then begin try SequenceNumber := StrToInt(Value); except // ignore exception end; end else if ACol = 3 then Text := Value; end; class function TNote.HeadingString : string; begin Result := 'Notes'; end; class function TNote.ColumnHeadingsString : string; begin Result := (inherited ColumnHeadingsString) + 'Obj ID Fld No Seq No Text'; end; function TNote.DetailsString : string; begin Result := (inherited DetailsString) + Format('%-7d %-7d %-7d %80s ',[ObjectId,FieldNumber,SequenceNumber,Text]); end; {***** TUser methods **********************************************************} class function TUser.TableName : string; begin Result := 'User'; end; class procedure TUser.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,20,false); Table.FieldDefs.Add('Password',ftString,20,false); Table.FieldDefs.Add('Administrator',ftInteger,0,false); end; class procedure TUser.UpdateTable; begin // add code to update old table structures here if not FieldExists(TableName,'Administrator') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'Administrator',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; end; procedure TUser.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Password := TStringField(Table.FieldByName('Password')).Value; Administrator := TIntegerField(Table.FieldByName('Administrator')).Value <> 0; end; procedure TUser.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Password')).Value := Password; TIntegerField(Table.FieldByName('Administrator')).Value := integer(Administrator); end; function TUser.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); if Firebird then Result := Result + ',' + DelimitSQLFieldName('Password') else Result := Result + ',"User"."Password"'; Result := Result + ',' + DelimitSQLFieldName('Administrator'); end; function TUser.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,20); Result := Result + ',' + DelimitSQLStringValue(Password,20); Result := Result + ',' + IntToStr(integer(Administrator)); end; procedure TUser.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Password := ReadStrFromStream(Stream); Stream.Read(Administrator,SizeOf(Administrator)); end; procedure TUser.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(Password,Stream); Stream.Write(Administrator,SizeOf(Administrator)); end; procedure TUser.SetNewEntryValues; begin Administrator := false; end; class procedure TUser.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Name', 'Password', 'Administrator' ],[ 30, // Name 30, // Password 30 ]); end; class function TUser.FindFormCaption : string; begin Result := 'Find User'; end; class function TUser.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Name'); end; function TUser.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then begin if Length(Password) > 0 then Result := '**********' else Result := ''; end else if ACol = 2 then begin Result := FormatBoolean(Administrator) end else Result := ''; end; class procedure TUser.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Name', 'Password', 'Administrator' ],[ 20, // Name 20, // Password 20 // Administrator ]); end; class function TUser.MaintainFormCaption : string; begin Result := 'Maintain Users'; end; class function TUser.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TUser.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TUser.MaintainStringGridGetEditText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then Result := UnencryptedPassword else if ACol = 2 then Result := FormatBoolean(Administrator) else Result := ''; end; procedure TUser.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Name := UpperCase(Value) else if ACol = 1 then Password := ReplaceNullsEtc(Encrypt(LowerCase(Value),UserPasswordEncryptRandSeed)) else if ACol = 2 then Administrator := (UpperCase(Value) = 'Y'); end; function TUser.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; if ACol = 2 then begin Administrator := not Administrator; Result := true; end; end; class function TUser.HeadingString : string; begin Result := 'Users'; end; class function TUser.ColumnHeadingsString : string; begin Result := 'Name Administrator'; end; function TUser.DetailsString : string; begin Result := Format('%-20s %-1s',[Name,FormatBoolean(Administrator)]); end; function TUser.UnencryptedPassword : string; begin Result := Encrypt(RestoreNullsEtc(Password),UserPasswordEncryptRandSeed); end; {***** T? methods *************************************************************} { class function T?.TableName : string; begin Result := '?'; end; class procedure T?.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('Abbreviation',ftString,15,false); end; class procedure T?.UpdateTable; begin // add code to update old table structures here end; procedure T?.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Abbreviation := TStringField(Table.FieldByName('Abbreviation')).Value; end; procedure T?.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Abbreviation')).Value := Abbreviation; end; function T?.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('Abbreviation'); end; function T?.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(Abbreviation,15); end; procedure T?.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Abbreviation := ReadStrFromStream(Stream); end; procedure T?.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(Abbreviation,Stream); end; class procedure T?.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Abbrev', 'Name' ],[ 7, // Abbrev 30 // Name ]); end; class function T?.FindFormCaption : string; begin Result := 'Find ?'; end; class function T?.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Abbreviation'); end; function T?.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Abbreviation else if ACol = 1 then Result := Name else Result := ''; end; class procedure T?.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Abbrev', 'Name' ],[ 7, // Abbrev 30 // Name ]); end; class function T?.MaintainFormCaption : string; begin Result := 'Maintain ?s'; end; class function T?.MaintainSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Abbreviation'); end; function T?.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function T?.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure T?.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Abbreviation := Value else if ACol = 1 then Name := Value; end; function T?.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; class function T?.HeadingString : string; begin Result := '?s'; end; class function T?.ColumnHeadingsString : string; begin Result := (inherited ColumnHeadingsString) + 'Abbrev Name '; end; function T?.DetailsString : string; begin Result := (inherited DetailsString) + Format('%-10s %-20s ',[Abbreviation,Name]); end; function T?.ComboBoxDisplayString : string; begin Result := Format('%s - %s',[Abbreviation,Name]); end; } {***** TCombinedEntry methods *************************************************} procedure TCombinedEntry.SaveToStream (Stream : TStream); begin Stream.Write(Id,SizeOf(Id)); Stream.Write(Date,SizeOf(Date)); Entries.SaveToStream(Stream); end; procedure TCombinedEntry.LoadFromStream (Stream : TStream); begin Stream.Read(Id,SizeOf(Id)); Stream.Read(Date,SizeOf(Date)); FEntries.Free; FEntries := TDatabaseObjectCollection.Create; FEntries.LoadFromStream(Stream); end; destructor TCombinedEntry.Destroy; begin FEntries.Free; inherited; end; procedure TCombinedEntry.Assign (CombinedEntry : TCombinedEntry); var StringStream : TStringStream; begin StringStream := TStringStream.Create(''); CombinedEntry.SaveToStream(StringStream); StringStream.Position := 0; Self.LoadFromStream(StringStream); StringStream.Free; end; procedure TCombinedEntry.LoadEntries; var SelectionString : string; begin // slower method SelectionString := 'WHERE ' + DelimitSQLFieldName('CombinedEntryId') + ' = ' + IntToStr(Id); // load entries LoadSomeDatabaseObjects(FEntries,TEntry,SelectionString); // set date field to any one of the entries // as all entries should have identical date if FEntries.Count > 0 then Date := TEntry(FEntries[0]).Date; // sort the entries FEntries.Sort(CompareEntries); end; function TCombinedEntry.Entries : TDatabaseObjectCollection; begin if FEntries = nil then LoadEntries; Result := FEntries; end; procedure TCombinedEntry.UpdateDatabase (NotifyWorkstations : boolean); var i : integer; SelectionString : string; begin // if no id assigned then do that first if Id = 0 then Id := GenerateId(1); // ensure that all entries have this combined id // and also the date is correct for i := 0 to Entries.Count - 1 do begin TEntry(Entries[i]).CombinedEntryId := Id; TEntry(Entries[i]).Date := Date; end; // update other account id field if PairedEntries then begin for i := 0 to Entries.Count - 1 do if i mod 2 = 0 then begin TEntry(Entries[i]).OtherAccountId := TEntry(Entries[i+1]).AccountId; TEntry(Entries[i+1]).OtherAccountId := TEntry(Entries[i]).AccountId; end; end else if Entries.Count > 2 then begin for i := 0 to Entries.Count - 1 do TEntry(Entries[i]).OtherAccountId := -1; // attempt to match individually paired entries if they are next to each other for i := 0 to Entries.Count - 2 do begin if ((TEntry(Entries[i]).AbsoluteAmount = TEntry(Entries[i+1]).AbsoluteAmount) and (TEntry(Entries[i]).Debit = TEntry(Entries[i+1]).Credit)) or ((TEntry(Entries[i]).Amount = 0) and (TEntry(Entries[i+1]).Amount = 0)) then begin TEntry(Entries[i]).OtherAccountId := TEntry(Entries[i+1]).AccountId; TEntry(Entries[i+1]).OtherAccountId := TEntry(Entries[i]).AccountId; end; end; end else if Entries.Count = 2 then begin TEntry(Entries[0]).OtherAccountId := TEntry(Entries[1]).AccountId; TEntry(Entries[1]).OtherAccountId := TEntry(Entries[0]).AccountId; end else begin for i := 0 to Entries.Count - 1 do TEntry(Entries[i]).OtherAccountId := 0; end; AcquireDatabaseCriticalUpdate; try // delete existing entries SelectionString := 'WHERE ' + DelimitSQLFieldName('CombinedEntryId') + ' = ' + IntToStr(Id); DeleteSomeDatabaseObjects(TEntry,SelectionString); // save updated entries Entries.SaveToDatabase(TEntry,false); finally ReleaseDatabaseCriticalUpdate; end; // send entries to all workstations so that // they can also update their cached accounts if NotifyWorkstations then UpdateCombinedEntryOnLoggedOnWorkstations(Self); end; procedure TCombinedEntry.DeleteFromDatabase (NotifyWorkstations : boolean); var SelectionString : string; begin // delete entries SelectionString := 'WHERE ' + DelimitSQLFieldName('CombinedEntryId') + ' = ' + IntToStr(Id); DeleteSomeDatabaseObjects(TEntry,SelectionString); // send message to all workstations that this combined // entry has been deleted so that they can update // their cached accounts if NotifyWorkstations then DeleteCombinedEntryFromLoggedOnWorkstations(Id); end; function TCombinedEntry.TotalDebits : int64; var i : integer; begin Result := 0; for i := 0 to Entries.Count - 1 do if TEntry(Entries[i]).Debit then Result := Result + TEntry(Entries[i]).AbsoluteAmount; end; function TCombinedEntry.TotalCredits : int64; var i : integer; begin Result := 0; for i := 0 to Entries.Count - 1 do if TEntry(Entries[i]).Credit then Result := Result + TEntry(Entries[i]).AbsoluteAmount; end; function TCombinedEntry.Balanced : boolean; begin Result := (TotalDebits = TotalCredits); end; function TCombinedEntry.SplitCompany : boolean; var i : integer; CompanyId : int64; begin CompanyId := 0; for i := 0 to Entries.Count - 1 do begin if i = 0 then CompanyId := TEntry(Entries[i]).Account.CompanyId else begin if CompanyId <> TEntry(Entries[i]).Account.CompanyId then begin Result := true; Exit; end; end; end; Result := false; end; function TCombinedEntry.MissingAccountIndex : integer; var i : integer; begin Result := -1; for i := 0 to Entries.Count - 1 do if TEntry(Entries[i]).AccountId = 0 then begin Result := i; Exit; end; end; function TCombinedEntry.PairedEntries : boolean; var i : integer; begin Result := false; if Entries.Count mod 2 <> 0 then Exit; for i := 0 to Entries.Count - 1 do if i mod 2 = 0 then begin if TEntry(Entries[i]).AbsoluteAmount <> TEntry(Entries[i+1]).AbsoluteAmount then Exit; if TEntry(Entries[i]).Debit <> TEntry(Entries[i+1]).Credit then Exit; end; Result := true; end; procedure TCombinedEntry.SetDefaults; begin Date := SysUtils.Date; end; {***** TCompany methods *******************************************************} class function TCompany.TableName : string; begin Result := 'Company'; end; class procedure TCompany.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('Abbreviation',ftString,15,false); end; class procedure TCompany.UpdateTable; begin // add code to update old table structures here end; procedure TCompany.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Abbreviation := TStringField(Table.FieldByName('Abbreviation')).Value; end; procedure TCompany.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Abbreviation')).Value := Abbreviation; end; function TCompany.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('Abbreviation'); end; function TCompany.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(Abbreviation,15); end; procedure TCompany.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Abbreviation := ReadStrFromStream(Stream); end; procedure TCompany.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(Abbreviation,Stream); end; function TCompany.HasReferences : boolean; var SelectionString : string; begin Result := true; SelectionString := 'WHERE ' + DelimitSQLFieldName('CompanyId') + ' = ' + IntToStr(Id); if CountDatabaseObjects(TAccount,SelectionString) > 0 then Exit; if CountDatabaseObjects(TWorkstationConfiguration,SelectionString) > 0 then Exit; Result := false; end; class procedure TCompany.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Abbrev', 'Name' ],[ 10, // Abbrev 30 // Name ]); end; class function TCompany.FindFormCaption : string; begin Result := 'Find Company'; end; class function TCompany.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Abbreviation'); end; function TCompany.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Abbreviation else if ACol = 1 then Result := Name else Result := ''; end; class procedure TCompany.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TCompany.MaintainFormCaption : string; begin Result := 'Maintain Companies'; end; class function TCompany.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TCompany.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TCompany.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TCompany.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Abbreviation := UpperCase(Value) else if ACol = 1 then Name := Value; end; class function TCompany.HeadingString : string; begin Result := 'Companies'; end; class function TCompany.ColumnHeadingsString : string; begin Result := 'Abbrev Name '; end; function TCompany.DetailsString : string; begin Result := Format('%-10s %-20s ',[Abbreviation,Name]); end; function TCompany.ComboBoxDisplayString : string; begin Result := Format('%s - %s',[Abbreviation,Name]); end; {***** TAccount methods *******************************************************} class function TAccount.TableName : string; begin Result := 'Account'; end; class procedure TAccount.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('Abbreviation',ftString,15,false); Table.FieldDefs.Add('Description',ftString,50,false); Table.FieldDefs.Add('CompanyId',IdFieldType,0,false); Table.FieldDefs.Add('AccountType',ftInteger,0,false); end; class procedure TAccount.UpdateTable; begin // add code to update old table structures here { if not FieldExists(TableName,'AccountType') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'AccountType',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; } end; procedure TAccount.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Abbreviation := TStringField(Table.FieldByName('Abbreviation')).Value; Description := TStringField(Table.FieldByName('Description')).Value; CompanyId := IdFieldValue(Table.FieldByName('CompanyId')); AccountType := TAccountType(TIntegerField(Table.FieldByName('AccountType')).Value); end; procedure TAccount.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Abbreviation')).Value := Abbreviation; TStringField(Table.FieldByName('Description')).Value := Description; SetIdFieldValue(Table.FieldByName('CompanyId'),CompanyId); TIntegerField(Table.FieldByName('AccountType')).Value := integer(AccountType); end; function TAccount.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('Abbreviation'); Result := Result + ',' + DelimitSQLFieldName('Description'); Result := Result + ',' + DelimitSQLFieldName('CompanyId'); Result := Result + ',' + DelimitSQLFieldName('AccountType'); end; function TAccount.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(Abbreviation,15); Result := Result + ',' + DelimitSQLStringValue(Description,50); Result := Result + ',' + IntToStr(CompanyId); Result := Result + ',' + IntToStr(integer(AccountType)); end; procedure TAccount.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Abbreviation := ReadStrFromStream(Stream); Description := ReadStrFromStream(Stream); Stream.Read(CompanyId,SizeOf(CompanyId)); Stream.Read(AccountType,SizeOf(AccountType)); end; procedure TAccount.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(Abbreviation,Stream); WriteStrToStream(Description,Stream); Stream.Write(CompanyId,SizeOf(CompanyId)); Stream.Write(AccountType,SizeOf(AccountType)); end; destructor TAccount.Destroy; begin FEntriesInPeriod.Free; FEntries.Free; inherited; end; function TAccount.HasReferences : boolean; var SelectionString : string; begin Result := true; SelectionString := 'WHERE ' + DelimitSQLFieldName('AccountId') + ' = ' + IntToStr(Id); if CountDatabaseObjects(TEntry,SelectionString) > 0 then Exit; if CountDatabaseObjects(TCashbook,SelectionString) > 0 then Exit; SelectionString := 'WHERE ' + DelimitSQLFieldName('OtherAccountId') + ' = ' + IntToStr(Id); if CountDatabaseObjects(TCashbookEntry,SelectionString) > 0 then Exit; Result := false; end; class procedure TAccount.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Company', 'Type', 'Abbrev', 'Name', 'Description' ],[ 10, // Company 7, // Type 10, // Abbrev 25, // Name 40 // Description ]); end; class function TAccount.FindFormCaption : string; begin Result := 'Find Account'; end; class function TAccount.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('CompanyId') + ',' + DelimitSQLFieldName('AccountType') + ',' + DelimitSQLFieldName('Abbreviation'); end; function TAccount.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := CompanyAbbreviation else if ACol = 1 then Result := AccountTypeString else if ACol = 2 then Result := Abbreviation else if ACol = 3 then Result := Name else if ACol = 4 then Result := Description else Result := ''; end; class procedure TAccount.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TAccount.MaintainFormCaption : string; begin Result := 'Maintain Accounts'; end; class function TAccount.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TAccount.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TAccount.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TAccount.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then begin if HasReferences then ShowMessage('Account is referred to') else CompanyId := CompanyIdFromAbbreviation(UpperCase(Value)); end else if ACol = 1 then begin if HasReferences then ShowMessage('Account is referred to') else AccountType := ConvertStringToAccountType(Value) end else if ACol = 2 then Abbreviation := UpperCase(Value) else if ACol = 3 then Name := Value else if ACol = 4 then Description := Value; end; function TAccount.MaintainStringGridDblClick (ACol : integer) : boolean; var DatabaseObject : TDatabaseObject; begin Result := false; if ACol = 0 then begin if HasReferences then ShowMessage('Account is referred to') else begin DatabaseObject := Find(TCompany); if DatabaseObject <> nil then begin CompanyId := TCompany(DatabaseObject).Id; DatabaseObject.Free; Result := true; end; end; end else if ACol = 1 then begin if HasReferences then ShowMessage('Account is referred to') else begin if PromptAccountTypeForm.Prompt then begin AccountType := PromptAccountTypeForm.AccountType; Result := true; end; end; end; end; procedure TAccount.ProcessUpdate; begin AccountsCache.UpdateAccounts; end; class procedure TAccount.ProcessDelete (Id : int64); begin // do nothing, should never happen! // well actually this is no longer true because we do let // accounts be deleted if there are no entries end; class function TAccount.HeadingString : string; begin Result := 'Accounts'; end; class function TAccount.ColumnHeadingsString : string; begin Result := 'Abbrev Type Name Description'; end; function TAccount.DetailsString : string; begin Result := Format('%-10.10s %-1.1s %-25.25s %-45.45s',[CombinedAbbreviation,AccountTypeString,Name,Description]); end; function TAccount.ComboBoxDisplayString : string; begin Result := Format('%s - %s',[CombinedAbbreviation,Name]); end; procedure TAccount.LoadEntries; var SelectionString : string; Cursor : TCursor; begin SelectionString := 'WHERE ' + DelimitSQLFieldName('AccountId') + ' = ' + IntToStr(Id); // tried this instead of using Sort below but didn't make // much difference to the time taken // it may be better to do it this way on a more powerful // database server // + ' ORDER BY Entry."Date", Id'; Cursor := Screen.Cursor; Screen.Cursor := crHourGlass; try // load entries LoadSomeDatabaseObjects(FEntries,TEntry,SelectionString); // sort the entries SortEntries; finally Screen.Cursor := Cursor; end; end; function TAccount.Entries : TDatabaseObjectCollection; begin if FEntries = nil then LoadEntries; Result := FEntries; end; function TAccount.EntriesCount : integer; var SelectionString : string; begin if FEntries <> nil then Result := FEntries.Count else begin SelectionString := 'WHERE ' + DelimitSQLFieldName('AccountId') + ' = ' + IntToStr(Id); Result := CountDatabaseObjects(TEntry,SelectionString); end; end; function TAccount.CompanyName : string; begin Result := Utilities.CompanyName(CompanyId); end; function TAccount.CompanyAbbreviation : string; begin Result := Utilities.CompanyAbbreviation(CompanyId); end; function TAccount.CombinedAbbreviation : string; begin if CompanyAbbreviation <> '' then Result := Format('%s (%s)',[Abbreviation,CompanyAbbreviation]) else Result := Format('%s',[Abbreviation]) end; function TAccount.AccountTypeString : string; begin Result := ConvertAccountTypeToString(AccountType); end; function TAccount.AccountTypeDescription : string; begin Result := ConvertAccountTypeToDescription(AccountType); end; function TAccount.Debit : boolean; begin // debits always appear on the left of the ledger // for asset and expense accounts these are positive amounts // for other types they are negative Result := AccountType in [atAsset,atExpense]; end; function TAccount.Credit : boolean; begin Result := not Debit; end; function TAccount.IncomeStatement : boolean; begin Result := AccountType in [atIncome,atExpense]; end; function TAccount.BalanceSheet : boolean; begin Result := not IncomeStatement; end; function TAccount.TotalBalance : int64; begin Result := Balance(false,0,false,0); end; function TAccount.PeriodBalance : int64; begin Result := Balance( FUseBeginPeriod, FBeginPeriodDate, FUseEndPeriod, FEndPeriodDate); end; function TAccount.BeginBalance : int64; begin if not FUseBeginPeriod then Result := 0 else Result := BalanceAsAt(FBeginPeriodDate-1); end; function TAccount.EndBalance : int64; begin if not FUseEndPeriod then Result := TotalBalance else Result := BalanceAsAt(FEndPeriodDate); end; function TAccount.Balance (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime) : int64; var i : integer; Entry : TEntry; begin Result := 0; // work through entries and decide which ones to accumulate for i := 0 to Entries.Count - 1 do begin Entry := TEntry(Entries[i]); if UseBeginPeriod and (Entry.Date < BeginPeriodDate) then Continue; if UseEndPeriod and (Entry.Date > EndPeriodDate) then Break; Result := Result + Entry.Amount; end; end; function TAccount.BalanceAsAt (AsAtDate : TDateTime) : int64; begin Result := Balance(false,0,true,AsAtDate); end; function TAccount.AccountingPeriodBalanceString : string; function FormatBalance (Balance : int64) : string; begin if (Debit and (Balance > 0)) or (Credit and (Balance < 0)) then Result := FormatCurrencyForDisplay(Abs(Balance)) + ' Dr' else if Balance <> 0 then Result := FormatCurrencyForDisplay(Abs(Balance)) + ' Cr' else Result := '0.00'; end; begin // if it is an income or expense account then show period balance if IncomeStatement then Result := FormatBalance(PeriodBalance) else Result := FormatBalance(BeginBalance) + ' ----> ' + FormatBalance(EndBalance); end; procedure TAccount.SortEntries; begin // sort the entries if FEntries <> nil then FEntries.Sort(CompareEntries); end; procedure TAccount.SetPeriod (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime); begin FUseBeginPeriod := UseBeginPeriod; FBeginPeriodDate := BeginPeriodDate; FUseEndPeriod := UseEndPeriod; FEndPeriodDate := EndPeriodDate; end; procedure TAccount.LoadEntriesInPeriod; var i : integer; Entry : TEntry; begin FEntriesInPeriod.Free; FEntriesInPeriod := TDatabaseObjectCollection.Create; FEntriesInPeriod.Owned := false; // work through entries and decide which ones to add to subset // collection for i := 0 to Entries.Count - 1 do begin Entry := TEntry(Entries[i]); if FUseBeginPeriod and (Entry.Date < FBeginPeriodDate) then Continue; if FUseEndPeriod and (Entry.Date > FEndPeriodDate) then Break; FEntriesInPeriod.Add(Entry); end; end; function TAccount.EntriesInPeriod : TDatabaseObjectCollection; begin // if not period set then just return the entire collection if (not FUseBeginPeriod) and (not FUseEndPeriod) then begin Result := Entries; Exit; end; // otherwise create subset collection if FEntriesInPeriod = nil then LoadEntriesInPeriod; Result := FEntriesInPeriod; end; procedure TAccount.Print; begin PrintAccountStatement( Id, WorkstationConfiguration.UseBeginPeriod, WorkstationConfiguration.BeginPeriodDate, WorkstationConfiguration.UseEndPeriod, WorkstationConfiguration.EndPeriodDate, false, false); end; procedure TAccount.Email; begin PrintAccountStatement( Id, WorkstationConfiguration.UseBeginPeriod, WorkstationConfiguration.BeginPeriodDate, WorkstationConfiguration.UseEndPeriod, WorkstationConfiguration.EndPeriodDate, false, true); end; procedure TAccount.PrintSummary; begin PrintAccountStatement( Id, WorkstationConfiguration.UseBeginPeriod, WorkstationConfiguration.BeginPeriodDate, WorkstationConfiguration.UseEndPeriod, WorkstationConfiguration.EndPeriodDate, true, false); end; function TAccount.EarliestDate : TDateTime; begin if Entries.Count > 0 then Result := TEntry(Entries[0]).Date else Result := Date; end; function TAccount.LatestDate : TDateTime; begin if Entries.Count > 0 then Result := TEntry(Entries[Entries.Count-1]).Date else Result := Date; end; procedure TAccount.Purge (PurgeDate : TDateTime); var SelectionString : string; begin SelectionString := 'WHERE ' + DelimitSQLFieldName('AccountId') + ' = ' + IntToStr(Id); if Firebird then SelectionString := SelectionString + ' AND ' + DelimitSQLFieldName('Date') else SelectionString := SelectionString + ' AND Entry."Date"'; SelectionString := SelectionString + ' <= ' + DelimitSQLStringValue(ConvertDateToDatabaseString(PurgeDate)); DeleteSomeDatabaseObjects(TEntry,SelectionString); end; function TAccount.FindNextEntryInPeriod (FindText : string; CurrentEntry : TEntry; Earlier : boolean) : int64; var Index : integer; i : integer; Entry : TEntry; begin Index := EntriesInPeriod.IndexOf(CurrentEntry); if Index <> -1 then begin if Earlier then begin for i := Index - 1 downto 0 do begin Entry := TEntry(EntriesInPeriod[i]); if Entry.TextInDescription(FindText) then begin Result := Entry.Id; Exit; end; end; end else begin for i := Index + 1 to EntriesInPeriod.Count - 1 do begin Entry := TEntry(EntriesInPeriod[i]); if Entry.TextInDescription(FindText) then begin Result := Entry.Id; Exit; end; end; end; end; Result := 0; end; {***** TEntry methods *********************************************************} class function TEntry.TableName : string; begin Result := 'Entry'; end; class procedure TEntry.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('CombinedEntryId',IdFieldType,0,false); Table.FieldDefs.Add('AccountId',IdFieldType,0,false); Table.FieldDefs.Add('Description',ftString,50,false); Table.FieldDefs.Add('Date',ftString,10,false); Table.FieldDefs.Add('Amount',CurrencyFieldType,CurrencyFieldSize,false); Table.FieldDefs.Add('OtherAccountId',IdFieldType,0,false); if Firebird then begin TIBTable(Table).IndexDefs.Add(TableName + 'CombinedEntryId','CombinedEntryId',[]); TIBTable(Table).IndexDefs.Add(TableName + 'AccountId','AccountId',[]); end; end; class procedure TEntry.UpdateTable; begin // add code to update old table structures here { if not FieldExists(TableName,'OtherAccountId') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'OtherAccountId',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; } end; procedure TEntry.LoadFromTable (Table : TDataset); begin inherited; CombinedEntryId := IdFieldValue(Table.FieldByName('CombinedEntryId')); AccountId := IdFieldValue(Table.FieldByName('AccountId')); Description := TStringField(Table.FieldByName('Description')).Value; Date := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('Date')).Value); Amount := CurrencyFieldValue(Table.FieldByName('Amount')); OtherAccountId := IdFieldValue(Table.FieldByName('OtherAccountId')); end; procedure TEntry.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('CombinedEntryId'),CombinedEntryId); SetIdFieldValue(Table.FieldByName('AccountId'),AccountId); TStringField(Table.FieldByName('Description')).Value := Description; TStringField(Table.FieldByName('Date')).Value := ConvertDateToDatabaseString(Date); SetCurrencyFieldValue(Table.FieldByName('Amount'),Amount); SetIdFieldValue(Table.FieldByName('OtherAccountId'),OtherAccountId); end; function TEntry.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('CombinedEntryId'); Result := Result + ',' + DelimitSQLFieldName('AccountId'); Result := Result + ',' + DelimitSQLFieldName('Description'); if Firebird then Result := Result + ',' + DelimitSQLFieldName('Date') else Result := Result + ',Entry."Date"'; Result := Result + ',' + DelimitSQLFieldName('Amount'); Result := Result + ',' + DelimitSQLFieldName('OtherAccountId'); end; function TEntry.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(CombinedEntryId); Result := Result + ',' + IntToStr(AccountId); Result := Result + ',' + DelimitSQLStringValue(Description,50); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(Date),10); Result := Result + ',' + CurrencyToSQLStr(Amount); Result := Result + ',' + IntToStr(OtherAccountId); end; procedure TEntry.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(CombinedEntryId,SizeOf(CombinedEntryId)); Stream.Read(AccountId,SizeOf(AccountId)); Description := ReadStrFromStream(Stream); Stream.Read(Date,SizeOf(Date)); Stream.Read(Amount,SizeOf(Amount)); Stream.Read(OtherAccountId,SizeOf(OtherAccountId)); end; procedure TEntry.SaveToStream (Stream : TStream); begin inherited; Stream.Write(CombinedEntryId,SizeOf(CombinedEntryId)); Stream.Write(AccountId,SizeOf(AccountId)); WriteStrToStream(Description,Stream); Stream.Write(Date,SizeOf(Date)); Stream.Write(Amount,SizeOf(Amount)); Stream.Write(OtherAccountId,SizeOf(OtherAccountId)); end; destructor TEntry.Destroy; begin FCombinedEntry.Free; inherited; end; class procedure TEntry.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'ID', 'Comb ID', 'A/c ID', 'Desc', 'Date', 'Amount', 'Other A/c' ],[ 10, // ID 10, // Comb ID 10, // A/c ID 20, // Desc 12, // Date 12, // Amount 20 // Other A/c ]); end; class function TEntry.FindFormCaption : string; begin Result := 'Find Entry'; end; class function TEntry.FindSelectionString : string; begin Result := 'ORDER BY Entry."Date" DESC'; end; function TEntry.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(Id) else if ACol = 1 then Result := IntToStr(CombinedEntryId) else if ACol = 2 then Result := IntToStr(AccountId) else if ACol = 3 then Result := Description else if ACol = 4 then Result := FormatDate(Date) else if ACol = 5 then Result := FormatCurrency(Amount) else if ACol = 6 then Result := OtherAccountName else Result := ''; end; class procedure TEntry.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TEntry.MaintainFormCaption : string; begin Result := 'Maintain Entries'; end; class function TEntry.MaintainSelectionString : string; begin Result := ''; // Result := 'ORDER BY Entry."Date" DESC'; end; function TEntry.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TEntry.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TEntry.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 3 then Description := Value; end; class function TEntry.HeadingString : string; begin Result := 'Entries'; end; class function TEntry.ColumnHeadingsString : string; begin Result := (inherited ColumnHeadingsString) + 'Account Description Date Amount'; end; function TEntry.DetailsString : string; begin Result := (inherited DetailsString) + Format('%-15s %-20s %-10s %-20s',[AccountCombinedAbbreviation,Description,FormatDate(Date),FormatCurrency(Amount)]); end; function TEntry.CombinedEntry : TCombinedEntry; begin if FCombinedEntry = nil then FCombinedEntry := GetCombinedEntry(CombinedEntryId); Result := FCombinedEntry; end; function TEntry.AccountName : string; begin Result := Utilities.AccountName(AccountId); end; function TEntry.AccountAbbreviation : string; begin Result := Utilities.AccountAbbreviation(AccountId); end; function TEntry.AccountCombinedAbbreviation : string; begin Result := Utilities.AccountCombinedAbbreviation(AccountId); end; function TEntry.OtherAccountName : string; begin if OtherAccountId = -1 then Result := '*****' else if OtherAccountId = 0 then Result := '' else Result := Utilities.AccountName(OtherAccountId); end; function TEntry.OtherAccountAbbreviation : string; begin if OtherAccountId = -1 then Result := '*****' else if OtherAccountId = 0 then Result := '' else Result := Utilities.AccountAbbreviation(OtherAccountId); end; function TEntry.AbsoluteAmount : int64; begin Result := Abs(Amount); end; function TEntry.Debit : boolean; var Account : TAccount; begin Account := Self.Account; if Account = nil then begin if Amount < 0 then Result := true else Result := false; end else begin if Account.Debit then begin if Amount >= 0 then Result := true else Result := false; end else begin if Amount >= 0 then Result := false else Result := true; end; end; end; function TEntry.Credit : boolean; begin Result := not Debit; end; function TEntry.Account : TAccount; begin Result := TAccount(Accounts.ObjectsById[AccountId]); end; function TEntry.TextInDescription (Text : string) : boolean; begin Result := Pos(UpperCase(Text),UpperCase(Description)) <> 0; end; {***** TCashbook methods ******************************************************} class function TCashbook.TableName : string; begin Result := 'Cashbook'; end; class procedure TCashbook.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('Abbreviation',ftString,15,false); Table.FieldDefs.Add('Description',ftString,50,false); Table.FieldDefs.Add('AccountId',IdFieldType,0,false); end; class procedure TCashbook.UpdateTable; begin // add code to update old table structures here { if not FieldExists(TableName,'AccountType') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'AccountType',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; } end; procedure TCashbook.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Abbreviation := TStringField(Table.FieldByName('Abbreviation')).Value; Description := TStringField(Table.FieldByName('Description')).Value; AccountId := IdFieldValue(Table.FieldByName('AccountId')); end; procedure TCashbook.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Abbreviation')).Value := Abbreviation; TStringField(Table.FieldByName('Description')).Value := Description; SetIdFieldValue(Table.FieldByName('AccountId'),AccountId); end; function TCashbook.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('Abbreviation'); Result := Result + ',' + DelimitSQLFieldName('Description'); Result := Result + ',' + DelimitSQLFieldName('AccountId'); end; function TCashbook.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(Abbreviation,15); Result := Result + ',' + DelimitSQLStringValue(Description,50); Result := Result + ',' + IntToStr(AccountId); end; procedure TCashbook.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Abbreviation := ReadStrFromStream(Stream); Description := ReadStrFromStream(Stream); Stream.Read(AccountId,SizeOf(AccountId)); end; procedure TCashbook.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(Abbreviation,Stream); WriteStrToStream(Description,Stream); Stream.Write(AccountId,SizeOf(AccountId)); end; destructor TCashbook.Destroy; begin FCashbookEntriesInPeriod.Free; FCashbookEntries.Free; inherited; end; function TCashbook.HasReferences : boolean; var SelectionString : string; begin Result := true; SelectionString := 'WHERE ' + DelimitSQLFieldName('CashbookId') + ' = ' + IntToStr(Id); if CountDatabaseObjects(TCashbookEntry,SelectionString) > 0 then Exit; Result := false; end; class procedure TCashbook.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Account', 'Abbrev', 'Name', 'Description' ],[ 20, // Account 10, // Abbrev 25, // Name 40 // Description ]); end; class function TCashbook.FindFormCaption : string; begin Result := 'Find Cashbook'; end; class function TCashbook.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('AccountId') + ',' + DelimitSQLFieldName('Abbreviation'); end; function TCashbook.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := AccountCombinedAbbreviation(AccountId) else if ACol = 1 then Result := Abbreviation else if ACol = 2 then Result := Name else if ACol = 3 then Result := Description else Result := ''; end; class procedure TCashbook.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TCashbook.MaintainFormCaption : string; begin Result := 'Maintain Cashbooks'; end; class function TCashbook.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TCashbook.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TCashbook.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TCashbook.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then AccountId := AccountIdFromCombinedAbbreviation(UpperCase(Value)) else if ACol = 1 then Abbreviation := UpperCase(Value) else if ACol = 2 then Name := Value else if ACol = 3 then Description := Value; end; function TCashbook.MaintainStringGridDblClick (ACol : integer) : boolean; var DatabaseObject : TDatabaseObject; begin Result := false; if ACol = 0 then begin DatabaseObject := FindAccount; if DatabaseObject <> nil then begin AccountId := TAccount(DatabaseObject).Id; DatabaseObject.Free; Result := true; end; end; end; procedure TCashbook.ProcessUpdate; begin CashbooksCache.UpdateCashbooks; end; class procedure TCashbook.ProcessDelete (Id : int64); begin // do nothing, should never happen! end; class function TCashbook.HeadingString : string; begin Result := 'Cashbooks'; end; class function TCashbook.ColumnHeadingsString : string; begin Result := 'Abbrev Name Description'; end; function TCashbook.DetailsString : string; begin Result := Format('%-10.10s %-25.25s %-45.45s',[CombinedAbbreviation,Name,Description]); end; function TCashbook.ComboBoxDisplayString : string; begin Result := Format('%s - %s',[CombinedAbbreviation,Name]); end; procedure TCashbook.LoadCashbookEntries; var SelectionString : string; Cursor : TCursor; begin SelectionString := 'WHERE ' + DelimitSQLFieldName('CashbookId') + ' = ' + IntToStr(Id); // tried this instead of using Sort below but didn't make // much difference to the time taken // it may be better to do it this way on a more powerful // database server // + ' ORDER BY CashbookEntry."Date", Id'; Cursor := Screen.Cursor; Screen.Cursor := crHourGlass; try // load cashbook entries LoadSomeDatabaseObjects(FCashbookEntries,TCashbookEntry,SelectionString); // sort the entries SortCashbookEntries; finally Screen.Cursor := Cursor; end; end; function TCashbook.CashbookEntries : TDatabaseObjectCollection; begin if FCashbookEntries = nil then LoadCashbookEntries; Result := FCashbookEntries; end; function TCashbook.CashbookEntriesCount : integer; var SelectionString : string; begin if FCashbookEntries <> nil then Result := FCashbookEntries.Count else begin SelectionString := 'WHERE ' + DelimitSQLFieldName('CashbookId') + ' = ' + IntToStr(Id); Result := CountDatabaseObjects(TCashbookEntry,SelectionString); end; end; function TCashbook.CombinedAbbreviation : string; begin if (Account <> nil) and (Account.CompanyAbbreviation <> '') then Result := Format('%s (%s)',[Abbreviation,Account.CompanyAbbreviation]) else Result := Format('%s',[Abbreviation]) end; function TCashbook.TotalBalance (StatementOnly : boolean) : int64; begin Result := Balance(false,0,false,0,StatementOnly); end; function TCashbook.PeriodBalance (StatementOnly : boolean) : int64; begin Result := Balance( FUseBeginPeriod, FBeginPeriodDate, FUseEndPeriod, FEndPeriodDate, StatementOnly); end; function TCashbook.BeginBalance (StatementOnly : boolean) : int64; begin if not FUseBeginPeriod then Result := 0 else Result := BalanceAsAt(FBeginPeriodDate-1,StatementOnly); end; function TCashbook.EndBalance (StatementOnly : boolean) : int64; begin if not FUseEndPeriod then Result := TotalBalance(StatementOnly) else Result := BalanceAsAt(FEndPeriodDate,StatementOnly); end; function TCashbook.Balance (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime; StatementOnly : boolean) : int64; var i : integer; CashbookEntry : TCashbookEntry; begin Result := 0; // work through cashbook entries and decide which ones to accumulate for i := 0 to CashbookEntries.Count - 1 do begin CashbookEntry := TCashbookEntry(CashbookEntries[i]); if StatementOnly and (not CashbookEntry.OnStatement) then Continue; if UseBeginPeriod and (CashbookEntry.Date < BeginPeriodDate) then Continue; if UseEndPeriod and (CashbookEntry.Date > EndPeriodDate) then Break; Result := Result + CashbookEntry.Amount; end; end; function TCashbook.BalanceAsAt (AsAtDate : TDateTime; StatementOnly : boolean) : int64; begin Result := Balance(false,0,true,AsAtDate,StatementOnly); end; function TCashbook.AccountingPeriodBalanceString (StatementOnly : boolean) : string; function FormatBalance (Balance : int64) : string; begin if Balance > 0 then Result := FormatCurrencyForDisplay(Abs(Balance)) + ' Dr' else if Balance <> 0 then Result := FormatCurrencyForDisplay(Abs(Balance)) + ' Cr' else Result := '0.00'; end; begin Result := FormatBalance(BeginBalance(StatementOnly)) + ' ----> ' + FormatBalance(EndBalance(StatementOnly)); end; procedure TCashbook.SortCashbookEntries; begin // sort the cashbook entries if FCashbookEntries <> nil then FCashbookEntries.Sort(CompareCashbookEntries); end; procedure TCashbook.SetPeriod (UseBeginPeriod : boolean; BeginPeriodDate : TDateTime; UseEndPeriod : boolean; EndPeriodDate : TDateTime); begin FUseBeginPeriod := UseBeginPeriod; FBeginPeriodDate := BeginPeriodDate; FUseEndPeriod := UseEndPeriod; FEndPeriodDate := EndPeriodDate; end; procedure TCashbook.LoadCashbookEntriesInPeriod; var i : integer; CashbookEntry : TCashbookEntry; begin FCashbookEntriesInPeriod.Free; FCashbookEntriesInPeriod := TDatabaseObjectCollection.Create; FCashbookEntriesInPeriod.Owned := false; // work through cashbook entries and decide which ones to add to subset // collection for i := 0 to CashbookEntries.Count - 1 do begin CashbookEntry := TCashbookEntry(CashbookEntries[i]); if FUseBeginPeriod and (CashbookEntry.Date < FBeginPeriodDate) then Continue; if FUseEndPeriod and (CashbookEntry.Date > FEndPeriodDate) then Break; FCashbookEntriesInPeriod.Add(CashbookEntry); end; end; function TCashbook.CashbookEntriesInPeriod : TDatabaseObjectCollection; begin // if not period set then just return the entire collection if (not FUseBeginPeriod) and (not FUseEndPeriod) then begin Result := CashbookEntries; Exit; end; // otherwise create subset collection if FCashbookEntriesInPeriod = nil then LoadCashbookEntriesInPeriod; Result := FCashbookEntriesInPeriod; end; // this function will add a cashbook entry to the // memory collections only, not the database procedure TCashbook.AddCashbookEntry (CashbookEntry : TCashbookEntry); begin // add to subsetted collection first if FCashbookEntriesInPeriod <> nil then FCashbookEntriesInPeriod.Add(CashbookEntry); // then add to main collection CashbookEntries.Add(CashbookEntry); end; // this function will delete a cashbook entry from // the memory collection only, not the database procedure TCashbook.DeleteCashbookEntry (CashbookEntry : TCashbookEntry); var Index : integer; begin // delete from subsetted collection first if FCashbookEntriesInPeriod <> nil then begin Index := FCashbookEntriesInPeriod.IndexOf(CashbookEntry); if Index <> -1 then FCashbookEntriesInPeriod.Delete(Index); end; // then delete from main collection Index := CashbookEntries.IndexOf(CashbookEntry); if Index <> -1 then CashbookEntries.Delete(Index); end; procedure TCashbook.Print; begin PrintCashbookStatement( Id, WorkstationConfiguration.UseBeginPeriod, WorkstationConfiguration.BeginPeriodDate, WorkstationConfiguration.UseEndPeriod, WorkstationConfiguration.EndPeriodDate, false); end; procedure TCashbook.Email; begin PrintCashbookStatement( Id, WorkstationConfiguration.UseBeginPeriod, WorkstationConfiguration.BeginPeriodDate, WorkstationConfiguration.UseEndPeriod, WorkstationConfiguration.EndPeriodDate, true); end; function TCashbook.Account : TAccount; begin Result := TAccount(Accounts.ObjectsById[AccountId]); end; function TCashbook.CachedAccount : TAccount; begin // get account from account cache when we // need to access the entries Result := AccountsCache.GetAccount(AccountId); end; function TCashbook.FindNextCashbookEntryInPeriod (FindText : string; CurrentCashbookEntry : TCashbookEntry; Earlier : boolean) : int64; var Index : integer; i : integer; CashbookEntry : TCashbookEntry; begin Index := CashbookEntriesInPeriod.IndexOf(CurrentCashbookEntry); if Index <> -1 then begin if Earlier then begin for i := Index - 1 downto 0 do begin CashbookEntry := TCashbookEntry(CashbookEntriesInPeriod[i]); if CashbookEntry.TextInDescription(FindText) then begin Result := CashbookEntry.Id; Exit; end; end; end else begin for i := Index + 1 to CashbookEntriesInPeriod.Count - 1 do begin CashbookEntry := TCashbookEntry(CashbookEntriesInPeriod[i]); if CashbookEntry.TextInDescription(FindText) then begin Result := CashbookEntry.Id; Exit; end; end; end; end; Result := 0; end; {***** TCashbookEntry methods *************************************************} class function TCashbookEntry.TableName : string; begin Result := 'CashbookEntry'; end; class procedure TCashbookEntry.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('CashbookId',IdFieldType,0,false); Table.FieldDefs.Add('OtherAccountId',IdFieldType,0,false); Table.FieldDefs.Add('CombinedEntryId',IdFieldType,0,false); Table.FieldDefs.Add('Description',ftString,50,false); Table.FieldDefs.Add('Date',ftString,10,false); Table.FieldDefs.Add('Amount',CurrencyFieldType,CurrencyFieldSize,false); Table.FieldDefs.Add('OnStatement',ftInteger,0,false); if Firebird then TIBTable(Table).IndexDefs.Add(TableName + 'CashbookId','CashbookId',[]); end; class procedure TCashbookEntry.UpdateTable; begin // add code to update old table structures here { if not FieldExists(TableName,'OtherAccountId') then begin ProgressForm.SetStep(2); ProgressForm.SetCaption('Database Restructure. Please wait...'); ProgressForm.Show; ProgressForm.StepIt; AddFieldToTable(TableName,'OtherAccountId',ftInteger,0,0); ProgressForm.StepIt; ProgressForm.Hide; end; } end; procedure TCashbookEntry.LoadFromTable (Table : TDataset); begin inherited; CashbookId := IdFieldValue(Table.FieldByName('CashbookId')); OtherAccountId := IdFieldValue(Table.FieldByName('OtherAccountId')); CombinedEntryId := IdFieldValue(Table.FieldByName('CombinedEntryId')); Description := TStringField(Table.FieldByName('Description')).Value; Date := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('Date')).Value); Amount := CurrencyFieldValue(Table.FieldByName('Amount')); OnStatement := TIntegerField(Table.FieldByName('OnStatement')).Value <> 0; end; procedure TCashbookEntry.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('CashbookId'),CashbookId); SetIdFieldValue(Table.FieldByName('OtherAccountId'),OtherAccountId); SetIdFieldValue(Table.FieldByName('CombinedEntryId'),CombinedEntryId); TStringField(Table.FieldByName('Description')).Value := Description; TStringField(Table.FieldByName('Date')).Value := ConvertDateToDatabaseString(Date); SetCurrencyFieldValue(Table.FieldByName('Amount'),Amount); TIntegerField(Table.FieldByName('OnStatement')).Value := integer(OnStatement); end; function TCashbookEntry.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('CashbookId'); Result := Result + ',' + DelimitSQLFieldName('OtherAccountId'); Result := Result + ',' + DelimitSQLFieldName('CombinedEntryId'); Result := Result + ',' + DelimitSQLFieldName('Description'); if Firebird then Result := Result + ',' + DelimitSQLFieldName('Date') else Result := Result + ',CashbookEntry."Date"'; Result := Result + ',' + DelimitSQLFieldName('Amount'); Result := Result + ',' + DelimitSQLFieldName('OnStatement'); end; function TCashbookEntry.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(CashbookId); Result := Result + ',' + IntToStr(OtherAccountId); Result := Result + ',' + IntToStr(CombinedEntryId); Result := Result + ',' + DelimitSQLStringValue(Description,50); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(Date),10); Result := Result + ',' + CurrencyToSQLStr(Amount); Result := Result + ',' + IntToStr(integer(OnStatement)); end; procedure TCashbookEntry.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(CashbookId,SizeOf(CashbookId)); Stream.Read(OtherAccountId,SizeOf(OtherAccountId)); Stream.Read(CombinedEntryId,SizeOf(CombinedEntryId)); Description := ReadStrFromStream(Stream); Stream.Read(Date,SizeOf(Date)); Stream.Read(Amount,SizeOf(Amount)); Stream.Read(OnStatement,SizeOf(OnStatement)); end; procedure TCashbookEntry.SaveToStream (Stream : TStream); begin inherited; Stream.Write(CashbookId,SizeOf(CashbookId)); Stream.Write(OtherAccountId,SizeOf(OtherAccountId)); Stream.Write(CombinedEntryId,SizeOf(CombinedEntryId)); WriteStrToStream(Description,Stream); Stream.Write(Date,SizeOf(Date)); Stream.Write(Amount,SizeOf(Amount)); Stream.Write(OnStatement,SizeOf(OnStatement)); end; destructor TCashbookEntry.Destroy; begin FCombinedEntry.Free; inherited; end; class function TCashbookEntry.FindFormCaption : string; begin Result := 'Find Cashbook Entry'; end; class function TCashbookEntry.MaintainFormCaption : string; begin Result := 'Maintain Cashbook Entries'; end; procedure TCashbookEntry.ProcessUpdate; var CashbookEntry : TCashbookEntry; begin if CashbooksCache <> nil then begin CashbooksCache.DeleteCashbookEntry(Id); CashbookEntry := TCashbookEntry.Create; CashbookEntry.Assign(Self); CashbooksCache.AddCashbookEntry(CashbookEntry); end; // update cashbook display in case one of the entries // on the displayed cashbook has changed if MainForm <> nil then MainForm.CashbooksFrame.UpdateCashbookEntry; end; class procedure TCashbookEntry.ProcessDelete (Id : int64); begin if CashbooksCache <> nil then CashbooksCache.DeleteCashbookEntry(Id); // update cashbook display in case one of the entries // on the displayed cashbook has changed if MainForm <> nil then MainForm.CashbooksFrame.UpdateCashbookEntry; end; class function TCashbookEntry.HeadingString : string; begin Result := 'Cashbook Entries'; end; class function TCashbookEntry.ColumnHeadingsString : string; begin Result := (inherited ColumnHeadingsString) + 'Cashbook Other A/c Description Date Amount'; end; function TCashbookEntry.DetailsString : string; begin Result := (inherited DetailsString) + Format('%-15s %-15s %-20s %-10s %-20s',[CashbookAbbreviation,OtherAccountAbbreviation,Description,FormatDate(Date),FormatCurrency(Amount)]); end; function TCashbookEntry.CombinedEntry : TCombinedEntry; begin if FCombinedEntry = nil then FCombinedEntry := GetCombinedEntry(CombinedEntryId); Result := FCombinedEntry; end; procedure TCashbookEntry.UpdateCombinedEntry; var CombinedEntry : TCombinedEntry; begin if (Cashbook.AccountId <> 0) and (OtherAccountId <> 0) then begin // create ledger entry CombinedEntry := nil; if Debit then CreateDoubleEntry (CombinedEntry, Date, Cashbook.AccountId, Description, OtherAccountId, Description, AbsoluteAmount) else CreateDoubleEntry (CombinedEntry, Date, OtherAccountId, Description, Cashbook.AccountId, Description, AbsoluteAmount); // apply tax before posting to ledger // CombinedEntry.ApplyTax; CombinedEntryId := PostDoubleEntry (CombinedEntryId, CombinedEntry, true); // otherwise delete the combined entry end else DeleteCombinedEntry; end; procedure TCashbookEntry.DeleteCombinedEntry; begin if CombinedEntry <> nil then begin // delete the combined entry CombinedEntry.DeleteFromDatabase(true); // clean up FCombinedEntry.Free; FCombinedEntry := nil; CombinedEntryId := 0; end; end; function TCashbookEntry.CashbookName : string; begin Result := Utilities.CashbookName(CashbookId); end; function TCashbookEntry.CashbookAbbreviation : string; begin Result := Utilities.CashbookAbbreviation(CashbookId); end; function TCashbookEntry.OtherAccountName : string; begin Result := Utilities.AccountName(OtherAccountId); end; function TCashbookEntry.OtherAccountAbbreviation : string; begin Result := Utilities.AccountAbbreviation(OtherAccountId); end; function TCashbookEntry.OtherAccountComboBoxDisplayString : string; begin Result := Utilities.AccountComboBoxDisplayString(OtherAccountId); end; function TCashbookEntry.AbsoluteAmount : int64; begin Result := Abs(Amount); end; function TCashbookEntry.Debit : boolean; begin if Amount >= 0 then Result := true else Result := false; end; function TCashbookEntry.Credit : boolean; begin Result := not Debit; end; function TCashbookEntry.Cashbook : TCashbook; begin Result := TCashbook(Cashbooks.ObjectsById[CashbookId]); end; function TCashbookEntry.OtherAccount : TAccount; begin Result := TAccount(Accounts.ObjectsById[OtherAccountId]); end; function TCashbookEntry.TextInDescription (Text : string) : boolean; begin Result := Pos(UpperCase(Text),UpperCase(Description)) <> 0; end; {***** TItem methods **********************************************************} class function TItem.TableName : string; begin Result := 'Item'; end; class procedure TItem.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('Price',ftString,20,false); Table.FieldDefs.Add('Active',ftInteger,0,false); end; class procedure TItem.UpdateTable; begin // add code to update old table structures here end; procedure TItem.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Price := ConvertStringToDouble(TStringField(Table.FieldByName('Price')).Value); Active := TIntegerField(Table.FieldByName('Active')).Value <> 0; end; procedure TItem.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Price')).Value := ConvertDoubleToString(Price); TIntegerField(Table.FieldByName('Active')).Value := integer(Active); end; function TItem.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('Price'); Result := Result + ',' + DelimitSQLFieldName('Active'); end; function TItem.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(ConvertDoubleToString(Price),20); Result := Result + ',' + IntToStr(integer(Active)); end; procedure TItem.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Stream.Read(Price,SizeOf(Price)); Stream.Read(Active,SizeOf(Active)); end; procedure TItem.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); Stream.Write(Price,SizeOf(Price)); Stream.Write(Active,SizeOf(Active)); end; procedure TItem.SetNewEntryValues; begin Active := true; end; constructor TItem.Create; begin inherited; Picture := TAttachmentManager.Create(Self,1); end; destructor TItem.Destroy; begin FLookupCodes.Free; Picture.Free; inherited; end; procedure TItem.SaveDetailsToDatabase; var i : integer; begin for i := 0 to LookupCodes.Count - 1 do TLookupCode(LookupCodes[i]).ItemId := Id; LookupCodes.SaveToDatabase(TLookupCode,false); if Picture.Changed then Picture.Save; end; procedure TItem.DeleteDetailsFromDatabase (BeforeSave : boolean); var SelectionString : string; begin SelectionString := 'WHERE ' + DelimitSQLFieldName('ItemId') + ' = ' + IntToStr(Id); DeleteSomeDatabaseObjects(TLookupCode,SelectionString); if BeforeSave and (not Picture.Changed) then // do nothing else Picture.Delete; end; procedure TItem.LoadDetailsFromStream (Stream : TStream); begin Picture.LoadFromStream(Stream); LookupCodes.Clear; LookupCodes.LoadFromStream(Stream); end; procedure TItem.SaveDetailsToStream (Stream : TStream); begin Picture.SaveToStream(Stream); LookupCodes.SaveToStream(Stream); end; class procedure TItem.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Description', 'Price', 'Active' ],[ 30, // Description 20, // Price 8 // Active ]); end; class function TItem.FindFormCaption : string; begin Result := 'Find Item'; end; class function TItem.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Name'); end; function TItem.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then Result := ConvertDoubleToString(Price) else if ACol = 2 then Result := FormatBoolean(Active) else Result := ''; end; class procedure TItem.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TItem.MaintainFormCaption : string; begin Result := 'Maintain Items'; end; class function TItem.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TItem.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TItem.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TItem.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Name := Value else if ACol = 1 then Price := ConvertStringToDouble(Value); end; function TItem.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; procedure TItem.ProcessUpdate; begin if POSMode then SavePOSData; end; class procedure TItem.ProcessDelete (Id : int64); begin if POSMode then SavePOSData; end; class function TItem.HeadingString : string; begin Result := 'Items'; end; class function TItem.ColumnHeadingsString : string; begin Result := 'Name Price'; end; function TItem.DetailsString : string; begin Result := Format('%-30s %10s',[Name,FloatToStrF(Price,ffFixed,10,4)]); end; procedure TItem.ReloadLookupCodes; begin FLookupCodes.Free; FLookupCodes := nil; end; function TItem.LookupCodes : TDatabaseObjectCollection; var SelectionString : string; begin if FLookupCodes = nil then begin if Id = 0 then begin FLookupCodes := TDatabaseObjectCollection.Create; FLookupCodes.Owned := true; end else begin SelectionString := 'WHERE ' + DelimitSQLFieldName('ItemId') + ' = ' + IntToStr(Id); LoadSomeDatabaseObjects(FLookupCodes,TLookupCode,SelectionString); end; end; Result := FLookupCodes; end; {***** TLookupCode methods *************************************************************} class function TLookupCode.TableName : string; begin Result := 'LookupCode'; end; class procedure TLookupCode.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('ItemId',IdFieldType,0,false); Table.FieldDefs.Add('Code',ftString,50,false); end; class procedure TLookupCode.UpdateTable; begin // add code to update old table structures here end; procedure TLookupCode.LoadFromTable (Table : TDataset); begin inherited; ItemId := IdFieldValue(Table.FieldByName('ItemId')); Code := TStringField(Table.FieldByName('Code')).Value; end; procedure TLookupCode.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('ItemId'),ItemId); TStringField(Table.FieldByName('Code')).Value := Code; end; function TLookupCode.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('ItemId'); Result := Result + ',' + DelimitSQLFieldName('Code'); end; function TLookupCode.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(ItemId); Result := Result + ',' + DelimitSQLStringValue(Code,50); end; procedure TLookupCode.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(ItemId,SizeOf(ItemId)); Code := ReadStrFromStream(Stream); end; procedure TLookupCode.SaveToStream (Stream : TStream); begin inherited; Stream.Write(ItemId,SizeOf(ItemId)); WriteStrToStream(Code,Stream); end; class procedure TLookupCode.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Code', 'Item' ],[ 30, // Code 30 // Item ]); end; class function TLookupCode.FindFormCaption : string; begin Result := 'Find Lookup Code'; end; class function TLookupCode.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Code'); end; function TLookupCode.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Code else if ACol = 1 then Result := Utilities.ItemName(ItemId) else Result := ''; end; class procedure TLookupCode.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TLookupCode.MaintainFormCaption : string; begin Result := 'Maintain Lookup Codes'; end; class function TLookupCode.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TLookupCode.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TLookupCode.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TLookupCode.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Code := Value; end; function TLookupCode.MaintainStringGridDblClick (ACol : integer) : boolean; var DatabaseObject : TDatabaseObject; begin Result := false; if ACol = 1 then begin DatabaseObject := Find(TItem); if DatabaseObject <> nil then begin ItemId := TItem(DatabaseObject).Id; DatabaseObject.Free; Result := true; end; end; end; procedure TLookupCode.ProcessUpdate; begin if POSMode then SavePOSData; end; class procedure TLookupCode.ProcessDelete (Id : int64); begin if POSMode then SavePOSData; end; class function TLookupCode.HeadingString : string; begin Result := 'Lookup Codes'; end; class function TLookupCode.ColumnHeadingsString : string; begin Result := 'Code Item'; end; function TLookupCode.DetailsString : string; begin Result := Format('%-30s %-30s ',[Code,Utilities.ItemName(ItemId)]); end; {***** TSalesperson methods *************************************************************} class function TSalesperson.TableName : string; begin Result := 'Salesperson'; end; class procedure TSalesperson.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,20,false); Table.FieldDefs.Add('Password',ftString,20,false); Table.FieldDefs.Add('Active',ftInteger,0,false); end; class procedure TSalesperson.UpdateTable; begin // add code to update old table structures here end; procedure TSalesperson.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; Password := TStringField(Table.FieldByName('Password')).Value; Active := TIntegerField(Table.FieldByName('Active')).Value <> 0; end; procedure TSalesperson.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('Password')).Value := Password; TIntegerField(Table.FieldByName('Active')).Value := integer(Active); end; function TSalesperson.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); if Firebird then Result := Result + ',' + DelimitSQLFieldName('Password') else Result := Result + ',Salesperson."Password"'; if Firebird then Result := Result + ',' + DelimitSQLFieldName('Active') else Result := Result + ',Salesperson."Active"'; end; function TSalesperson.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,20); Result := Result + ',' + DelimitSQLStringValue(Password,20); Result := Result + ',' + IntToStr(integer(Active)); end; procedure TSalesperson.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Password := ReadStrFromStream(Stream); Stream.Read(Active,SizeOf(Active)); end; procedure TSalesperson.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(Password,Stream); Stream.Write(Active,SizeOf(Active)); end; procedure TSalesperson.SetNewEntryValues; begin Active := true; end; function TSalesperson.HasReferences : boolean; var SelectionString : string; begin Result := true; SelectionString := 'WHERE ' + DelimitSQLFieldName('SalespersonId') + ' = ' + IntToStr(Id); if CountDatabaseObjects(TSale,SelectionString) > 0 then Exit; Result := false; end; class procedure TSalesperson.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Name', 'Password', 'Active' ],[ 20, // Name 20, // Password 8 // Active ]); end; class function TSalesperson.FindFormCaption : string; begin Result := 'Find Salesperson'; end; class function TSalesperson.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Name'); end; function TSalesperson.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then begin if Length(Password) > 0 then Result := '**********' else Result := ''; end else if ACol = 2 then Result := FormatBoolean(Active) else Result := ''; end; class procedure TSalesperson.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TSalesperson.MaintainFormCaption : string; begin Result := 'Maintain Salespersons'; end; class function TSalesperson.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TSalesperson.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TSalesperson.MaintainStringGridGetEditText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then Result := UnencryptedPassword else if ACol = 2 then Result := FormatBoolean(Active) else Result := ''; end; procedure TSalesperson.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Name := UpperCase(Value) else if ACol = 1 then Password := ReplaceNullsEtc(Encrypt(LowerCase(Value),UserPasswordEncryptRandSeed)) else if ACol = 2 then Active := (UpperCase(Value) = 'Y') end; function TSalesperson.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; if ACol = 2 then begin Active := not Active; Result := true; end; end; procedure TSalesperson.ProcessUpdate; begin if POSMode then SavePOSData; end; class procedure TSalesperson.ProcessDelete (Id : int64); begin if POSMode then SavePOSData; end; class function TSalesperson.HeadingString : string; begin Result := 'Salespersons'; end; class function TSalesperson.ColumnHeadingsString : string; begin Result := 'Name'; end; function TSalesperson.DetailsString : string; begin Result := Format('%-20s',[Name]); end; function TSalesperson.UnencryptedPassword : string; begin Result := Encrypt(RestoreNullsEtc(Password),UserPasswordEncryptRandSeed); end; {***** TPaymentType methods *************************************************************} class function TPaymentType.TableName : string; begin Result := 'PaymentType'; end; class procedure TPaymentType.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('KeyboardShortcut',ftString,1,false); Table.FieldDefs.Add('OpenCashDrawer',ftInteger,0,false); Table.FieldDefs.Add('EFT',ftInteger,0,false); Table.FieldDefs.Add('PromptCashOut',ftInteger,0,false); Table.FieldDefs.Add('PromptDetails',ftInteger,0,false); Table.FieldDefs.Add('Active',ftInteger,0,false); end; class procedure TPaymentType.UpdateTable; begin // add code to update old table structures here end; procedure TPaymentType.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; if Length(TStringField(Table.FieldByName('KeyboardShortcut')).Value) > 0 then KeyboardShortcut := TStringField(Table.FieldByName('KeyboardShortcut')).Value[1] else KeyboardShortcut := ' '; OpenCashDrawer := TIntegerField(Table.FieldByName('OpenCashDrawer')).Value <> 0; EFT := TIntegerField(Table.FieldByName('EFT')).Value <> 0; PromptCashOut := TIntegerField(Table.FieldByName('PromptCashOut')).Value <> 0; PromptDetails := TIntegerField(Table.FieldByName('PromptDetails')).Value <> 0; Active := TIntegerField(Table.FieldByName('Active')).Value <> 0; end; procedure TPaymentType.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('KeyboardShortcut')).Value := KeyboardShortcut; TIntegerField(Table.FieldByName('OpenCashDrawer')).Value := integer(OpenCashDrawer); TIntegerField(Table.FieldByName('EFT')).Value := integer(EFT); TIntegerField(Table.FieldByName('PromptCashOut')).Value := integer(PromptCashOut); TIntegerField(Table.FieldByName('PromptDetails')).Value := integer(PromptDetails); TIntegerField(Table.FieldByName('Active')).Value := integer(Active); end; function TPaymentType.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('KeyboardShortcut'); Result := Result + ',' + DelimitSQLFieldName('OpenCashDrawer'); Result := Result + ',' + DelimitSQLFieldName('EFT'); Result := Result + ',' + DelimitSQLFieldName('PromptCashOut'); Result := Result + ',' + DelimitSQLFieldName('PromptDetails'); if Firebird then Result := Result + ',' + DelimitSQLFieldName('Active') else Result := Result + ',PaymentType."Active"'; end; function TPaymentType.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(KeyboardShortcut,1); Result := Result + ',' + IntToStr(integer(OpenCashDrawer)); Result := Result + ',' + IntToStr(integer(EFT)); Result := Result + ',' + IntToStr(integer(PromptCashOut)); Result := Result + ',' + IntToStr(integer(PromptDetails)); Result := Result + ',' + IntToStr(integer(Active)); end; procedure TPaymentType.LoadFromStream (Stream : TStream); var Str : string; begin inherited; Name := ReadStrFromStream(Stream); Str := ReadStrFromStream(Stream); if Length(Str) > 0 then KeyboardShortcut := Str[1] else KeyboardShortcut := ' '; Stream.Read(OpenCashDrawer,SizeOf(OpenCashDrawer)); Stream.Read(EFT,SizeOf(EFT)); Stream.Read(PromptCashOut,SizeOf(PromptCashOut)); Stream.Read(PromptDetails,SizeOf(PromptDetails)); Stream.Read(Active,SizeOf(Active)); end; procedure TPaymentType.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); WriteStrToStream(KeyboardShortcut,Stream); Stream.Write(OpenCashDrawer,SizeOf(OpenCashDrawer)); Stream.Write(EFT,SizeOf(EFT)); Stream.Write(PromptCashOut,SizeOf(PromptCashOut)); Stream.Write(PromptDetails,SizeOf(PromptDetails)); Stream.Write(Active,SizeOf(Active)); end; procedure TPaymentType.SetNewEntryValues; begin KeyboardShortcut := ' '; end; function TPaymentType.HasReferences : boolean; var SelectionString : string; begin Result := true; SelectionString := 'WHERE ' + DelimitSQLFieldName('PaymentTypeId') + ' = ' + IntToStr(Id); if CountDatabaseObjects(TPaymentItem,SelectionString) > 0 then Exit; Result := false; end; class procedure TPaymentType.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Name', 'Keyboard', 'Open Cash Drawer', 'EFT', 'Prompt Cash Out', 'Prompt Details', 'Active' ],[ 23, // Name 11, // Shortcut 19, // Open Cash Drawer 7, // EFT 17, // Prompt Cash Out 15, // Prompt Details 8 // Active ]); end; class function TPaymentType.FindFormCaption : string; begin Result := 'Find Payment Type'; end; class function TPaymentType.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Name'); end; function TPaymentType.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then Result := KeyboardShortcut else if ACol = 2 then Result := FormatBoolean(OpenCashDrawer) else if ACol = 3 then Result := FormatBoolean(EFT) else if ACol = 4 then Result := FormatBoolean(PromptCashOut) else if ACol = 5 then Result := FormatBoolean(PromptDetails) else if ACol = 6 then Result := FormatBoolean(Active) else Result := ''; end; class procedure TPaymentType.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TPaymentType.MaintainFormCaption : string; begin Result := 'Maintain Payment Types'; end; class function TPaymentType.MaintainSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Name'); end; function TPaymentType.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TPaymentType.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TPaymentType.MaintainStringGridSetEditText (ACol : integer; Value : string); begin if ACol = 0 then Name := Value else if ACol = 1 then begin if Length(Value) > 0 then KeyboardShortcut := UpperCase(Value)[1] else KeyboardShortcut := ' '; end else if ACol = 2 then OpenCashDrawer := (UpperCase(Value) = 'Y') else if ACol = 3 then EFT := (UpperCase(Value) = 'Y') else if ACol = 4 then PromptCashOut := (UpperCase(Value) = 'Y') else if ACol = 5 then PromptDetails := (UpperCase(Value) = 'Y') else if ACol = 6 then Active := (UpperCase(Value) = 'Y') end; function TPaymentType.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; if ACol = 2 then begin OpenCashDrawer := not OpenCashDrawer; Result := true; end else if ACol = 3 then begin EFT := not EFT; Result := true; end else if ACol = 4 then begin PromptCashOut := not PromptCashOut; Result := true; end else if ACol = 5 then begin PromptDetails := not PromptDetails; Result := true; end else if ACol = 6 then begin Active := not Active; Result := true; end; end; procedure TPaymentType.ProcessUpdate; begin if POSMode then SavePOSData; end; class procedure TPaymentType.ProcessDelete (Id : int64); begin if POSMode then SavePOSData; end; class function TPaymentType.HeadingString : string; begin Result := 'Payment Types'; end; class function TPaymentType.ColumnHeadingsString : string; begin Result := 'Name Key Open Cash Drawer EFT Prompt Cash Out Prompt Details Active'; end; function TPaymentType.DetailsString : string; begin Result := Format('%-20s%-1s %-1s %-1s %-1s %-1s %-1s',[Name,KeyboardShortcut,FormatBoolean(OpenCashDrawer),FormatBoolean(EFT),FormatBoolean(PromptCashOut),FormatBoolean(PromptDetails),FormatBoolean(Active)]); end; function TPaymentType.ComboBoxDisplayString : string; begin Result := Format('%s',[Name]); end; {***** TSale methods *************************************************************} class function TSale.TableName : string; begin Result := 'Sale'; end; class procedure TSale.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Date',ftString,10,false); Table.FieldDefs.Add('Time',ftString,10,false); Table.FieldDefs.Add('SalespersonId',IdFieldType,0,false); end; class procedure TSale.UpdateTable; begin // add code to update old table structures here end; procedure TSale.LoadFromTable (Table : TDataset); begin inherited; DateTime := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('Date')).Value) + ConvertDatabaseStringToTime(TStringField(Table.FieldByName('Time')).Value); SalespersonId := IdFieldValue(Table.FieldByName('SalespersonId')); end; procedure TSale.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Date')).Value := ConvertDateToDatabaseString(DateTime); TStringField(Table.FieldByName('Time')).Value := ConvertTimeToDatabaseString(DateTime); SetIdFieldValue(Table.FieldByName('SalespersonId'),SalespersonId); end; function TSale.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Date'); Result := Result + ',' + DelimitSQLFieldName('Time'); Result := Result + ',' + DelimitSQLFieldName('SalespersonId'); end; function TSale.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(DateTime),10); Result := Result + ',' + DelimitSQLStringValue(ConvertTimeToDatabaseString(DateTime),10); Result := Result + ',' + IntToStr(SalespersonId); end; procedure TSale.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(DateTime,SizeOf(DateTime)); Stream.Read(SalespersonId,SizeOf(SalespersonId)); end; procedure TSale.SaveToStream (Stream : TStream); begin inherited; Stream.Write(DateTime,SizeOf(DateTime)); Stream.Write(SalespersonId,SizeOf(SalespersonId)); end; destructor TSale.Destroy; begin FSaleItems.Free; FPaymentItems.Free; inherited; end; procedure TSale.SaveDetailsToDatabase; var i : integer; begin for i := 0 to SaleItems.Count - 1 do TSaleItem(SaleItems[i]).SaleId := Id; SaleItems.SaveToDatabase(TSaleItem,false); for i := 0 to PaymentItems.Count - 1 do TPaymentItem(PaymentItems[i]).SaleId := Id; PaymentItems.SaveToDatabase(TPaymentItem,false); end; procedure TSale.DeleteDetailsFromDatabase (BeforeSave : boolean); var SelectionString : string; begin SelectionString := 'WHERE ' + DelimitSQLFieldName('SaleId') + ' = ' + IntToStr(Id); DeleteSomeDatabaseObjects(TSaleItem,SelectionString); DeleteSomeDatabaseObjects(TPaymentItem,SelectionString); end; procedure TSale.LoadDetailsFromStream (Stream : TStream); begin SaleItems.Clear; SaleItems.LoadFromStream(Stream); PaymentItems.Clear; PaymentItems.LoadFromStream(Stream); end; procedure TSale.SaveDetailsToStream (Stream : TStream); begin SaleItems.SaveToStream(Stream); PaymentItems.SaveToStream(Stream); end; class procedure TSale.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Date', 'Time', 'Salesperson' ],[ 12, // Date 12, // Time 30 // Salesperson ]); end; class function TSale.FindFormCaption : string; begin Result := 'Find Sale'; end; class function TSale.FindSelectionString : string; begin if Firebird then Result := 'ORDER BY ' + DelimitSQLFieldName('Date') + 'DESC, ' + DelimitSQLFieldName('Time') + ' DESC' else Result := 'ORDER BY Sale."Date" DESC, Sale."Time" DESC'; end; function TSale.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := FormatDate(Trunc(DateTime)) else if ACol = 1 then Result := FormatTime(DateTime) else if ACol = 2 then Result := Utilities.SalespersonName(SalespersonId) else Result := ''; end; class procedure TSale.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TSale.MaintainFormCaption : string; begin Result := 'Maintain Sales'; end; class function TSale.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TSale.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TSale.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TSale.MaintainStringGridSetEditText (ACol : integer; Value : string); begin end; function TSale.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; procedure TSale.ProcessUpdate; begin if MainForm <> nil then MainForm.SalesFrame.UpdateSale(TSale(Self)); end; class procedure TSale.ProcessDelete (Id : int64); begin // do nothing, should never happen! end; class function TSale.HeadingString : string; begin Result := 'Sales'; end; class function TSale.ColumnHeadingsString : string; begin Result := 'Date Time Salesperson'; end; function TSale.DetailsString : string; begin Result := Format('%-10s %-10s %-30s',[FormatDate(Trunc(DateTime)),FormatTime(DateTime),Utilities.SalespersonName(SalespersonId)]); end; class procedure TSale.ShowHeadingsOnSalesReport (ReportData : TReportData); var Str : string; begin // display headings Str := ''; Str := Str + 'Date '; Str := Str + 'Time '; Str := Str + 'Salesperson '; Str := Str + ' Total Price '; Str := Str + ' Items '; Str := Str + ' Payment Type'; ReportData.WriteBoldLine(Str); end; procedure TSale.ShowOnSalesReport (ReportData : TReportData); var Str : string; begin // display details Str := ''; Str := Str + Format('%-8.8s ',[ShortFormatDate(Trunc(DateTime))]); Str := Str + FormatDateTime('hh:mm:ss ',DateTime); Str := Str + Format('%-15.15s ',[SalespersonName(SalespersonId)]); Str := Str + Format(' %15.15s',[FormatCurrencyForDisplay(TotalPrice)]); Str := Str + Format(' %-35.35s ',[SaleItemsAsString]); Str := Str + Format(' %-25.25s ',[PaymentItemsAsString]); ReportData.WriteLine(Str); end; // call this before accessing any method that uses // detail collections when operating in a multi-threaded // environment procedure TSale.LoadDetails; var SelectionString : string; begin SelectionString := 'WHERE ' + DelimitSQLFieldName('SaleId') + ' = ' + IntToStr(Id); LoadSomeDatabaseObjects(FSaleItems,TSaleItem,SelectionString); LoadSomeDatabaseObjects(FPaymentItems,TPaymentItem,SelectionString); end; function TSale.SaleItems : TDatabaseObjectCollection; var SelectionString : string; begin if FSaleItems = nil then begin if Id = 0 then begin FSaleItems := TDatabaseObjectCollection.Create; FSaleItems.Owned := true; end else begin SelectionString := 'WHERE ' + DelimitSQLFieldName('SaleId') + ' = ' + IntToStr(Id); LoadSomeDatabaseObjects(FSaleItems,TSaleItem,SelectionString); end; end; Result := FSaleItems; end; function TSale.PaymentItems : TDatabaseObjectCollection; var SelectionString : string; begin if FPaymentItems = nil then begin if Id = 0 then begin FPaymentItems := TDatabaseObjectCollection.Create; FPaymentItems.Owned := true; end else begin SelectionString := 'WHERE ' + DelimitSQLFieldName('SaleId') + ' = ' + IntToStr(Id); LoadSomeDatabaseObjects(FPaymentItems,TPaymentItem,SelectionString); end; end; Result := FPaymentItems; end; function TSale.TotalPrice : int64; var i : integer; SaleItem : TSaleItem; begin Result := 0; for i := 0 to SaleItems.Count - 1 do begin SaleItem := TSaleItem(SaleItems[i]); Result := Result + SaleItem.NetPrice; end; end; function TSale.TotalPayment : int64; var i : integer; PaymentItem : TPaymentItem; begin Result := 0; for i := 0 to PaymentItems.Count - 1 do begin PaymentItem := TPaymentItem(PaymentItems[i]); Result := Result + PaymentItem.Amount; end; end; function TSale.Change : int64; begin Result := TotalPayment - TotalPrice; end; procedure TSale.OpenCashDrawer; var i : integer; Open : boolean; begin Open := false; for i := 0 to PaymentItems.Count - 1 do if TPaymentItem(PaymentItems[i]).PaymentType.OpenCashDrawer then begin Open := true; break; end; if Open then Utilities.OpenCashDrawer; end; procedure TSale.PrintReceipt (Preview : boolean); begin Utilities.PrintReceipt(Self,Preview); end; function TSale.SaleItemsAsString : string; var i : integer; Str : string; begin Result := ''; for i := 0 to SaleItems.Count - 1 do begin Str := ItemName(TSaleItem(SaleItems[i]).ItemId); if TSaleItem(SaleItems[i]).Quantity <> 1 then Str := Str + '(' + ConvertDoubleToString(TSaleItem(SaleItems[i]).Quantity) + ')'; Result := CommaConcat(Result,Str); end; end; function TSale.PaymentItemsAsString : string; var i : integer; Str : string; begin Result := ''; for i := 0 to PaymentItems.Count - 1 do begin Str := PaymentTypeName(TPaymentItem(PaymentItems[i]).PaymentTypeId); Result := CommaConcat(Result,Str); end; end; function TSale.IncludesItem (ItemId : int64) : boolean; var i : integer; begin Result := false; for i := 0 to SaleItems.Count - 1 do if TSaleItem(SaleItems[i]).ItemId = ItemId then begin Result := true; Exit; end; end; function TSale.IncludesPaymentType (PaymentTypeId : int64) : boolean; var i : integer; begin Result := false; for i := 0 to PaymentItems.Count - 1 do if TPaymentItem(PaymentItems[i]).PaymentTypeId = PaymentTypeId then begin Result := true; Exit; end; end; function TSale.Date : TDateTime; begin Result := Trunc(DateTime); end; function TSale.Time : TDateTime; begin Result := DateTime - Trunc(DateTime); end; {***** TSaleItem methods *************************************************************} class function TSaleItem.TableName : string; begin Result := 'SaleItem'; end; class procedure TSaleItem.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('SaleId',IdFieldType,0,false); Table.FieldDefs.Add('ItemId',IdFieldType,0,false); Table.FieldDefs.Add('Quantity',ftString,20,false); Table.FieldDefs.Add('FullPrice',CurrencyFieldType,CurrencyFieldSize,false); Table.FieldDefs.Add('Discount',CurrencyFieldType,CurrencyFieldSize,false); end; class procedure TSaleItem.UpdateTable; begin // add code to update old table structures here end; procedure TSaleItem.LoadFromTable (Table : TDataset); begin inherited; SaleId := IdFieldValue(Table.FieldByName('SaleId')); ItemId := IdFieldValue(Table.FieldByName('ItemId')); Quantity := ConvertStringToDouble(TStringField(Table.FieldByName('Quantity')).Value); FullPrice := CurrencyFieldValue(Table.FieldByName('FullPrice')); Discount := CurrencyFieldValue(Table.FieldByName('Discount')); end; procedure TSaleItem.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('SaleId'),SaleId); SetIdFieldValue(Table.FieldByName('ItemId'),ItemId); TStringField(Table.FieldByName('Quantity')).Value := ConvertDoubleToString(Quantity); SetCurrencyFieldValue(Table.FieldByName('FullPrice'),FullPrice); SetCurrencyFieldValue(Table.FieldByName('Discount'),Discount); end; function TSaleItem.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('SaleId'); Result := Result + ',' + DelimitSQLFieldName('ItemId'); Result := Result + ',' + DelimitSQLFieldName('Quantity'); Result := Result + ',' + DelimitSQLFieldName('FullPrice'); Result := Result + ',' + DelimitSQLFieldName('Discount'); end; function TSaleItem.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(SaleId); Result := Result + ',' + IntToStr(ItemId); Result := Result + ',' + DelimitSQLStringValue(ConvertDoubleToString(Quantity),20); Result := Result + ',' + CurrencyToSQLStr(FullPrice); Result := Result + ',' + CurrencyToSQLStr(Discount); end; procedure TSaleItem.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(SaleId,SizeOf(SaleId)); Stream.Read(ItemId,SizeOf(ItemId)); Stream.Read(Quantity,SizeOf(Quantity)); Stream.Read(FullPrice,SizeOf(FullPrice)); Stream.Read(Discount,SizeOf(Discount)); end; procedure TSaleItem.SaveToStream (Stream : TStream); begin inherited; Stream.Write(SaleId,SizeOf(SaleId)); Stream.Write(ItemId,SizeOf(ItemId)); Stream.Write(Quantity,SizeOf(Quantity)); Stream.Write(FullPrice,SizeOf(FullPrice)); Stream.Write(Discount,SizeOf(Discount)); end; destructor TSaleItem.Destroy; begin if FSaleOwned then FSale.Free; inherited; end; class procedure TSaleItem.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Sale', 'Item', 'Quantity', 'Full Price', 'Discount' ],[ 10, // Sale 30, // Item 10, // Quantity 10, // Full Price 10 // Discount ]); end; class function TSaleItem.FindFormCaption : string; begin Result := 'Find Sale Item'; end; class function TSaleItem.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('SaleId') + ' DESC'; end; function TSaleItem.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(SaleId) else if ACol = 1 then Result := Utilities.ItemName(ItemId) else if ACol = 2 then Result := ConvertDoubleToString(Quantity) else if ACol = 3 then Result := FormatCurrency(FullPrice) else if ACol = 4 then Result := FormatCurrency(Discount) else Result := ''; end; class procedure TSaleItem.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TSaleItem.MaintainFormCaption : string; begin Result := 'Maintain Sale Items'; end; class function TSaleItem.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TSaleItem.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TSaleItem.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TSaleItem.MaintainStringGridSetEditText (ACol : integer; Value : string); begin end; function TSaleItem.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; class function TSaleItem.HeadingString : string; begin Result := 'Sale Items'; end; class function TSaleItem.ColumnHeadingsString : string; begin Result := 'Qty Description Price Discount Net'; end; function TSaleItem.DetailsString : string; begin Result := Format('%-5s %-30s %15s %15s %15s', [FloatToStr(Quantity), Utilities.ItemName(ItemId), FormatCurrencyForDisplay(FullPrice), FormatCurrencyForDisplay(Discount), FormatCurrencyForDisplay(NetPrice)]); end; class procedure TSaleItem.ShowHeadingsOnSalesReport (ReportData : TReportData); var Str : string; begin // display headings Str := ''; Str := Str + ' Qty '; Str := Str + ' Item '; Str := Str + ' Full Price '; Str := Str + ' Discount '; Str := Str + ' Net Price '; Str := Str + ' Date '; Str := Str + ' Payment Type'; ReportData.WriteBoldLine(Str); end; procedure TSaleItem.ShowOnSalesReport (ReportData : TReportData); var Str : string; begin // display details Str := ''; Str := Str + Format(' %-5s ',[FloatToStr(Quantity)]); Str := Str + Format(' %-30.30s ',[ItemName(ItemId)]); Str := Str + Format(' %15.15s',[FormatCurrencyForDisplay(FullPrice)]); Str := Str + Format(' %15.15s',[FormatCurrencyForDisplay(Discount)]); Str := Str + Format(' %15.15s',[FormatCurrencyForDisplay(NetPrice)]); Str := Str + Format(' %-8.8s',[ShortFormatDate(Trunc(Sale.DateTime))]); Str := Str + Format(' %-25.25s ',[Sale.PaymentItemsAsString]); ReportData.WriteLine(Str); end; function TSaleItem.NetPrice : int64; begin Result := FullPrice - Discount; end; procedure TSaleItem.SetSale (Sale : TSale); begin // destroy any existing sale if it is owned if (FSale <> nil) and FSaleOwned then FSale.Free; // assign new reference to order FSale := Sale; // but let assignor retain ownership FSaleOwned := false; end; function TSaleItem.Sale : TSale; begin // if a sale has been assigned then return a reference to it if (FSale <> nil) and (not FSaleOwned) then begin Result := FSale; Exit; end; // first check that sale is correct and if not destroy this instance if (FSale <> nil) and (FSale.Id <> SaleId) then begin FSale.Free; FSale := nil; end; if FSale = nil then FSale := TSale(LoadDatabaseObject(TSale,'Id = ' + IntToStr(SaleId))); FSaleOwned := true; Result := FSale; end; {***** TPaymentItem methods *************************************************************} class function TPaymentItem.TableName : string; begin Result := 'PaymentItem'; end; class procedure TPaymentItem.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('SaleId',IdFieldType,0,false); Table.FieldDefs.Add('PaymentTypeId',IdFieldType,0,false); Table.FieldDefs.Add('Amount',CurrencyFieldType,CurrencyFieldSize,false); Table.FieldDefs.Add('Details',ftString,50,false); end; class procedure TPaymentItem.UpdateTable; begin // add code to update old table structures here end; procedure TPaymentItem.LoadFromTable (Table : TDataset); begin inherited; SaleId := IdFieldValue(Table.FieldByName('SaleId')); PaymentTypeId := IdFieldValue(Table.FieldByName('PaymentTypeId')); Amount := CurrencyFieldValue(Table.FieldByName('Amount')); Details := TStringField(Table.FieldByName('Details')).Value; end; procedure TPaymentItem.SaveToTable (Table : TDataset); begin inherited; SetIdFieldValue(Table.FieldByName('SaleId'),SaleId); SetIdFieldValue(Table.FieldByName('PaymentTypeId'),PaymentTypeId); SetCurrencyFieldValue(Table.FieldByName('Amount'),Amount); TStringField(Table.FieldByName('Details')).Value := Details; end; function TPaymentItem.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('SaleId'); Result := Result + ',' + DelimitSQLFieldName('PaymentTypeId'); Result := Result + ',' + DelimitSQLFieldName('Amount'); Result := Result + ',' + DelimitSQLFieldName('Details'); end; function TPaymentItem.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + IntToStr(SaleId); Result := Result + ',' + IntToStr(PaymentTypeId); Result := Result + ',' + CurrencyToSQLStr(Amount); Result := Result + ',' + DelimitSQLStringValue(Details,50); end; procedure TPaymentItem.LoadFromStream (Stream : TStream); begin inherited; Stream.Read(SaleId,SizeOf(SaleId)); Stream.Read(PaymentTypeId,SizeOf(PaymentTypeId)); Stream.Read(Amount,SizeOf(Amount)); Details := ReadStrFromStream(Stream); end; procedure TPaymentItem.SaveToStream (Stream : TStream); begin inherited; Stream.Write(SaleId,SizeOf(SaleId)); Stream.Write(PaymentTypeId,SizeOf(PaymentTypeId)); Stream.Write(Amount,SizeOf(Amount)); WriteStrToStream(Details,Stream); end; destructor TPaymentItem.Destroy; begin if FSaleOwned then FSale.Free; inherited; end; class procedure TPaymentItem.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Sale', 'Payment Type', 'Amount', 'Details' ],[ 10, // Sale 30, // Payment Type 10, // Amount 30 // Details ]); end; class function TPaymentItem.FindFormCaption : string; begin Result := 'Find Payment Item'; end; class function TPaymentItem.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('SaleId') + ' DESC'; end; function TPaymentItem.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := IntToStr(SaleId) else if ACol = 1 then Result := Utilities.PaymentTypeName(PaymentTypeId) else if ACol = 2 then Result := FormatCurrency(Amount) else if ACol = 3 then Result := Details else Result := ''; end; class procedure TPaymentItem.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TPaymentItem.MaintainFormCaption : string; begin Result := 'Maintain Payment Items'; end; class function TPaymentItem.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TPaymentItem.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TPaymentItem.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TPaymentItem.MaintainStringGridSetEditText (ACol : integer; Value : string); begin end; function TPaymentItem.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; class function TPaymentItem.HeadingString : string; begin Result := 'Payment Items'; end; class function TPaymentItem.ColumnHeadingsString : string; begin Result := ' Payment Details Amount'; end; function TPaymentItem.DetailsString : string; var Str : string; begin Str := PaymentTypeName(PaymentTypeId); if Amount < 0 then Str := Str + ' Refund'; if Details <> '' then Str := Str + ' - ' + Details; Result := Format('%-5s %-30s %15s %15s %15s', ['', Str, '', '', FormatCurrencyForDisplay(Amount)]); end; class procedure TPaymentItem.ShowHeadingsOnSalesReport (ReportData : TReportData); var Str : string; begin // display headings Str := ''; Str := Str + ' Payment '; Str := Str + ' Details '; Str := Str + ' Amount '; Str := Str + ' Date '; Str := Str + ' Time '; Str := Str + ' Salesperson'; ReportData.WriteBoldLine(Str); end; procedure TPaymentItem.ShowOnSalesReport (ReportData : TReportData); var Str : string; begin // display details Str := ''; Str := Str + Format(' %-25.25s ',[PaymentTypeName(PaymentTypeId)]); Str := Str + Format(' %-25.25s ',[Details]); Str := Str + Format(' %15.15s',[FormatCurrencyForDisplay(Amount)]); Str := Str + Format(' %-8.8s',[ShortFormatDate(Trunc(Sale.DateTime))]); Str := Str + FormatDateTime(' hh:mm:ss',Sale.DateTime); Str := Str + Format(' %-15.15s ',[SalespersonName(Sale.SalespersonId)]); ReportData.WriteLine(Str); end; procedure TPaymentItem.SetSale (Sale : TSale); begin // destroy any existing sale if it is owned if (FSale <> nil) and FSaleOwned then FSale.Free; // assign new reference to order FSale := Sale; // but let assignor retain ownership FSaleOwned := false; end; function TPaymentItem.Sale : TSale; begin // if a sale has been assigned then return a reference to it if (FSale <> nil) and (not FSaleOwned) then begin Result := FSale; Exit; end; // first check that sale is correct and if not destroy this instance if (FSale <> nil) and (FSale.Id <> SaleId) then begin FSale.Free; FSale := nil; end; if FSale = nil then FSale := TSale(LoadDatabaseObject(TSale,'Id = ' + IntToStr(SaleId))); FSaleOwned := true; Result := FSale; end; function TPaymentItem.PaymentType : TPaymentType; begin Result := TPaymentType(PaymentTypes.ObjectsById[PaymentTypeId]); end; {***** TDocument methods ******************************************************} class function TDocument.TableName : string; begin Result := 'Document'; end; class procedure TDocument.AddNewTableFields (Table : TDataset); begin inherited; Table.FieldDefs.Add('Name',ftString,50,false); Table.FieldDefs.Add('EntryDate',ftString,10,false); end; class procedure TDocument.UpdateTable; begin // add code to update old table structures here end; procedure TDocument.LoadFromTable (Table : TDataset); begin inherited; Name := TStringField(Table.FieldByName('Name')).Value; EntryDate := ConvertDatabaseStringToDate(TStringField(Table.FieldByName('EntryDate')).Value); end; procedure TDocument.SaveToTable (Table : TDataset); begin inherited; TStringField(Table.FieldByName('Name')).Value := Name; TStringField(Table.FieldByName('EntryDate')).Value := ConvertDateToDatabaseString(EntryDate); end; function TDocument.InsertSQLStrColumnNames : string; begin Result := inherited InsertSQLStrColumnNames; Result := Result + ',' + DelimitSQLFieldName('Name'); Result := Result + ',' + DelimitSQLFieldName('EntryDate'); end; function TDocument.InsertSQLStrValues : string; begin Result := inherited InsertSQLStrValues; Result := Result + ',' + DelimitSQLStringValue(Name,50); Result := Result + ',' + DelimitSQLStringValue(ConvertDateToDatabaseString(EntryDate),10); end; procedure TDocument.LoadFromStream (Stream : TStream); begin inherited; Name := ReadStrFromStream(Stream); Stream.Read(EntryDate,SizeOf(EntryDate)); end; procedure TDocument.SaveToStream (Stream : TStream); begin inherited; WriteStrToStream(Name,Stream); Stream.Write(EntryDate,SizeOf(EntryDate)); end; procedure TDocument.SetNewEntryValues; begin if EntryDate = 0 then EntryDate := Date; end; constructor TDocument.Create; begin inherited; Notes := TNotesManager.Create(Self,1); Attachment := TAttachmentManager.Create(Self,1); end; destructor TDocument.Destroy; begin Notes.Free; Attachment.Free; inherited; end; procedure TDocument.SaveDetailsToDatabase; begin if Notes.Changed then Notes.Save; if Attachment.Changed then Attachment.Save; end; procedure TDocument.DeleteDetailsFromDatabase (BeforeSave : boolean); begin if BeforeSave and (not Notes.Changed) then // do nothing else Notes.Delete; if BeforeSave and (not Attachment.Changed) then // do nothing else Attachment.Delete; end; procedure TDocument.LoadDetailsFromStream (Stream : TStream); begin Notes.LoadFromStream(Stream); Attachment.LoadFromStream(Stream); end; procedure TDocument.SaveDetailsToStream (Stream : TStream); begin Notes.SaveToStream(Stream); Attachment.SaveToStream(Stream); end; class procedure TDocument.SetupFindStringGrid (StringGrid : TStringGrid); begin SetUpStringGrid(StringGrid,[ 'Description', 'File Name', 'Size', 'ID', 'Entry Date' ],[ 43, // Description 23, // File Name 14, // Size 10, // ID 10 // Entry Date ]); end; class function TDocument.FindFormCaption : string; begin Result := 'Find Document'; end; class function TDocument.FindSelectionString : string; begin Result := 'ORDER BY ' + DelimitSQLFieldName('Id') + ' DESC'; end; function TDocument.FindStringGridText (ACol : integer) : string; begin if ACol = 0 then Result := Name else if ACol = 1 then Result := Attachment.FileName else if ACol = 2 then begin if Attachment.FileName <> '' then Result := AddCommasToNumber(IntToStr(Attachment.FileSize)) else Result := ''; end else if ACol = 3 then begin if Attachment.AttachmentId <> 0 then Result := IntToStr(Attachment.AttachmentId) else Result := ''; end else if ACol = 4 then Result := FormatDate(EntryDate) else Result := ''; end; class procedure TDocument.SetupMaintainStringGrid (StringGrid : TStringGrid); begin SetupFindStringGrid(StringGrid); end; class function TDocument.MaintainFormCaption : string; begin Result := 'Maintain Documents'; end; class function TDocument.MaintainSelectionString : string; begin Result := FindSelectionString; end; function TDocument.MaintainStringGridDrawText (ACol : integer) : string; begin Result := FindStringGridText(ACol); end; function TDocument.MaintainStringGridGetEditText (ACol : integer) : string; begin Result := MaintainStringGridDrawText(ACol); end; procedure TDocument.MaintainStringGridSetEditText (ACol : integer; Value : string); begin end; function TDocument.MaintainStringGridDblClick (ACol : integer) : boolean; begin Result := false; end; class function TDocument.HeadingString : string; begin Result := 'Documents'; end; class function TDocument.ColumnHeadingsString : string; begin Result := 'Name '; end; function TDocument.DetailsString : string; begin Result := Format('%-30s',[Name]); end; {******************************************************************************} end.