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 artikelListe; public List auftraglist; public List sonderlist; public List 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)))); } }); } /// /// Controls werden nach Benutzer deaktiviert. /// 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; } } /// /// Auftragslisten erstellen und in ObjectListViews laden. /// 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(); } } /// /// Wenn Timer abgelaufen Funktion für Autorefresh & Neuer Timer für Auftragliste Refresh. /// /// /// 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(); } /// /// Laden Events (GroupBox_Etikett und GridView) /// 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 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().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 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 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); } /// /// Blendet die Button Spalten aus wenn der User dazu nicht berechtigt ist. /// 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); } /// /// Ausgelöst durch DoppelClick /// /// /// 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 /// /// Zeigt Fenster zur Eingabe von Text. /// /// /// /// /// Anzahl Saubere Container als string 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."); } /// /// Wird aufgerufen wenn ein Auftrag Fertig ist und so gespeichert werden soll. /// 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; } /// /// 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. /// /// /// private void tabControlAuftragList_Selected(object sender, TabControlEventArgs e) { TabControl tabControl = (TabControl)sender; } /// /// Beim Wechsel der Tabs wird die DataGridView geleert. /// /// /// 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; } } /// /// Vor Beginn der Editierung wird Wert gespeichert um nach der Editierung vergleichen und eventuell speichern zu können. /// /// /// 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 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 fahrerliste = Benutzer.GetFahrerList(); Benutzer fahrer = new Benutzer(); FormBenutzerVW benutzerVW = new FormBenutzerVW(fahrerliste); if (benutzerVW.ShowDialog() == DialogResult.OK) { fahrer = benutzerVW.user; } if (fahrer != null) { List 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)