{*******************************************************} { } { Responsive Software http://www.responsive.co.nz } { } { Copyright (c) 2003-2006 Responsive Software Limited } { } {*******************************************************} unit POSMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Base, Menus, StdCtrls, ExtCtrls, Grids, DatabaseObjects; type TMode = (mBegin, mItemCode, mQuantity, mDiscount, mPercentageDiscount, mPayment, mSalesperson, mPassword); TPOSMainForm = class(TBaseForm) MainMenu1: TMainMenu; File1: TMenuItem; Configure1: TMenuItem; Exit1: TMenuItem; Help1: TMenuItem; Help2: TMenuItem; About1: TMenuItem; Panel: TPanel; InputEdit: TEdit; StringGrid: TStringGrid; Button1: TButton; InputLabel: TLabel; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Button7: TButton; Button8: TButton; Button9: TButton; Button10: TButton; Button11: TButton; Label2: TLabel; Button12: TButton; Label10: TLabel; Label11: TLabel; Label12: TLabel; Label13: TLabel; Label14: TLabel; Label15: TLabel; Label16: TLabel; Label17: TLabel; Label19: TLabel; F8Label: TLabel; StatusLabel: TLabel; TotalLabel: TLabel; PaidLabel: TLabel; ChangeLabel: TLabel; QueuedSaleCountLabel: TLabel; SalespersonLabel: TLabel; HTTP1: TMenuItem; procedure Help2Click(Sender: TObject); procedure About1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure Exit1Click(Sender: TObject); procedure Configure1Click(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure Button8Click(Sender: TObject); procedure Button9Click(Sender: TObject); procedure Button10Click(Sender: TObject); procedure Button11Click(Sender: TObject); procedure Button12Click(Sender: TObject); procedure InputEditKeyPress(Sender: TObject; var Key: Char); procedure HTTP1Click(Sender: TObject); private { Private declarations } InitialMemoryAllocated : integer; IgnoreKeyboardEvent : boolean; // flag to indicate current entry mode Mode : TMode; // current sale in progress Sale : TSale; // current salesperson logged on SalespersonId : int64; // hold salesperson until password validated HoldSalespersonId : int64; // default to credit in item code entry mode Credit : boolean; // flag to indicate entire sale discount DiscountSale : boolean; // current payment type PaymentTypeId : int64; // cash out amount CashOutAmount : int64; // payment details PaymentDetails : string; procedure OnIdle(Sender : TObject; var Done : boolean); procedure OnException(Sender: TObject; E: Exception); function CurrentItem : TDatabaseObject; public { Public declarations } procedure UpdateStringGrid; procedure UpdateLabels; procedure UpdateCaption; function CanClose : boolean; end; var POSMainForm: TPOSMainForm; implementation uses Globals, GeneralUtilities, CommunicationsManager, Splash, POSConfig, Utilities, PromptPaymentType, PromptString; {$R *.dfm} // process unhandled exceptions procedure TPOSMainForm.OnException(Sender: TObject; E: Exception); begin MessageLog.Log(E.Message); Application.ShowException(E); end; // perform processing during idle time procedure TPOSMainForm.OnIdle(Sender : TObject; var Done : boolean); begin if SplashForm.Visible and Visible then begin SplashForm.Hide; if POSConfiguration.MaximiseOnStart then WindowState := wsMaximized; InputEdit.SetFocus; UpdateStringGrid; end; if InitialMemoryAllocated = 0 then begin LogMsgInDevMode('First call to OnIdle handler'); InitialMemoryAllocated := MemoryAllocated; end; UpdateCaption; UpdateLabels; CommunicationsManager.ProcessReceivedData; SalesManager.PostSales; if OffLine then LoadPOSData; end; function TPOSMainForm.CurrentItem : TDatabaseObject; var Index : integer; begin if Sale = nil then begin Result := nil; Exit; end; Index := StringGrid.Row - 1; if Index < Sale.SaleItems.Count then Result := Sale.SaleItems[Index] else if Index - Sale.SaleItems.Count < Sale.PaymentItems.Count then Result := Sale.PaymentItems[Index - Sale.SaleItems.Count] else Result := nil; end; procedure TPOSMainForm.UpdateStringGrid; var i : integer; SaleItem : TSaleItem; PaymentItem : TPaymentItem; Str : string; begin // display entries in string grid if Sale <> nil then begin if Sale.SaleItems.Count + Sale.PaymentItems.Count = 0 then StringGrid.RowCount := 2 else StringGrid.RowCount := Sale.SaleItems.Count + Sale.PaymentItems.Count + 1; for i := 0 to Sale.SaleItems.Count - 1 do begin SaleItem := TSaleItem(Sale.SaleItems[i]); StringGrid.Cells[0,i+1] := FloatToStr(SaleItem.Quantity); StringGrid.Cells[1,i+1] := Utilities.ItemName(SaleItem.ItemId); StringGrid.Cells[2,i+1] := FormatCurrencyForDisplay(SaleItem.FullPrice); if SaleItem.Discount <> 0 then StringGrid.Cells[3,i+1] := FormatCurrencyForDisplay(SaleItem.Discount) else StringGrid.Cells[3,i+1] := ''; StringGrid.Cells[4,i+1] := FormatCurrencyForDisplay(SaleItem.NetPrice); end; for i := 0 to Sale.PaymentItems.Count - 1 do begin PaymentItem := TPaymentItem(Sale.PaymentItems[i]); StringGrid.Cells[0,Sale.SaleItems.Count+i+1] := ''; Str := PaymentTypeName(PaymentItem.PaymentTypeId); if PaymentItem.Amount < 0 then Str := Str + ' Refund'; if PaymentItem.Details <> '' then Str := Str + ' - ' + PaymentItem.Details; StringGrid.Cells[1,Sale.SaleItems.Count+i+1] := Str; StringGrid.Cells[2,Sale.SaleItems.Count+i+1] := ''; StringGrid.Cells[3,Sale.SaleItems.Count+i+1] := ''; StringGrid.Cells[4,Sale.SaleItems.Count+i+1] := FormatCurrencyForDisplay(PaymentItem.Amount); end; // update total labels if Sale.TotalPrice <> 0 then TotalLabel.Caption := 'Tot: ' + FormatCurrencyForDisplay(Sale.TotalPrice) else TotalLabel.Caption := ''; if Sale.TotalPayment <> 0 then PaidLabel.Caption := 'Pd: ' + FormatCurrencyForDisplay(Sale.TotalPayment) else PaidLabel.Caption := ''; if ((Sale.TotalPrice >= 0) and (Sale.Change > 0)) or ((Sale.TotalPrice <= 0) and (Sale.Change < 0)) then ChangeLabel.Caption := 'Chg: ' + FormatCurrencyForDisplay(Sale.Change) else ChangeLabel.Caption := ''; end else begin StringGrid.RowCount := 2; // clear last row for i := 0 to StringGrid.ColCount - 1 do StringGrid.Cells[i,1] := ''; // update total labels TotalLabel.Caption := ''; PaidLabel.Caption := ''; ChangeLabel.Caption := ''; end; end; procedure TPOSMainForm.UpdateLabels; begin // update input label if Mode in [mBegin,mItemCode] then InputLabel.Caption := 'Item Code' else if Mode in [mQuantity,mDiscount,mPayment] then InputLabel.Caption := 'Amount' else if Mode = mPercentageDiscount then InputLabel.Caption := 'Percentage' else if Mode = mSalesperson then InputLabel.Caption := 'Name' else if Mode = mPassword then InputLabel.Caption := 'Password'; // update status label if Mode in [mBegin,mItemCode] then begin if Credit then begin StatusLabel.Font.Color := clRed; StatusLabel.Caption := 'Credit'; F8Label.Caption := 'Sale'; end else begin if Mode <> mBegin then begin StatusLabel.Font.Color := clBlue; StatusLabel.Caption := 'Sale'; end else StatusLabel.Caption := ''; F8Label.Caption := 'Credit'; end; end else if Mode = mQuantity then begin StatusLabel.Font.Color := clOlive; StatusLabel.Caption := 'Qty'; end else if Mode in [mDiscount,mPercentageDiscount] then begin StatusLabel.Font.Color := clOlive; StatusLabel.Caption := 'Discount'; end else if Mode = mPayment then begin if (Sale <> nil) and (Sale.TotalPrice < 0) then begin StatusLabel.Font.Color := clRed; StatusLabel.Caption := 'Refund'; end else begin StatusLabel.Font.Color := clGreen; StatusLabel.Caption := 'Pay'; end; end else if Mode in [mSalesperson,mPassword] then begin StatusLabel.Font.Color := clOlive; StatusLabel.Caption := 'S/P'; end else StatusLabel.Caption := ''; if SalesManager.SaleCount = 0 then QueuedSaleCountLabel.Caption := '' else QueuedSaleCountLabel.Caption := 'Queued: ' + IntToStr(SalesManager.SaleCount); if SalespersonId = 0 then SalespersonLabel.Caption := '' else SalespersonLabel.Caption := 'S/P: ' + SalespersonName(SalespersonId); end; // update the caption if it has changed // also show memory allocated if under development procedure TPOSMainForm.UpdateCaption; var OldCaption, NewCaption : string; begin OldCaption := Caption; NewCaption := ProgramName; if ModeString <> '' then NewCaption := NewCaption + ' ' + ModeString; if Offline then NewCaption := NewCaption + ' - OFFLINE'; if DevelopmentMode then NewCaption := NewCaption + ' (Mem = ' + IntToStr(MemoryAllocated - InitialMemoryAllocated) + ')'; if NewCaption <> OldCaption then Caption := NewCaption; end; function TPOSMainForm.CanClose : boolean; begin if Mode <> mBegin then Result := false else Result := true; end; procedure TPOSMainForm.FormCreate(Sender: TObject); begin // set the name of the OnIdle event handler ie. the // procedure which gets called when nothing else is happening Application.OnIdle := OnIdle; // set the name of the OnException event handler Application.OnException := OnException; SetUpStringGrid(StringGrid,[ 'Qty', 'Description', 'Price', 'Disc', 'Net' ],[ 8, // Qty 49, // Description 15, // Price 15, // Disc 15 // Net ]); StringGrid.Row := 1; QueuedSaleCountLabel.Caption := ''; TotalLabel.Caption := ''; PaidLabel.Caption := ''; ChangeLabel.Caption := ''; SalespersonLabel.Caption := ''; if Offline then HTTP1.Visible := false; end; procedure TPOSMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if (not DevelopmentMode) and (MessageDlg('Are you sure you wish to quit?', mtConfirmation, [mbYes,mbNo], 0) = mrNo) then CanClose := false else CanClose := true; end; procedure TPOSMainForm.Help2Click(Sender: TObject); begin MessageDlg(HelpText,mtInformation,[mbOk],0); end; procedure TPOSMainForm.About1Click(Sender: TObject); var IPAddressesStr : string; ExpiryStr : string; begin CombineStrings(IPAddresses,IPAddressesStr,','); if GlobalConfiguration.Unlimited then ExpiryStr := '' else ExpiryStr := Chr(VK_RETURN) + 'Software expires on ' + FormatDateTime('d mmmm yyyy',GlobalConfiguration.ExpiryDate); if Offline then MessageDlg('Program: ' + Application.Title + ' ' + ModeString + Chr(VK_RETURN) + 'Version: ' + ProgramVersion + Chr(VK_RETURN) + 'Computer: ' + ComputerName + Chr(VK_RETURN) + 'IP Address(es): ' + IPAddressesStr + Chr(VK_RETURN) + Chr(VK_RETURN) + 'Copyright (c) 2000-' + CurrentYearStr + ' ' + DevelopmentCompanyName + Chr(VK_RETURN) + ExpiryStr, mtInformation,[mbOk],0) else MessageDlg('Program: ' + Application.Title + ' ' + ModeString + Chr(VK_RETURN) + 'Version: ' + ProgramVersion + Chr(VK_RETURN) + 'User: ' + ClientUserName + Chr(VK_RETURN) + 'Computer: ' + ComputerName + Chr(VK_RETURN) + 'IP Address(es): ' + IPAddressesStr + Chr(VK_RETURN) + 'Server: ' + ServerIPAddress + Chr(VK_RETURN) + 'Port: ' + IntToStr(ServerPortNumber) + Chr(VK_RETURN) + Chr(VK_RETURN) + 'Copyright (c) 2000-' + CurrentYearStr + ' ' + DevelopmentCompanyName + Chr(VK_RETURN) + ExpiryStr, mtInformation,[mbOk],0); end; procedure TPOSMainForm.Exit1Click(Sender: TObject); begin Close; end; procedure TPOSMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin if not CanClose then begin MessageDlg('Unable to exit while sale is in progress', mtInformation,[mbOk],0); Action := caNone; end else MessageLog.Log('Memory ' + IntToStr(MemoryAllocated - InitialMemoryAllocated)); end; procedure TPOSMainForm.Configure1Click(Sender: TObject); begin POSConfigForm.Configure; end; procedure TPOSMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if (Key = VK_ESCAPE) then begin if Mode in [mQuantity,mDiscount,mPercentageDiscount,mPayment,mSalesperson,mPassword] then begin InputEdit.Text := ''; InputEdit.PasswordChar := #0; if Sale = nil then Mode := mBegin else Mode := mItemCode; StringGrid.Enabled := true; Key := 0; end else if DevelopmentMode then begin Close; Key := 0; end; end else if (Key in [VK_UP,VK_DOWN,VK_PRIOR,VK_NEXT,VK_HOME,VK_END]) then begin if IgnoreKeyboardEvent then Exit; if not StringGrid.Enabled then Exit; InputEdit.Text := ''; InputEdit.PasswordChar := #0; if Sale = nil then Mode := mBegin else Mode := mItemCode; IgnoreKeyboardEvent := true; SendMessage(StringGrid.Handle, WM_KEYDOWN, Key, 0); IgnoreKeyboardEvent := false; Key := 0; end else if (Key = VK_F1) then begin Button1Click(nil); Key := 0; end else if (Key = VK_F2) or ((Key = VK_DELETE) and (InputEdit.Text = '')) then begin Button2Click(nil); Key := 0; end else if (Key = VK_F3) then begin Button3Click(nil); Key := 0; end else if (Key = VK_F4) then begin Button4Click(nil); Key := 0; end else if (Key = VK_F5) then begin Button5Click(nil); Key := 0; end else if (Key = VK_F6) then begin Button6Click(nil); Key := 0; end else if (Key = VK_F7) then begin Button7Click(nil); Key := 0; end else if (Key = VK_F8) then begin Button8Click(nil); Key := 0; end else if (Key = VK_F9) then begin Button9Click(nil); Key := 0; end else if (Key = VK_F10) then begin Button10Click(nil); Key := 0; end else if (Key = VK_F11) then begin Button11Click(nil); Key := 0; end else if (Key = VK_F12) then begin Button12Click(nil); Key := 0; end; end; procedure TPOSMainForm.Button1Click(Sender: TObject); begin // quantity if (Mode = mItemCode) and (CurrentItem is TSaleItem) then begin Mode := mQuantity; StringGrid.Enabled := false; end; InputEdit.SetFocus; end; procedure TPOSMainForm.Button2Click(Sender: TObject); var Index : integer; begin // cancel line if Mode = mItemCode then begin if CurrentItem is TSaleItem then begin Index := StringGrid.Row - 1; Sale.SaleItems.Delete(Index); end else if CurrentItem is TPaymentItem then begin Index := StringGrid.Row - 1 - Sale.SaleItems.Count; Sale.PaymentItems.Delete(Index); end; if (Sale <> nil) and (Sale.SaleItems.Count = 0) and (Sale.PaymentItems.Count = 0) then begin Sale.Free; Sale := nil; Mode := mBegin; Credit := false; end else Mode := mItemCode; UpdateStringGrid; end; InputEdit.SetFocus; end; procedure TPOSMainForm.Button3Click(Sender: TObject); begin // cancel sale Sale.Free; Sale := nil; Mode := mBegin; Credit := false; StringGrid.Enabled := true; UpdateStringGrid; StringGrid.Row := StringGrid.RowCount - 1; InputEdit.SetFocus; end; procedure TPOSMainForm.Button4Click(Sender: TObject); begin // salesperson if Mode in [mBegin,mItemCode] then begin Mode := mSalesperson; StringGrid.Enabled := false; end; InputEdit.SetFocus; end; procedure TPOSMainForm.Button5Click(Sender: TObject); var PaymentType : TPaymentType; begin // payment try if Mode in [mBegin,mItemCode] then begin if PromptPaymentTypeForm.Prompt then begin CashOutAmount := 0; PaymentDetails := ''; PaymentTypeId := PromptPaymentTypeForm.PaymentTypeId; PaymentType := TPaymentType(Globals.PaymentTypes.ObjectsById[PaymentTypeId]); // prompt for cash out if PaymentType.PromptCashOut and ((Sale = nil) or (Sale.TotalPrice >= 0)) then begin if PromptStringForm.Prompt(false,'Enter Cash Out','Cash Out') then CashOutAmount := Abs(ConvertCurrencyStringToInt64(PromptStringForm.Value)) else Exit; end; // prompt for details if PaymentType.PromptDetails then begin if PromptStringForm.Prompt(true,'Enter Details','Details') then PaymentDetails := PromptStringForm.Value else Exit; end; Mode := mPayment; StringGrid.Enabled := false; if (Sale <> nil) and (((Sale.TotalPrice > 0) and (Sale.Change < 0)) or ((Sale.TotalPrice < 0) and (Sale.Change > 0))) then begin InputEdit.Text := FormatCurrency(Abs(Sale.Change)); InputEdit.SelectAll; end else InputEdit.Text := ''; end; end; finally InputEdit.SetFocus; end; end; procedure TPOSMainForm.Button6Click(Sender: TObject); begin // discount if (Mode = mItemCode) and (CurrentItem is TSaleItem) then begin Mode := mDiscount; StringGrid.Enabled := false; end; InputEdit.SetFocus; end; procedure TPOSMainForm.Button7Click(Sender: TObject); begin // percentage discount if (Mode = mItemCode) and (CurrentItem is TSaleItem) then begin Mode := mPercentageDiscount; StringGrid.Enabled := false; // if position is last sale item then prompt whether // to discount entire sale if (Sale.SaleItems.Count > 1) and (StringGrid.Row = Sale.SaleItems.Count) and (MessageDlg('Discount entire sale?', mtConfirmation, [mbYes,mbNo], 0) = mrYes) then DiscountSale := true else DiscountSale := false; end; InputEdit.SetFocus; end; procedure TPOSMainForm.Button8Click(Sender: TObject); begin // credit if Mode in [mBegin,mItemCode] then Credit := not Credit; InputEdit.SetFocus; end; procedure TPOSMainForm.Button9Click(Sender: TObject); begin // undefined InputEdit.SetFocus; end; procedure TPOSMainForm.Button10Click(Sender: TObject); begin // undefined InputEdit.SetFocus; end; procedure TPOSMainForm.Button11Click(Sender: TObject); begin // undefined InputEdit.SetFocus; end; procedure TPOSMainForm.Button12Click(Sender: TObject); begin // undefined InputEdit.SetFocus; end; procedure TPOSMainForm.InputEditKeyPress(Sender: TObject; var Key: Char); var Item : TItem; SaleItem : TSaleItem; PaymentItem : TPaymentItem; Salesperson : TSalesperson; Discount : double; Index : integer; PaymentAmount : int64; begin if Key = Char(VK_RETURN) then begin // if nothing entered then don't do anything // unless salesperson or password is being entered and there is none if (not (Mode in [mSalesperson,mPassword,mPayment])) and (Trim(TEdit(Sender).Text) = '') then Exit; if Mode in [mBegin,mItemCode] then begin // if there is no sale then create one if Sale = nil then Sale := TSale.Create; // find item and add to sale Item := TItem(Globals.Items.ObjectsById[ItemIdFromLookupCode(Trim(TEdit(Sender).Text))]); if (Item <> nil) and Item.Active then begin SaleItem := TSaleItem.Create; SaleItem.ItemId := Item.Id; if Credit then SaleItem.Quantity := -1 else SaleItem.Quantity := 1; SaleItem.FullPrice := Round(SaleItem.Quantity*Item.Price*100); Sale.SaleItems.Add(SaleItem); Mode := mItemCode; UpdateStringGrid; StringGrid.Row := StringGrid.RowCount - 1; end else MessageBeep(MB_ICONEXCLAMATION); end else if Mode = mQuantity then begin SaleItem := TSaleItem(CurrentItem); Item := TItem(Globals.Items.ObjectsById[SaleItem.ItemId]); if Credit then SaleItem.Quantity := -ConvertStringToDouble(TEdit(Sender).Text) else SaleItem.Quantity := ConvertStringToDouble(TEdit(Sender).Text); SaleItem.FullPrice := Round(SaleItem.Quantity*Item.Price*100); Mode := mItemCode; StringGrid.Enabled := true; UpdateStringGrid; end else if Mode = mDiscount then begin SaleItem := TSaleItem(CurrentItem); Discount := ConvertStringToDouble(TEdit(Sender).Text); if SaleItem.FullPrice < 0 then SaleItem.Discount := -Round(Discount*100) else SaleItem.Discount := Round(Discount*100); Mode := mItemCode; StringGrid.Enabled := true; UpdateStringGrid; end else if Mode = mPercentageDiscount then begin Discount := ConvertStringToDouble(TEdit(Sender).Text); if Discount > 100 then Discount := 100; if DiscountSale then begin for Index := 0 to Sale.SaleItems.Count - 1 do begin SaleItem := TSaleItem(Sale.SaleItems[Index]); if SaleItem.FullPrice < 0 then SaleItem.Discount := -Round((-SaleItem.FullPrice)*Discount/100) else SaleItem.Discount := Round(SaleItem.FullPrice*Discount/100); end; end else begin SaleItem := TSaleItem(CurrentItem); if SaleItem.FullPrice < 0 then SaleItem.Discount := -Round((-SaleItem.FullPrice)*Discount/100) else SaleItem.Discount := Round(SaleItem.FullPrice*Discount/100); end; Mode := mItemCode; StringGrid.Enabled := true; UpdateStringGrid; end else if Mode = mSalesperson then begin if Trim(TEdit(Sender).Text) = '' then begin SalespersonId := 0; if Sale <> nil then Mode := mItemCode else Mode := mBegin; StringGrid.Enabled := true; end else begin HoldSalespersonId := SalespersonIdFromName(UpperCase(Trim(TEdit(Sender).Text))); if HoldSalespersonId <> 0 then begin Salesperson := TSalesperson(Globals.Salespersons.ObjectsById[HoldSalespersonId]); if (Salesperson <> nil) and (Salesperson.Active) then begin Mode := mPassword; InputEdit.PasswordChar := '*'; end else MessageBeep(MB_ICONEXCLAMATION); end else MessageBeep(MB_ICONEXCLAMATION); end; end else if Mode = mPassword then begin Salesperson := TSalesperson(Globals.Salespersons.ObjectsById[HoldSalespersonId]); if (Salesperson <> nil) and (Salesperson.UnencryptedPassword = LowerCase(Trim(TEdit(Sender).Text))) then SalespersonId := HoldSalespersonId else MessageBeep(MB_ICONEXCLAMATION); if Sale <> nil then Mode := mItemCode else Mode := mBegin; StringGrid.Enabled := true; InputEdit.PasswordChar := #0; end else if Mode = mPayment then begin // if there is no sale then create one if Sale = nil then Sale := TSale.Create; // determine payment amount if Sale.TotalPrice < 0 then PaymentAmount := -(Abs(ConvertCurrencyStringToInt64(TEdit(Sender).Text)) + CashOutAmount) else PaymentAmount := ConvertCurrencyStringToInt64(TEdit(Sender).Text) + CashOutAmount; // add payment item to sale if PaymentAmount <> 0 then begin PaymentItem := TPaymentItem.Create; PaymentItem.PaymentTypeId := PaymentTypeId; PaymentItem.Details := PaymentDetails; PaymentItem.Amount := PaymentAmount; Sale.PaymentItems.Add(PaymentItem); end; StringGrid.Enabled := true; UpdateStringGrid; StringGrid.Row := StringGrid.RowCount - 1; // if payment is complete then complete sale if (((Sale.TotalPrice >= 0) and (Sale.Change >= 0)) or ((Sale.TotalPrice <= 0) and (Sale.Change <= 0))) and (MessageDlg('Complete transaction?',mtConfirmation,[mbYes,mbNo],0) = mrYes) then begin Sale.DateTime := Now; Sale.SalespersonId := SalespersonId; CompleteSale(Sale); Sale.Free; Sale := nil; Mode := mBegin; Credit := false; StringGrid.Enabled := true; UpdateStringGrid; StringGrid.Row := StringGrid.RowCount - 1; end else begin Mode := mItemCode; // force another payment entry if tendering not complete if (((Sale.TotalPrice >= 0) and (Sale.Change < 0)) or ((Sale.TotalPrice <= 0) and (Sale.Change > 0))) then begin Button5Click(nil); Key := Char(0); Exit; end; end; end; TEdit(Sender).Text := ''; Key := Char(0); end; end; procedure TPOSMainForm.HTTP1Click(Sender: TObject); begin OpenWebpage(ServerIPAddress + ':' + IntToStr(GlobalConfiguration.HTTPPortNumber)); end; end.