#!/usr/bin/perl -Tw use strict; use warnings; use Mail::POP3Client; use IO::Handle; use IO::File; use MIME::Parser; use Date::Format; use DBI; use MIME::Base64; use Text::Iconv; # KONFIGURACJA IMPORTU # login uzytkownika bazy mysql - mozna utworzyc osobnego uzytkownika dla potrzeb # importu (nadajac mu tylko konieczne prawa) my $lms_login = "lms"; my $lms_password = "lmshaslo"; # serwer pop3 (np poczta.o2.pl) my $mail_host = ""; # login do konta pop3 (np user, user\@poczta.pl) my $mail_login = ""; # haslo do konta pop3 my $mail_password = ""; # id rejestru kasowego, w ktorym maja byc ksiegowane wplaty my $cashreg_id = "8"; # id planu numeracyjnego, wg którego mają być numerowane KP my $numberplanid = "17"; # id firmy/oddziału (divisionid) - pole ID z tabeli DIVISIONS my $divisionid = 1; # ponizsze zostalo wprowadzone w celu sprawdzania tylko maili z banku... # zasadniczo mozna to wylaczyc, ale w wypadku sprawdzania maili z duzymi # zalacznikami wykonanie skryptu trwa sporo czasu my $bank_domain = "centrum.uslug.rozliczeniowych\@bzwbk.pl"; # plik tymczasowy, do ktorego beda zapisywane operacje finansowe pochodzace # z uruchomienia skryptu my $bank_file = "/tmp/bzwbk"; # KONIEC KONFIGURACJI IMPORTU # sprawdzamy date - biezaca i wczorajsza my $today_date = time2str("%e %b %Y",time); my $year_current = time2str("%Y",time); my $year_next = $year_current+1; #print STDOUT "\nDzisiejsza data do celu porownania\n".$today_date; print STDOUT "\nData: ".time2str("%C", time)."\n"; my $yesterday_date = time2str("%e %b %Y",time-86400); unless (exists($ARGV[0]) && $ARGV[0] eq "--from-disk") { my $fh = new IO::File ">$bank_file"; my $parser = new MIME::Parser; $parser->decode_headers(0); $parser->output_to_core(1); $parser->tmp_to_core(1); $parser->use_inner_files(1); # ponizszych linijek radze nie ruszac... jesli juz, to po zapoznaniu sie # z dokumentacja MIME::Parser #$parser->output_dir("/tmp/bzwbk"); #$parser->ignore_errors(0); #$parser->extract_uuencode(1); #$parser->decode_bodies(1); #$parser->extract_nested_messages(1); # my $fh = new IO::File ">/tmp/bzwbk"; # Logujemy sie na skrzynke my $pop = new Mail::POP3Client(USER => "$mail_login", PASSWORD => "$mail_password", HOST => "$mail_host", AUTH_MODE => "BEST", DEBUG => 0) or die; # liczba wiadomosci w skrzynce (jednoczesnie ID ostatniej wiadomosci) my $msg_count = $pop->Count(); print STDOUT $msg_count." wiadomosci w skrzynce pocztowej.\n"; while ($msg_count > 0) { my $msg = $pop->Head($msg_count); my $ent = $parser->parse_data($msg); # przydatne do debugowania # $ent->dump_skeleton; my @msg_from = split(/head->get('from')); $msg_from[1] =~ s/[>]//g; $msg_from[1] = $ent->head->get('from'); # my $msg_from = $ent->head->get('from'); print STDOUT "mail od: $msg_from\n"; unless ($msg_from[1] =~ $bank_domain) { $msg_count--; next; } my $msg_date = $ent->head->get('date'); # print STDOUT "data otrzymania maila przed obrobka:$msg_date\n"; my @msg_date = split(/\W/,$msg_date); my $recv_date = $msg_date[2]." ".$msg_date[3]." ".$msg_date[4]; print STDOUT "data otrzymania maila:$recv_date\n"; # ponizszy warunek - sprawdzamy maile z data dzisiejsza i wczorajsza # mozna dodac date 2daysago_date itp - trzeba dodac wtedy odpowiednia zmienną # na poczatku skryptu # cos w stylu my $2daysago_date = time2str("%e %b %Y",time-132800); unless ($today_date =~ $recv_date || $yesterday_date =~ $recv_date || '30 Oct 2010' =~ $recv_date || '29 Oct 2010' =~ $recv_date || '28 Oct 2010' =~ $recv_date) # unless ($today_date =~ $recv_date || $yesterday_date =~ $recv_date) { # biezaca wiadomosc ma date inna niz ta, ktora nas interesuje # podajac z wiersza polecen parametr --get-all mozemy parsowac # wszystkie wiadomosci w skrzynce - bez sprawdzania daty unless (exists($ARGV[0]) && $ARGV[0] eq "--get-all") { last; } } $msg = $pop->HeadAndBody($msg_count); $ent = $parser->parse_data($msg); my $msg_parts = $ent->parts; # print STDOUT "ilosc czesci: $msg_parts \n\n"; for (my $part=0; $part < $msg_parts; $part++) { # print STDOUT "czesc $part : ".$ent->parts($part)->mime_type."\n"; # sprawdzamy, czy dana czesc jest typu text/plain if ($ent->parts($part)->mime_type() =~ /^text\/plain$/) { $ent->parts($part)->bodyhandle->print($fh); encode_base64($fh); } } $msg_count--; } $pop->Close(); $fh->close; } # podajac parametr --no-db-update sprawimy, iz skrypt wykona sie # z pominieciem insertu do bazy (natomiast zapisze wszystkie operacje # finansowe w pliku tekstowym - raczej do debugowania unless (exists($ARGV[0]) && $ARGV[0] eq "--no-db-update") { # otwieramy dopiero co zapisany plik - troche tak glupio...chyba, ze czytamy z dysku # co do czytania z dysku - na poczatku skryptu ponizszy plik jest zamazywany... my $fh = new IO::File "<$bank_file"; my $dbase = DBI->connect("DBI:mysql:database=lms;host=localhost","$lms_login","$lms_password", { RaiseError => 1 }); my $dbinsert = $dbase->prepare("INSERT INTO cashimport ( Hash, Date, Value, Customer, CustomerId, Description ) VALUES (?,unix_timestamp(?),?,?,?,?)") or die $dbase->errstr(); my $i=0; while (<$fh>) { unless ( ($_ =~ /^[K|S].*/) || ($_ !~ /^\d{5,}\|/) ) { $_ =~ /(\d*)\|(\d\d)(\d\d)(\d\d\d\d)\|([\d\.]*)\|(.*)\|[0-9]{14}([0-9]{12})\|(.*)/; # print STDOUT; # konwersja kodowania - roznie to jest na roznych serwerach... # my $win2utf = Text::Iconv->new("WINDOWS-1250", "ISO-8859-2"); my $win2utf = Text::Iconv->new("ISO-8859-2", "UTF-8"); # $op_id zapakujemy do kolumny hash - bo tez jest unikalna, # nie potrzebujemy wyliczac sumy kontrolnej operacji... my $op_id = $1; # print STDOUT "hash operacji $op_id\n"; my $dbq = $dbase->prepare("SELECT id FROM cashimport WHERE hash='$op_id'"); $dbq->execute(); $dbase->do("SET NAMES 'utf8'") or die $dbase->errstr(); my $row = $dbq->fetchrow_hashref(); unless ($row->{'id'}) { # $op_data idzie do kolumny date my $op_data = "$4$3$2"; # print STDOUT "data operacji:$op_data\n"; # $op_kwota idzie do kolumny value my $op_kwota = $5; # print STDOUT "kwota operacji $op_kwota\n"; #$op_klient dla sportu zaladujemy do kolumny customer my $op_nazwisko = $6; $op_nazwisko = $win2utf->convert($op_nazwisko); # print STDOUT "nazwisko klienta $op_nazwisko\n"; # z $op_konto wyciagamy numer klienta i pakujemy do kolumny customerid my $op_klientid = $7; # print STDOUT "id klienta $op_klientid\n"; # $op_tytul dla sportu zaladujemy do kolumny description my $op_tytul = $8; $op_tytul = $win2utf->convert($op_tytul); # print STDOUT "tytul transakcji $op_tytul\n"; print STDOUT "do cashimport: id operacji $op_id\|id klienta $op_klientid\|data:$op_data\|kwota:$op_kwota\n"; $dbinsert->execute($op_id, $op_data, $op_kwota, $op_nazwisko, $op_klientid, $op_tytul) or die $dbase->errstr(); $i++; } } } print STDOUT "Wrzucilem do tabeli cashimport $i operacji\n"; $fh->close; $dbase->disconnect(); } # W tej czesci odbywa sie "zatwierdzanie" operacji... mozna wylaczyc # za pomoca parametru --do-not-cash - wtedy operacje moze zatwierdzac # operator LMS'a recznie w UI (tylko co to da?) # Przegladamy tabele cashimport w poszukiwaniu rekordow, ktore nie maja # statusu 'closed' oraz maja ustalone id klienta (id!=0) # unless (exists($ARGV[0]) && $ARGV[0] eq "--do-not-cash") { my $dbase = DBI->connect("DBI:mysql:database=lms;host=localhost","$lms_login","$lms_password", { RaiseError => 1 }); # dodane po przeprawach z kodowaniem $dbase->do("SET NAMES 'utf8'"); my $dbq = $dbase->prepare("SELECT ci.id, ci.date, ci.value, ci.customerid, ci.description, CONCAT_WS(' ',c.name, c.lastname) AS customername, c.address, c.zip, c.city FROM cashimport ci LEFT JOIN customers c ON (c.id = ci.customerid) WHERE ci.closed='0' AND ci.customerid!='0'"); $dbq->execute() or die $dbase->errstr(); my $cash_insert = $dbase->prepare("INSERT INTO cash (time, value, customerid, comment, type) VALUES (UNIX_TIMESTAMP(),?,?,?,?)"); my $docnumber_select = $dbase->prepare("SELECT max(number)+1 FROM documents WHERE divisionid=? AND numberplanid=? AND type=? AND FROM_UNIXTIME(cdate,'%Y')>=YEAR(NOW()) AND FROM_UNIXTIME(cdate,'%Y')prepare("INSERT INTO documents (divisionid, type, number, numberplanid, cdate, customerid, name, address, zip, city, userid, closed) VALUES (?,?,?,?,UNIX_TIMESTAMP(),?,?,?,?,?,?,?)"); my $docid_select = $dbase->prepare("SELECT id FROM documents WHERE divisionid=? AND type=? AND number=? AND cdate BETWEEN (UNIX_TIMESTAMP()-3600) AND (UNIX_TIMESTAMP()+3600)"); my $receiptcontents_insert = $dbase->prepare("INSERT INTO receiptcontents (docid, itemid, value, description, regid) VALUES (?,?,?,?,?)"); my $i=0; while (my $row = $dbq->fetchrow_hashref()) { $cash_insert->execute($row->{'value'}, $row->{'customerid'}, $row->{'description'}, 3) or die $dbase->errstr(); $docnumber_select->execute($divisionid, $numberplanid, 2) or die $dbase->errstr(); my $docnumber = $docnumber_select->fetchrow_array(); $documents_insert->execute($divisionid, 2,$docnumber, $numberplanid, $row->{'customerid'}, $row->{'customername'}, $row->{'address'}, $row->{'zip'}, $row->{'city'}, 0, 1) or die $dbase->errstr(); $docid_select->execute($divisionid, 2, $docnumber) or die $dbase->errstr(); my $docid = $docid_select->fetchrow_array(); $receiptcontents_insert->execute($docid, 1, $row->{'value'}, $row->{'description'},$cashreg_id) or die $dbase->errstr(); $dbase->do("UPDATE cashimport SET closed='1' WHERE id=".$row->{'id'}."") or die $dbase->errstr(); $i++; } $docnumber_select->finish(); $docid_select->finish(); print STDOUT "Zaksiegowalem $i nowych operacji na podstawie wyciagu z konta bankowego.\n\n"; $dbase->disconnect(); }