Programm_Wirl/AA-Forms/FormExpedit.cs
2026-02-25 08:29:57 +01:00

1297 lines
49 KiB
C#

using BrightIdeasSoftware;
using DatenDB;
using Deckungsbeitrag.Properties;
using Microsoft.VisualBasic;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Resolvers;
namespace Deckungsbeitrag
{
public partial class FormExpedit : Form
{
#region Properties
private readonly UserSettingsManager _settings = new UserSettingsManager();
private ToolTip cellTooltip = new ToolTip();
public Meldungen fehlermeldung = new Meldungen();
public Image etikett = null;
public Auftrag auftrag;
public Auftrag NeuerAuftrag; //NEU FÜR UPDATE
public AuftragArtikel auftragArtikel;
public Benutzer benutzer;
public Kunde kunde;
DateTime? letzterLiefertag = null;
bool nocont = false;
bool nocontclicked = false;
Meldungen meldung = new Meldungen();
public List<KundeArtikel> artikelListe;
public List<Auftrag> auftraglist;
public List<Auftrag> sonderlist;
public List<Auftrag> geschaeftlist;
int beforeedit = 0;
int? neuecontclean = null;
int? altecontclean = null;
Timer timer1 = new Timer(); // Timer für Nachwäsche
Timer timer2 = new Timer(); // Timer für Refresh Auftragliste
private Screen[] screens;
private Point itemlocation;
//private ImageList imagelist = new ImageList();
private volatile bool _isProcessing = false; // Thread-sicherer Flag gegen Überlappung
private int _consecutiveFailures = 0;
private const int MAX_FAILURES = 3;
private bool aufgabeDone = false;
private bool isttest = true;
private bool _doubleClickPending = false;
#endregion
public FormExpedit(Benutzer benutzer)
{
InitializeComponent();
this.benutzer = benutzer;
this.StartPosition = FormStartPosition.CenterScreen;
this.tabControlAuftragList.Visible = true;
//Timer für Autorefresh & Auftragliste Refresh
//timer1.Interval = 10000; // 10 sekunden
//timer1.Tick += Timer1_Tick;
//timer1.Start();
//timer2.Interval = 60000; // 1 Minute
//timer2.Tick += Timer2_Tick;
//timer2.Start();
}
public FormExpedit(Benutzer benutzer, Screen[] screens) : this(benutzer)
{
this.screens = screens;
foreach (Screen screen in screens)
{
if (screen.Primary)
{
if (screen.WorkingArea.Width < 1500) { this.objectListViewAuftrag.Font = new Font(this.objectListViewAuftrag.Font.FontFamily, 10); }
}
}
}
private void FormExpedit_Load(object sender, EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
this.Visible = false;
this.SuspendLayout();
this.Text += " Version: " + Assembly.GetExecutingAssembly().GetName().Version.ToString();
_settings.Load();
//Auftragslisten erstellen und in ObjectListViews laden.
// DB PARALLEL im Background (NON-BLOCKING!)
_ = Task.Run(async () =>
{
try
{
await Task.WhenAll(GetAuftraege(), GetSonderAuftraege());
// UI-Update NACH Daten (Invoke!)
this.Invoke(new Action(() =>
{
// OLVs füllen
OLV_Load(objectListViewAuftrag, auftraglist);
// Lade die ListView für Sonderaufträge wie STV, STH, oder INVENTUR
if (sonderlist.Count == 0) { this.tabControlAuftragList.TabPages.Remove(tabPageSonder); }
else { OLV_Load(this.objectListViewSonder, sonderlist); if (this.tabControlAuftragList.TabPages.Count < 2) this.tabControlAuftragList.TabPages.Insert(1, tabPageSonder); }
// Lade die ListView für Geschäftaufträge
if (geschaeftlist.Count == 0) { this.tabControlAuftragList.TabPages.Remove(tabPageGeschaeft); }
else { OLV_Load(this.objectListViewGeschaeft, geschaeftlist); if (this.tabControlAuftragList.TabPages.Count < 2) this.tabControlAuftragList.TabPages.Insert(1, tabPageGeschaeft); }
// Controls aktivieren und deaktivieren nach BenutzerRolle
Enable_Controls();
this.Visible = true;
}));
}
catch (Exception ex)
{
// Vermeide $ für C# älter
this.BeginInvoke(new Action(() => MessageBox.Show(
string.Format("Laden fehlgeschlagen: {0}", ex.Message))));
}
});
}
/// <summary>
/// Controls werden nach Benutzer deaktiviert.
/// </summary>
private void Enable_Controls()
{
switch (this.benutzer.Rolle)
{
case BenutzerRolle.Verwaltung:
this.buttonNeuerAuftrag.Enabled = this.buttonSWS_Drucken.Enabled = false;
GetUnvisibleButtonColumn();
break;
case BenutzerRolle.Fahrer:
this.buttonNeuerAuftrag.Enabled = this.buttonSWS_Drucken.Enabled = false;
GetUnvisibleButtonColumn();
break;
case BenutzerRolle.Admin:
break;
case BenutzerRolle.Waschstrasse:
this.buttonNeuerAuftrag.Enabled = this.buttonSWS_Drucken.Enabled = false;
GetUnvisibleButtonColumn();
break;
case BenutzerRolle.Master:
break;
case BenutzerRolle.Expedit:
break;
case BenutzerRolle.Frottee:
this.buttonNeuerAuftrag.Visible = this.buttonSWS_Drucken.Visible = false;
this.buttonStats.Visible = this.buttonEinstellung.Visible = this.buttonReklamation.Visible = false; // buttonReklamation einblenden wenn Frottee-Expedit auch Reklamation bearbeitet.
GetUnvisibleButtonColumn();
break;
case BenutzerRolle.Flach:
this.buttonNeuerAuftrag.Enabled = this.buttonSWS_Drucken.Enabled = false;
// WENN Rolle Frottee (Expedit Frottee) dann ButtonClick deaktiviert. Nur Expedit kann abschließen
GetUnvisibleButtonColumn();
break;
default:
break;
}
}
/// <summary>
/// Auftragslisten erstellen und in ObjectListViews laden.
/// </summary>
private async Task GetSonderAuftraege()
{
var conn = new NpgsqlConnection(ConfigurationManager.AppSettings["ConnectionString"]); // Pro Task!
try
{
await conn.OpenAsync();
if (this.benutzer.Rolle == BenutzerRolle.Frottee)
{
sonderlist = await Auftrag.GetExpeditListsAsync((int)AuftragTyp.Inventur, conn, (int)AbteilungsStatus.FrotteeBegonnen);
}
else
{
sonderlist = await Auftrag.GetExpeditListsAsync((int)AuftragTyp.Inventur, conn, null);
}
}
finally
{
if (conn.State == ConnectionState.Open)
conn.Close();
conn.Dispose();
}
}
private async Task GetAuftraege()
{
var conn = new NpgsqlConnection(ConfigurationManager.AppSettings["ConnectionString"]); // Pro Task!
try
{
await conn.OpenAsync();
if (this.benutzer.Rolle == BenutzerRolle.Frottee)
{
auftraglist = await Auftrag.GetAuftragListTodayAsync(DateTime.Today, conn, AuftragTyp.Standart, (int)AbteilungsStatus.FrotteeBegonnen);
geschaeftlist = await Auftrag.GetAuftragListTodayAsync(DateTime.Today, conn, AuftragTyp.Geschäft, (int)AbteilungsStatus.FrotteeBegonnen);
}
else
{
auftraglist = await Auftrag.GetAuftragListTodayAsync(DateTime.Today, conn, AuftragTyp.Standart, null);
geschaeftlist = await Auftrag.GetAuftragListTodayAsync(DateTime.Today, conn, AuftragTyp.Geschäft, null);
}
}
finally
{
if (conn.State == ConnectionState.Open)
conn.Close();
conn.Dispose();
}
}
/// <summary>
/// Wenn Timer abgelaufen Funktion für Autorefresh & Neuer Timer für Auftragliste Refresh.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Timer1_Tick(object sender, EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
// Nur durchführen wenn DataGridView ist sichtbar.
if (dGArtikel.Visible == true)
{
// Vermeide Überlappung (wenn vorheriger Tick noch läuft)
if (_isProcessing) return;
Cursor.Current = Cursors.WaitCursor;
_isProcessing = true;
try
{
if (auftrag != null)
{
const int maxRetries = 3;
Auftrag _auftrag = null;
for (int retry = 0; retry < maxRetries; retry++)
{
try
{
_auftrag = Auftrag.GetAuftrag(auftrag.AuftragID);
if (dGArtikel.Visible) Load_Gridview(_auftrag);
_consecutiveFailures = 0; // Erfolg -> Zähler reset
break;
}
catch (NpgsqlException ex) when ((ex.InnerException is SocketException || ex.InnerException is System.IO.IOException) && retry < maxRetries - 1)
{
_consecutiveFailures++;
Debug.WriteLine($"Retry {retry + 1}/{maxRetries}: {ex.Message}"); // Logging
System.Threading.Thread.Sleep(1000 * (int)Math.Pow(2, retry)); // Backoff: 1s, 2s, 4s
}
catch (Exception ex)
{
Debug.WriteLine($"Unexpected error: {ex.Message}"); // Logging
break;
}
}
// Timer stoppen bei zu vielen aufeinanderfolgenden Fehlern
if (_consecutiveFailures >= MAX_FAILURES)
{
timer1.Enabled = false;
Debug.WriteLine("Timer gestoppt nach " + MAX_FAILURES + " Fehlern. Bitte manuell neu starten.");
// Optional: Status-Label setzen oder MessageBox
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Timer1_Tick globaler Fehler: {ex}");
_consecutiveFailures++;
}
finally
{
_isProcessing = false;
Cursor.Current = Cursors.Default;
}
}
this.Cursor = Cursors.Default;
}
private async void Timer2_Tick(object sender, EventArgs e)
{
await GetAuftraege();
}
/// <summary>
/// Laden Events (GroupBox_Etikett und GridView)
/// </summary>
private void Load_Etikett_GroupBox()
{
//if (this.auftrag == null)
//{
// this.buttonKundeNr.Text = "Auftrag in der Liste wählen";
// this.pictureBoxEntwurf.Image = null;
// this.textBoxCont.Text = "0";
// this.pictureBoxMinus.Enabled = this.pictureBoxPlus.Enabled = this.buttonDrucken.Enabled = false;
// this.buttonDrucken.BackColor = Color.Gray;
// this.pictureBoxMinus.BackColor = Color.Gray;
// this.pictureBoxPlus.BackColor = Color.Gray;
// foreach (Control ctr in this.groupBoxEtikett.Controls)
// {
// if (ctr.GetType() == typeof(Label)) ctr.Enabled = true;
// else if (ctr.Enabled == true) ctr.Enabled = false;
// }
// this.dTPLiefertag.Enabled = true;
//}
//else
//{
// //Kunde holen und Name anzeigen
// this.kunde = Kunde.GetKunde(null, this.auftrag.KundeID, null);
// this.buttonKundeNr.Text = kunde.Suchtext;
// //Containeranzahl und Liefertag anzeigen
// if (neuecontclean != null) this.textBoxCont.Text = neuecontclean.ToString();
// else this.textBoxCont.Text = this.auftrag.ContainerClean.ToString();
// this.dTPLiefertag.Value = this.auftrag.Liefertag;
// this.dTPLiefertag.Enabled = false;
// //Buttons Enablen und BackColor wechsel
// this.buttonDrucken.BackColor = Color.FromArgb(0, 192, 0);
// this.pictureBoxPlus.BackColor = Color.FromArgb(0, 192, 0);
// this.buttonDrucken.Enabled = this.pictureBoxPlus.Enabled = true;
// this.auftrag.ContainerClean = int.Parse(this.textBoxCont.Text);
// etikett = Funktionen.Etikett_Entwurf(this, this.auftrag, nocont);
// this.pictureBoxEntwurf.Image = etikett;
//}
}
#region Button_Click-Events
private async void buttonNeuerAuftrag_Click(object sender, EventArgs e)
{
FormNeuerAuftrag neuerAuftrag = new FormNeuerAuftrag(benutzer, this.letzterLiefertag, AuftragTyp.Standart);
if (neuerAuftrag.ShowDialog() == DialogResult.OK)
{
this.letzterLiefertag = neuerAuftrag.letzterLiefertag;
await GetAuftraege();
//List<Auftrag> auftraglist = Auftrag.GetAuftragListToday(DateTime.Today);
OLV_SetObjects(this.objectListViewAuftrag, auftraglist);
OLV_SetObjects(this.objectListViewGeschaeft, geschaeftlist);
}
}
private void buttonSWS_Drucken_Click(object sender, EventArgs e)
{
Kunde knd = Funktionen.KundenAuswahl();
if(knd != null) Funktionen.SWS_Drucken(null, knd, this);
}
private void buttonEinstellung_Click(object sender, EventArgs e)
{
FormEinstellung einstellung = new FormEinstellung(this);
einstellung.ShowDialog();
}
private void buttonSuchen_Click(object sender, EventArgs e)
{
int kundeID = 0;
string eingabe = PromptForText(
"Welchen Kunden suchst du? \n\n" +
"Bitte scanne den QR-Code des Kunden den du suchst:",
"QR-Code Inhalt",
"0");
if (string.IsNullOrWhiteSpace(eingabe))
{
this.Cursor = Cursors.Default;
return;
}
string cleanedText = new string(eingabe.Where(char.IsLetterOrDigit).ToArray());
if (int.TryParse(cleanedText, out _)) kundeID = Kunde.GetKundeID(cleanedText, null);
else kundeID = Kunde.GetKundeID(null, cleanedText);
var model = objectListViewAuftrag.Objects.Cast<Auftrag>().FirstOrDefault(a => a.KundeID == kundeID);
if (model != null)
{
objectListViewAuftrag.SelectedObject = model;
objectListViewAuftrag.EnsureModelVisible(model);
objectListViewAuftrag.Focus();
}
}
private void buttonReklamation_Click(object sender, EventArgs e)
{
FormFehlmengeCount reklamation = new FormFehlmengeCount(this.benutzer);
reklamation.ShowDialog();
}
#endregion
#region ObjectListView-Events
private void OLV_Load(ObjectListView olv, List<Auftrag> auftraglist)
{
// Cursor auf Warte-Status setzen, um Benutzerfeedback zu geben
this.Cursor = Cursors.WaitCursor;
// Layout- und Update-Änderungen pausieren, um Flackern zu verhindern
olv.SuspendLayout(); // Stoppt Layout-Berechnungen (Anchor/Dock)
// SpaltenHeader-Design anpassen
//this.objectListViewAuftrag.HeaderFormatStyle = Funktionen.GetHeader();
// Automatisch alle Properties von Auftrag als Spalten generieren
Generator.GenerateColumns(olv, typeof(Auftrag), true);
// ========== CHECKBOX COLUMNS FÜR ABTEILUNGEN ===========
// Spalten-Setup
var colFrottee = new OLVColumn("Frottee", "Frottee");
colFrottee.Text = "FR";
colFrottee.DisplayIndex = 6;
colFrottee.TextAlign = HorizontalAlignment.Center;
colFrottee.Width = 50;
colFrottee.AspectGetter = row => {
var auftrag = (Auftrag)row;
int abteilungen = (int)auftrag.Abteilungen;
bool begonnen = (abteilungen & (int)AbteilungsStatus.FrotteeBegonnen) != 0;
bool beendet = (abteilungen & (int)AbteilungsStatus.FrotteeBeendet) != 0;
if (beendet) return "☑"; // Status 1 ✓
if (begonnen) return "☐"; // Status 2 ✓
return "☒"; // Status 0 ✓
};
var colKleinteile = new OLVColumn("Kleinteile", "Kleinteile");
colKleinteile.Text = "KT";
colKleinteile.DisplayIndex = 7;
colKleinteile.TextAlign = HorizontalAlignment.Center;
colKleinteile.Width = 50;
colKleinteile.AspectGetter = row => {
var auftrag = (Auftrag)row;
int abteilungen = (int)auftrag.Abteilungen;
bool begonnen = (abteilungen & (int)AbteilungsStatus.KleinteileBegonnen) != 0;
bool beendet = (abteilungen & (int)AbteilungsStatus.KleinteileBeendet) != 0;
if (beendet) return "☑"; // Status 1 ✓
if (begonnen) return "☐"; // Status 2 ✓
return "☒"; // Status 0 ✓
};
var colGrosseTeile = new OLVColumn("Großteile", "Großteile");
colGrosseTeile.Text = "GT";
colGrosseTeile.DisplayIndex = 8;
colGrosseTeile.TextAlign = HorizontalAlignment.Center;
colGrosseTeile.Width = 50;
colGrosseTeile.AspectGetter = row => {
var auftrag = (Auftrag)row;
int abteilungen = (int)auftrag.Abteilungen;
bool begonnen = (abteilungen & (int)AbteilungsStatus.GrossteileBegonnen) != 0;
bool beendet = (abteilungen & (int)AbteilungsStatus.GrossteileBeendet) != 0;
if (beendet) return "☑"; // Status 1 ✓
if (begonnen) return "☐"; // Status 2 ✓
return "☒"; // Status 0 ✓
};
olv.AllColumns.Add(colFrottee);
olv.AllColumns.Add(colKleinteile);
olv.AllColumns.Add(colGrosseTeile);
// ========== COLUMNS DURCHLAUFEN ===========
foreach (OLVColumn col in olv.AllColumns)
{
switch (col.Text)
{
case "Abschließen": // Abschließen Spalte als Button-Spalte konfigurieren
OLVColumn fertigColumn = col;
fertigColumn.DisplayIndex = 11;
fertigColumn.IsButton = true; // Als Button rendern
fertigColumn.ButtonSizing = OLVColumn.ButtonSizingMode.CellBounds; // Button füllt gesamte Zelle
fertigColumn.HeaderImageKey = "abschließen";
fertigColumn.ShowTextInHeader = false;
fertigColumn.MinimumWidth = fertigColumn.MaximumWidth = 80;
break;
case "Etikett": // Etikett Spalte als Button-Spalte konfigurieren
OLVColumn etikettColumn = col;
etikettColumn.DisplayIndex = 10;
etikettColumn.IsButton = true; // Als Button rendern
etikettColumn.ButtonSizing = OLVColumn.ButtonSizingMode.CellBounds; // Button füllt gesamte Zelle
etikettColumn.HeaderImageKey = "print";
etikettColumn.ShowTextInHeader = false;
etikettColumn.MinimumWidth = etikettColumn.MaximumWidth = 80;
break;
case "Aufgabe": // Aufgabe Spalte als Icon-Spalte: zeigt Icon wenn AufgabeID vorhanden
OLVColumn iconColumn = col;
iconColumn.DisplayIndex = 9;
iconColumn.ImageGetter = rowObject =>
{
var auftrag = rowObject as Auftrag;
if (auftrag.AufgabeID == null) return null; // null = kein Icon anzeigen
else return 0; // Index 0 aus SmallImageList
};
break;
case "Kunde": // Kunde Spalte als Icon-Spalte: zeigt Icon und Kundename wenn Anmerkung vorhanden
OLVColumn iColumn = col;
iColumn.DisplayIndex = 0;
iColumn.ImageGetter = rowObject =>
{
var _auftrag = rowObject as Auftrag;
if (string.IsNullOrWhiteSpace(Kunde.GetKunde(null, _auftrag.KundeID, null).Anmerkung)) return null; // null = kein Icon anzeigen
else return 1; // Index 0 aus SmallImageList
};
break;
case "Liefertag": // Liefertag Spalte Zeigt Liefertag als String an.
OLVColumn dateColumn = col;
dateColumn.DisplayIndex = 1;
dateColumn.AspectToStringFormat = "{0:ddd dd.MM.}";
// DateTime-Filter für Spalte 2
dateColumn.ClusteringStrategy = new DateTimeClusteringStrategy(DateTimePortion.Day, "ddd dd.MM."); // DateTimeClustering damit Datum gefiltert werden kann
olv.SecondarySortColumn = dateColumn; // Primary Sortiert nach Spalte 5 (Status) Secundary Sortiert nach Spalte 2 (Liefertag)
break;
case "Status": // Spalte 5 als primäre Sortier-Spalte festlegen (nach Datenbindung!)
OLVColumn sortcolumn = col;
sortcolumn.DisplayIndex = 4;
olv.PrimarySortColumn = sortcolumn; // Primary Sortiert nach Spalte 5 (Status) Secundary Sortiert nach Spalte 2 (Liefertag)
break;
case "Auswählen":
col.IsVisible = false;
break;
case "Erstellt":
col.IsVisible = false;
break;
case "Gedruckt":
col.IsVisible = false;
break;
case "Erledigt":
col.IsVisible = false;
break;
case "schmutzig":
col.HeaderImageKey = "dirt";
col.ShowTextInHeader = false;
col.MinimumWidth = col.MaximumWidth = 80;
col.DisplayIndex = 2;
break;
case "sauber":
col.HeaderImageKey = "clean";
col.ShowTextInHeader = false;
col.MinimumWidth = col.MaximumWidth = 80;
col.DisplayIndex = 3;
break;
default:
break;
}
}
//TODO: Reihenfolge Spalten richtig stellen.
//TODO: OLV State speichern.
olv.RebuildColumns();
if (_settings.OLV_State != null) olv.RestoreState(_settings.OLV_State);
// ========== IMAGE-LIST ZUWEISEN ==========
// SmallImageList für Icons in Detail-View (24x24 Pixel standard)
Program.imagelist.ImageSize = new Size(24, 24);
olv.SmallImageList = Program.imagelist; // Voraussetzung: imagelist mit Icon Index 0 gefüllt
// ========== TOOLTIP FÜR ICON-SPALTEN ==========
// Automatischer Cell-Tooltip bei Mouse-Hover über Spalte 10 und 1
olv.CellToolTipShowing += (s, e) =>
{
// Nur für Icon-Spalte (Index 10) und gültiges Model
if (e.Column.Text == "Aufgabe" && e.Model is Auftrag auftrag)
{
// Lade Bezeichnung der Aufgabe als Tooltip-Text
e.Text = Aufgabe.GetAufgabe(null, auftrag.AufgabeID).Bezeichnung;
}
// Nur für Icon-Spalte (Index 1) und gültiges Model
if (e.Column.Text == "Kunde" && e.Model is Auftrag _auftrag)
{
if (!string.IsNullOrWhiteSpace(Kunde.GetKunde(null, _auftrag.KundeID, null).Anmerkung))
{
// Lade Anmerkung als Tooltip-Text
e.Text = Kunde.GetKunde(null, _auftrag.KundeID, null).Anmerkung;
}
}
//TODO: Alle Icons in einer Spalte anzeigen? I für Anmerkung und ! für Aufgabe??
if (e.Column.Text == "Fahrer" && e.Model is Auftrag auftrag1)
{
e.Text = auftrag1.ZusatzInfo;
}
};
// ========== DATEN LADEN ==========
// WICHTIG: SetObjects NACH Column-Konfiguration aufrufen!
OLV_SetObjects(olv, auftraglist);
olv.RefreshObjects(auftraglist);
// ========== SORTIERUNG ==========
olv.Sort();
// ========== SPALTEN ANPASSEN ==========
olv = Funktionen.Columns_Resize(null, olv).olv; // Externe Funktion für responsive Spaltenbreiten
olv.FilterMenuBuildStrategy = new MeinFilterMenu();
// ========== AUFRÄUMEN & AKTIVIEREN ==========
olv.ResumeLayout(); // Layout-Pause beenden, Positionen neu berechnen
olv.Focus(); // OLV fokussieren (Tastaturbedienung aktivieren)
// Cursor zurücksetzen
this.Cursor = Cursors.Default;
}
private void objectListViewAuftrag_FormatCell(object sender, FormatCellEventArgs e)
{
if (e.ColumnIndex == 11)
{ // jede zweite Spalte
if(e.SubItem.BackColor != Color.LightBlue)
{
e.SubItem.BackColor = Color.AliceBlue;
e.SubItem.ForeColor = Color.Black;
}
else
{
e.SubItem.BackColor = Color.LightSteelBlue;
e.SubItem.ForeColor = Color.Black;
}
}
if (e.Column.Text == "FR" || e.Column.Text == "KT" || e.Column.Text == "GT")
{
if (e.CellValue.ToString() == "☑")
{
e.SubItem.Font = new Font("Segoe UI Symbol", 14F, FontStyle.Bold);
e.SubItem.ForeColor = Color.YellowGreen;
}
if (e.CellValue.ToString() == "☐")
{
e.SubItem.Font = new Font("Segoe UI Symbol", 14F, FontStyle.Bold);
e.SubItem.ForeColor = Color.Yellow;
}
if (e.CellValue.ToString() == "☒")
{
e.SubItem.Font = new Font("Segoe UI Symbol", 14F, FontStyle.Bold);
e.SubItem.ForeColor = Color.Red;
}
}
}
private async void objectListViewAuftrag_CellClick(object sender, CellClickEventArgs e)
{
itemlocation = new Point(e.Location.X, e.Location.Y);
ObjectListView olv = (ObjectListView)sender;
Auftrag _auftrag = (Auftrag)e.Model;
if (e.Model == null) { dGArtikel.Visible = false; chartFehlmenge.Visible = false; groupBoxKndInfo.Visible = false; return; }
if (_auftrag.Status != AuftragStatus.Fertig)
{
this.auftrag = _auftrag;
this.kunde = Kunde.GetKunde(null, this.auftrag.KundeID, null);
this.neuecontclean = null;
Load_Etikett_GroupBox();
this.artikelListe = KundeArtikel.GetList(kunde.KundeID.ToString());
// Chart wird eingeblendet statt Gridview. NUR wenn isttest = true.
var chartliste = await KundeArtikel.LoadArtikelDatenAsync(kunde);
if (chartliste.Count > 0)
{
if (isttest) { this.chartFehlmenge = Funktionen.Load_FehlmengeChart(chartliste, chartFehlmenge); this.chartFehlmenge.Visible = true; }
else Load_Gridview(this.auftrag);
}
this.groupBoxKndInfo.Visible = true;
if (!string.IsNullOrWhiteSpace(this.kunde.Anmerkung)) { labelAnmerkung.Visible = true; labelAnmerkung.Text = this.kunde.Anmerkung; }
else labelAnmerkung.Visible = false;
if (auftrag.AufgabeID != null)
{
labelAufgabe.Text = Aufgabe.GetAufgabe(null, auftrag.AufgabeID).Bezeichnung.ToString();
labelAufgabe.Visible = true;
}
else labelAufgabe.Visible = false;
}
else
{
fehlermeldung.AuftragFertig();
}
olv.Focus();
}
private async void objectListViewAuftrag_CellRightClick(object sender, CellRightClickEventArgs e)
{
ObjectListView olv = (ObjectListView)sender;
// Überprüfung ob Click auf einem Item war.
if (e.Model == null) return;
Auftrag _auftrag = (Auftrag)e.Model;
Kunde _kunde = Kunde.GetKunde(null, _auftrag.KundeID, null);
string name = string.Empty;
if (string.IsNullOrWhiteSpace(_kunde.Suchtext)) name = _kunde.KundeName;
else name = _kunde.Suchtext;
// Überprüfung welche Spalte geclickt wurde.
switch (e.Column.Text)
{
case "Abschließen":
// Wenn Click in Gruppe Vorbereitet dann ignorieren.
if (e.Item.Group.Header == "Vorbereitet") return;
if (meldung.GetFrage(this, $"Für den Auftrag wurden keine Etiketten gedruckt, ohne Etikettendruck als Vorbereitet markieren?", false) == DialogResult.Yes)
{
// Wenn Aufgabe hinterlegt ist wir nachgefragt ob diese erledigt wurde.
if (_auftrag.AufgabeID != null)
{
// Wenn NEIN wird der Click abgebrochen.
if (meldung.GetAchtung(this, $"Bei dem ausgewählten Auftrag ist die Aufgabe {Aufgabe.GetAufgabe(null, _auftrag.AufgabeID).Bezeichnung} zu erledigen. Hast du die Aufgabe erledigt? ", false) == DialogResult.No) return;
else Aufgabe_erledigt(_auftrag, _kunde, name);
}
// Wenn TextBox für saubere Container 0 ist wird der Click abgebrochen.
if (_auftrag.ContainerClean == 0) { meldung.GetFehler(this, "Du kannst den Auftrag nicht als vorbereitet markieren. Wähle zuerst die Anzahl der sauberen Container."); return; }
// Auftrag wird bearbeitet und gespeichert.
_auftrag.Status = AuftragStatus.Vorbereitet;
_auftrag.Save();
olv.RefreshObject(e);
await GetAuftraege();
if (olv.Name.Contains("Sonder")) OLV_SetObjects(olv, sonderlist);
if (olv.Name.Contains("Geschaeft")) OLV_SetObjects(olv, geschaeftlist);
if (olv.Name.Contains("Auftrag")) OLV_SetObjects(olv, auftraglist);
}
break;
case "Aufgabe":
if (_auftrag.AufgabeID == null) return;
Aufgabe _aufgabe = Aufgabe.GetAufgabe(null, ((Auftrag)e.Model).AufgabeID);
if (meldung.GetFrage(this, $"Hast du beim Kunden {name} die Aufgabe {_aufgabe.Bezeichnung} erledigt und möchtest sie als erledigt markieren?", false) == DialogResult.Yes)
{
Aufgabe_erledigt(_auftrag, _kunde, name);
}
break;
default:
break;
}
}
private void objectListViewAuftrag_ButtonClick(object sender, BrightIdeasSoftware.CellClickEventArgs e)
{
Cursor.Current = Cursors.WaitCursor;
ObjectListView olv = (ObjectListView)sender;
Auftrag _auftrag = (Auftrag)e.Model;
if (e.Column.Text == "Abschließen")
{
if (this.auftrag != _auftrag) altecontclean = null;
this.auftrag = _auftrag;
//Load_Etikett_GroupBox();
if (this.benutzer.Rolle == BenutzerRolle.Frottee) { Auftrag_Fertig(); Neuladen_OLV(olv); return; }
if (this.auftrag.ContainerClean == 0)
{
switch (this.auftrag.Typ)
{
case AuftragTyp.Geschäft:
break;
case AuftragTyp.Standart:
if (meldung.NoCleanContainer() == DialogResult.OK) return;
break;
case AuftragTyp.Inventur:
if (meldung.SonderNoCleanContainer() == DialogResult.Yes)
{
Auftrag_Fertig();
Load_Etikett_GroupBox();
}
else return;
break;
case AuftragTyp.Standverminderung:
if (meldung.SonderNoCleanContainer() == DialogResult.Yes)
{
Auftrag_Fertig();
Load_Etikett_GroupBox();
}
else return;
break;
case AuftragTyp.Standerhöhung:
if (meldung.SonderNoCleanContainer() == DialogResult.Yes)
{
Auftrag_Fertig();
Load_Etikett_GroupBox();
}
else return;
break;
default:
break;
}
}
else
{
if (altecontclean != null && this.auftrag.ContainerClean != altecontclean)
{
switch (meldung.DifferentCleanContainer())
{
case DialogResult.Cancel:
return;
case DialogResult.Yes:
{
this.auftrag.ContainerClean = (int)neuecontclean;
neuecontclean = null;
//this.auftrag.Liefertag = dTPLiefertag.Value;
//WENN BEARBEITET UND AUSGEDRUCKT WIRD GEDRUCKT VERÄNDERT UND ERSTELLT IST URSPRUNGSDATUM
this.auftrag.Gedruckt = DateTime.Now;
this.auftrag.GedrucktVon = benutzer.BenutzerID;
bool nocont = false;
if (MessageBox.Show("Möchtest du die Containeranzahl am Etikett anzeigen?", "FRAGE", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
{
nocont = true;
}
else nocont = false;
DialogResult result = Funktionen.Etikett_Drucken(auftrag, nocont);
if (result == DialogResult.OK)
{
Auftrag_Fertig();
}
}
break;
case DialogResult.No:
{
this.auftrag.ContainerClean = (int)neuecontclean;
neuecontclean = null;
//this.auftrag.Liefertag = dTPLiefertag.Value;
//WENN BEARBEITET UND NICHT GEDRUCKT WIRD ERSTELLT VERÄNDERT UND GEDRUCKT IST URSPRUNGSDATUM
this.auftrag.Erstellt = DateTime.Now;
this.auftrag.ErstelltVon = (int)benutzer.BenutzerID;
Auftrag_Fertig();
}
break;
default:
break;
}
}
else
{
Auftrag_Fertig();
Load_Etikett_GroupBox();
}
}
}
if (e.Column.Text == "Etikett")
{
this.auftrag = _auftrag;
if (this.auftrag.AufgabeID != null)
{
if (meldung.GetAchtung(this, $"Bei dem ausgewählten Auftrag ist die Aufgabe {Aufgabe.GetAufgabe(null, auftrag.AufgabeID).Bezeichnung} zu erledigen. Hast du die Aufgabe erledigt? ", false) == DialogResult.No) return;
}
if (this.auftrag.ContainerClean != 0)
{
//AUFTRAG UPDATE
letzterLiefertag = this.auftrag.Liefertag;
}
else
{
string eingabe = PromptForText(
"Der Auftrag hat keine sauberen Container oder Pakete. \n\n" +
"Bitte gib an wieviele Container oder Pakete der Auftrag hat:",
"Container/Pakete eingeben",
"0");
if (string.IsNullOrWhiteSpace(eingabe) || !int.TryParse(eingabe, out int anzahl))
{
this.Cursor = Cursors.Default;
return;
}
this.auftrag.ContainerClean = anzahl;
}
//ETIKETT DRUCKEN
DialogResult result = Funktionen.Etikett_Drucken(auftrag, nocont);
if (result == DialogResult.OK)
{
this.auftrag.Gedruckt = this.auftrag.Erstellt = DateTime.Now;
this.auftrag.GedrucktVon = this.auftrag.ErstelltVon = (int)this.benutzer.BenutzerID;
this.auftrag.Status = AuftragStatus.Vorbereitet;
this.auftrag.Save();
this.auftrag = null;
}
}
Neuladen_OLV(olv);
// DataGridView ausblenden.
this.dGArtikel.DataSource = null;
this.dGArtikel.Rows.Clear();
this.dGArtikel.Visible = false;
this.Cursor = Cursors.Default;
}
private void OLV_SetObjects(ObjectListView olv, List<Auftrag> auftraglist)
{
olv.BeginUpdate(); // Stoppt Datenaktualisierungen/Redraws
olv.SetObjects(auftraglist); // Bindet die Auftragsliste an die View
olv.EndUpdate(); // Update-Pause beenden, Daten neu zeichnen
}
private async void Neuladen_OLV(ObjectListView olv)
{
// Neu-Laden der ObjectListView
await GetAuftraege();
if (olv.Name.Contains("Sonder")) OLV_SetObjects(olv, sonderlist);
if (olv.Name.Contains("Geschaeft")) OLV_SetObjects(olv, geschaeftlist);
if (olv.Name.Contains("Auftrag")) OLV_SetObjects(olv, auftraglist);
}
/// <summary>
/// Blendet die Button Spalten aus wenn der User dazu nicht berechtigt ist.
/// </summary>
private void GetUnvisibleButtonColumn()
{
foreach (OLVColumn col in this.objectListViewAuftrag.Columns)
{
if (this.benutzer.Rolle == BenutzerRolle.Frottee)
{
if (col.Text == "Etikett" & col.IsButton) col.IsVisible = false;
}
else if (col.IsButton) col.IsVisible = false;
}
}
private void objectListViewAuftrag_CellEditFinished(object sender, CellEditEventArgs e)
{
Auftrag auftrag = (Auftrag)e.RowObject;
if (e.Column.Text == "sauber") altecontclean = (int?)e.Value;
if (!string.IsNullOrWhiteSpace(e.NewValue.ToString())) Save_Auftrag(auftrag, false);
}
/// <summary>
/// Ausgelöst durch DoppelClick
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void objectListViewAuftrag_CellEditStarting(object sender, CellEditEventArgs e)
{
if (e.Column.AspectName == "Kundename")
{
ObjectListView olv = (ObjectListView)sender;
Auftrag _auftrag = e.RowObject as Auftrag;
if (_auftrag == null) return;
Kunde _kunde = Kunde.GetKunde(null, _auftrag.KundeID, null);
FormKundeVW kndVW = new FormKundeVW(_kunde, this.benutzer);
kndVW.ShowDialog();
olv.RefreshObject(olv.SelectedObject);
e.Cancel = true;
}
else if (meldung.GetFrage(this, $"Möchtest du die gewählte Zelle wirklich bearbeiten?", false) == DialogResult.No) e.Cancel = true;
//TODO: Soll hier ebenfalls der nächste Liefertag laut Rhythmus vorgeschlagen werden?
}
#endregion
/// <summary>
/// Zeigt Fenster zur Eingabe von Text.
/// </summary>
/// <param name="titel"></param>
/// <param name="prompt"></param>
/// <param name="defaultText"></param>
/// <returns>Anzahl Saubere Container als string</returns>
private string PromptForText(string titel, string prompt, string defaultText)
{
return Interaction.InputBox(prompt, titel, defaultText);
}
private void Save_Auftrag(Auftrag auftrag, bool show)
{
if (auftrag.Save()[0] == 1) if (show) meldung.Gespeichert();
else if (show) meldung.Speicherfehler();
}
private void Aufgabe_erledigt(Auftrag _auftrag,Kunde _kunde, string name)
{
if (_kunde.AufgabeCount <= 0) _kunde.Aufgabe = null;
else _kunde.AufgabeCount--;
_auftrag.AufgabeErledigt = true;
if (_auftrag.Save()[0] != 1) meldung.GetFehler(this, $"Der Auftrag konnte nicht gespeichert und die Aufgabe nicht als erledigt markiert werden. Melde das bitte deinem Vorgesetzten.");
if (_kunde.Save() != 1) meldung.GetFehler(this, $"Der Kunde {name} konnte nicht gespeichert und die Aufgabe nicht entfernt werden. Melde das bitte deinem Vorgesetzten.");
}
/// <summary>
/// Wird aufgerufen wenn ein Auftrag Fertig ist und so gespeichert werden soll.
/// </summary>
private void Auftrag_Fertig()
{
if (this.benutzer.Rolle == BenutzerRolle.Frottee) this.auftrag.Abteilungen |= AbteilungsStatus.FrotteeBeendet;
else this.auftrag.Status = AuftragStatus.Fertig;
this.auftrag.Save();
this.auftrag = null;
this.dGArtikel.Visible = false;
}
/// <summary>
/// Wenn das dritte Tab gewählt ist, wird die GroupBox Etikett ausgeblendet. 3.Tab wäre für vergangene Aufträge - weshalb kein Etikett mehr notwendig.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tabControlAuftragList_Selected(object sender, TabControlEventArgs e)
{
TabControl tabControl = (TabControl)sender;
}
/// <summary>
/// Beim Wechsel der Tabs wird die DataGridView geleert.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tabControlAuftragList_SelectedIndexChanged(object sender, EventArgs e)
{
this.dGArtikel.DataSource = null;
this.dGArtikel.Rows.Clear();
this.dGArtikel.Visible = false;
}
#region DataGridView-Events
private void Load_Gridview(Auftrag auftrag)
{
//Alle Einträge der DataGridView entfernen. Davor muss die DataSource entfernt werden.
this.dGArtikel.DataSource = null;
this.dGArtikel.Rows.Clear();
if (auftrag != null)
{
Aufgabe auf;
if (auftrag.AufgabeID.HasValue) auf = Aufgabe.GetAufgabe(null, auftrag.AufgabeID);
else auf = new Aufgabe();
this.dGArtikel.AutoGenerateColumns = false;
foreach (DataGridViewColumn col in this.dGArtikel.Columns)
{
col.DataPropertyName = col.Name;
if (col.Name.Contains("Bearbeitet")) col.Visible = false;
if (col.Name == "Fehlmenge")
{
if (auf.Bezeichnung == null || auf.Bezeichnung == "STA") col.Visible = true;
else col.Visible = false;
}
if (col.Name == "Korrektur")
{
if (auf.Bezeichnung != null && auf.Bezeichnung != "STA") col.Visible = true;
else col.Visible = false;
}
}
// DataGridView mit ArtikelStand Liste verbinden und Columns automatisch generieren.
artikelListe = KundeArtikel.GetList(auftrag.KundeID.ToString());
this.dGArtikel.DataSource = artikelListe;
this.dGArtikel.ClearSelection();
//this.dGArtikel.CurrentCell = this.dGArtikel.Rows[0].Cells[3];
// Alle Spaltenbreiten an den Zellinhalt anpassen
this.dGArtikel.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
// Alle Zeilenhöhen an Zellinhalt anpassen
this.dGArtikel.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
this.dGArtikel = Funktionen.DGV_Size_Anpassen(this.dGArtikel);
//this.dGArtikel.Width = this.Width - dGArtikel.Left;
// Rechter Rand von letzter Spalte berechnen.
int x = 0;
foreach (ColumnHeader col in objectListViewAuftrag.Columns) x += col.Width; // x ist jetzt die Position direkt rechts von der letzten Spalte (relativ zu client origin)
this.dGArtikel.Location = new Point(this.objectListViewAuftrag.Left + x, this.dGArtikel.Location.Y);
if (this.dGArtikel.Rows.Count > 0) { this.dGArtikel.Visible = true; timer1.Start(); }
else this.dGArtikel.Visible = false;
}
}
/// <summary>
/// Vor Beginn der Editierung wird Wert gespeichert um nach der Editierung vergleichen und eventuell speichern zu können.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dGArtikel_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
DataGridView dataGrid = sender as DataGridView;
beforeedit = int.Parse(dataGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString());
}
private void dGArtikel_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
DataGridView dgV = (DataGridView)sender;
// Aktuelle Zeile holen
var row = dgV.Rows[e.RowIndex];
var cell = row.Cells[e.ColumnIndex];
// Werte aus der Zeile auslesen
KundeArtikel stand = KundeArtikel.GetItem((int)row.Cells[0].Value);
if (cell.ColumnIndex == 4) { stand.Stand = int.Parse(row.Cells[4].Value.ToString()); stand.StandBearbeitet = this.benutzer.BenutzerName + " am " + DateTime.Now.ToString(); }
if (cell.ColumnIndex == 5) { stand.Fehlmenge = int.Parse(row.Cells[5].Value.ToString()); stand.FehlmengeBearbeitet = this.benutzer.BenutzerName + " am " + DateTime.Now.ToString(); }
if (cell.ColumnIndex == 6)
{
if (int.Parse(row.Cells[6].Value.ToString()) == 0)
{
stand.Stand = stand.Stand + beforeedit;
stand.Korrektur = int.Parse(row.Cells[6].Value.ToString());
stand.StandBearbeitet = stand.KorrekturBearbeitet = this.benutzer.BenutzerName + " am " + DateTime.Now.ToString();
}
}
// Hier kannst du die Werte speichern, z.B. in Datenbank oder Liste
if (stand.Save() != 1) meldung.Speicherfehler();
else Load_Gridview(this.auftrag);
}
private void dGArtikel_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (e.ColumnIndex >= 5)
{
DataGridView dgv = (DataGridView)sender;
DataGridViewCell dgc = dgv.CurrentCell;
if (!int.TryParse(dgc.EditedFormattedValue.ToString(), out int i))
{
meldung.Eingabefehler();
e.Cancel = true;
}
}
}
private void dGArtikel_VisibleChanged(object sender, EventArgs e)
{
if (dGArtikel.Visible) { Load_Gridview(this.auftrag); timer1.Start(); }
else timer1.Stop();
}
#endregion
#region Statistik
private void buttonStats_Click(object sender, EventArgs e)
{
if (this.groupBoxStatistik.Visible) this.groupBoxStatistik.Visible = false;
else this.groupBoxStatistik.Visible = true;
}
private void GetStatistik()
{
string[] statistik = { "Container gewaschen:", "Container hergerichtet:", "Container fertig:", "Kunden gewaschen:", "Kunden hergerichtet:", "Kunden abgeschlossen:" };
List<Auftrag> auftraglist = Auftrag.GetAuftragStatsListToday(this.dateTimePicker1.Value.Date);
for (int i = 1; i <= statistik.Length; i++)
{
foreach (Control ctr in this.tableLayoutPanelStatistik.Controls)
{
Label lbl;
if (ctr.GetType() == typeof(Label))
{
lbl = (Label)ctr;
if (lbl.Text.Contains("Statistik") && lbl.Text.Contains($"{i.ToString()}")) lbl.Text = statistik[i - 1].ToString();
if (lbl.Name.Contains("Zahl"))
{
int j = 0;
switch (lbl.Name)
{
case var s when s.Contains($"1"):
foreach (Auftrag a in auftraglist) if (a.Status == AuftragStatus.Aufgelegt) j += a.Container;
lbl.Text = j.ToString();
break;
case var s when s.Contains($"2"):
foreach (Auftrag a in auftraglist) if (a.Status == AuftragStatus.Vorbereitet) j += a.ContainerClean;
lbl.Text = j.ToString();
break;
case var s when s.Contains($"3"):
foreach (Auftrag a in auftraglist) if (a.Status == AuftragStatus.Fertig) j += a.ContainerClean;
lbl.Text = j.ToString();
break;
case var s when s.Contains($"4"):
foreach (Auftrag a in auftraglist) if (a.Status == AuftragStatus.Aufgelegt) j++;
lbl.Text = j.ToString();
break;
case var s when s.Contains($"5"):
foreach (Auftrag a in auftraglist) if (a.Status == AuftragStatus.Vorbereitet) j++;
lbl.Text = j.ToString();
break;
case var s when s.Contains($"{i}"):
foreach (Auftrag a in auftraglist) if (a.Status == AuftragStatus.Fertig) j++;
lbl.Text = j.ToString();
break;
default:
break;
}
}
}
}
}
//this.groupBoxStatistik.Width = this.tableLayoutPanelStatistik.Right;
}
private void dateTimePicker1_CloseUp(object sender, EventArgs e)
{
GetStatistik();
}
#endregion
private void FormExpedit_FormClosing(object sender, FormClosingEventArgs e)
{
_settings.OLV_State = this.objectListViewAuftrag.SaveState();
_settings.Save();
}
private void FormExpedit_Resize(object sender, EventArgs e)
{
int chartHeight = (int)(this.tabControlAuftragList.Height * 0.75);
int gbHeight = (int)(this.tabControlAuftragList.Height * 0.25);
chartFehlmenge.Location = new Point(this.tabControlAuftragList.Right + 10, this.tabControlAuftragList.Top);
chartFehlmenge.Size = new Size(groupBoxKndInfo.Width, chartHeight);
groupBoxKndInfo.Height = gbHeight;
groupBoxKndInfo.Location = new Point(chartFehlmenge.Left, chartHeight + 10);
}
private void groupBoxKndInfo_Paint(object sender, PaintEventArgs e)
{
Funktionen.GetGroupBoxBoarder(groupBoxKndInfo, e);
}
private void FormExpedit_Shown(object sender, EventArgs e)
{
this.ResumeLayout();
this.Cursor = Cursors.Default;
WindowState = FormWindowState.Maximized;
}
private void buttonTourenListe_Click(object sender, EventArgs e)
{
List<Benutzer> fahrerliste = Benutzer.GetFahrerList();
Benutzer fahrer = new Benutzer();
FormBenutzerVW benutzerVW = new FormBenutzerVW(fahrerliste);
if (benutzerVW.ShowDialog() == DialogResult.OK)
{
fahrer = benutzerVW.user;
}
if (fahrer != null)
{
List<Auftrag> auftragliste = Auftrag.GetTourenListe(fahrer);
if (auftragliste.Count > 0) Funktionen.TourenListe_Drucken(auftragliste, fahrer, this, this.benutzer);
}
}
}
}
//CHANGES: Expedit hat Button für Reklamation statt Nachwäsche. Dadurch kann Mirka die Reklamation bearbeiten und eingeben. (AKTUELL NUR MIRKA)
//CHANGES: Frottee Expedit zeigt Aufträge die bei Frottee nicht Beendet wurden.
//CHANGES: Frottee Expedit alle Buttons werden ausgeblendet.
//CHANGES: Chart statt GridView für Fehlmengen.
//CHANGES: Es wird der zuständige Fahrer angezeigt. ToolTip zeigt den Grund falls vorhanden(Bei Sonderzuteilung).
//CHANGES: Geschäftaufträge werden in neuem Tab angezeigt.
//CHANGES: Kundeneingabe mit ? fügt einmaligen Kundenname als ZusatzInfo hinzu und wählt Kunde BAR DIVERSE 2320000
//CHANGES: Highlightfarbe bleibt auch wenn unfocused. (Farbe ändern?)
//CHANGES: Sortierte Spalte hat leicht andere Farbe. (Besser zu erkennen wonach sortiert wird)
//CHANGES: Rechtsklick auf Spaltenheader ermöglicht Filtern.
//CHANGES: Wenn HEUTE als Liefertag gewählt, kommt Frage ob heute oder in einer Woche. Wenn JA dann HEUTE.
//CHANGES: Bearbeiten in ObjectListView möglich. (Kundename wird aber zurückgesetzt)
//CHANGES: Wenn Doppelklick auf Zelle, wird gefragt ob bearbeiten wirklich gewollt. Sonst wird blockiert.
//CHANGES: Bei DoppelClick auf Kundename wird KundenVW geöffnet.
//DONE: Testen ob Scan mit Prompt bei Suchen funktioniert. Kundenummer und Text etc. (meherere QR-Codes teseten)