diff --git a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs index 1b867fe..4001551 100644 --- a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs +++ b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs @@ -400,6 +400,8 @@ create table if not exists [OrderItems] ( TryAddColumn(conForCheckTables, "Invoice", "SourceOrderId", "integer"); TryAddColumn(conForCheckTables, "Invoice", "SourceOrderNumber", "nvarchar(36)"); TryAddColumn(conForCheckTables, "Invoice", "IsRequestedInvoice", "int(1)"); + TryAddColumn(conForCheckTables, "PriceList", "TradeName", "nvarchar(256)"); + TryAddColumn(conForCheckTables, "PriceList", "Dosage", "nvarchar(128)"); } catch (Exception ex) { diff --git a/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs b/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs index 44af562..f837110 100644 --- a/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs +++ b/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs @@ -144,6 +144,18 @@ namespace Электронная_Фармация.HelpForms }); tablePrice.Columns.Add("Zakaz"); tablePrice.Columns.Add("SummaZakaza"); + tablePrice.Columns.Add(new DataColumn + { + ColumnName = "TradeName", + DataType = typeof(string), + AllowDBNull = true + }); + tablePrice.Columns.Add(new DataColumn + { + ColumnName = "Dosage", + DataType = typeof(string), + AllowDBNull = true + }); if (allSuppliersSummary?.Summary == null) { @@ -153,18 +165,20 @@ namespace Электронная_Фармация.HelpForms foreach (var item in allSuppliersSummary.Summary) { var row = tablePrice.NewRow(); - row["supplier_price_id"] = item.SupplierPriceID.ToString(); - row["guid_es"] = item.GuidEs.ToString(); - row["es_code"] = item.EsCode.ToString(); - row["supplier_id"] = item.SupplierId.ToString(); - row["DrugName"] = item.DrugName.ToString(); - row["SupplierName"] = item.SupplierName.ToString(); - row["Price"] = item.Price.ToString(); - row["Quantity"] = item.Quantity.ToString(); - row["ExpiryPeriod"] = item.ExpiryPeriod == null ? (object)DBNull.Value : item.ExpiryPeriod.ToString(); - row["Description"] = item.Description == null ? (object)DBNull.Value : item.Description.ToString(); + row["supplier_price_id"] = item.SupplierPriceID ?? string.Empty; + row["guid_es"] = item.GuidEs ?? string.Empty; + row["es_code"] = item.EsCode?.ToString() ?? "0"; + row["supplier_id"] = item.SupplierId ?? string.Empty; + row["DrugName"] = item.DrugName ?? string.Empty; + row["SupplierName"] = item.SupplierName ?? string.Empty; + row["Price"] = item.Price?.ToString() ?? string.Empty; + row["Quantity"] = item.Quantity?.ToString() ?? string.Empty; + row["ExpiryPeriod"] = item.ExpiryPeriod == null ? (object)DBNull.Value : item.ExpiryPeriod; + row["Description"] = item.Description == null ? (object)DBNull.Value : item.Description; row["Zakaz"] = string.Empty; row["SummaZakaza"] = string.Empty; + row["TradeName"] = string.IsNullOrWhiteSpace(item.TradeName) ? (object)DBNull.Value : item.TradeName.Trim(); + row["Dosage"] = string.IsNullOrWhiteSpace(item.Dosage) ? (object)DBNull.Value : item.Dosage.Trim(); tablePrice.Rows.Add(row); } @@ -187,7 +201,9 @@ namespace Электронная_Фармация.HelpForms [Description], [Zakaz], [SummaZakaza], -[supplier_price_id] +[supplier_price_id], +[TradeName], +[Dosage] ) VALUES ( @@ -202,7 +218,9 @@ VALUES @Description, @Zakaz, @SummaZakaza, -@supplier_price_id +@supplier_price_id, +@TradeName, +@Dosage )"; using (var sqliteCon = new SQLiteConnection(_connectionStringToLocalDb)) @@ -234,6 +252,8 @@ VALUES cmd.Parameters.AddWithValue("@Zakaz", row["Zakaz"]); cmd.Parameters.AddWithValue("@SummaZakaza", row["SummaZakaza"]); cmd.Parameters.AddWithValue("@supplier_price_id", row["supplier_price_id"]); + cmd.Parameters.AddWithValue("@TradeName", row.Table.Columns.Contains("TradeName") ? row["TradeName"] : DBNull.Value); + cmd.Parameters.AddWithValue("@Dosage", row.Table.Columns.Contains("Dosage") ? row["Dosage"] : DBNull.Value); cmd.ExecuteNonQuery(); } } diff --git a/src/ElectronicPharmacy/UserControls/UCPriceList.cs b/src/ElectronicPharmacy/UserControls/UCPriceList.cs index a34dafb..b27840a 100644 --- a/src/ElectronicPharmacy/UserControls/UCPriceList.cs +++ b/src/ElectronicPharmacy/UserControls/UCPriceList.cs @@ -6,6 +6,7 @@ using System.Drawing; using System.Globalization; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; using System.Data.SQLite; @@ -37,7 +38,26 @@ namespace Электронная_Фармация.UserControls string _selectedSupplierFilter = string.Empty; bool _suppressSupplierFilterEvents; bool _suppressPriceListSelectionEvents; + bool _suppressBrowseEvents; int _committedSupplierFilterIndex; + DataTable _fullPriceTable; + PriceBrowseMode _browseMode = PriceBrowseMode.Browse; + string _selectedBaseName = string.Empty; + string _selectedMg = string.Empty; + int _browseSplitterDistance = 0; + ModernSplitContainer splitNameMgBrowse; + ModernDataGridView dgvBrowseNames; + ModernDataGridView dgvBrowseMg; + static readonly Regex DosageRegex = new Regex( + @"(\d+(?:[.,]\d+)?\s*(?:мг|мкг|г|мл|МЕ|ме)\b)", + RegexOptions.IgnoreCase | RegexOptions.Compiled); + const string EmptyMgDisplay = "(без МГ)"; + + private enum PriceBrowseMode + { + Browse, + Offers + } string connectionStringToLocalDB = AppConfig.SqliteConnectionString; @@ -51,6 +71,7 @@ namespace Электронная_Фармация.UserControls private void UCPriceList_Load(object sender, EventArgs e) { + EnsureBrowseSplit(); UiThemeHelper.ApplyToControlTree(this); Tag = "chrome-child"; @@ -71,6 +92,64 @@ namespace Электронная_Фармация.UserControls txtSearchGoodNameInPriceList.Focus(); } + private void EnsureBrowseSplit() + { + if (splitNameMgBrowse != null) + { + return; + } + + dgvBrowseNames = CreateBrowseGrid("dgvBrowseNames"); + dgvBrowseMg = CreateBrowseGrid("dgvBrowseMg"); + + splitNameMgBrowse = new ModernSplitContainer + { + Name = "splitNameMgBrowse", + Dock = DockStyle.Fill, + Orientation = Orientation.Vertical, + SplitterWidth = 4, + Visible = false + }; + splitNameMgBrowse.Panel1.Controls.Add(dgvBrowseNames); + splitNameMgBrowse.Panel2.Controls.Add(dgvBrowseMg); + + TLPPriceList.Controls.Add(splitNameMgBrowse, 0, 0); + splitNameMgBrowse.BringToFront(); + + dgvBrowseNames.SelectionChanged += dgvBrowseNames_SelectionChanged; + dgvBrowseMg.CellClick += dgvBrowseMg_CellClick; + dgvBrowseNames.KeyPress += dgvPriceList_KeyPress; + dgvBrowseMg.KeyPress += dgvPriceList_KeyPress; + splitNameMgBrowse.SplitterMoved += (_, __) => + { + if (splitNameMgBrowse.SplitterDistance > 80) + { + _browseSplitterDistance = splitNameMgBrowse.SplitterDistance; + } + }; + } + + private static ModernDataGridView CreateBrowseGrid(string name) + { + var grid = new ModernDataGridView + { + Name = name, + Dock = DockStyle.Fill, + AllowUserToAddRows = false, + AllowUserToDeleteRows = false, + AllowUserToResizeRows = false, + AllowUserToResizeColumns = true, + MultiSelect = false, + ReadOnly = true, + RowHeadersVisible = false, + SelectionMode = DataGridViewSelectionMode.FullRowSelect, + AutoGenerateColumns = true, + Font = new Font("Segoe UI", 13F, FontStyle.Regular), + TabStop = true + }; + return grid; + } + private void AlignSearchRow() { const int searchY = 9; @@ -167,6 +246,26 @@ namespace Электронная_Фармация.UserControls private void ApplyPriceListFilters() { + if (_browseMode == PriceBrowseMode.Browse) + { + ShowBrowseView(); + return; + } + + if (_fullPriceTable == null) + { + return; + } + + // Фильтр по базовому имени/МГ применим только к полной таблице прайса. + if (!ReferenceEquals(bsPriceList.DataSource, _fullPriceTable) + || !_fullPriceTable.Columns.Contains("Базовое наименование") + || !_fullPriceTable.Columns.Contains("МГ")) + { + ShowOffersView(_selectedBaseName, string.IsNullOrEmpty(_selectedMg) ? EmptyMgDisplay : _selectedMg); + return; + } + var parts = new List(); if (!string.IsNullOrWhiteSpace(_selectedSupplierFilter)) @@ -175,13 +274,27 @@ namespace Электронная_Фармация.UserControls parts.Add($"[Поставщик] = '{escapedSupplier}'"); } - if (!string.IsNullOrWhiteSpace(GoodsFilter)) + if (!string.IsNullOrWhiteSpace(_selectedBaseName)) + { + var escapedName = _selectedBaseName.Replace("'", "''"); + var escapedMg = (_selectedMg ?? string.Empty).Replace("'", "''"); + parts.Add($"[Базовое наименование] = '{escapedName}'"); + parts.Add($"[МГ] = '{escapedMg}'"); + } + else if (!string.IsNullOrWhiteSpace(GoodsFilter)) { var escaped = GoodsFilter.Replace("'", "''"); parts.Add($"[Наименование] like '%{escaped}%'"); } - bsPriceList.Filter = parts.Count == 0 ? null : string.Join(" AND ", parts); + try + { + bsPriceList.Filter = parts.Count == 0 ? string.Empty : string.Join(" AND ", parts); + } + catch (EvaluateException) + { + ShowOffersView(_selectedBaseName, string.IsNullOrEmpty(_selectedMg) ? EmptyMgDisplay : _selectedMg); + } } private void comboSupplierFilter_SelectionChangeCommitted(object sender, EventArgs e) @@ -277,30 +390,377 @@ namespace Электронная_Фармация.UserControls tablePrice.Columns[10].ColumnName = "Сумма"; tablePrice.Columns[12].ColumnName = "ИДПрайсЛистАйтем"; + if (tablePrice.Columns.Contains("TradeName")) + { + tablePrice.Columns["TradeName"].ColumnName = "Торговое название"; + } + + if (tablePrice.Columns.Contains("Dosage")) + { + tablePrice.Columns["Dosage"].ColumnName = "Дозировка"; + } + + EnsureDerivedBrowseColumns(tablePrice); + _fullPriceTable = tablePrice; + _selectedBaseName = string.Empty; + _selectedMg = string.Empty; + ShowBrowseView(); + } + + private void HideOptionalColumn(string columnName) + { + if (dgvPriceList.Columns.Contains(columnName)) + { + dgvPriceList.Columns[columnName].Visible = false; + } + } + + private void EnsureDerivedBrowseColumns(DataTable tablePrice) + { + if (!tablePrice.Columns.Contains("Базовое наименование")) + { + tablePrice.Columns.Add("Базовое наименование", typeof(string)); + } + + if (!tablePrice.Columns.Contains("МГ")) + { + tablePrice.Columns.Add("МГ", typeof(string)); + } + + foreach (DataRow row in tablePrice.Rows) + { + var drugName = row["Наименование"]?.ToString() ?? string.Empty; + var tradeName = tablePrice.Columns.Contains("Торговое название") + ? row["Торговое название"]?.ToString() + : null; + var dosage = tablePrice.Columns.Contains("Дозировка") + ? row["Дозировка"]?.ToString() + : null; + + if (string.IsNullOrWhiteSpace(dosage)) + { + dosage = ExtractDosageFromName(drugName); + } + + dosage = (dosage ?? string.Empty).Trim(); + row["МГ"] = dosage; + row["Базовое наименование"] = BuildBaseName(drugName, tradeName, dosage); + } + } + + private static string ExtractDosageFromName(string drugName) + { + if (string.IsNullOrWhiteSpace(drugName)) + { + return string.Empty; + } + + var match = DosageRegex.Match(drugName); + return match.Success ? match.Value.Trim() : string.Empty; + } + + private static string BuildBaseName(string drugName, string tradeName, string dosage) + { + if (!string.IsNullOrWhiteSpace(tradeName)) + { + return tradeName.Trim(); + } + + var name = drugName ?? string.Empty; + if (!string.IsNullOrWhiteSpace(dosage)) + { + var idx = name.IndexOf(dosage, StringComparison.OrdinalIgnoreCase); + if (idx >= 0) + { + name = name.Remove(idx, dosage.Length); + } + } + + name = Regex.Replace(name, @"\s{2,}", " ").Trim(' ', ',', '-', '.', ';'); + return string.IsNullOrWhiteSpace(name) ? (drugName ?? string.Empty).Trim() : name; + } + + private void ShowBrowseView() + { + if (_fullPriceTable == null) + { + return; + } + + EnsureBrowseSplit(); + _browseMode = PriceBrowseMode.Browse; + _selectedMg = string.Empty; + try { + bsPriceList.RemoveFilter(); + } + catch + { + bsPriceList.Filter = string.Empty; + } + + var search = (GoodsFilter ?? string.Empty).Trim(); + var namesTable = new DataTable(); + namesTable.Columns.Add("Наименование", typeof(string)); + + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (DataRow row in _fullPriceTable.Rows) + { + var baseName = row["Базовое наименование"]?.ToString() ?? string.Empty; + if (string.IsNullOrWhiteSpace(baseName) || !seen.Add(baseName)) + { + continue; + } + + if (!string.IsNullOrWhiteSpace(search)) + { + var keep = false; + foreach (DataRow candidate in _fullPriceTable.Rows) + { + if (!string.Equals(candidate["Базовое наименование"]?.ToString(), baseName, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var drugName = candidate["Наименование"]?.ToString() ?? string.Empty; + var mgText = candidate["МГ"]?.ToString() ?? string.Empty; + if (baseName.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 + || drugName.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 + || mgText.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0) + { + keep = true; + break; + } + } + + if (!keep) + { + continue; + } + } + + namesTable.Rows.Add(baseName); + } + + namesTable.DefaultView.Sort = "Наименование ASC"; + var sortedNames = namesTable.DefaultView.ToTable(); + + try + { + _suppressBrowseEvents = true; + dgvBrowseNames.DataSource = sortedNames; + ConfigureSingleColumnGrid(dgvBrowseNames, "Наименование"); + ClearMgPanel(); + + if (!string.IsNullOrWhiteSpace(_selectedBaseName)) + { + SelectBrowseName(_selectedBaseName); + } + else + { + dgvBrowseNames.ClearSelection(); + } + } + finally + { + _suppressBrowseEvents = false; + } + + ShowBrowsePanels(true); + if (!string.IsNullOrWhiteSpace(_selectedBaseName) && dgvBrowseNames.SelectedRows.Count > 0) + { + FillMgPanelForName(_selectedBaseName); + } + + ShowMePriceWithMarkupPercentForSelectedGood(); + } + + private void ClearMgPanel() + { + var empty = new DataTable(); + empty.Columns.Add("МГ", typeof(string)); + dgvBrowseMg.DataSource = empty; + ConfigureSingleColumnGrid(dgvBrowseMg, "МГ"); + dgvBrowseMg.ClearSelection(); + } + + private void FillMgPanelForName(string baseName) + { + var mgTable = new DataTable(); + mgTable.Columns.Add("МГ", typeof(string)); + + var seen = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (DataRow row in _fullPriceTable.Rows) + { + if (!string.Equals(row["Базовое наименование"]?.ToString(), baseName, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var dosage = row["МГ"]?.ToString() ?? string.Empty; + if (!seen.Add(dosage)) + { + continue; + } + + mgTable.Rows.Add(string.IsNullOrEmpty(dosage) ? EmptyMgDisplay : dosage); + } + + mgTable.DefaultView.Sort = "МГ ASC"; + try + { + _suppressBrowseEvents = true; + dgvBrowseMg.DataSource = mgTable.DefaultView.ToTable(); + ConfigureSingleColumnGrid(dgvBrowseMg, "МГ"); + dgvBrowseMg.ClearSelection(); + } + finally + { + _suppressBrowseEvents = false; + } + } + + private void ConfigureSingleColumnGrid(ModernDataGridView grid, string columnName) + { + grid.AutoGenerateColumns = true; + grid.AllowUserToResizeColumns = true; + grid.Font = new Font("Segoe UI", 13F, FontStyle.Regular); + + foreach (DataGridViewColumn column in grid.Columns) + { + var match = string.Equals(column.Name, columnName, StringComparison.Ordinal) + || string.Equals(column.HeaderText, columnName, StringComparison.Ordinal); + column.Visible = match; + column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; + column.Resizable = DataGridViewTriState.True; + column.MinimumWidth = 80; + } + } + + private void SelectBrowseName(string baseName) + { + for (var i = 0; i < dgvBrowseNames.Rows.Count; i++) + { + var value = dgvBrowseNames.Rows[i].Cells["Наименование"]?.Value?.ToString(); + if (string.Equals(value, baseName, StringComparison.OrdinalIgnoreCase)) + { + dgvBrowseNames.ClearSelection(); + dgvBrowseNames.Rows[i].Selected = true; + if (dgvBrowseNames.Columns.Contains("Наименование")) + { + dgvBrowseNames.CurrentCell = dgvBrowseNames.Rows[i].Cells["Наименование"]; + } + + dgvBrowseNames.FirstDisplayedScrollingRowIndex = Math.Max(0, i); + return; + } + } + } + + private void ShowBrowsePanels(bool browseVisible) + { + EnsureBrowseSplit(); + + if (browseVisible) + { + dgvPriceList.Visible = false; + splitNameMgBrowse.Visible = true; + splitNameMgBrowse.BringToFront(); + + var width = Math.Max(200, splitNameMgBrowse.Width); + var preferred = _browseSplitterDistance > 80 + ? _browseSplitterDistance + : Math.Max(180, width / 2); + preferred = Math.Min(preferred, Math.Max(120, width - 120)); + try + { + splitNameMgBrowse.SplitterDistance = preferred; + } + catch + { + // splitter not ready yet + } + } + else + { + if (splitNameMgBrowse.SplitterDistance > 80) + { + _browseSplitterDistance = splitNameMgBrowse.SplitterDistance; + } + + splitNameMgBrowse.Visible = false; + dgvPriceList.Visible = true; + dgvPriceList.BringToFront(); + } + } + + private void ShowOffersView(string baseName, string mgDisplay) + { + if (_fullPriceTable == null || string.IsNullOrWhiteSpace(baseName)) + { + return; + } + + if (splitNameMgBrowse != null && splitNameMgBrowse.SplitterDistance > 80) + { + _browseSplitterDistance = splitNameMgBrowse.SplitterDistance; + } + + _selectedBaseName = baseName; + _selectedMg = string.Equals(mgDisplay, EmptyMgDisplay, StringComparison.Ordinal) + ? string.Empty + : (mgDisplay ?? string.Empty); + + _suppressSupplierFilterEvents = true; + if (comboSupplierFilter.Items.Count > 0) + { + comboSupplierFilter.SelectedIndex = 0; + } + _suppressSupplierFilterEvents = false; + _committedSupplierFilterIndex = comboSupplierFilter.SelectedIndex; + _selectedSupplierFilter = string.Empty; + btnClearSupplierFilter.Visible = false; + + BindOffersTable(); + ApplyOffersColumns(); + ShowBrowsePanels(false); + + var escapedName = _selectedBaseName.Replace("'", "''"); + var escapedMg = _selectedMg.Replace("'", "''"); + try + { + bsPriceList.Filter = $"[Базовое наименование] = '{escapedName}' AND [МГ] = '{escapedMg}'"; + } + catch (EvaluateException ex) + { + AppDebugLog.Error("PriceList", "Не удалось применить фильтр наименование+МГ", ex); + bsPriceList.RemoveFilter(); + } + } + + private void BindOffersTable() + { + try + { + _suppressBrowseEvents = true; _suppressPriceListSelectionEvents = true; - bsPriceList.DataSource = tablePrice; + _browseMode = PriceBrowseMode.Offers; + + try + { + bsPriceList.RemoveFilter(); + } + catch + { + bsPriceList.Filter = string.Empty; + } + + bsPriceList.DataSource = _fullPriceTable; dgvPriceList.DataSource = bsPriceList; dgvPriceList.AutoGenerateColumns = true; dgvPriceList.Font = new Font("Segoe UI", 13, FontStyle.Regular); - - dgvPriceList.Columns[0].Visible = false; - dgvPriceList.Columns[1].Visible = false; - dgvPriceList.Columns[2].Visible = false; - dgvPriceList.Columns[7].Visible = true; - dgvPriceList.Columns[8].Visible = false; - dgvPriceList.Columns[11].Visible = false; - dgvPriceList.Columns[12].Visible = false; - - dgvPriceList.Columns[3].Width = 760; - dgvPriceList.Columns[4].Width = 150; - dgvPriceList.Columns[5].Width = 120; - dgvPriceList.Columns[6].Width = 100; - dgvPriceList.Columns[7].Width = 140; - dgvPriceList.Columns[9].Width = 100; - dgvPriceList.Columns[10].Width = 100; - + dgvPriceList.AllowUserToResizeColumns = true; if (dgvPriceList.Rows.Count > 0) { dgvPriceList.ClearSelection(); @@ -309,10 +769,107 @@ namespace Электронная_Фармация.UserControls finally { _suppressPriceListSelectionEvents = false; + _suppressBrowseEvents = false; ShowMePriceWithMarkupPercentForSelectedGood(); } } + private void ApplyOffersColumns() + { + if (dgvPriceList.Columns.Count == 0) + { + return; + } + + foreach (DataGridViewColumn column in dgvPriceList.Columns) + { + column.Visible = true; + column.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + column.Resizable = DataGridViewTriState.True; + } + + if (dgvPriceList.Columns.Count > 0) dgvPriceList.Columns[0].Visible = false; + if (dgvPriceList.Columns.Count > 1) dgvPriceList.Columns[1].Visible = false; + if (dgvPriceList.Columns.Count > 2) dgvPriceList.Columns[2].Visible = false; + if (dgvPriceList.Columns.Count > 7) dgvPriceList.Columns[7].Visible = true; + if (dgvPriceList.Columns.Count > 8) dgvPriceList.Columns[8].Visible = false; + if (dgvPriceList.Columns.Count > 11) dgvPriceList.Columns[11].Visible = false; + if (dgvPriceList.Columns.Count > 12) dgvPriceList.Columns[12].Visible = false; + + HideOptionalColumn("Торговое название"); + HideOptionalColumn("Дозировка"); + HideOptionalColumn("Базовое наименование"); + HideOptionalColumn("МГ"); + + if (dgvPriceList.Columns.Count > 3) dgvPriceList.Columns[3].Width = 760; + if (dgvPriceList.Columns.Count > 4) dgvPriceList.Columns[4].Width = 150; + if (dgvPriceList.Columns.Count > 5) dgvPriceList.Columns[5].Width = 120; + if (dgvPriceList.Columns.Count > 6) dgvPriceList.Columns[6].Width = 100; + if (dgvPriceList.Columns.Count > 7) dgvPriceList.Columns[7].Width = 140; + if (dgvPriceList.Columns.Count > 9) dgvPriceList.Columns[9].Width = 100; + if (dgvPriceList.Columns.Count > 10) dgvPriceList.Columns[10].Width = 100; + } + + private void dgvBrowseNames_SelectionChanged(object sender, EventArgs e) + { + if (_suppressBrowseEvents || _browseMode != PriceBrowseMode.Browse) + { + return; + } + + if (dgvBrowseNames.SelectedRows.Count == 0) + { + _selectedBaseName = string.Empty; + ClearMgPanel(); + return; + } + + var name = dgvBrowseNames.SelectedRows[0].Cells["Наименование"]?.Value?.ToString() ?? string.Empty; + _selectedBaseName = name; + _selectedMg = string.Empty; + if (string.IsNullOrWhiteSpace(name)) + { + ClearMgPanel(); + return; + } + + FillMgPanelForName(name); + ShowMePriceWithMarkupPercentForSelectedGood(); + } + + private void dgvBrowseMg_CellClick(object sender, DataGridViewCellEventArgs e) + { + if (_suppressBrowseEvents || e.RowIndex < 0 || e.RowIndex >= dgvBrowseMg.Rows.Count) + { + return; + } + + if (_browseMode != PriceBrowseMode.Browse || string.IsNullOrWhiteSpace(_selectedBaseName)) + { + return; + } + + var mg = dgvBrowseMg.Rows[e.RowIndex].Cells["МГ"]?.Value?.ToString() ?? string.Empty; + ShowOffersView(_selectedBaseName, mg); + } + + private void NavigateBrowseBack() + { + if (_browseMode == PriceBrowseMode.Offers) + { + // Оставляем выбранное наименование, справа снова покажем его МГ. + _selectedMg = string.Empty; + ShowBrowseView(); + } + } + + private void ResetBrowseToNames() + { + _selectedBaseName = string.Empty; + _selectedMg = string.Empty; + ShowBrowseView(); + } + void loadInfoAboutDefaultMarkup() { @@ -762,7 +1319,7 @@ select strftime('%Y-%m-%d','{OrderDate}'), { txtSearchGoodNameInPriceList.Text = string.Empty; GoodsFilter = string.Empty; - ApplyPriceListFilters(); + ResetBrowseToNames(); //loadPriceList(string.Empty); } @@ -824,23 +1381,85 @@ and tempOrder.GoodName = PriceList.GoodName private void dgvPriceList_KeyPress(object sender, KeyPressEventArgs e) { + if (e.KeyChar == (Char)Keys.Escape) + { + if (_browseMode != PriceBrowseMode.Browse) + { + NavigateBrowseBack(); + } + else + { + txtSearchGoodNameInPriceList.Text = string.Empty; + GoodsFilter = string.Empty; + ResetBrowseToNames(); + } + + return; + } + int RowIndex = -1; string idPriceListItem = string.Empty; - if (dgvPriceList.SelectedRows.Count > 0) + if (_browseMode == PriceBrowseMode.Offers && dgvPriceList.SelectedRows.Count > 0) { RowIndex = dgvPriceList.SelectedRows[0].Index; - if (RowIndex >= 0 && RowIndex < dgvPriceList.Rows.Count) + if (RowIndex >= 0 + && RowIndex < dgvPriceList.Rows.Count + && dgvPriceList.Rows[RowIndex].Cells.Count > 11) { idPriceListItem = dgvPriceList.Rows[RowIndex].Cells[11].Value?.ToString() ?? string.Empty; } } + if (_browseMode != PriceBrowseMode.Offers) + { + if (e.KeyChar == (Char)Keys.Back && GoodsFilter.Length > 0) + { + GoodsFilter = GoodsFilter.Substring(0, GoodsFilter.Length - 1); + txtSearchGoodNameInPriceList.Text = GoodsFilter; + ResetBrowseToNames(); + + if (GoodsFilter.Length == 0) + { + timerForResetSearch.Enabled = false; + timerForResetSearch.Stop(); + } + else + { + timerForResetSearch.Enabled = true; + timerForResetSearch.Start(); + } + + return; + } + + if ((e.KeyChar >= 'A' && e.KeyChar <= 'Z') + || (e.KeyChar >= 'a' && e.KeyChar <= 'z') + || (e.KeyChar >= 'А' && e.KeyChar <= 'Я') + || (e.KeyChar >= 'а' && e.KeyChar <= 'я') + || (e.KeyChar == (Char)Keys.Subtract)) + { + dgvPriceList.ClearSelection(); + GoodsFilter += e.KeyChar.ToString(); + txtSearchGoodNameInPriceList.Text = GoodsFilter; + ResetBrowseToNames(); + if (dgvPriceList.Rows.Count == 0) + { + UiDialogs.ShowInfo("Подходящий товар не найден.", "Поиск", FindForm()); + GoodsFilter = string.Empty; + txtSearchGoodNameInPriceList.Text = string.Empty; + ResetBrowseToNames(); + } + } + + return; + } + if (e.KeyChar == (Char)Keys.Back && GoodsFilter.Length > 0) { GoodsFilter = GoodsFilter.Substring(0, GoodsFilter.Length - 1); txtSearchGoodNameInPriceList.Text = GoodsFilter; - ApplyPriceListFilters(); + ResetBrowseToNames(); if (GoodsFilter.Length == 0) { @@ -893,14 +1512,14 @@ and tempOrder.GoodName = PriceList.GoodName #region старый способ поиска с фильтрацией данных if (!string.IsNullOrEmpty(GoodsFilter)) { - ApplyPriceListFilters(); + ResetBrowseToNames(); if (dgvPriceList.Rows.Count == 0) { UiDialogs.ShowInfo("Подходящий товар не найден.", "Поиск", FindForm()); GoodsFilter = string.Empty; txtSearchGoodNameInPriceList.Text = string.Empty; - ApplyPriceListFilters(); + ResetBrowseToNames(); } } #endregion @@ -908,9 +1527,13 @@ and tempOrder.GoodName = PriceList.GoodName return; } else if ((e.KeyChar >= (Char)Keys.D1 && e.KeyChar <= (Char)Keys.D9) || - (e.KeyChar >= (Char)Keys.NumPad1 && e.KeyChar <= (Char)Keys.NumPad9) || (e.KeyChar == (Char)Keys.D0 && dgvPriceList.Rows[RowIndex].Cells[10].Value.ToString().Length > 1) || (e.KeyChar == (Char)Keys.NumPad0 && dgvPriceList.Rows[RowIndex].Cells[10].Value.ToString().Length > 1)) + (e.KeyChar >= (Char)Keys.NumPad1 && e.KeyChar <= (Char)Keys.NumPad9) || (RowIndex >= 0 && e.KeyChar == (Char)Keys.D0 && dgvPriceList.Rows[RowIndex].Cells[10].Value.ToString().Length > 1) || (RowIndex >= 0 && e.KeyChar == (Char)Keys.NumPad0 && dgvPriceList.Rows[RowIndex].Cells[10].Value.ToString().Length > 1)) { - + if (RowIndex < 0) + { + return; + } + if (!TryReadInt32(dgvPriceList.Rows[RowIndex].Cells[6].Value, out var quantityForSale)) { return; @@ -950,13 +1573,6 @@ and tempOrder.GoodName = PriceList.GoodName } } } - else if (e.KeyChar == (Char)Keys.Escape) - { - txtSearchGoodNameInPriceList.Text = string.Empty; - GoodsFilter = string.Empty; - ApplyPriceListFilters(); - return; - } else if (e.KeyChar == (Char)Keys.Back) { if (RowIndex < 0) return; @@ -1267,6 +1883,25 @@ and SumOrderedItems = '{SumOrder}'"; { try { + if (_browseMode != PriceBrowseMode.Offers) + { + if (!string.IsNullOrWhiteSpace(_selectedBaseName)) + { + lblSelectedGoodName.Text = _selectedBaseName; + } + else if (dgvBrowseNames?.SelectedRows.Count > 0 && dgvBrowseNames.Columns.Contains("Наименование")) + { + lblSelectedGoodName.Text = dgvBrowseNames.SelectedRows[0].Cells["Наименование"]?.Value?.ToString() ?? string.Empty; + } + else + { + lblSelectedGoodName.Text = string.Empty; + } + + lblPriceForGood.Text = string.Empty; + return; + } + var currentRow = dgvPriceList.CurrentRow; if (currentRow == null || currentRow.IsNewRow) { @@ -1701,7 +2336,7 @@ and SumOrderedItems = '{SumOrder}'"; { txtSearchGoodNameInPriceList.Text = string.Empty; GoodsFilter = string.Empty; - ApplyPriceListFilters(); + ResetBrowseToNames(); timerForResetSearch.Enabled = false; timerForResetSearch.Stop(); } diff --git a/src/Elfisa.UI/Controls/ModernDataGridView.cs b/src/Elfisa.UI/Controls/ModernDataGridView.cs index 7320f8d..4167af2 100644 --- a/src/Elfisa.UI/Controls/ModernDataGridView.cs +++ b/src/Elfisa.UI/Controls/ModernDataGridView.cs @@ -178,6 +178,12 @@ namespace Elfisa.UI.Controls e.CellStyle.Font = theme.FontRegular13; } + protected override void OnDataSourceChanged(EventArgs e) + { + _hoverRowIndex = null; + base.OnDataSourceChanged(e); + } + protected override void OnCellMouseEnter(DataGridViewCellEventArgs e) { base.OnCellMouseEnter(e); @@ -188,12 +194,8 @@ namespace Elfisa.UI.Controls var previous = _hoverRowIndex; _hoverRowIndex = e.RowIndex; - if (previous.HasValue) - { - InvalidateRow(previous.Value); - } - - InvalidateRow(e.RowIndex); + InvalidateRowSafe(previous); + InvalidateRowSafe(e.RowIndex); } protected override void OnCellMouseLeave(DataGridViewCellEventArgs e) @@ -205,7 +207,23 @@ namespace Elfisa.UI.Controls } _hoverRowIndex = null; - InvalidateRow(e.RowIndex); + InvalidateRowSafe(e.RowIndex); + } + + private void InvalidateRowSafe(int? rowIndex) + { + if (!rowIndex.HasValue) + { + return; + } + + var index = rowIndex.Value; + if (index < 0 || index >= Rows.Count) + { + return; + } + + InvalidateRow(index); } } }