{*******************************************************} { } { Responsive Software http://www.responsive.co.nz } { } { Copyright (c) 2003-2006 Responsive Software Limited } { } {*******************************************************} unit Entries; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, BaseFrameUnit, StdCtrls, ExtCtrls, Grids, ComCtrls, DatabaseObjects; type TEntriesFrame = class(TBaseFrame) Label1: TLabel; Bevel1: TBevel; Label5: TLabel; StatusLabel: TLabel; Label13: TLabel; Label10: TLabel; BalanceLabel: TLabel; BalanceHeadingLabel: TLabel; AccountComboBox: TComboBox; DateDateTimePicker: TDateTimePicker; EntriesStringGrid: TStringGrid; EditButton: TButton; CancelButton: TButton; SaveButton: TButton; AmountEdit: TEdit; DescriptionEdit: TEdit; DebitCreditRadioGroup: TRadioGroup; DebitRadioButton: TRadioButton; CreditRadioButton: TRadioButton; NewButton: TButton; DeleteButton: TButton; DescriptionLabel: TLabel; AccountTypeLabel: TLabel; HeadingLabel: TLabel; HeadingShape: TShape; DuplicateButton: TButton; procedure NewButtonClick(Sender: TObject); procedure EditButtonClick(Sender: TObject); procedure CancelButtonClick(Sender: TObject); procedure SaveButtonClick(Sender: TObject); procedure EntriesStringGridClick(Sender: TObject); procedure AccountComboBoxSelect(Sender: TObject); procedure DateDateTimePickerKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure DateDateTimePickerEnter(Sender: TObject); procedure DateDateTimePickerChange(Sender: TObject); procedure DescriptionEditChange(Sender: TObject); procedure AmountEditChange(Sender: TObject); procedure DebitRadioButtonKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure DebitRadioButtonClick(Sender: TObject); procedure CreditRadioButtonKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure CreditRadioButtonClick(Sender: TObject); procedure DeleteButtonClick(Sender: TObject); procedure AmountEditEnter(Sender: TObject); procedure EntriesStringGridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure DescriptionEditEnter(Sender: TObject); procedure EntriesStringGridDblClick(Sender: TObject); procedure DateDateTimePickerCloseUp(Sender: TObject); procedure DuplicateButtonClick(Sender: TObject); procedure AccountComboBoxKeyPress(Sender: TObject; var Key: Char); procedure DescriptionEditKeyPress(Sender: TObject; var Key: Char); procedure AmountEditKeyPress(Sender: TObject; var Key: Char); private { Private declarations } EditMode : boolean; // true if editing entries, otherwise view mode assumed IgnoreChangeEvents : boolean; // set this to true when updating controls in code FirstChangeEvent : boolean; // used to workaround bug in TDateTimePicker // CombinedEntry refers to the combined entries currently being viewed or edited CombinedEntry : TCombinedEntry; // Entry refers to the entry currently being viewed or edited Entry : TEntry; DefaultNewEntryDate : TDateTime; procedure CreateEntry; procedure SetEntry; procedure UpdateControls; procedure UpdateEntriesStringGrid; procedure UpdateControlStates; procedure CancelChanges; procedure SaveChanges; procedure DeleteCombinedEntry; function NewCombinedEntry : boolean; procedure SwitchToAccount; public { Public declarations } procedure UpdateDisplay; procedure UpdateComboBoxes (Full : boolean); override; procedure Setup; override; function CanClose : boolean; override; procedure HandleEscape; override; procedure HandleF6; override; procedure ClearDisplay; procedure ShowCombinedEntry (CombinedEntryId : int64); procedure PositionEntriesStringGrid (EntryId : int64); end; implementation uses GeneralUtilities, DatabaseManager, Globals, Base, Utilities, Main, Accounts; {$R *.dfm} function TEntriesFrame.NewCombinedEntry : boolean; begin Result := (CombinedEntry <> nil) and (CombinedEntry.Id = 0); end; procedure TEntriesFrame.CancelChanges; var CombinedEntryId : int64; begin if CombinedEntry <> nil then CombinedEntryId := CombinedEntry.Id else CombinedEntryId := 0; CombinedEntry.Free; try CombinedEntry := GetCombinedEntry(CombinedEntryId); except CombinedEntry := nil; end; EditMode := false; EntriesStringGrid.Row := 1; UpdateComboBoxes(false); UpdateDisplay; end; procedure TEntriesFrame.SaveChanges; var CombinedEntryId : int64; begin // if this is a new entry then record date to use as default // on subsequent entries if NewCombinedEntry then DefaultNewEntryDate := CombinedEntry.Date; // save combined entry to database if CombinedEntry <> nil then begin CombinedEntry.UpdateDatabase(true); // record id CombinedEntryId := CombinedEntry.Id; end else CombinedEntryId := 0; // reload from database after saving to cause entries // to be shown in correct order CombinedEntry.Free; CombinedEntry := GetCombinedEntry(CombinedEntryId); EditMode := false; EntriesStringGrid.Row := 1; UpdateComboBoxes(false); UpdateDisplay; // update account display also in case one of the entries // on the displayed account has changed MainForm.AccountsFrame.UpdateDisplay; MainForm.CashbooksFrame.UpdateDisplay; end; procedure TEntriesFrame.DeleteCombinedEntry; begin // delete combined entry entirely CombinedEntry.DeleteFromDatabase(true); CombinedEntry.Free; CombinedEntry := nil; EditMode := false; EntriesStringGrid.Row := 1; UpdateComboBoxes(false); UpdateDisplay; // update account display also in case one of the entries // on the displayed account has changed MainForm.AccountsFrame.UpdateDisplay; MainForm.CashbooksFrame.UpdateDisplay; end; function TEntriesFrame.CanClose : boolean; begin if EditMode then Result := false else Result := true; end; procedure TEntriesFrame.HandleEscape; begin if EditMode then if MessageDlg('Do you wish to cancel changes to this entry?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then CancelChanges; end; procedure TEntriesFrame.HandleF6; begin MainForm.SwitchToFrame('Accounts'); end; procedure TEntriesFrame.Setup; begin SetUpStringGrid(EntriesStringGrid,[ 'A/c', 'Description', 'Debit', 'Credit' ],[ 25, // A/c 37, // Description 13, // Debit 13 // Credit ]); // create and initialise objects EditMode := false; EntriesStringGrid.Row := 1; UpdateComboBoxes(false); UpdateDisplay; end; procedure TEntriesFrame.CreateEntry; begin if CombinedEntry = nil then Exit; if Entry = nil then begin Entry := TEntry.Create; CombinedEntry.Entries.Add(Entry); end; end; procedure TEntriesFrame.SetEntry; var Index : integer; begin Index := EntriesStringGrid.Row - 1; if (CombinedEntry <> nil) and (Index < CombinedEntry.Entries.Count) then Entry := TEntry(CombinedEntry.Entries.Objects[Index]) else Entry := nil; end; procedure TEntriesFrame.UpdateComboBoxes (Full : boolean); var i : integer; begin AccountComboBox.Items.Clear; for i := 0 to Globals.Accounts.Count - 1 do if (not EditMode) or (TAccount(Globals.Accounts[i]).CompanyId = WorkstationConfiguration.CompanyId) then AccountComboBox.Items.Add(TAccount(Globals.Accounts[i]).ComboBoxDisplayString); end; procedure TEntriesFrame.UpdateControls; begin IgnoreChangeEvents := true; if CombinedEntry <> nil then begin DateDateTimePicker.DateTime := CombinedEntry.Date; BalanceHeadingLabel.Visible := true; end else begin DateDateTimePicker.DateTime := Date; BalanceHeadingLabel.Visible := false; end; if Entry <> nil then begin if (Entry.Account <> nil) then begin AccountComboBox.ItemIndex := AccountComboBox.Items.IndexOf(Entry.Account.ComboBoxDisplayString); DescriptionLabel.Caption := Entry.Account.Description; AccountTypeLabel.Caption := Entry.Account.AccountTypeString + ' - ' + Entry.Account.AccountTypeDescription; end else begin AccountComboBox.ItemIndex := -1; DescriptionLabel.Caption := ''; AccountTypeLabel.Caption := ''; end; DescriptionEdit.Text := Entry.Description; AmountEdit.Text := FormatCurrency(Entry.AbsoluteAmount); if Entry.Debit then DebitRadioButton.Checked := true else CreditRadioButton.Checked := true; end else begin AccountComboBox.ItemIndex := -1; DescriptionLabel.Caption := ''; AccountTypeLabel.Caption := ''; DescriptionEdit.Text := ''; AmountEdit.Text := ''; DebitRadioButton.Checked := false; CreditRadioButton.Checked := false; end; IgnoreChangeEvents := false; end; procedure TEntriesFrame.UpdateEntriesStringGrid; var i : integer; Entry : TEntry; Str : string; begin // display entries in string grid if CombinedEntry <> nil then begin EntriesStringGrid.RowCount := CombinedEntry.Entries.Count + 2; for i := 0 to CombinedEntry.Entries.Count - 1 do begin Entry := TEntry(CombinedEntry.Entries[i]); EntriesStringGrid.Cells[0,i+1] := Entry.AccountName; EntriesStringGrid.Cells[1,i+1] := Entry.Description; EntriesStringGrid.Cells[2,i+1] := ''; EntriesStringGrid.Cells[3,i+1] := ''; if Entry.Debit then EntriesStringGrid.Cells[2,i+1] := FormatCurrencyForDisplay(Entry.AbsoluteAmount) else EntriesStringGrid.Cells[3,i+1] := FormatCurrencyForDisplay(Entry.AbsoluteAmount); end; // clear details on last row for i := 0 to EntriesStringGrid.ColCount - 1 do EntriesStringGrid.Cells[i,CombinedEntry.Entries.Count+1] := ''; // update balance labels if CombinedEntry.TotalDebits > CombinedEntry.TotalCredits then Str := FormatCurrencyForDisplay(CombinedEntry.TotalDebits - CombinedEntry.TotalCredits) + ' Dr' else if CombinedEntry.TotalDebits < CombinedEntry.TotalCredits then Str := FormatCurrencyForDisplay(CombinedEntry.TotalCredits - CombinedEntry.TotalDebits) + ' Cr' else Str := '0.00'; BalanceLabel.Caption := Str; end else begin EntriesStringGrid.RowCount := 2; // clear last row for i := 0 to EntriesStringGrid.ColCount - 1 do EntriesStringGrid.Cells[i,1] := ''; // update balance labels BalanceLabel.Caption := ''; end; end; procedure TEntriesFrame.UpdateControlStates; begin DateDateTimePicker.Enabled := EditMode; AccountComboBox.Enabled := EditMode; DescriptionEdit.Enabled := EditMode; AmountEdit.Enabled := EditMode; DebitRadioButton.Enabled := EditMode; CreditRadioButton.Enabled := EditMode; // display status label if EditMode then begin if NewCombinedEntry then StatusLabel.Caption := 'New Entry' else StatusLabel.Caption := 'Edit Mode' end else StatusLabel.Caption := ''; NewButton.Enabled := not EditMode; EditButton.Enabled := (CombinedEntry <> nil) and (not EditMode); DeleteButton.Enabled := EditMode and (not NewCombinedEntry); CancelButton.Enabled := EditMode; SaveButton.Enabled := EditMode; DuplicateButton.Enabled := (CombinedEntry <> nil) and (not EditMode); end; procedure TEntriesFrame.UpdateDisplay; begin UpdateEntriesStringGrid; SetEntry; UpdateControls; UpdateControlStates; end; procedure TEntriesFrame.ClearDisplay; begin CombinedEntry.Free; CombinedEntry := nil; EditMode := false; EntriesStringGrid.Row := 1; UpdateComboBoxes(false); UpdateDisplay; end; procedure TEntriesFrame.SwitchToAccount; begin // switch to accounts frame and position on this entry MainForm.SwitchToFrame('Accounts'); MainForm.AccountsFrame.ShowAccount(Entry.AccountId); MainForm.AccountsFrame.PositionEntriesStringGrid(Entry.Id); end; procedure TEntriesFrame.ShowCombinedEntry (CombinedEntryId : int64); var TempCombinedEntry : TCombinedEntry; begin TempCombinedEntry := GetCombinedEntry(CombinedEntryId); // destroy existing combined entry CombinedEntry.Free; CombinedEntry := TempCombinedEntry; EditMode := false; EntriesStringGrid.Row := 1; UpdateComboBoxes(false); UpdateDisplay; end; procedure TEntriesFrame.PositionEntriesStringGrid (EntryId : int64); var i : integer; begin if CombinedEntry = nil then Exit; for i := 0 to CombinedEntry.Entries.Count - 1 do begin if CombinedEntry.Entries[i].Id = EntryId then begin EntriesStringGrid.Row := i + 1; Exit; end; end; EntriesStringGrid.Row := 1; end; {******************************************************************************} procedure TEntriesFrame.NewButtonClick(Sender: TObject); begin // destroy any existing combined entry CombinedEntry.Free; // create new combined entry CombinedEntry := TCombinedEntry.Create; CombinedEntry.SetDefaults; // if a default date has been set from a previous new entry // that was saved then use this if DefaultNewEntryDate <> 0 then CombinedEntry.Date := DefaultNewEntryDate; EditMode := true; UpdateComboBoxes(false); UpdateDisplay; if DateDateTimePicker.Enabled then DateDateTimePicker.SetFocus; end; procedure TEntriesFrame.EditButtonClick(Sender: TObject); begin EditMode := true; UpdateComboBoxes(false); UpdateDisplay; if DateDateTimePicker.Enabled then DateDateTimePicker.SetFocus; end; procedure TEntriesFrame.DeleteButtonClick(Sender: TObject); begin if MessageDlg('Are you sure you wish to delete this combined entry?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then DeleteCombinedEntry; end; procedure TEntriesFrame.CancelButtonClick(Sender: TObject); begin CancelChanges; end; procedure TEntriesFrame.SaveButtonClick(Sender: TObject); var MissingAccountIndex : integer; begin if (CombinedEntry <> nil) and (not CombinedEntry.Balanced) then begin MessageDlg('Debits and credits not equal',mtError,[mbOk],0); Exit; end; if CombinedEntry <> nil then begin MissingAccountIndex := CombinedEntry.MissingAccountIndex; if MissingAccountIndex <> -1 then begin MessageDlg('Account missing',mtError,[mbOk],0); EntriesStringGrid.Row := MissingAccountIndex + 1; Exit; end; end; SaveChanges; end; procedure TEntriesFrame.DuplicateButtonClick(Sender: TObject); var TempCombinedEntry : TCombinedEntry; i : integer; begin // record existing combined entry TempCombinedEntry := CombinedEntry; // create new duplicate combined entry CombinedEntry := TCombinedEntry.Create; CombinedEntry.Assign(TempCombinedEntry); // free the existing entry TempCombinedEntry.Free; // set the id's to 0 to indicate this is a new entry CombinedEntry.Id := 0; for i := 0 to CombinedEntry.Entries.Count - 1 do TEntry(CombinedEntry.Entries[i]).Id := 0; // set date to current date CombinedEntry.Date := Date; // if a default date has been set from a previous new entry // that was saved then use this if DefaultNewEntryDate <> 0 then CombinedEntry.Date := DefaultNewEntryDate; EditMode := true; UpdateComboBoxes(false); UpdateDisplay; if DateDateTimePicker.Enabled then DateDateTimePicker.SetFocus; end; procedure TEntriesFrame.EntriesStringGridClick(Sender: TObject); begin SetEntry; UpdateControls; UpdateControlStates; end; procedure TEntriesFrame.EntriesStringGridDblClick(Sender: TObject); begin if (Entry <> nil) and (not EditMode) then SwitchToAccount; end; procedure TEntriesFrame.EntriesStringGridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var Index : integer; begin // Ctl-Del is used to delete entry if (Key = VK_DELETE) and (ssCtrl in Shift) then begin if EditMode and (Entry <> nil) then begin Index := CombinedEntry.Entries.IndexOf(Entry); if Index <> -1 then begin CombinedEntry.Entries.Delete(Index); UpdateDisplay; end; end; Key := 0; end; end; {***** DateDateTimePicker event handling **************************************} procedure TEntriesFrame.DateDateTimePickerKeyDown( Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_RETURN then begin TBaseForm(Parent).GoToNextControl; Key := 0; end; FirstChangeEvent := true; end; procedure TEntriesFrame.DateDateTimePickerEnter( Sender: TObject); begin FirstChangeEvent := true; end; procedure TEntriesFrame.DateDateTimePickerCloseUp(Sender: TObject); begin keybd_event(VK_LEFT,0,0,0); keybd_event(VK_RIGHT,0,0,0); end; procedure TEntriesFrame.DateDateTimePickerChange( Sender: TObject); begin if IgnoreChangeEvents then Exit; // there is a bug in the DateTimePicker where change events // fire twice so ignore the second one FirstChangeEvent := not FirstChangeEvent; if FirstChangeEvent then Exit; if CombinedEntry = nil then Exit; CombinedEntry.Date := Trunc(TDateTimePicker(Sender).Date); end; {***** AccountComboBox event handling *****************************************} procedure TEntriesFrame.AccountComboBoxKeyPress(Sender: TObject; var Key: Char); begin if Key = Char(VK_RETURN) then begin // if empty then ask user whether or not to save docket if Trim(TComboBox(Sender).Text) = '' then begin if (CombinedEntry <> nil) and CombinedEntry.Balanced and (MessageDlg('Do you wish to save this combined entry to the database?', mtConfirmation, [mbYes, mbNo], 0) = mrYes) then SaveChanges; end else TBaseForm(Parent).GoToNextControl; Key := Char(0); end; end; procedure TEntriesFrame.AccountComboBoxSelect(Sender: TObject); begin CreateEntry; if Entry = nil then Exit; Entry.AccountId := Globals.Accounts.GetIdFromComboBoxDisplayString(TComboBox(Sender).Text); UpdateDisplay; end; {***** DescriptionEdit event handling *****************************************} procedure TEntriesFrame.DescriptionEditKeyPress(Sender: TObject; var Key: Char); begin if Key = Char(VK_RETURN) then begin // if Trim(TEdit(Sender).Text) = '' then // Exit; TBaseForm(Parent).GoToNextControl; Key := Char(0); end; end; procedure TEntriesFrame.DescriptionEditEnter(Sender: TObject); var Index : integer; begin // set default value if empty if Trim(TEdit(Sender).Text) = '' then begin if Entry = nil then Exit; // set default to description on previous entry Index := CombinedEntry.Entries.IndexOf(Entry); if Index > 0 then TEdit(Sender).Text := TEntry(CombinedEntry.Entries[Index-1]).Description; end; end; procedure TEntriesFrame.DescriptionEditChange(Sender: TObject); begin if IgnoreChangeEvents then Exit; CreateEntry; if Entry = nil then Exit; Entry.Description := TEdit(Sender).Text; UpdateEntriesStringGrid; end; {***** AmountEdit event handling **********************************************} procedure TEntriesFrame.AmountEditKeyPress(Sender: TObject; var Key: Char); begin if Key = Char(VK_RETURN) then begin if Trim(TEdit(Sender).Text) = '' then Exit; TBaseForm(Parent).GoToNextControl; Key := Char(0); end; end; procedure TEntriesFrame.AmountEditEnter(Sender: TObject); begin // set default value if empty if Trim(TEdit(Sender).Text) = '' then begin if Entry = nil then Exit; if Entry.Amount = 0 then begin if (Entry.Account = nil) or (Entry.Account.Credit) then Entry.Amount := CombinedEntry.TotalDebits - CombinedEntry.TotalCredits else Entry.Amount := CombinedEntry.TotalCredits - CombinedEntry.TotalDebits; end; UpdateDisplay; end; end; procedure TEntriesFrame.AmountEditChange(Sender: TObject); begin if IgnoreChangeEvents then Exit; CreateEntry; if Entry = nil then Exit; if (Entry.Account = nil) or (Entry.Amount = 0) or (Entry.Account.Debit and Entry.Debit) or (Entry.Account.Credit and Entry.Credit) then Entry.Amount := Abs(ConvertCurrencyStringToInt64(TEdit(Sender).Text)) else Entry.Amount := -Abs(ConvertCurrencyStringToInt64(TEdit(Sender).Text)); if Entry.Debit then DebitRadioButton.Checked := true else CreditRadioButton.Checked := true; UpdateEntriesStringGrid; end; {***** DebitRadioButton event handling ****************************************} procedure TEntriesFrame.DebitRadioButtonKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_RETURN then begin EntriesStringGrid.Row := EntriesStringGrid.Row + 1; SetEntry; UpdateControls; AccountComboBox.SetFocus; Key := 0; end; end; procedure TEntriesFrame.DebitRadioButtonClick(Sender: TObject); begin if IgnoreChangeEvents then Exit; if Entry = nil then Exit; if (Entry.Account = nil) or Entry.Account.Debit then Entry.Amount := Entry.AbsoluteAmount else Entry.Amount := -Entry.AbsoluteAmount; UpdateEntriesStringGrid; end; {***** CreditRadioButton event handling ***************************************} procedure TEntriesFrame.CreditRadioButtonKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if Key = VK_RETURN then begin EntriesStringGrid.Row := EntriesStringGrid.Row + 1; SetEntry; UpdateControls; AccountComboBox.SetFocus; Key := 0; end; end; procedure TEntriesFrame.CreditRadioButtonClick(Sender: TObject); begin if IgnoreChangeEvents then Exit; if Entry = nil then Exit; if (Entry.Account = nil) or Entry.Account.Credit then Entry.Amount := Entry.AbsoluteAmount else Entry.Amount := -Entry.AbsoluteAmount; UpdateEntriesStringGrid; end; {******************************************************************************} end.