469 lines
12 KiB
C++
469 lines
12 KiB
C++
|
/***************************************************************************
|
||
|
|
||
|
libMiniCash
|
||
|
Copyright © 2013-2018 christoph holzheuer
|
||
|
c.holzheuer@sourceworx.org
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
***************************************************************************/
|
||
|
|
||
|
|
||
|
#include <QtGui>
|
||
|
#include <QWidget>
|
||
|
#include <QMessageBox>
|
||
|
#include <QShortcut>
|
||
|
#include <QInputDialog>
|
||
|
#include <QDir>
|
||
|
#include <QPalette>
|
||
|
#include <QApplication>
|
||
|
|
||
|
#include <mcmainwindowbase.h>
|
||
|
#include <mcbillingview.h>
|
||
|
/// Für den Test auf CapsLock
|
||
|
///#include "Windows.h"
|
||
|
/*
|
||
|
* bool QMyClass::checkCapsLock()
|
||
|
{
|
||
|
// platform dependent method of determining if CAPS LOCK is on
|
||
|
#ifdef Q_OS_WIN32 // MS Windows version
|
||
|
return GetKeyState(VK_CAPITAL) == 1;
|
||
|
#else // X11 version (Linux/Unix/Mac OS X/etc...)
|
||
|
Display * d = XOpenDisplay((char*)0);
|
||
|
bool caps_state = false;
|
||
|
if (d)
|
||
|
{
|
||
|
unsigned n;
|
||
|
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
|
||
|
caps_state = (n & 0x01) == 1;
|
||
|
}
|
||
|
return caps_state;
|
||
|
#endif
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Der Konstruktor, die Initialisierungen des GUI
|
||
|
* und der Netzverbindung werden in den Unterklassen vorgenommen.
|
||
|
*/
|
||
|
|
||
|
MCMainWindowBase::MCMainWindowBase( QWidget* parent )
|
||
|
: QMainWindow( parent )
|
||
|
{
|
||
|
|
||
|
/// model setzen
|
||
|
_salesModel.setParent( this );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief MCMainWindowBase::~MCMainWindowBase !
|
||
|
*/
|
||
|
|
||
|
MCMainWindowBase::~MCMainWindowBase()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief MCMainWindowBase::transCount !
|
||
|
* @return
|
||
|
*/
|
||
|
|
||
|
QString MCMainWindowBase::transCount()
|
||
|
{
|
||
|
return MCSalesModel::formatInt( _transCount );
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Dateipfad anhand der Vorgaben aus dem Setup-Dialog erzeugen.
|
||
|
* @param key settingskey
|
||
|
*
|
||
|
* Dateipfad anhand der Vorgaben aus den settings zusammenbasteln, Hilfsfunktion
|
||
|
*
|
||
|
* @return pfad
|
||
|
*/
|
||
|
|
||
|
QString MCMainWindowBase::fetchSetting( const QString& key )
|
||
|
{
|
||
|
return _mainSettings.value( key ).toString();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Datei-Fehlermeldung anzeigen: Meistens sind einfach die Pfadeinstellungen falsch.
|
||
|
* @param filename
|
||
|
* @param errormsg
|
||
|
*
|
||
|
* Conveniencefunction zur vereinfachten Handhabung von Dateifehlern,
|
||
|
* errortxt holen, Messagebox erzeugen etc.
|
||
|
* @return immer false
|
||
|
*/
|
||
|
|
||
|
bool MCMainWindowBase::showFileError( const QString& filename, const QString& errormsg )
|
||
|
{
|
||
|
|
||
|
QString msg( "Datei '%0' konnte nicht geöffnet werden.\nFehlercode: %1" );
|
||
|
/// je schlimmer je geiler
|
||
|
QMessageBox::critical( this, "Fehler", msg.arg( filename, errormsg ) );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Dateien & Settings initialisieren
|
||
|
*
|
||
|
* Wird direkt beim Programmstart aufgerufen, setzt die Vorgabewerte
|
||
|
* und erzeugt die Arbeitsdateien.
|
||
|
*
|
||
|
* @see setupDataFile
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void MCMainWindowBase::setupDefaults()
|
||
|
{
|
||
|
|
||
|
/// leer? dann defaults reinschreiben
|
||
|
if( !_mainSettings.contains( miniCash::keyMobileDrive) )
|
||
|
{
|
||
|
_mainSettings.setValue( miniCash::keyMobileDrive, miniCash::mobileDrive );
|
||
|
_mainSettings.setValue( miniCash::keyProfit, miniCash::profit );
|
||
|
_mainSettings.setValue( miniCash::keySelfID, miniCash::selfID );
|
||
|
_mainSettings.setValue( miniCash::keyFooterText, "" );
|
||
|
}
|
||
|
|
||
|
/// solange Setup-Dialog zeigen bis die Arbeitsfiles
|
||
|
/// nutzbar sind
|
||
|
|
||
|
bool allfine = false;
|
||
|
while( !allfine )
|
||
|
{
|
||
|
allfine = setupDataFile();
|
||
|
if( !allfine )
|
||
|
{
|
||
|
QString msg( "Die lokale Arbeitsdatei konnte nicht angelegt werden.\nBitte die Programmeinstellungen überprüfen." );
|
||
|
QMessageBox::critical( this, "Dateifehler", msg );
|
||
|
setupDataFile();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Kassendateien öffnen oder erzeugen
|
||
|
*
|
||
|
* Hier werden die beiden Arbeitsdateien geöffnet bzw. erzeugt: es
|
||
|
* wird entschieden, ob ein zum heutigen Datum (also Freitag oder Samstag)
|
||
|
* ein passender Kleidermarkt vorhanden ist.
|
||
|
* Falls nicht, wird eine Kassendatei fürs heutige Datum angelegt.
|
||
|
*/
|
||
|
|
||
|
bool MCMainWindowBase::setupDataFile()
|
||
|
{
|
||
|
|
||
|
/// step 1: Filenamen aus Datum bauen, hack: wir nehmen immer den Freitag
|
||
|
QDate date = QDate::currentDate();
|
||
|
/// Hack: Am Samstag schalten wir auf Freitag
|
||
|
if( date.dayOfWeek() == 6 )
|
||
|
date = date.addDays( -1 );
|
||
|
|
||
|
/// Filenames aus Datum
|
||
|
_dataFileName = date.toString( "MM-dd-yyyy" ) + miniCash::filetype;
|
||
|
QString dirName = QDir::homePath() + miniCash::dataDir;
|
||
|
QDir dir( dirName );
|
||
|
if( !dir.exists() )
|
||
|
dir.mkpath( dirName );
|
||
|
|
||
|
_dataFilePath = dirName + _dataFileName;
|
||
|
|
||
|
/// erstmal alles auf Anfang
|
||
|
_transCount = 1;
|
||
|
|
||
|
/// Ist das File schon vorhanden ? Sonst anlegen
|
||
|
QFile datafile( _dataFilePath );
|
||
|
|
||
|
if( !datafile.exists() )
|
||
|
{
|
||
|
/// hats auch wirklich geklappt?
|
||
|
if( !datafile.open( QIODevice::WriteOnly ) )
|
||
|
/// wenn schon der start misslingt, hat
|
||
|
/// nichts mehr einen sinn
|
||
|
return showFileError( _dataFilePath, datafile.errorString() );
|
||
|
_mainSettings.setValue( miniCash::keyTransCount, "1" );
|
||
|
}
|
||
|
|
||
|
/// Zählerstand lesen.
|
||
|
_transCount = _mainSettings.value( miniCash::keyTransCount ).toInt();
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Speichert die Transaktionsdaten in eine Datei, hier Stick und Festplatte
|
||
|
* @param filename Zieldatei
|
||
|
* @param result die Verkaufstransaktion
|
||
|
*
|
||
|
* Verkaufte Artikel auf Platte oder Stick sichern. Ds ist eine Hilfsfunktion für
|
||
|
* @see onSaveTransaction, wir zweimal aufgerufen: einmal auf Stick und einmal
|
||
|
* als Geheim-Backup auf Platte.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
bool MCMainWindowBase::saveTransactionToFile( const QString& filename, const QString& transaction )
|
||
|
{
|
||
|
QFile datafile( filename );
|
||
|
if (!datafile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append ))
|
||
|
return showFileError( filename, datafile.errorString() );
|
||
|
|
||
|
/// file nicht direkt beschreiben
|
||
|
QTextStream output( &datafile );
|
||
|
/// Stream füllen
|
||
|
output << transaction;
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Schreibt die Einkäufe eines Kunden (==Transaktion)
|
||
|
* via QTextStream formatiert in einen String.
|
||
|
*
|
||
|
* Alle bisherigen Verkäufe, also der Inhalt des @see MCSalesModel, werden
|
||
|
* mit Hilfe eines QTextStreams als Tab-Separated-Values in einen QString
|
||
|
* geschrieben
|
||
|
*
|
||
|
* @return Die Verkaufstransaktion als String
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
QString MCMainWindowBase::saveTransactionToString()
|
||
|
{
|
||
|
|
||
|
QString result;
|
||
|
QTextStream output( &result );
|
||
|
|
||
|
/// Stream füllen
|
||
|
for( int row = 0; row < _salesModel.rowCount(); ++row )
|
||
|
{
|
||
|
|
||
|
/// TransactionNo
|
||
|
QModelIndex idx = _salesModel.index( row, MCSalesItemMap::Count );
|
||
|
QString content = _salesModel.data( idx ).toString();
|
||
|
output << content << " ";
|
||
|
|
||
|
/// SellerID
|
||
|
idx = _salesModel.index( row, MCSalesItemMap::SellerID );
|
||
|
content = _salesModel.data( idx ).toString();
|
||
|
output << content << " ";
|
||
|
|
||
|
/// Laufende ArtikelNo
|
||
|
idx = _salesModel.index( row, MCSalesItemMap::ItemNo );
|
||
|
|
||
|
/// passiert jetzt schon im model
|
||
|
///output << formatInt( _salesModel.data( idx ).toInt(), 3 );
|
||
|
output << _salesModel.data( idx ).toString();
|
||
|
|
||
|
idx = _salesModel.index( row, MCSalesItemMap::Price );
|
||
|
content = _salesModel.data( idx ).toString();
|
||
|
|
||
|
/// Eurozeichen absäbeln, komma statt punkt wg. kompatibilität
|
||
|
/// mit PCK
|
||
|
|
||
|
if( content.isEmpty() || content.isNull() )
|
||
|
{
|
||
|
/// FIX, murx: log error
|
||
|
content = "0,0";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
content = content.split( ' ' ).at(0);
|
||
|
}
|
||
|
|
||
|
/// so sollte man es eigentlich machen
|
||
|
output << Qt::right << qSetFieldWidth(15) << content << Qt::reset;
|
||
|
/// datum/zeit dranhängen & zeile fertig
|
||
|
QString dtstr = QDateTime::currentDateTime().toString( "MM-dd-yyyy hh:mm:ss" );
|
||
|
output << ' ' << dtstr << Qt::endl;
|
||
|
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Vom aktuellen Kunden gekaufte Artikel auf Platte sichern.
|
||
|
*
|
||
|
* Speichert die vom aktuellen Kunden gekauften Artikel auf Platte.
|
||
|
* @see saveTransactionToString()
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
bool MCMainWindowBase::onSaveTransaction()
|
||
|
{
|
||
|
|
||
|
// View auslesen
|
||
|
/// an datei anhängen
|
||
|
/// zähler zurücksetzen
|
||
|
/// die Lineedits wieder löschen
|
||
|
|
||
|
///
|
||
|
/// Step 0: gibbet überhaupt View-Daten?
|
||
|
///
|
||
|
|
||
|
if( _salesModel.rowCount() == 0 )
|
||
|
{
|
||
|
QApplication::beep();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
///
|
||
|
/// Step 1: View-Daten via Textstream in einen String schreiben
|
||
|
///
|
||
|
|
||
|
QString transaction = saveTransactionToString();
|
||
|
|
||
|
///
|
||
|
/// Step 2a: Die (nunmehr formatierte) Transaktion speichern und sichern
|
||
|
///
|
||
|
|
||
|
/// Offizieller Speicherort auf dem Stick bzw. nunmehr auf SSD
|
||
|
if( !saveTransactionToFile( _dataFilePath, transaction ) )
|
||
|
return false;
|
||
|
|
||
|
/// Dummy für Speichern im Netz
|
||
|
emit transactionCreated( transaction );
|
||
|
|
||
|
///
|
||
|
/// Step 4: das Zählerfeld neu setzen
|
||
|
///
|
||
|
_newTransCount++;
|
||
|
_transCount++;
|
||
|
_mainSettings.setValue( miniCash::keyTransCount, _transCount );
|
||
|
|
||
|
///
|
||
|
/// Step 4: Die Lineedits & view wieder löschen
|
||
|
///
|
||
|
|
||
|
_inputViewProxy->onResetView();
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Verkaufstransaktionen kopieren
|
||
|
*
|
||
|
* Alle Verkaufstransaktionen zur Auswertung auf einen Memorystick
|
||
|
* kopieren
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
void MCMainWindowBase::onCopyTransactions()
|
||
|
{
|
||
|
|
||
|
/// FIX murx
|
||
|
QMessageBox msg;
|
||
|
msg.setWindowTitle( "Auswertungsdatei erzeugen" );
|
||
|
QString txt( "Wenn der Memorystick in Laufwerk '%0' \neingesteckt ist bitte auf 'Speichern' klicken" );
|
||
|
msg.setText( txt.arg( fetchSetting( miniCash::keyMobileDrive ) ) );
|
||
|
msg.setStandardButtons( QMessageBox::Save | QMessageBox::Cancel );
|
||
|
msg.addButton("Speichern",QMessageBox::AcceptRole);
|
||
|
msg.addButton("Abbrechen",QMessageBox::RejectRole);
|
||
|
msg.setDefaultButton(QMessageBox::Save);
|
||
|
msg.setIcon ( QMessageBox::Information );
|
||
|
|
||
|
if( msg.exec() == QMessageBox::Cancel )
|
||
|
return;
|
||
|
|
||
|
QString target = fetchSetting( miniCash::keyMobileDrive ) + fetchSetting( miniCash::keySelfID );
|
||
|
target += "_" + _dataFileName;
|
||
|
|
||
|
/// falls vorhanden löschen weil copy nicht überschreibt
|
||
|
QFileInfo fitarget( target );
|
||
|
|
||
|
if( fitarget.exists() )
|
||
|
QFile::remove( target );
|
||
|
|
||
|
if( !QFile::copy( _dataFilePath, target ) )
|
||
|
{
|
||
|
QString msg( "Dateifehler: Kopieren von %0\nnach %1 fehlgeschlagen.");
|
||
|
QMessageBox::warning(this, "Dateifehler", msg.arg( _dataFilePath, target ) );
|
||
|
|
||
|
return onSetup();
|
||
|
}
|
||
|
|
||
|
QMessageBox::information( this, "Auswertungsdatei erzeugen", "Auswertungsdatei wurde erfolgreich erzeugt." );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Handbuch anzeigen
|
||
|
*
|
||
|
* @bug Handbuch ist noch nicht fertig.
|
||
|
*/
|
||
|
|
||
|
void MCMainWindowBase::onHelpManual()
|
||
|
{
|
||
|
//QMessageBox::StandardButton reply = QMessageBox::information(this, "onHelpContents()", "onHelpContents()" );
|
||
|
//_helpViewer->show();
|
||
|
onHelpAbout();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Speichern und Beenden, die Toolbarversion: Es wird nachgefragt.
|
||
|
*/
|
||
|
|
||
|
void MCMainWindowBase::onExitConfirmed()
|
||
|
{
|
||
|
/// murx
|
||
|
QMessageBox msg;
|
||
|
msg.setWindowTitle("Programm beenden");
|
||
|
msg.setText("Programm wirklich beenden?");
|
||
|
msg.setStandardButtons( QMessageBox::Save | QMessageBox::Cancel );
|
||
|
msg.addButton("OK", QMessageBox::AcceptRole );
|
||
|
msg.addButton("Abbrechen",QMessageBox::RejectRole);
|
||
|
msg.setDefaultButton(QMessageBox::Save);
|
||
|
msg.setIcon ( QMessageBox::Information );
|
||
|
|
||
|
if( msg.exec() == QMessageBox::Cancel )
|
||
|
return;
|
||
|
|
||
|
onExit();
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @brief Speichern und Beenden, die Menuversion: Es wird nicht nachgefragt.
|
||
|
*/
|
||
|
|
||
|
void MCMainWindowBase::onExit()
|
||
|
{
|
||
|
|
||
|
/// Falls noch Transaktion vorhanden,
|
||
|
/// diese sichern
|
||
|
if( _salesModel.rowCount() != 0 )
|
||
|
onSaveTransaction();
|
||
|
qApp->exit();
|
||
|
}
|