unit Main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Buttons, ComCtrls,
ExtCtrls, StdCtrls;
type
modes = (mGuthaben, mAngekuendigt, mUmsaetze, mEnde);
headerTyp = (NIX, ANGEKUENDIGT, UMSAETZE, ENDE);
{ TMainForm }
TMainForm = class(TForm)
BitBtnClose: TBitBtn;
BitBtnSave: TBitBtn;
BitBtnGetClip: TBitBtn;
Image1: TImage;
Label1: TLabel;
Label2: TLabel;
LabelLink: TLabel;
ListView: TListView;
Panel1: TPanel;
StatusBar: TStatusBar;
procedure BitBtnCloseClick(Sender: TObject);
procedure BitBtnGetClipClick(Sender: TObject);
procedure BitBtnSaveClick(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormShow(Sender: TObject);
procedure LabelLinkClick(Sender: TObject);
private
TL:TStringList;
function getDownloadFolder:string;
function isDatum(s:string):string;
function isEuro(s:string):string;
function isHeader(s:string):headerTyp;
procedure addRow(s1,s2,s3,s4,s5,s6:string);
public
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
{ TMainForm }
uses Windows, Clipbrd, Shellapi, Shlobj;
const WINDOWNAME:string='PostBank-Exporter';
function TMainForm.getDownloadFolder:string;
var s:integer;
pPath: pWideChar;
path: array[0..Max_Path] of WideChar;
g:TGuid ='{374DE290-123F-4565-9164-39C4925E467B}';
begin
pPath:=@path;
s:=shlobj.SHGetKnownFolderPath(g,0,0,pPath);
if s=0 then
result:=pPath
else
result:='';
end;
function TMainForm.isDatum(s:string):string;
var d:TDate; // "13.12.2022"
begin
try
d:=StrToDate(s);
except
s:=''
end;
result:=s;
end;
function TMainForm.isEuro(s:string):string;
var e:extended; // "-138,83 EUR"
st:string; // "40.029,86 EUR"
begin
st:=RightStr(s,3);
if st<>'EUR' then begin
result:='';
exit;
end;
s:=LeftStr(s,length(s)-4); // = copy(s,1,length(s)-4);
s:=StringReplace(s, '.', '',[rfReplaceAll]); // Tausender-Trenner entfernen
try
e:=StrToFloat(s);
except
s:='';
end;
result:=s;
end;
function TMainForm.isHeader(s:string):headerTyp;
begin
result:=NIX;
if (s='clock') then begin // Angekündigt folgt
result:=ANGEKUENDIGT;
exit;
end;
if (s='transfer') then begin // UMSAETZE folgt
result:=UMSAETZE;
exit;
end;
if (length(s)=1) or
(length(s)=2) then begin // ? und ?? auch: UMSAETZE folgt
result:=UMSAETZE;
exit;
end;
if (s='favorite-outline') then begin // ENDE: nichts Weiteres folgt
result:=ENDE;
exit;
end;
end;
procedure TMainForm.addRow(s1,s2,s3,s4,s5,s6:string);
var idx:integer;
begin
ListView.Items.Add.Caption:=''; // 0. Spalte nicht sichtbar
idx:=ListView.Items.Count-1;
ListView.Items.Item[idx].SubItems.Add(s1);
ListView.Items.Item[idx].SubItems.Add(s2);
ListView.Items.Item[idx].SubItems.Add(s3);
ListView.Items.Item[idx].SubItems.Add(s4);
ListView.Items.Item[idx].SubItems.Add(s5);
ListView.Items.Item[idx].SubItems.Add(s6);
end;
procedure TMainForm.FormShow(Sender: TObject);
var wowner:THandle;
begin
wowner:=GetWindow(handle,GW_OWNER);
SetWindowText(wowner,pchar(WINDOWNAME));
MainForm.Caption:=WINDOWNAME;
TL:=TStringList.Create;
end;
procedure TMainForm.BitBtnGetClipClick(Sender: TObject);
var lauf:integer;
zeile,s:string;
mode:modes;
ht:headerTyp;
guthaben:Extended;
betragAngekuendigt:Extended;
artAngekuendigt:string;
datumAngekuendigt:string;
buchungsZeile:integer;
buchungsEmpfaenger, buchungsHinweis, buchungsArt, buchungsDatum:string;
buchungsBetrag,saldo:Extended;
ignoreNext:boolean;
begin
listview.Clear;
TL.Clear;
if clipboard.HasFormat(CF_TEXT) then begin
TL.AddText(clipboard.asText);
// ShowMessage( inttostr(TL.Count) );
mode:=mGuthaben; // damit fängt es an
ignoreNext:=false;
for lauf:=1 to tl.Count do begin
zeile:=tl.Strings[lauf-1];
if zeile='' then begin
ignoreNext:=true;
continue; // Leerzeile: ignorieren...
end;
if ignoreNext then begin
ignoreNext:=false;
continue; // ...und nächste Zeile auch !!!!
end;
ht:=isHeader(zeile); // Header gefunden?
case ht of
ANGEKUENDIGT: mode:=mAngekuendigt;
UMSAETZE: mode:=mUmsaetze;
ENDE: mode:=mEnde;
end;
case mode of
mGuthaben: begin
s:=isEuro(zeile);
if s='' then
continue; // Zeile kein Guthaben
guthaben:=StrToFloat(s); // erstes Muster "xxx,yy EUR" gefunden = Guthaben
saldo:=guthaben;
end;
mAngekuendigt: begin
s:=isEuro(zeile);
if s<>'' then // angekündigte Position gefunden
betragAngekuendigt:=StrToFloat(s);
s:=isDatum(zeile);
if s<>'' then begin
datumAngekuendigt:=s; // das zugehörige Datum
artAngekuendigt:=tl.Strings[lauf-3]; // "Art" steht 2 Zeilen oberhalb
addRow( datumAngekuendigt, artAngekuendigt, '', 'angekündigt', format('%.2F',[betragAngekuendigt]), format('%.2F',[saldo]) );
saldo:=saldo-betragAngekuendigt;
end;
end;
mUmsaetze: begin
ht:=isHeader(zeile);
if ht>NIX then begin // nächster Header gefunden:
buchungsZeile:=1; // neu zählen
continue;
end;
case buchungsZeile of
1: begin
buchungsEmpfaenger:=zeile;
inc(buchungsZeile);
end;
2: begin
s:=isEuro(zeile);
if s<>'' then begin
buchungsBetrag:=strtofloat(s);
inc(buchungsZeile);
end;
end;
3: begin
s:=isDatum(zeile);
if s<>'' then begin // wenn KEIN Buchungshinweis, sondern BuchungsDatum !!!
buchungsHinweis:='';
BuchungsDatum:=s;
inc(buchungsZeile,2); // "4" überspringen, da Datum schon da!
continue;
end;
buchungsHinweis:=zeile;
inc(buchungsZeile);
end;
4: begin
s:=isDatum(zeile);
if s<>'' then begin
buchungsDatum:=s;
inc(buchungsZeile);
end;
end;
5: begin
buchungsArt:=zeile;
addRow( buchungsDatum, buchungsArt, buchungsEmpfaenger, buchungsHinweis, format('%.2F',[buchungsBetrag]), format('%.2F',[saldo]) );
saldo:=saldo-buchungsBetrag;
inc(buchungsZeile);
end;
6..1000: begin // wenn es sie gibt:
inc(buchungsZeile); // ignorieren und auf Header warten
end;
end; // 2. case
end;
mEnde: begin // "Header" ist Postambel
break; // raus aus der for-Schleife
end;
end; // 1. case
end; // for...
end;
StatusBar.Panels.Items[0].Text:='Zeilen gelesen: '+inttostr(TL.Count);
StatusBar.Panels.Items[1].Text:='Umsätze extrahiert: '+inttostr(ListView.Items.Count);
StatusBar.Panels.Items[2].Text:='';
if ListView.Items.Count >0 then
ActiveControl:=BitBtnSave
else
ActiveControl:=BitBtnClose;
end;
procedure TMainForm.BitBtnSaveClick(Sender: TObject);
var expPath:string;
strL:TStrings;
lauf:integer;
tf:textfile;
begin
ActiveControl:=BitBtnClose;
if ListView.Items.Count=0 then begin
StatusBar.Panels.Items[2].Text:='nichts gespeichert!';
exit;
end;
expPath:=getDownloadFolder;
if paramcount>0 then // bei Parameter /t wird das Clipboard gespeichert
if paramstr(1)='/t' then
TL.SaveToFile(expPath+'\pb-export.txt');
assignfile(tf,expPath+'\pb-export.csv');
rewrite(tf);
for lauf:=1 to ListView.Items.Count do begin
strL:=ListView.Items.Item[lauf-1].SubItems;
strL.Delimiter:=';'; // Trenner
strL.StrictDelimiter:=TRUE;
strL.QuoteChar := #0;
writeln(tf,strL.DelimitedText);
end;
closefile(tf);
StatusBar.Panels.Items[2].Text:='Zeilen gespeichert: '+inttostr(ListView.Items.Count);
end;
procedure TMainForm.LabelLinkClick(Sender: TObject);
begin
Shellexecute(Application.Handle,'open',PChar(TLabel(Sender).Hint),nil,nil,0);
end;
procedure TMainForm.BitBtnCloseClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
TL.Free;
//CloseAction:=caFree;
end;
end.