diff --git a/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs b/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
new file mode 100644
index 0000000..4c91df2
--- /dev/null
+++ b/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
@@ -0,0 +1,218 @@
+using System;
+using System.Collections.Generic;
+using System.Data.SQLite;
+
+namespace Электронная_Фармация.Classes
+{
+ ///
+ /// Аптека = грузополучатель + LocationId. Прайс общий, заказы уходят с location_id выбранной аптеки.
+ ///
+ public static class ConsigneeHelper
+ {
+ public static string GetDefaultConsigneeName()
+ {
+ try
+ {
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"SELECT ConsigneesName
+ FROM Consignees
+ ORDER BY ConsigneesUseAsDefault DESC, ConsigneesName
+ LIMIT 1",
+ connection))
+ {
+ var result = command.ExecuteScalar();
+ return result == null || result == DBNull.Value
+ ? string.Empty
+ : result.ToString().Trim();
+ }
+ }
+ }
+ catch
+ {
+ return string.Empty;
+ }
+ }
+
+ public static string GetLocationIdByName(string consigneeName)
+ {
+ if (string.IsNullOrWhiteSpace(consigneeName))
+ {
+ return string.Empty;
+ }
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"SELECT ifnull(LocationId, '')
+ FROM Consignees
+ WHERE ConsigneesName = @name
+ LIMIT 1",
+ connection))
+ {
+ command.Parameters.AddWithValue("@name", consigneeName.Trim());
+ var result = command.ExecuteScalar();
+ return result == null || result == DBNull.Value
+ ? string.Empty
+ : result.ToString().Trim();
+ }
+ }
+ }
+
+ public static string ResolveLocationId(string consigneeName)
+ {
+ var fromConsignee = GetLocationIdByName(consigneeName);
+ if (!string.IsNullOrWhiteSpace(fromConsignee))
+ {
+ return fromConsignee;
+ }
+
+ return AppConfig.LocationId ?? string.Empty;
+ }
+
+ ///
+ /// Помечает аптеку активной и синхронизирует AppConfig.LocationId.
+ ///
+ public static void SetActiveConsignee(string consigneeName)
+ {
+ if (string.IsNullOrWhiteSpace(consigneeName))
+ {
+ return;
+ }
+
+ var name = consigneeName.Trim();
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var tx = connection.BeginTransaction())
+ {
+ using (var clear = new SQLiteCommand(
+ "UPDATE Consignees SET ConsigneesUseAsDefault = 0",
+ connection,
+ tx))
+ {
+ clear.ExecuteNonQuery();
+ }
+
+ using (var setDefault = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET ConsigneesUseAsDefault = 1
+ WHERE ConsigneesName = @name",
+ connection,
+ tx))
+ {
+ setDefault.Parameters.AddWithValue("@name", name);
+ setDefault.ExecuteNonQuery();
+ }
+
+ tx.Commit();
+ }
+
+ var locationId = GetLocationIdByName(name);
+ if (!string.IsNullOrWhiteSpace(locationId))
+ {
+ AppConfig.SetLocationId(locationId);
+ }
+ }
+ }
+
+ public static void SetLocationId(string consigneeName, string locationId)
+ {
+ if (string.IsNullOrWhiteSpace(consigneeName))
+ {
+ return;
+ }
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET LocationId = @loc
+ WHERE ConsigneesName = @name",
+ connection))
+ {
+ command.Parameters.AddWithValue("@loc", (locationId ?? string.Empty).Trim());
+ command.Parameters.AddWithValue("@name", consigneeName.Trim());
+ command.ExecuteNonQuery();
+ }
+ }
+ }
+
+ public static void MigrateGlobalLocationIdIfNeeded(SQLiteConnection connection)
+ {
+ var global = AppConfig.LocationId;
+ if (string.IsNullOrWhiteSpace(global))
+ {
+ return;
+ }
+
+ using (var command = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET LocationId = @loc
+ WHERE (LocationId IS NULL OR trim(LocationId) = '')
+ AND (
+ ConsigneesUseAsDefault = 1
+ OR (SELECT COUNT(*) FROM Consignees) = 1
+ )",
+ connection))
+ {
+ command.Parameters.AddWithValue("@loc", global.Trim());
+ command.ExecuteNonQuery();
+ }
+ }
+
+ public static void ApplyLocationIdToEmptyConsignees(string locationId)
+ {
+ if (string.IsNullOrWhiteSpace(locationId))
+ {
+ return;
+ }
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET LocationId = @loc
+ WHERE LocationId IS NULL OR trim(LocationId) = ''",
+ connection))
+ {
+ command.Parameters.AddWithValue("@loc", locationId.Trim());
+ command.ExecuteNonQuery();
+ }
+ }
+ }
+
+ public static IList GetKnownSupplierIds()
+ {
+ var result = new List();
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"SELECT DISTINCT trim(supplier_id)
+ FROM PriceList
+ WHERE supplier_id IS NOT NULL AND trim(supplier_id) <> ''
+ ORDER BY 1",
+ connection))
+ using (var reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ var id = reader.GetString(0);
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ result.Add(id);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/ElectronicPharmacy/Classes/DataSender.cs b/src/ElectronicPharmacy/Classes/DataSender.cs
index 941e0f3..0d16877 100644
--- a/src/ElectronicPharmacy/Classes/DataSender.cs
+++ b/src/ElectronicPharmacy/Classes/DataSender.cs
@@ -35,20 +35,6 @@ namespace Электронная_Фармация.Classes
return;
}
- if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
- {
- if (!HF_LocationId.PromptAndSave(Form.ActiveForm)
- || string.IsNullOrWhiteSpace(AppConfig.LocationId))
- {
- AppDebugLog.Error("DataSender", "Отправка заказов: location_id не указан");
- ToastNotification.ShowCustom(
- "Не указан location_id. Заполните его после авторизации.",
- Color.DarkOrange,
- Color.White);
- return;
- }
- }
-
List orders;
try
{
@@ -66,6 +52,11 @@ namespace Электронная_Фармация.Classes
return;
}
+ if (!EnsureOrdersHaveLocationId(orders))
+ {
+ return;
+ }
+
int successCount = 0;
int failCount = 0;
@@ -110,20 +101,6 @@ namespace Электронная_Фармация.Classes
return false;
}
- if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
- {
- if (!HF_LocationId.PromptAndSave(Form.ActiveForm)
- || string.IsNullOrWhiteSpace(AppConfig.LocationId))
- {
- AppDebugLog.Error("DataSender", $"Отправка заказа {localOrderId}: location_id не указан");
- ToastNotification.ShowCustom(
- "Не указан location_id. Заполните его после авторизации.",
- Color.DarkOrange,
- Color.White);
- return false;
- }
- }
-
List orders;
try
{
@@ -141,6 +118,11 @@ namespace Электронная_Фармация.Classes
return false;
}
+ if (!EnsureOrdersHaveLocationId(orders))
+ {
+ return false;
+ }
+
bool ok = await SendOrderAsync(orders[0]);
if (ok)
{
@@ -153,7 +135,7 @@ namespace Электронная_Фармация.Classes
public List FetchDataFromSql(string onlyOrderId)
{
var orders = new Dictionary();
- string locationId = AppConfig.LocationId;
+ var locationCache = new Dictionary(StringComparer.OrdinalIgnoreCase);
using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
{
@@ -161,6 +143,7 @@ namespace Электронная_Фармация.Classes
string query = @"
SELECT
Orders.Id_Order,
+ Orders.ConsigneeName,
OrderItems.supplier_price_id,
OrderItems.DrugName,
OrderItems.es_code,
@@ -189,6 +172,9 @@ WHERE Orders.OrderState = 'НОВЫЙ'
while (reader.Read())
{
string orderId = reader["Id_Order"].ToString().Trim();
+ string consigneeName = reader["ConsigneeName"] == DBNull.Value
+ ? string.Empty
+ : reader["ConsigneeName"].ToString().Trim();
string supplierPriceId = reader["supplier_price_id"].ToString().Trim();
string drugName = reader["DrugName"] == DBNull.Value ? null : reader["DrugName"].ToString().Trim();
string esCode = reader["es_code"] == DBNull.Value ? null : reader["es_code"].ToString().Trim();
@@ -206,6 +192,7 @@ WHERE Orders.OrderState = 'НОВЫЙ'
if (!orders.TryGetValue(orderId, out BuyerOrderRequest order))
{
+ string locationId = ResolveLocationIdCached(consigneeName, locationCache);
order = new BuyerOrderRequest
{
LocalOrderId = orderId,
@@ -230,6 +217,65 @@ WHERE Orders.OrderState = 'НОВЫЙ'
return orders.Values.ToList();
}
+ private static string ResolveLocationIdCached(string consigneeName, Dictionary cache)
+ {
+ var key = consigneeName ?? string.Empty;
+ if (cache.TryGetValue(key, out var cached))
+ {
+ return cached;
+ }
+
+ var locationId = ConsigneeHelper.ResolveLocationId(consigneeName);
+ cache[key] = locationId ?? string.Empty;
+ return cache[key];
+ }
+
+ private bool EnsureOrdersHaveLocationId(List orders)
+ {
+ var missing = orders
+ .Where(o => string.IsNullOrWhiteSpace(o.LocationId))
+ .Select(o => o.LocalOrderId)
+ .Distinct()
+ .ToList();
+
+ if (missing.Count == 0)
+ {
+ return true;
+ }
+
+ if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
+ {
+ if (!HF_LocationId.PromptAndSave(Form.ActiveForm)
+ || string.IsNullOrWhiteSpace(AppConfig.LocationId))
+ {
+ AppDebugLog.Error("DataSender", "Отправка заказов: location_id не указан");
+ ToastNotification.ShowCustom(
+ "Не указан Location ID у аптеки заказа. Задайте его в «Грузополучатели» или в настройках.",
+ Color.DarkOrange,
+ Color.White);
+ return false;
+ }
+
+ ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
+ }
+
+ foreach (var order in orders.Where(o => string.IsNullOrWhiteSpace(o.LocationId)))
+ {
+ order.LocationId = AppConfig.LocationId;
+ }
+
+ if (orders.Any(o => string.IsNullOrWhiteSpace(o.LocationId)))
+ {
+ ToastNotification.ShowCustom(
+ "У части заказов нет Location ID. Укажите ID у грузополучателя.",
+ Color.DarkOrange,
+ Color.White);
+ return false;
+ }
+
+ return true;
+ }
+
private async Task SendOrderAsync(BuyerOrderRequest order)
{
var options = new JsonSerializerOptions
diff --git a/src/ElectronicPharmacy/Classes/SqlSyntaxHighlighter.cs b/src/ElectronicPharmacy/Classes/SqlSyntaxHighlighter.cs
new file mode 100644
index 0000000..8c42715
--- /dev/null
+++ b/src/ElectronicPharmacy/Classes/SqlSyntaxHighlighter.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+
+namespace Электронная_Фармация.Classes
+{
+ ///
+ /// Лёгкая подсветка SQL в RichTextBox (ключевые слова, строки, комментарии, числа, параметры).
+ ///
+ public static class SqlSyntaxHighlighter
+ {
+ private static readonly Regex TokenRegex = new Regex(
+ @"(--[^\r\n]*)|(/\*[\s\S]*?\*/)|('(?:''|[^'])*')|(""(?:""""|[^""])*"")|(\[[^\]]*\])|(@[A-Za-z_][A-Za-z0-9_]*)|(\{\{[A-Za-z_][A-Za-z0-9_]*\}\})|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_][A-Za-z0-9_]*\b)",
+ RegexOptions.Compiled);
+
+ private static readonly System.Collections.Generic.HashSet Keywords =
+ new System.Collections.Generic.HashSet(StringComparer.OrdinalIgnoreCase)
+ {
+ "SELECT", "FROM", "WHERE", "AND", "OR", "NOT", "IN", "IS", "NULL", "AS",
+ "JOIN", "INNER", "LEFT", "RIGHT", "OUTER", "CROSS", "ON", "USING",
+ "GROUP", "BY", "ORDER", "HAVING", "LIMIT", "OFFSET", "DISTINCT", "ALL",
+ "INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE", "REPLACE",
+ "CREATE", "ALTER", "DROP", "TABLE", "INDEX", "VIEW", "TRIGGER", "IF", "EXISTS",
+ "PRIMARY", "KEY", "FOREIGN", "REFERENCES", "UNIQUE", "DEFAULT", "CHECK",
+ "BEGIN", "COMMIT", "ROLLBACK", "TRANSACTION", "PRAGMA", "EXPLAIN", "WITH",
+ "CASE", "WHEN", "THEN", "ELSE", "END", "BETWEEN", "LIKE", "GLOB", "ESCAPE",
+ "UNION", "INTERSECT", "EXCEPT", "CAST", "COLLATE", "ASC", "DESC",
+ "COUNT", "SUM", "AVG", "MIN", "MAX", "IFNULL", "COALESCE", "NULLIF",
+ "LENGTH", "TRIM", "UPPER", "LOWER", "SUBSTR", "REPLACE", "ROUND", "ABS",
+ "DATETIME", "DATE", "TIME", "STRFTIME", "ADD", "COLUMN", "RENAME", "TO"
+ };
+
+ private const int WM_SETREDRAW = 0x000B;
+
+ [DllImport("user32.dll")]
+ private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
+
+ public static void Apply(RichTextBox box, bool darkTheme)
+ {
+ if (box == null || box.IsDisposed)
+ {
+ return;
+ }
+
+ string text = box.Text ?? string.Empty;
+ int selStart = box.SelectionStart;
+ int selLength = box.SelectionLength;
+ int firstVisibleChar = box.GetCharIndexFromPosition(new Point(1, 1));
+
+ Color defaultFg = darkTheme ? Color.FromArgb(220, 220, 220) : Color.FromArgb(30, 30, 30);
+ Color keyword = darkTheme ? Color.FromArgb(86, 156, 214) : Color.FromArgb(0, 0, 180);
+ Color str = darkTheme ? Color.FromArgb(206, 145, 120) : Color.FromArgb(163, 21, 21);
+ Color comment = darkTheme ? Color.FromArgb(106, 153, 85) : Color.FromArgb(0, 128, 0);
+ Color number = darkTheme ? Color.FromArgb(181, 206, 168) : Color.FromArgb(9, 134, 88);
+ Color param = darkTheme ? Color.FromArgb(197, 134, 192) : Color.FromArgb(128, 0, 128);
+ Color ident = darkTheme ? Color.FromArgb(156, 220, 254) : Color.FromArgb(0, 100, 120);
+
+ SuspendRedraw(box);
+ try
+ {
+ box.SelectAll();
+ box.SelectionColor = defaultFg;
+ box.SelectionFont = box.Font;
+
+ foreach (Match match in TokenRegex.Matches(text))
+ {
+ Color color = defaultFg;
+ bool bold = false;
+
+ if (match.Groups[1].Success || match.Groups[2].Success)
+ {
+ color = comment;
+ }
+ else if (match.Groups[3].Success || match.Groups[4].Success || match.Groups[5].Success)
+ {
+ color = str;
+ }
+ else if (match.Groups[6].Success || match.Groups[7].Success)
+ {
+ color = param;
+ bold = true;
+ }
+ else if (match.Groups[8].Success)
+ {
+ color = number;
+ }
+ else if (match.Groups[9].Success)
+ {
+ var word = match.Groups[9].Value;
+ if (Keywords.Contains(word))
+ {
+ color = keyword;
+ bold = true;
+ }
+ else
+ {
+ color = ident;
+ }
+ }
+
+ if (color == defaultFg && !bold)
+ {
+ continue;
+ }
+
+ box.Select(match.Index, match.Length);
+ box.SelectionColor = color;
+ if (bold)
+ {
+ box.SelectionFont = new Font(box.Font, FontStyle.Bold);
+ }
+ else
+ {
+ box.SelectionFont = box.Font;
+ }
+ }
+ }
+ finally
+ {
+ box.Select(selStart, selLength);
+ try
+ {
+ box.Select(firstVisibleChar, 0);
+ var pt = box.GetPositionFromCharIndex(firstVisibleChar);
+ if (pt.Y < 0)
+ {
+ // keep best-effort scroll
+ }
+ }
+ catch
+ {
+ // ignore scroll restore issues
+ }
+
+ box.Select(selStart, selLength);
+ ResumeRedraw(box);
+ box.Invalidate();
+ }
+ }
+
+ private static void SuspendRedraw(Control control)
+ {
+ SendMessage(control.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
+ }
+
+ private static void ResumeRedraw(Control control)
+ {
+ SendMessage(control.Handle, WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
+ }
+ }
+}
diff --git a/src/ElectronicPharmacy/ElectronicPharmacy.csproj b/src/ElectronicPharmacy/ElectronicPharmacy.csproj
index ba8999f..e670c27 100644
--- a/src/ElectronicPharmacy/ElectronicPharmacy.csproj
+++ b/src/ElectronicPharmacy/ElectronicPharmacy.csproj
@@ -104,6 +104,8 @@
+
+
diff --git a/src/ElectronicPharmacy/Form1.UiShell.cs b/src/ElectronicPharmacy/Form1.UiShell.cs
index 649dff5..4d06500 100644
--- a/src/ElectronicPharmacy/Form1.UiShell.cs
+++ b/src/ElectronicPharmacy/Form1.UiShell.cs
@@ -67,9 +67,9 @@ namespace Электронная_Фармация
{
Name = "statusFooter",
Dock = DockStyle.Bottom,
- Height = 30,
+ Height = 28,
Tag = "status-footer",
- Padding = new Padding(12, 0, 16, 0)
+ Padding = new Padding(10, 0, 4, 0)
};
_statusFooter.Paint += (_, e) =>
{
@@ -86,19 +86,27 @@ namespace Электронная_Фармация
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleLeft,
AutoEllipsis = true,
- Tag = "muted"
+ Tag = "muted",
+ Padding = new Padding(0, 0, 4, 0)
};
_btnSettings = new ModernButton
{
Name = "btnSettings",
Dock = DockStyle.Right,
- Width = 150,
- Margin = new Padding(0),
- Text = "Настройки"
+ Margin = new Padding(0, 2, 2, 2),
+ Text = "Настройки",
+ CompactIconMode = false,
+ AllowTextEllipsis = false,
+ MinimumSize = new Size(88, 22),
+ Size = new Size(88, 22),
+ Padding = new Padding(6, 0, 6, 0),
+ Font = new Font("Segoe UI Semibold", 8.5f, FontStyle.Bold),
+ TabIndex = 0
};
_btnSettings.Click += (_, __) => OpenSettingsDialog();
- _statusFooter.Controls.Add(_btnSettings);
+ // Сначала Fill (пользователь), потом Right (кнопка) — кнопка справа, текст не перекрывается.
_statusFooter.Controls.Add(_lblCurrentUser);
+ _statusFooter.Controls.Add(_btnSettings);
Controls.Add(_loadingOverlay);
Controls.Add(_documentHost);
@@ -111,6 +119,8 @@ namespace Электронная_Фармация
contractorsItem.Click += (_, __) => tsItemContractors_Click(_, __);
var consigneesItem = new ToolStripMenuItem("Грузополучатели");
consigneesItem.Click += (_, __) => tsConsignee_Click(_, __);
+ var switchConsigneeItem = new ToolStripMenuItem("Сменить грузополучателя");
+ switchConsigneeItem.Click += (_, __) => SwitchConsignee();
var invoicesItem = new ToolStripMenuItem("Накладные");
invoicesItem.Click += (_, __) => tsItemInvoices_Click(_, __);
var debugItem = new ToolStripMenuItem("Отладка");
@@ -119,6 +129,7 @@ namespace Электронная_Фармация
{
contractorsItem,
consigneesItem,
+ switchConsigneeItem,
invoicesItem,
new ToolStripSeparator(),
debugItem
@@ -194,18 +205,38 @@ namespace Электронная_Фармация
var login = Settings.Default.stringLogin?.Trim();
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
+ var pharmacy = ConsigneeName?.Trim();
+ if (string.IsNullOrWhiteSpace(pharmacy) || pharmacy == "Прайс-Лист")
+ {
+ pharmacy = ConsigneeHelper.GetDefaultConsigneeName();
+ if (!string.IsNullOrWhiteSpace(pharmacy) &&
+ (string.IsNullOrWhiteSpace(ConsigneeName) || ConsigneeName == "Прайс-Лист"))
+ {
+ ConsigneeName = pharmacy;
+ }
+ }
+ string userPart;
if (!string.IsNullOrWhiteSpace(login) && hasToken)
{
- _lblCurrentUser.Text = $"Пользователь: {login}";
+ userPart = $"Пользователь: {login}";
}
else if (!string.IsNullOrWhiteSpace(login))
{
- _lblCurrentUser.Text = $"Пользователь: {login} (не авторизован)";
+ userPart = $"Пользователь: {login} (не авторизован)";
}
else
{
- _lblCurrentUser.Text = "Пользователь не авторизован";
+ userPart = "Пользователь не авторизован";
+ }
+
+ if (!string.IsNullOrWhiteSpace(pharmacy) && pharmacy != "Прайс-Лист")
+ {
+ _lblCurrentUser.Text = $"{userPart} | Аптека: {pharmacy}";
+ }
+ else
+ {
+ _lblCurrentUser.Text = $"{userPart} | Аптека: не выбрана";
}
}
diff --git a/src/ElectronicPharmacy/Form1.cs b/src/ElectronicPharmacy/Form1.cs
index c929301..9c49f08 100644
--- a/src/ElectronicPharmacy/Form1.cs
+++ b/src/ElectronicPharmacy/Form1.cs
@@ -74,17 +74,45 @@ namespace Электронная_Фармация
UiThemeHelper.ApplyToControlTree(consigneeDialog);
consigneeDialog.ShowDialog();
+ if (string.IsNullOrWhiteSpace(ConsigneeName))
+ {
+ var defaultName = ConsigneeHelper.GetDefaultConsigneeName();
+ if (!string.IsNullOrWhiteSpace(defaultName))
+ {
+ ConsigneeName = defaultName;
+ ConsigneeHelper.SetActiveConsignee(defaultName);
+ }
+ }
+
if (!HasDocumentTabs())
{
loadDefaultPriceList();
}
+
+ RefreshCurrentUserStatus();
}
void loadDefaultPriceList()
{
- var ucPriceList = new UCPriceList("Прайс-Лист");
- OpenDocumentTab("Прайс-лист", "price", ucPriceList, allowDuplicate: true);
+ var consignee = !string.IsNullOrWhiteSpace(ConsigneeName)
+ ? ConsigneeName
+ : ConsigneeHelper.GetDefaultConsigneeName();
+ if (string.IsNullOrWhiteSpace(consignee))
+ {
+ consignee = "Прайс-Лист";
+ }
+ else
+ {
+ ConsigneeName = consignee;
+ }
+
+ var title = consignee == "Прайс-Лист"
+ ? "Прайс-лист"
+ : $"Прайс-лист ({consignee})";
+ var ucPriceList = new UCPriceList(consignee);
+ OpenDocumentTab(title, "price", ucPriceList, allowDuplicate: true);
ucPriceList.dgvPriceList.Focus();
+ RefreshCurrentUserStatus();
}
#region загрузкаПрайсЛиста_старая версия
@@ -164,9 +192,23 @@ namespace Электронная_Фармация
public void showMePriceList(string consignee)
{
ConsigneeName = consignee ?? string.Empty;
+ if (!string.IsNullOrWhiteSpace(ConsigneeName))
+ {
+ ConsigneeHelper.SetActiveConsignee(ConsigneeName);
+ }
+
var ucPriceList = new UCPriceList(consignee);
OpenDocumentTab($"Прайс-лист ({consignee})", "price", ucPriceList, allowDuplicate: true);
ucPriceList.dgvPriceList.Focus();
+ RefreshCurrentUserStatus();
+ }
+
+ private void SwitchConsignee()
+ {
+ var consigneeDialog = new HFConsignees { ParentForm = this };
+ UiThemeHelper.ApplyToControlTree(consigneeDialog);
+ consigneeDialog.ShowDialog(this);
+ RefreshCurrentUserStatus();
}
private void tsDebug_Click(object sender, EventArgs e)
diff --git a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs
index 289cfd6..cff0da6 100644
--- a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs
+++ b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs
@@ -76,7 +76,8 @@ namespace Электронная_Фармация.Forms
[codeConsignees] integer not null,
[ConsigneesName] nvarchar(128) not null,
[ConsigneesAddress] nvarchar(256) not null,
- [ConsigneesUseAsDefault] int(1) not null
+ [ConsigneesUseAsDefault] int(1) not null,
+ [LocationId] nvarchar(64)
)
";
@@ -405,6 +406,8 @@ create table if not exists [OrderItems] (
TryAddColumn(conForCheckTables, "PriceList", "TradeName", "nvarchar(256)");
TryAddColumn(conForCheckTables, "PriceList", "Dosage", "nvarchar(128)");
TryAddColumn(conForCheckTables, "PriceList", "MarkupPercent", "real");
+ TryAddColumn(conForCheckTables, "Consignees", "LocationId", "nvarchar(64)");
+ ConsigneeHelper.MigrateGlobalLocationIdIfNeeded(conForCheckTables);
}
catch (Exception ex)
{
diff --git a/src/ElectronicPharmacy/Forms/FConsignees.Designer.cs b/src/ElectronicPharmacy/Forms/FConsignees.Designer.cs
index f9e7bbd..0b58132 100644
--- a/src/ElectronicPharmacy/Forms/FConsignees.Designer.cs
+++ b/src/ElectronicPharmacy/Forms/FConsignees.Designer.cs
@@ -2,15 +2,8 @@
{
partial class FConsignees
{
- ///
- /// Required designer variable.
- ///
private System.ComponentModel.IContainer components = null;
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
@@ -22,13 +15,12 @@
#region Windows Form Designer generated code
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
+ this.btnSetDefault = new Elfisa.UI.Controls.ModernButton();
+ this.btnAdd = new Elfisa.UI.Controls.ModernButton();
+ this.btnSave = new Elfisa.UI.Controls.ModernButton();
this.txtInteractiveShowMeConsignees = new Elfisa.UI.Controls.ModernTextBox();
this.label2 = new System.Windows.Forms.Label();
this.dgvConsignees = new Elfisa.UI.Controls.ModernDataGridView();
@@ -39,31 +31,67 @@
// panel1
//
this.panel1.BackColor = System.Drawing.SystemColors.ButtonHighlight;
+ this.panel1.Controls.Add(this.btnSetDefault);
+ this.panel1.Controls.Add(this.btnAdd);
+ this.panel1.Controls.Add(this.btnSave);
this.panel1.Controls.Add(this.txtInteractiveShowMeConsignees);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.dgvConsignees);
- this.panel1.Location = new System.Drawing.Point(13, 14);
- this.panel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
+ this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.panel1.Location = new System.Drawing.Point(0, 0);
+ this.panel1.Margin = new System.Windows.Forms.Padding(4);
this.panel1.Name = "panel1";
- this.panel1.Size = new System.Drawing.Size(724, 423);
+ this.panel1.Padding = new System.Windows.Forms.Padding(12);
+ this.panel1.Size = new System.Drawing.Size(820, 480);
this.panel1.TabIndex = 4;
//
+ // btnSetDefault
+ //
+ this.btnSetDefault.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.btnSetDefault.Location = new System.Drawing.Point(280, 430);
+ this.btnSetDefault.Name = "btnSetDefault";
+ this.btnSetDefault.Size = new System.Drawing.Size(180, 34);
+ this.btnSetDefault.TabIndex = 6;
+ this.btnSetDefault.Text = "По умолчанию";
+ this.btnSetDefault.Click += new System.EventHandler(this.btnSetDefault_Click);
+ //
+ // btnAdd
+ //
+ this.btnAdd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.btnAdd.Location = new System.Drawing.Point(148, 430);
+ this.btnAdd.Name = "btnAdd";
+ this.btnAdd.Size = new System.Drawing.Size(120, 34);
+ this.btnAdd.TabIndex = 5;
+ this.btnAdd.Text = "Добавить";
+ this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
+ //
+ // btnSave
+ //
+ this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.btnSave.Location = new System.Drawing.Point(16, 430);
+ this.btnSave.Name = "btnSave";
+ this.btnSave.Size = new System.Drawing.Size(120, 34);
+ this.btnSave.TabIndex = 4;
+ this.btnSave.Text = "Сохранить";
+ this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
+ //
// txtInteractiveShowMeConsignees
//
- this.txtInteractiveShowMeConsignees.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
- this.txtInteractiveShowMeConsignees.Location = new System.Drawing.Point(91, 6);
- this.txtInteractiveShowMeConsignees.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
+ this.txtInteractiveShowMeConsignees.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.txtInteractiveShowMeConsignees.Font = new System.Drawing.Font("Segoe UI", 11.25F);
+ this.txtInteractiveShowMeConsignees.Location = new System.Drawing.Point(91, 16);
+ this.txtInteractiveShowMeConsignees.Margin = new System.Windows.Forms.Padding(4);
this.txtInteractiveShowMeConsignees.Name = "txtInteractiveShowMeConsignees";
- this.txtInteractiveShowMeConsignees.Size = new System.Drawing.Size(623, 32);
+ this.txtInteractiveShowMeConsignees.Size = new System.Drawing.Size(700, 32);
this.txtInteractiveShowMeConsignees.TabIndex = 3;
this.txtInteractiveShowMeConsignees.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInteractiveShowMeConsignees_KeyPress);
//
// label2
//
this.label2.AutoSize = true;
- this.label2.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
- this.label2.Location = new System.Drawing.Point(5, 6);
- this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.label2.Font = new System.Drawing.Font("Segoe UI", 12F);
+ this.label2.Location = new System.Drawing.Point(16, 18);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(73, 28);
this.label2.TabIndex = 2;
@@ -73,45 +101,48 @@
//
this.dgvConsignees.AllowUserToAddRows = false;
this.dgvConsignees.AllowUserToDeleteRows = false;
- this.dgvConsignees.AllowUserToResizeColumns = false;
this.dgvConsignees.AllowUserToResizeRows = false;
+ this.dgvConsignees.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
this.dgvConsignees.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
- this.dgvConsignees.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.DisplayedHeaders;
this.dgvConsignees.BackgroundColor = System.Drawing.SystemColors.ButtonFace;
this.dgvConsignees.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- this.dgvConsignees.Location = new System.Drawing.Point(11, 44);
- this.dgvConsignees.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
+ this.dgvConsignees.Location = new System.Drawing.Point(16, 56);
this.dgvConsignees.Name = "dgvConsignees";
- this.dgvConsignees.ReadOnly = true;
+ this.dgvConsignees.ReadOnly = false;
this.dgvConsignees.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders;
- this.dgvConsignees.Size = new System.Drawing.Size(704, 369);
+ this.dgvConsignees.Size = new System.Drawing.Size(775, 360);
this.dgvConsignees.TabIndex = 1;
//
// FConsignees
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(749, 450);
+ this.ClientSize = new System.Drawing.Size(820, 480);
this.Controls.Add(this.panel1);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
- this.MaximizeBox = false;
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
+ this.MaximizeBox = true;
this.MinimizeBox = false;
+ this.MinimumSize = new System.Drawing.Size(700, 420);
this.Name = "FConsignees";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Грузополучатели";
+ this.Text = "Грузополучатели (аптеки)";
this.Load += new System.EventHandler(this.FConsignees_Load);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgvConsignees)).EndInit();
this.ResumeLayout(false);
-
}
#endregion
+
private System.Windows.Forms.Panel panel1;
private Elfisa.UI.Controls.ModernTextBox txtInteractiveShowMeConsignees;
private System.Windows.Forms.Label label2;
private Elfisa.UI.Controls.ModernDataGridView dgvConsignees;
+ private Elfisa.UI.Controls.ModernButton btnSave;
+ private Elfisa.UI.Controls.ModernButton btnAdd;
+ private Elfisa.UI.Controls.ModernButton btnSetDefault;
}
-}
\ No newline at end of file
+}
diff --git a/src/ElectronicPharmacy/Forms/FConsignees.cs b/src/ElectronicPharmacy/Forms/FConsignees.cs
index 0f6efc8..8142cf1 100644
--- a/src/ElectronicPharmacy/Forms/FConsignees.cs
+++ b/src/ElectronicPharmacy/Forms/FConsignees.cs
@@ -1,14 +1,8 @@
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Data.SQLite;
using System.Windows.Forms;
using Электронная_Фармация.Classes;
-using System.Data.SQLite;
namespace Электронная_Фармация.Forms
{
@@ -29,13 +23,15 @@ namespace Электронная_Фармация.Forms
{
using (SQLiteConnection conConsignees = new SQLiteConnection(AppConfig.SqliteConnectionString))
{
- string selectFromConsignees = $@"
-select [codeConsignees] as [Код грузополучателя],
+ string selectFromConsignees = @"
+select [idConsignees] as [Id],
+[codeConsignees] as [Код],
[ConsigneesName] as [Наименование],
[ConsigneesAddress] as [Адрес],
-[ConsigneesUseAsDefault] as [По умолчанию?]
+ifnull([LocationId], '') as [Location ID],
+[ConsigneesUseAsDefault] as [По умолчанию]
from [Consignees]
-order by [ConsigneesUseAsDefault] desc";
+order by [ConsigneesUseAsDefault] desc, [ConsigneesName]";
try
{
conConsignees.Open();
@@ -44,16 +40,20 @@ order by [ConsigneesUseAsDefault] desc";
SQLiteDataAdapter daConsigneeList = new SQLiteDataAdapter(cmdShowConsigneesList);
daConsigneeList.Fill(tableConsignees);
dgvConsignees.DataSource = tableConsignees;
- dgvConsignees.Columns[3].ValueType = typeof(float);
+ if (dgvConsignees.Columns.Contains("Id"))
+ {
+ dgvConsignees.Columns["Id"].Visible = false;
+ }
+
+ foreach (DataGridViewColumn column in dgvConsignees.Columns)
+ {
+ column.ReadOnly = column.Name != "Location ID" && column.Name != "Адрес";
+ }
}
catch (Exception ex)
{
MessageBox.Show($"Возникла ошибка при заполнении данных о грузополучателях.\nТекст ошибки:\n{ex}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
- finally
- {
- conConsignees.Close();
- }
}
}
@@ -61,9 +61,9 @@ order by [ConsigneesUseAsDefault] desc";
{
if (e.KeyChar == (char)Keys.Enter)
{
- if (dgvConsignees.Rows.Count > 0)
+ if (dgvConsignees.Rows.Count > 0 && dgvConsignees.DataSource is DataTable table)
{
- (dgvConsignees.DataSource as DataTable).DefaultView.RowFilter = string.Format($"[Наименование] like '%{txtInteractiveShowMeConsignees.Text}%'");
+ table.DefaultView.RowFilter = $"[Наименование] like '%{txtInteractiveShowMeConsignees.Text.Replace("'", "''")}%'";
if (dgvConsignees.Rows.Count == 0)
{
showMeContentFromConsigneesTable();
@@ -77,5 +77,133 @@ order by [ConsigneesUseAsDefault] desc";
}
}
+ private void btnSave_Click(object sender, EventArgs e)
+ {
+ if (!(dgvConsignees.DataSource is DataTable table))
+ {
+ return;
+ }
+
+ dgvConsignees.EndEdit();
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var tx = connection.BeginTransaction())
+ {
+ foreach (DataRow row in table.Rows)
+ {
+ if (row.RowState == DataRowState.Unchanged)
+ {
+ continue;
+ }
+
+ using (var cmd = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET ConsigneesAddress = @address,
+ LocationId = @locationId
+ WHERE idConsignees = @id",
+ connection,
+ tx))
+ {
+ cmd.Parameters.AddWithValue("@address", row["Адрес"]?.ToString() ?? string.Empty);
+ cmd.Parameters.AddWithValue("@locationId", row["Location ID"]?.ToString()?.Trim() ?? string.Empty);
+ cmd.Parameters.AddWithValue("@id", Convert.ToInt64(row["Id"]));
+ cmd.ExecuteNonQuery();
+ }
+ }
+
+ tx.Commit();
+ }
+ }
+
+ table.AcceptChanges();
+ ToastNotification.ShowSuccess("Грузополучатели сохранены");
+ showMeContentFromConsigneesTable();
+ }
+
+ private void btnAdd_Click(object sender, EventArgs e)
+ {
+ using (var dialog = new Form())
+ {
+ dialog.Text = "Новая аптека";
+ dialog.FormBorderStyle = FormBorderStyle.FixedDialog;
+ dialog.StartPosition = FormStartPosition.CenterParent;
+ dialog.ClientSize = new System.Drawing.Size(420, 220);
+ dialog.MaximizeBox = false;
+ dialog.MinimizeBox = false;
+
+ var lblName = new Label { Text = "Наименование", Left = 16, Top = 16, Width = 380 };
+ var txtName = new TextBox { Left = 16, Top = 40, Width = 380 };
+ var lblAddress = new Label { Text = "Адрес", Left = 16, Top = 76, Width = 380 };
+ var txtAddress = new TextBox { Left = 16, Top = 100, Width = 380 };
+ var lblLocation = new Label { Text = "Location ID", Left = 16, Top = 136, Width = 380 };
+ var txtLocation = new TextBox { Left = 16, Top = 160, Width = 380 };
+ var btnOk = new Button { Text = "Добавить", DialogResult = DialogResult.OK, Left = 220, Top = 188, Width = 80 };
+ var btnCancel = new Button { Text = "Отмена", DialogResult = DialogResult.Cancel, Left = 310, Top = 188, Width = 80 };
+ dialog.Controls.AddRange(new Control[] { lblName, txtName, lblAddress, txtAddress, lblLocation, txtLocation, btnOk, btnCancel });
+ dialog.AcceptButton = btnOk;
+ dialog.CancelButton = btnCancel;
+ UiThemeHelper.ApplyToControlTree(dialog);
+
+ if (dialog.ShowDialog(this) != DialogResult.OK)
+ {
+ return;
+ }
+
+ var name = (txtName.Text ?? string.Empty).Trim();
+ var address = (txtAddress.Text ?? string.Empty).Trim();
+ var locationId = (txtLocation.Text ?? string.Empty).Trim();
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ UiDialogs.ShowError("Укажите наименование аптеки.", "Грузополучатели", this);
+ return;
+ }
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ int nextCode = 1;
+ using (var codeCmd = new SQLiteCommand("SELECT ifnull(MAX(codeConsignees), 0) + 1 FROM Consignees", connection))
+ {
+ nextCode = Convert.ToInt32(codeCmd.ExecuteScalar());
+ }
+
+ using (var insert = new SQLiteCommand(
+ @"INSERT INTO Consignees (codeConsignees, ConsigneesName, ConsigneesAddress, ConsigneesUseAsDefault, LocationId)
+ VALUES (@code, @name, @address, 0, @locationId)",
+ connection))
+ {
+ insert.Parameters.AddWithValue("@code", nextCode);
+ insert.Parameters.AddWithValue("@name", name);
+ insert.Parameters.AddWithValue("@address", address);
+ insert.Parameters.AddWithValue("@locationId", locationId);
+ insert.ExecuteNonQuery();
+ }
+ }
+
+ ToastNotification.ShowSuccess("Аптека добавлена");
+ showMeContentFromConsigneesTable();
+ }
+ }
+
+ private void btnSetDefault_Click(object sender, EventArgs e)
+ {
+ if (dgvConsignees.CurrentRow == null)
+ {
+ UiDialogs.ShowInfo("Выберите аптеку в списке.", "Грузополучатели", this);
+ return;
+ }
+
+ var name = dgvConsignees.CurrentRow.Cells["Наименование"]?.Value?.ToString();
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ return;
+ }
+
+ ConsigneeHelper.SetActiveConsignee(name);
+ ToastNotification.ShowSuccess($"Аптека по умолчанию: {name}");
+ showMeContentFromConsigneesTable();
+ }
}
}
diff --git a/src/ElectronicPharmacy/Forms/FDebug.Designer.cs b/src/ElectronicPharmacy/Forms/FDebug.Designer.cs
index c00b120..c1f9252 100644
--- a/src/ElectronicPharmacy/Forms/FDebug.Designer.cs
+++ b/src/ElectronicPharmacy/Forms/FDebug.Designer.cs
@@ -2,15 +2,8 @@
{
partial class FDebug
{
- ///
- /// Required designer variable.
- ///
private System.ComponentModel.IContainer components = null;
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
@@ -22,115 +15,267 @@
#region Windows Form Designer generated code
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
private void InitializeComponent()
{
- this.RTXT_Sql = new System.Windows.Forms.RichTextBox();
- this.label1 = new System.Windows.Forms.Label();
- this.btnCleanSqlScript = new Elfisa.UI.Controls.ModernButton();
- this.btnExecuteSqlScript = new Elfisa.UI.Controls.ModernButton();
+ this.splitMain = new System.Windows.Forms.SplitContainer();
+ this.listTemplates = new System.Windows.Forms.ListBox();
+ this.lblTemplates = new System.Windows.Forms.Label();
+ this.splitRight = new System.Windows.Forms.SplitContainer();
+ this.panelTop = new System.Windows.Forms.Panel();
+ this.btnScanParams = new Elfisa.UI.Controls.ModernButton();
this.btnExecuteSelectSqlScript = new Elfisa.UI.Controls.ModernButton();
+ this.btnExecuteSqlScript = new Elfisa.UI.Controls.ModernButton();
+ this.btnCleanSqlScript = new Elfisa.UI.Controls.ModernButton();
+ this.lblDbPath = new System.Windows.Forms.Label();
+ this.label1 = new System.Windows.Forms.Label();
+ this.RTXT_Sql = new System.Windows.Forms.RichTextBox();
+ this.lblParams = new System.Windows.Forms.Label();
+ this.panelParams = new System.Windows.Forms.Panel();
this.dgvSelectCommand = new Elfisa.UI.Controls.ModernDataGridView();
+ this.lblStatus = new System.Windows.Forms.Label();
+ ((System.ComponentModel.ISupportInitialize)(this.splitMain)).BeginInit();
+ this.splitMain.Panel1.SuspendLayout();
+ this.splitMain.Panel2.SuspendLayout();
+ this.splitMain.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitRight)).BeginInit();
+ this.splitRight.Panel1.SuspendLayout();
+ this.splitRight.Panel2.SuspendLayout();
+ this.splitRight.SuspendLayout();
+ this.panelTop.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgvSelectCommand)).BeginInit();
this.SuspendLayout();
//
- // RTXT_Sql
+ // splitMain
//
- this.RTXT_Sql.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.RTXT_Sql.Location = new System.Drawing.Point(9, 70);
- this.RTXT_Sql.Margin = new System.Windows.Forms.Padding(4);
- this.RTXT_Sql.Name = "RTXT_Sql";
- this.RTXT_Sql.Size = new System.Drawing.Size(565, 118);
- this.RTXT_Sql.TabIndex = 0;
- this.RTXT_Sql.Text = "";
+ this.splitMain.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitMain.Location = new System.Drawing.Point(0, 0);
+ this.splitMain.Name = "splitMain";
//
- // label1
+ // splitMain.Panel1
//
- this.label1.AutoSize = true;
- this.label1.Location = new System.Drawing.Point(6, 50);
- this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
- this.label1.Name = "label1";
- this.label1.Size = new System.Drawing.Size(84, 16);
- this.label1.TabIndex = 1;
- this.label1.Text = "SQL-запрос";
+ this.splitMain.Panel1.Controls.Add(this.listTemplates);
+ this.splitMain.Panel1.Controls.Add(this.lblTemplates);
+ this.splitMain.Panel1MinSize = 180;
//
- // btnCleanSqlScript
+ // splitMain.Panel2
//
- this.btnCleanSqlScript.Location = new System.Drawing.Point(9, 15);
- this.btnCleanSqlScript.Margin = new System.Windows.Forms.Padding(4);
- this.btnCleanSqlScript.Name = "btnCleanSqlScript";
- this.btnCleanSqlScript.Size = new System.Drawing.Size(100, 28);
- this.btnCleanSqlScript.TabIndex = 2;
- this.btnCleanSqlScript.Text = "Очистить";
- this.btnCleanSqlScript.UseVisualStyleBackColor = true;
- this.btnCleanSqlScript.Click += new System.EventHandler(this.btnCleanSqlScript_Click);
+ this.splitMain.Panel2.Controls.Add(this.splitRight);
+ this.splitMain.Size = new System.Drawing.Size(980, 620);
+ this.splitMain.SplitterDistance = 240;
+ this.splitMain.TabIndex = 0;
//
- // btnExecuteSqlScript
+ // listTemplates
//
- this.btnExecuteSqlScript.Location = new System.Drawing.Point(128, 15);
- this.btnExecuteSqlScript.Margin = new System.Windows.Forms.Padding(4);
- this.btnExecuteSqlScript.Name = "btnExecuteSqlScript";
- this.btnExecuteSqlScript.Size = new System.Drawing.Size(100, 28);
- this.btnExecuteSqlScript.TabIndex = 3;
- this.btnExecuteSqlScript.Text = "Выполнить";
- this.btnExecuteSqlScript.UseVisualStyleBackColor = true;
- this.btnExecuteSqlScript.Click += new System.EventHandler(this.btnExecuteSqlScript_Click);
+ this.listTemplates.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.listTemplates.Font = new System.Drawing.Font("Segoe UI", 10F);
+ this.listTemplates.FormattingEnabled = true;
+ this.listTemplates.ItemHeight = 23;
+ this.listTemplates.Location = new System.Drawing.Point(0, 28);
+ this.listTemplates.Name = "listTemplates";
+ this.listTemplates.Size = new System.Drawing.Size(240, 592);
+ this.listTemplates.TabIndex = 1;
+ this.listTemplates.SelectedIndexChanged += new System.EventHandler(this.listTemplates_SelectedIndexChanged);
+ //
+ // lblTemplates
+ //
+ this.lblTemplates.Dock = System.Windows.Forms.DockStyle.Top;
+ this.lblTemplates.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold);
+ this.lblTemplates.Location = new System.Drawing.Point(0, 0);
+ this.lblTemplates.Name = "lblTemplates";
+ this.lblTemplates.Padding = new System.Windows.Forms.Padding(8, 4, 0, 0);
+ this.lblTemplates.Size = new System.Drawing.Size(240, 28);
+ this.lblTemplates.TabIndex = 0;
+ this.lblTemplates.Text = "Шаблоны SQL";
+ //
+ // splitRight
+ //
+ this.splitRight.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitRight.Location = new System.Drawing.Point(0, 0);
+ this.splitRight.Name = "splitRight";
+ this.splitRight.Orientation = System.Windows.Forms.Orientation.Horizontal;
+ //
+ // splitRight.Panel1
+ //
+ this.splitRight.Panel1.Controls.Add(this.RTXT_Sql);
+ this.splitRight.Panel1.Controls.Add(this.panelParams);
+ this.splitRight.Panel1.Controls.Add(this.lblParams);
+ this.splitRight.Panel1.Controls.Add(this.panelTop);
+ this.splitRight.Panel1MinSize = 220;
+ //
+ // splitRight.Panel2
+ //
+ this.splitRight.Panel2.Controls.Add(this.dgvSelectCommand);
+ this.splitRight.Panel2.Controls.Add(this.lblStatus);
+ this.splitRight.Panel2MinSize = 120;
+ this.splitRight.Size = new System.Drawing.Size(736, 620);
+ this.splitRight.SplitterDistance = 320;
+ this.splitRight.TabIndex = 0;
+ //
+ // panelTop
+ //
+ this.panelTop.Controls.Add(this.btnScanParams);
+ this.panelTop.Controls.Add(this.btnExecuteSelectSqlScript);
+ this.panelTop.Controls.Add(this.btnExecuteSqlScript);
+ this.panelTop.Controls.Add(this.btnCleanSqlScript);
+ this.panelTop.Controls.Add(this.lblDbPath);
+ this.panelTop.Controls.Add(this.label1);
+ this.panelTop.Dock = System.Windows.Forms.DockStyle.Top;
+ this.panelTop.Location = new System.Drawing.Point(0, 0);
+ this.panelTop.Name = "panelTop";
+ this.panelTop.Size = new System.Drawing.Size(736, 72);
+ this.panelTop.TabIndex = 0;
+ //
+ // btnScanParams
+ //
+ this.btnScanParams.Location = new System.Drawing.Point(430, 36);
+ this.btnScanParams.Name = "btnScanParams";
+ this.btnScanParams.Size = new System.Drawing.Size(150, 28);
+ this.btnScanParams.TabIndex = 5;
+ this.btnScanParams.Text = "Параметры из SQL";
+ this.btnScanParams.Click += new System.EventHandler(this.btnScanParams_Click);
//
// btnExecuteSelectSqlScript
//
- this.btnExecuteSelectSqlScript.Location = new System.Drawing.Point(254, 15);
- this.btnExecuteSelectSqlScript.Margin = new System.Windows.Forms.Padding(4);
+ this.btnExecuteSelectSqlScript.Location = new System.Drawing.Point(232, 36);
this.btnExecuteSelectSqlScript.Name = "btnExecuteSelectSqlScript";
this.btnExecuteSelectSqlScript.Size = new System.Drawing.Size(182, 28);
this.btnExecuteSelectSqlScript.TabIndex = 4;
this.btnExecuteSelectSqlScript.Text = "Выполнить SELECT";
- this.btnExecuteSelectSqlScript.UseVisualStyleBackColor = true;
this.btnExecuteSelectSqlScript.Click += new System.EventHandler(this.btnExecuteSelectSqlScript_Click);
//
+ // btnExecuteSqlScript
+ //
+ this.btnExecuteSqlScript.Location = new System.Drawing.Point(116, 36);
+ this.btnExecuteSqlScript.Name = "btnExecuteSqlScript";
+ this.btnExecuteSqlScript.Size = new System.Drawing.Size(100, 28);
+ this.btnExecuteSqlScript.TabIndex = 3;
+ this.btnExecuteSqlScript.Text = "Выполнить";
+ this.btnExecuteSqlScript.Click += new System.EventHandler(this.btnExecuteSqlScript_Click);
+ //
+ // btnCleanSqlScript
+ //
+ this.btnCleanSqlScript.Location = new System.Drawing.Point(10, 36);
+ this.btnCleanSqlScript.Name = "btnCleanSqlScript";
+ this.btnCleanSqlScript.Size = new System.Drawing.Size(100, 28);
+ this.btnCleanSqlScript.TabIndex = 2;
+ this.btnCleanSqlScript.Text = "Очистить";
+ this.btnCleanSqlScript.Click += new System.EventHandler(this.btnCleanSqlScript_Click);
+ //
+ // lblDbPath
+ //
+ this.lblDbPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.lblDbPath.Location = new System.Drawing.Point(100, 8);
+ this.lblDbPath.Name = "lblDbPath";
+ this.lblDbPath.Size = new System.Drawing.Size(620, 20);
+ this.lblDbPath.TabIndex = 1;
+ this.lblDbPath.Text = "db";
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(8, 8);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(84, 16);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "SQL / база:";
+ //
+ // RTXT_Sql
+ //
+ this.RTXT_Sql.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.RTXT_Sql.Font = new System.Drawing.Font("Consolas", 10F);
+ this.RTXT_Sql.Location = new System.Drawing.Point(0, 100);
+ this.RTXT_Sql.Name = "RTXT_Sql";
+ this.RTXT_Sql.Size = new System.Drawing.Size(736, 120);
+ this.RTXT_Sql.TabIndex = 3;
+ this.RTXT_Sql.Text = "";
+ //
+ // lblParams
+ //
+ this.lblParams.Dock = System.Windows.Forms.DockStyle.Top;
+ this.lblParams.Location = new System.Drawing.Point(0, 72);
+ this.lblParams.Name = "lblParams";
+ this.lblParams.Padding = new System.Windows.Forms.Padding(8, 4, 0, 0);
+ this.lblParams.Size = new System.Drawing.Size(736, 28);
+ this.lblParams.TabIndex = 1;
+ this.lblParams.Text = "Параметры (@name / {{name}})";
+ //
+ // panelParams
+ //
+ this.panelParams.AutoScroll = true;
+ this.panelParams.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.panelParams.Location = new System.Drawing.Point(0, 220);
+ this.panelParams.Name = "panelParams";
+ this.panelParams.Size = new System.Drawing.Size(736, 100);
+ this.panelParams.TabIndex = 2;
+ //
// dgvSelectCommand
//
- this.dgvSelectCommand.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
+ this.dgvSelectCommand.AllowUserToAddRows = false;
+ this.dgvSelectCommand.AllowUserToDeleteRows = false;
+ this.dgvSelectCommand.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.DisplayedCells;
this.dgvSelectCommand.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- this.dgvSelectCommand.Location = new System.Drawing.Point(9, 195);
+ this.dgvSelectCommand.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.dgvSelectCommand.Location = new System.Drawing.Point(0, 0);
this.dgvSelectCommand.Name = "dgvSelectCommand";
+ this.dgvSelectCommand.ReadOnly = true;
this.dgvSelectCommand.RowHeadersWidth = 51;
this.dgvSelectCommand.RowTemplate.Height = 24;
- this.dgvSelectCommand.Size = new System.Drawing.Size(562, 150);
- this.dgvSelectCommand.TabIndex = 5;
+ this.dgvSelectCommand.Size = new System.Drawing.Size(736, 268);
+ this.dgvSelectCommand.TabIndex = 0;
+ //
+ // lblStatus
+ //
+ this.lblStatus.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.lblStatus.Location = new System.Drawing.Point(0, 268);
+ this.lblStatus.Name = "lblStatus";
+ this.lblStatus.Padding = new System.Windows.Forms.Padding(8, 4, 8, 4);
+ this.lblStatus.Size = new System.Drawing.Size(736, 28);
+ this.lblStatus.TabIndex = 1;
+ this.lblStatus.Text = "Готово";
//
// FDebug
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(583, 351);
- this.Controls.Add(this.dgvSelectCommand);
- this.Controls.Add(this.btnExecuteSelectSqlScript);
- this.Controls.Add(this.btnExecuteSqlScript);
- this.Controls.Add(this.btnCleanSqlScript);
- this.Controls.Add(this.label1);
- this.Controls.Add(this.RTXT_Sql);
- this.Margin = new System.Windows.Forms.Padding(4);
+ this.ClientSize = new System.Drawing.Size(980, 620);
+ this.Controls.Add(this.splitMain);
+ this.MinimumSize = new System.Drawing.Size(800, 500);
this.Name = "FDebug";
- this.Text = "FDebug";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "SQL-отладка";
+ this.Load += new System.EventHandler(this.FDebug_Load);
+ this.splitMain.Panel1.ResumeLayout(false);
+ this.splitMain.Panel2.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.splitMain)).EndInit();
+ this.splitMain.ResumeLayout(false);
+ this.splitRight.Panel1.ResumeLayout(false);
+ this.splitRight.Panel2.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.splitRight)).EndInit();
+ this.splitRight.ResumeLayout(false);
+ this.panelTop.ResumeLayout(false);
+ this.panelTop.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgvSelectCommand)).EndInit();
this.ResumeLayout(false);
- this.PerformLayout();
-
}
#endregion
- private System.Windows.Forms.RichTextBox RTXT_Sql;
- private System.Windows.Forms.Label label1;
- private Elfisa.UI.Controls.ModernButton btnCleanSqlScript;
- private Elfisa.UI.Controls.ModernButton btnExecuteSqlScript;
+ private System.Windows.Forms.SplitContainer splitMain;
+ private System.Windows.Forms.ListBox listTemplates;
+ private System.Windows.Forms.Label lblTemplates;
+ private System.Windows.Forms.SplitContainer splitRight;
+ private System.Windows.Forms.Panel panelTop;
+ private Elfisa.UI.Controls.ModernButton btnScanParams;
private Elfisa.UI.Controls.ModernButton btnExecuteSelectSqlScript;
+ private Elfisa.UI.Controls.ModernButton btnExecuteSqlScript;
+ private Elfisa.UI.Controls.ModernButton btnCleanSqlScript;
+ private System.Windows.Forms.Label lblDbPath;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.RichTextBox RTXT_Sql;
+ private System.Windows.Forms.Label lblParams;
+ private System.Windows.Forms.Panel panelParams;
private Elfisa.UI.Controls.ModernDataGridView dgvSelectCommand;
+ private System.Windows.Forms.Label lblStatus;
}
-}
\ No newline at end of file
+}
diff --git a/src/ElectronicPharmacy/Forms/FDebug.cs b/src/ElectronicPharmacy/Forms/FDebug.cs
index bf8022a..8892f58 100644
--- a/src/ElectronicPharmacy/Forms/FDebug.cs
+++ b/src/ElectronicPharmacy/Forms/FDebug.cs
@@ -1,81 +1,385 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Data;
+using System.Data.SQLite;
+using System.Diagnostics;
using System.Drawing;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Text.RegularExpressions;
using System.Windows.Forms;
+using Elfisa.UI.Theming;
using Электронная_Фармация.Classes;
-using System.Data.SQLite;
namespace Электронная_Фармация.Forms
{
public partial class FDebug : Form
{
+ private sealed class SqlTemplate
+ {
+ public string Title { get; set; }
+ public string Sql { get; set; }
+ public string[] Parameters { get; set; }
+ }
+
+ private readonly List _templates = new List();
+ private readonly Dictionary _paramBoxes = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ private readonly Timer _highlightTimer = new Timer();
+ private bool _highlighting;
+
public FDebug()
{
InitializeComponent();
+ _highlightTimer.Interval = 180;
+ _highlightTimer.Tick += (_, __) =>
+ {
+ _highlightTimer.Stop();
+ ApplySqlHighlight();
+ };
}
- private void btnExecuteSqlScript_Click(object sender, EventArgs e)
+ private void FDebug_Load(object sender, EventArgs e)
{
- string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
-
- string sqlScript = RTXT_Sql.Text;
-
-
- using (SQLiteConnection debugCon = new SQLiteConnection(connectionStringToLocalDB))
+ UiThemeHelper.ApplyToControlTree(this);
+ Text = "SQL-отладка";
+ lblDbPath.Text = AppConfig.SqliteDbPath;
+ RTXT_Sql.DetectUrls = false;
+ RTXT_Sql.AcceptsTab = true;
+ RTXT_Sql.TextChanged += RTXT_Sql_TextChanged;
+ BuildTemplates();
+ listTemplates.Items.Clear();
+ foreach (var template in _templates)
{
- try
+ listTemplates.Items.Add(template.Title);
+ }
+
+ SetStatus("Готово. Выберите шаблон или введите свой SQL.");
+ ApplySqlHighlight();
+ FormClosed += (_, __) =>
+ {
+ _highlightTimer.Stop();
+ _highlightTimer.Dispose();
+ };
+ }
+
+ private void RTXT_Sql_TextChanged(object sender, EventArgs e)
+ {
+ if (_highlighting)
+ {
+ return;
+ }
+
+ _highlightTimer.Stop();
+ _highlightTimer.Start();
+ }
+
+ private void ApplySqlHighlight()
+ {
+ if (_highlighting || RTXT_Sql == null || RTXT_Sql.IsDisposed)
+ {
+ return;
+ }
+
+ _highlighting = true;
+ try
+ {
+ SqlSyntaxHighlighter.Apply(RTXT_Sql, ThemeManager.IsDarkProductivityActive);
+ }
+ finally
+ {
+ _highlighting = false;
+ }
+ }
+
+ private void BuildTemplates()
+ {
+ _templates.Clear();
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Счётчик прайса",
+ Sql = "SELECT COUNT(*) AS [Позиций] FROM PriceList;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Прайс по поставщикам",
+ Sql = @"SELECT SupplierName AS [Поставщик],
+ COUNT(*) AS [Позиций],
+ MIN(Price) AS [МинЦена],
+ MAX(Price) AS [МаксЦена]
+FROM PriceList
+GROUP BY SupplierName
+ORDER BY COUNT(*) DESC;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Заказы по статусам",
+ Sql = @"SELECT OrderState AS [Статус], COUNT(*) AS [Кол-во]
+FROM Orders
+GROUP BY OrderState
+ORDER BY COUNT(*) DESC;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Грузополучатели + LocationId",
+ Sql = @"SELECT codeConsignees AS [Код],
+ ConsigneesName AS [Аптека],
+ ConsigneesAddress AS [Адрес],
+ ifnull(LocationId, '') AS [LocationId],
+ ConsigneesUseAsDefault AS [ПоУмолчанию]
+FROM Consignees
+ORDER BY ConsigneesUseAsDefault DESC, ConsigneesName;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Корзина TempOrderItems",
+ Sql = @"SELECT COUNT(*) AS [Строк],
+ ifnull(SUM(CAST(SummaZakaza AS REAL)), 0) AS [Сумма]
+FROM TempOrderItems;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Поиск по наименованию",
+ Sql = @"SELECT DrugName, SupplierName, Price, Quantity, supplier_price_id
+FROM PriceList
+WHERE DrugName LIKE '%' || @q || '%'
+ORDER BY DrugName
+LIMIT 200;",
+ Parameters = new[] { "q" }
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Последние заказы",
+ Sql = @"SELECT Id_Order, OrderNumber, OrderDate, SupplierName, ConsigneeName, OrderState, SummaZakaza
+FROM Orders
+ORDER BY Id_Order DESC
+LIMIT 50;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Список таблиц",
+ Sql = @"SELECT name AS [Таблица], type AS [Тип]
+FROM sqlite_master
+WHERE type IN ('table', 'view')
+ORDER BY name;"
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Позиции заказа",
+ Sql = @"SELECT oi.Id_Order, oi.DrugName, oi.Zakaz, oi.Price, oi.supplier_price_id, o.ConsigneeName, o.OrderState
+FROM OrderItems oi
+INNER JOIN Orders o ON o.Id_Order = oi.Id_Order
+WHERE oi.Id_Order = @orderId
+ORDER BY oi.DrugName;",
+ Parameters = new[] { "orderId" }
+ });
+ _templates.Add(new SqlTemplate
+ {
+ Title = "Очистить корзину (DML)",
+ Sql = "DELETE FROM TempOrderItems;"
+ });
+ }
+
+ private void listTemplates_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (listTemplates.SelectedIndex < 0 || listTemplates.SelectedIndex >= _templates.Count)
+ {
+ return;
+ }
+
+ var template = _templates[listTemplates.SelectedIndex];
+ RTXT_Sql.Text = template.Sql;
+ RebuildParamEditors(template.Parameters);
+ SetStatus($"Шаблон: {template.Title}");
+ }
+
+ private void RebuildParamEditors(string[] parameters)
+ {
+ panelParams.Controls.Clear();
+ _paramBoxes.Clear();
+
+ if (parameters == null || parameters.Length == 0)
+ {
+ var empty = new Label
{
- debugCon.Open();
- SQLiteCommand cmdDebug = new SQLiteCommand(sqlScript, debugCon);
- cmdDebug.ExecuteNonQuery();
- }
- catch (Exception ex)
+ Text = "Параметры не требуются. Можно писать {{name}} или @name в SQL.",
+ AutoSize = true,
+ MaximumSize = new Size(panelParams.Width - 20, 0),
+ Location = new Point(8, 8)
+ };
+ panelParams.Controls.Add(empty);
+ return;
+ }
+
+ int y = 8;
+ foreach (var name in parameters)
+ {
+ var lbl = new Label
{
- MessageBox.Show($"При выполнении запроса возникла ошибка. Текст ошибки:\n{ex.ToString()}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- finally
+ Text = "@" + name,
+ AutoSize = true,
+ Location = new Point(8, y + 4)
+ };
+ var box = new TextBox
{
- debugCon.Close();
- }
+ Name = "param_" + name,
+ Width = Math.Max(180, panelParams.Width - 140),
+ Location = new Point(120, y)
+ };
+ panelParams.Controls.Add(lbl);
+ panelParams.Controls.Add(box);
+ _paramBoxes[name] = box;
+ y += 34;
}
}
private void btnCleanSqlScript_Click(object sender, EventArgs e)
{
- RTXT_Sql.Text = "";
+ RTXT_Sql.Text = string.Empty;
+ dgvSelectCommand.DataSource = null;
+ RebuildParamEditors(null);
+ SetStatus("Очищено.");
+ }
+
+ private void btnExecuteSqlScript_Click(object sender, EventArgs e)
+ {
+ ExecuteSql(forceSelect: false);
}
private void btnExecuteSelectSqlScript_Click(object sender, EventArgs e)
{
- string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
+ ExecuteSql(forceSelect: true);
+ }
- string sqlScript = RTXT_Sql.Text;
+ private void ExecuteSql(bool forceSelect)
+ {
+ var sql = (RTXT_Sql.Text ?? string.Empty).Trim();
+ if (string.IsNullOrWhiteSpace(sql))
+ {
+ SetStatus("Пустой SQL.");
+ return;
+ }
- using (SQLiteConnection debugSelectCon = new SQLiteConnection(connectionStringToLocalDB))
+ sql = ApplyMustachePlaceholders(sql);
+ bool isSelect = forceSelect || LooksLikeSelect(sql);
+ var sw = Stopwatch.StartNew();
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
{
try
{
- debugSelectCon.Open();
- SQLiteCommand cmdSelectSqlCommand = new SQLiteCommand(sqlScript, debugSelectCon);
- SQLiteDataAdapter daSelectDataAdapter = new SQLiteDataAdapter(cmdSelectSqlCommand);
- DataTable tableSelect = new DataTable();
- daSelectDataAdapter.Fill(tableSelect);
- dgvSelectCommand.DataSource = tableSelect;
+ connection.Open();
+ using (var command = new SQLiteCommand(sql, connection))
+ {
+ BindParameters(command, sql);
+
+ if (isSelect)
+ {
+ using (var adapter = new SQLiteDataAdapter(command))
+ {
+ var table = new DataTable();
+ adapter.Fill(table);
+ dgvSelectCommand.DataSource = table;
+ sw.Stop();
+ SetStatus($"SELECT: {table.Rows.Count} строк за {sw.ElapsedMilliseconds} мс");
+ }
+ }
+ else
+ {
+ int affected = command.ExecuteNonQuery();
+ dgvSelectCommand.DataSource = null;
+ sw.Stop();
+ SetStatus($"Выполнено. Затронуто строк: {affected}. {sw.ElapsedMilliseconds} мс");
+ }
+ }
}
- catch(Exception ex)
+ catch (Exception ex)
{
- MessageBox.Show($"Возникла ошибка при заполнении таблицы данными.\nТекст ошибки:\n{ex.ToString()}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- finally
- {
- debugSelectCon.Close();
+ sw.Stop();
+ SetStatus("Ошибка: " + ex.Message);
+ MessageBox.Show($"При выполнении запроса возникла ошибка.\n{ex}", "SQL-отладка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
+
+ private static bool LooksLikeSelect(string sql)
+ {
+ var trimmed = sql.TrimStart();
+ return trimmed.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)
+ || trimmed.StartsWith("PRAGMA", StringComparison.OrdinalIgnoreCase)
+ || trimmed.StartsWith("WITH", StringComparison.OrdinalIgnoreCase)
+ || trimmed.StartsWith("EXPLAIN", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private string ApplyMustachePlaceholders(string sql)
+ {
+ return Regex.Replace(sql, @"\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}", match =>
+ {
+ var name = match.Groups[1].Value;
+ if (_paramBoxes.TryGetValue(name, out var box))
+ {
+ return (box.Text ?? string.Empty).Replace("'", "''");
+ }
+
+ return match.Value;
+ });
+ }
+
+ private void BindParameters(SQLiteCommand command, string sql)
+ {
+ var names = new HashSet(StringComparer.OrdinalIgnoreCase);
+ foreach (Match match in Regex.Matches(sql, @"@([A-Za-z_][A-Za-z0-9_]*)"))
+ {
+ names.Add(match.Groups[1].Value);
+ }
+
+ foreach (var name in names)
+ {
+ object value = string.Empty;
+ if (_paramBoxes.TryGetValue(name, out var box))
+ {
+ value = box.Text ?? string.Empty;
+ }
+
+ command.Parameters.AddWithValue("@" + name, value);
+ }
+
+ // Параметры из панели, даже если в SQL их ещё нет (удобно для шаблонов).
+ foreach (var pair in _paramBoxes)
+ {
+ if (!names.Contains(pair.Key))
+ {
+ command.Parameters.AddWithValue("@" + pair.Key, pair.Value.Text ?? string.Empty);
+ }
+ }
+ }
+
+ private void SetStatus(string text)
+ {
+ lblStatus.Text = text;
+ }
+
+ private void btnScanParams_Click(object sender, EventArgs e)
+ {
+ var sql = RTXT_Sql.Text ?? string.Empty;
+ var names = Regex.Matches(sql, @"@([A-Za-z_][A-Za-z0-9_]*)")
+ .Cast()
+ .Select(m => m.Groups[1].Value)
+ .Concat(Regex.Matches(sql, @"\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}")
+ .Cast()
+ .Select(m => m.Groups[1].Value))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+
+ var existing = _paramBoxes.ToDictionary(p => p.Key, p => p.Value.Text, StringComparer.OrdinalIgnoreCase);
+ RebuildParamEditors(names);
+ foreach (var pair in existing)
+ {
+ if (_paramBoxes.TryGetValue(pair.Key, out var box))
+ {
+ box.Text = pair.Value;
+ }
+ }
+
+ SetStatus(names.Length == 0 ? "Параметры в SQL не найдены." : $"Найдено параметров: {names.Length}");
+ }
}
}
diff --git a/src/ElectronicPharmacy/HelpForms/HFConsignees.cs b/src/ElectronicPharmacy/HelpForms/HFConsignees.cs
index 04fcd84..6d719c7 100644
--- a/src/ElectronicPharmacy/HelpForms/HFConsignees.cs
+++ b/src/ElectronicPharmacy/HelpForms/HFConsignees.cs
@@ -1,21 +1,16 @@
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using Электронная_Фармация.Classes;
using System.Data.SQLite;
+using System.Windows.Forms;
using Электронная_Фармация;
+using Электронная_Фармация.Classes;
namespace Электронная_Фармация.HelpForms
{
public partial class HFConsignees : Form
{
- public ElectroPharmacy ParentForm { get; set; }
+ public new ElectroPharmacy ParentForm { get; set; }
+
public HFConsignees()
{
InitializeComponent();
@@ -31,7 +26,12 @@ namespace Электронная_Фармация.HelpForms
{
conConsigneesList.Open();
- string qShowMeConsigneesList = "select ConsigneesName as [Грузополучатель] from Consignees order by ConsigneesUseAsDefault desc";
+ string qShowMeConsigneesList = @"
+select ConsigneesName as [Грузополучатель],
+ ifnull(LocationId, '') as [Location ID],
+ ConsigneesAddress as [Адрес]
+from Consignees
+order by ConsigneesUseAsDefault desc, ConsigneesName";
SQLiteCommand cmdShowMeConsigneesList = new SQLiteCommand(qShowMeConsigneesList, conConsigneesList);
SQLiteDataAdapter daConsigneesList = new SQLiteDataAdapter(cmdShowMeConsigneesList);
@@ -43,16 +43,13 @@ namespace Электронная_Фармация.HelpForms
{
UiDialogs.ShowError($"При загрузке списка получателей возникла ошибка.\n{ex}", "Ошибка", this);
}
- finally
- {
- conConsigneesList.Close();
- }
}
}
private void btnCloseThis_Click(object sender, EventArgs e)
{
- this.Close();
+ DialogResult = DialogResult.Cancel;
+ Close();
}
private void btnConfirmConsignee_Click(object sender, EventArgs e)
@@ -63,7 +60,27 @@ namespace Электронная_Фармация.HelpForms
return;
}
- string consigneesName = dgvConsignees.CurrentRow.Cells[0].Value.ToString();
+ string consigneesName = dgvConsignees.CurrentRow.Cells[0].Value?.ToString();
+ if (string.IsNullOrWhiteSpace(consigneesName))
+ {
+ UiDialogs.ShowInfo("Выберите грузополучателя из списка.", "Грузополучатель", this);
+ return;
+ }
+
+ var locationId = ConsigneeHelper.GetLocationIdByName(consigneesName);
+ if (string.IsNullOrWhiteSpace(locationId))
+ {
+ var ask = UiDialogs.ConfirmYesNo(
+ $"У аптеки «{consigneesName}» не задан Location ID.\nПродолжить без него? Заказы этой аптеки нельзя будет отправить, пока ID не заполните.",
+ "Location ID",
+ this);
+ if (ask != DialogResult.Yes)
+ {
+ return;
+ }
+ }
+
+ ConsigneeHelper.SetActiveConsignee(consigneesName);
ParentForm?.showMePriceList(consigneesName);
DialogResult = DialogResult.OK;
Close();
diff --git a/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs b/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs
index 521aa05..a9f8ed2 100644
--- a/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs
+++ b/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs
@@ -149,68 +149,202 @@ namespace Электронная_Фармация.HelpForms
? null
: Settings.Default.RegionId;
- // Один крупный запрос (OFFSET на больших страницах валит сервер).
- const int pageSize = 200000;
- const int maxPages = 100;
+ const int supplierPageLimit = 100000;
+ const int discoveryLimit = 50000;
var combined = new PriceSummaryResponse { Summary = new List() };
var transferProgress = new Progress(ReportTransferProgress);
- for (int page = 0; page < maxPages; page++)
+ var supplierIds = new List(ConsigneeHelper.GetKnownSupplierIds());
+ if (supplierIds.Count == 0)
{
- int offset = page * pageSize;
+ AppendStatus("Первая загрузка: определяю список поставщиков...");
+ SetOverallProgress(3, "Определение поставщиков...");
+ var discovery = await GetPriceSummaryWithAuthRetryAsync(
+ client,
+ supplierId: null,
+ regionId: regionId,
+ limit: discoveryLimit,
+ offset: 0,
+ transferProgress: transferProgress);
+
+ if (discovery?.MarkupPercent.HasValue == true)
+ {
+ combined.MarkupPercent = discovery.MarkupPercent;
+ }
+
+ var discovered = new HashSet(StringComparer.OrdinalIgnoreCase);
+ if (discovery?.Summary != null)
+ {
+ foreach (var item in discovery.Summary)
+ {
+ if (!string.IsNullOrWhiteSpace(item.SupplierId))
+ {
+ discovered.Add(item.SupplierId.Trim());
+ }
+ }
+ }
+
+ supplierIds.AddRange(discovered);
+ AppendStatus($"Найдено поставщиков: {supplierIds.Count}");
+ }
+
+ if (supplierIds.Count == 0)
+ {
+ AppendStatus("Поставщики не найдены, пробую полный запрос...");
+ var fallback = await GetPriceSummaryWithAuthRetryAsync(
+ client,
+ supplierId: null,
+ regionId: regionId,
+ limit: 200000,
+ offset: 0,
+ transferProgress: transferProgress);
+ if (fallback?.MarkupPercent.HasValue == true)
+ {
+ combined.MarkupPercent = fallback.MarkupPercent;
+ }
+
+ if (fallback?.Summary != null)
+ {
+ combined.Summary.AddRange(fallback.Summary);
+ }
+
+ return combined;
+ }
+
+ int totalSuppliers = supplierIds.Count;
+ for (int i = 0; i < totalSuppliers; i++)
+ {
+ var supplierId = supplierIds[i];
+ int supplierProgressStart = 3 + (int)((DownloadPhaseEnd - 3) * ((double)i / totalSuppliers));
+ int supplierProgressEnd = 3 + (int)((DownloadPhaseEnd - 3) * ((double)(i + 1) / totalSuppliers));
SetOverallProgress(
- Math.Min(DownloadPhaseEnd - 1, 3 + page),
- page == 0 ? "Скачивание прайса с сервера..." : $"Скачивание страницы {page + 1}...");
+ supplierProgressStart,
+ $"Поставщик {i + 1}/{totalSuppliers}...");
+ AppendStatus($"Загрузка поставщика {i + 1}/{totalSuppliers}: {supplierId}");
PriceSummaryResponse chunk;
try
{
- chunk = await client.GetPriceSummaryAsync(
- supplierId: null,
+ chunk = await GetPriceSummaryWithAuthRetryAsync(
+ client,
+ supplierId: supplierId,
regionId: regionId,
- limit: pageSize,
- offset: offset,
+ limit: supplierPageLimit,
+ offset: 0,
transferProgress: transferProgress);
}
- catch (UnauthorizedAccessException)
+ catch (Exception ex)
{
- AppendStatus("Токен недействителен, повторная авторизация...");
- SetOverallProgress(2, "Повторная авторизация...");
- Settings.Default.stringToken = string.Empty;
- Settings.Default.Save();
- client.SetToken(null);
- await LoginAndSaveTokenAsync(client);
- SetOverallProgress(3, "Повторная загрузка прайса...");
- chunk = await client.GetPriceSummaryAsync(
- supplierId: null,
- regionId: regionId,
- limit: pageSize,
- offset: offset,
- transferProgress: transferProgress);
+ AppendStatus($"Ошибка поставщика {supplierId}: {ex.Message}");
+ AppDebugLog.Error("Download", $"Ошибка загрузки прайса поставщика {supplierId}", ex);
+ continue;
}
- int got = chunk?.Summary?.Count ?? 0;
- if (page == 0 && chunk?.MarkupPercent.HasValue == true)
+ if (i == 0 && chunk?.MarkupPercent.HasValue == true && !combined.MarkupPercent.HasValue)
{
combined.MarkupPercent = chunk.MarkupPercent;
}
+ int got = chunk?.Summary?.Count ?? 0;
if (got > 0)
{
combined.Summary.AddRange(chunk.Summary);
- AppendStatus($"Загружено {combined.Summary.Count} позиций...");
}
- if (got < pageSize)
+ SetOverallProgress(
+ Math.Max(supplierProgressStart, supplierProgressEnd - 1),
+ $"Поставщик {i + 1}/{totalSuppliers}: +{got}, всего {combined.Summary.Count}");
+ AppendStatus($"Поставщик {i + 1}/{totalSuppliers}: получено {got}, всего {combined.Summary.Count}");
+ }
+
+ // Лёгкий discovery новых поставщиков, которых ещё не было в локальной базе.
+ try
+ {
+ AppendStatus("Проверка новых поставщиков...");
+ var probe = await GetPriceSummaryWithAuthRetryAsync(
+ client,
+ supplierId: null,
+ regionId: regionId,
+ limit: 5000,
+ offset: 0,
+ transferProgress: null);
+ var known = new HashSet(supplierIds, StringComparer.OrdinalIgnoreCase);
+ var newcomers = new List();
+ if (probe?.Summary != null)
{
- break;
+ foreach (var item in probe.Summary)
+ {
+ if (!string.IsNullOrWhiteSpace(item.SupplierId) && known.Add(item.SupplierId.Trim()))
+ {
+ newcomers.Add(item.SupplierId.Trim());
+ }
+ }
}
+
+ for (int i = 0; i < newcomers.Count; i++)
+ {
+ var supplierId = newcomers[i];
+ AppendStatus($"Новый поставщик {i + 1}/{newcomers.Count}: {supplierId}");
+ var chunk = await GetPriceSummaryWithAuthRetryAsync(
+ client,
+ supplierId: supplierId,
+ regionId: regionId,
+ limit: supplierPageLimit,
+ offset: 0,
+ transferProgress: transferProgress);
+ int got = chunk?.Summary?.Count ?? 0;
+ if (got > 0)
+ {
+ combined.Summary.AddRange(chunk.Summary);
+ }
+
+ AppendStatus($"Новый поставщик: +{got}, всего {combined.Summary.Count}");
+ }
+ }
+ catch (Exception ex)
+ {
+ AppendStatus($"Проверка новых поставщиков пропущена: {ex.Message}");
+ AppDebugLog.Error("Download", "Ошибка discovery новых поставщиков", ex);
}
return combined;
}
+ private async Task GetPriceSummaryWithAuthRetryAsync(
+ ApiClient client,
+ string supplierId,
+ string regionId,
+ int limit,
+ int offset,
+ IProgress transferProgress)
+ {
+ try
+ {
+ return await client.GetPriceSummaryAsync(
+ supplierId: supplierId,
+ regionId: regionId,
+ limit: limit,
+ offset: offset,
+ transferProgress: transferProgress);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ AppendStatus("Токен недействителен, повторная авторизация...");
+ SetOverallProgress(2, "Повторная авторизация...");
+ Settings.Default.stringToken = string.Empty;
+ Settings.Default.Save();
+ client.SetToken(null);
+ await LoginAndSaveTokenAsync(client);
+ return await client.GetPriceSummaryAsync(
+ supplierId: supplierId,
+ regionId: regionId,
+ limit: limit,
+ offset: offset,
+ transferProgress: transferProgress);
+ }
+ }
+
private static void ApplyClientMarkupSettings(PriceSummaryResponse response)
{
if (response?.MarkupPercent == null)
@@ -379,29 +513,47 @@ VALUES
}
using (var transaction = sqliteCon.BeginTransaction())
+ using (var cmd = new SQLiteCommand(commandToInsert, sqliteCon, transaction))
{
+ var pGuidEs = cmd.Parameters.Add("@guid_es", DbType.String);
+ var pEsCode = cmd.Parameters.Add("@es_code", DbType.String);
+ var pSupplierId = cmd.Parameters.Add("@supplier_id", DbType.String);
+ var pDrugName = cmd.Parameters.Add("@DrugName", DbType.String);
+ var pSupplierName = cmd.Parameters.Add("@SupplierName", DbType.String);
+ var pPrice = cmd.Parameters.Add("@Price", DbType.String);
+ var pQuantity = cmd.Parameters.Add("@Quantity", DbType.String);
+ var pExpiry = cmd.Parameters.Add("@ExpiryPeriod", DbType.String);
+ var pDescription = cmd.Parameters.Add("@Description", DbType.String);
+ var pZakaz = cmd.Parameters.Add("@Zakaz", DbType.String);
+ var pSumma = cmd.Parameters.Add("@SummaZakaza", DbType.String);
+ var pSupplierPriceId = cmd.Parameters.Add("@supplier_price_id", DbType.String);
+ var pTradeName = cmd.Parameters.Add("@TradeName", DbType.String);
+ var pDosage = cmd.Parameters.Add("@Dosage", DbType.String);
+ var pMarkup = cmd.Parameters.Add("@MarkupPercent", DbType.Object);
+
int saved = 0;
+ bool hasTradeName = tablePrice.Columns.Contains("TradeName");
+ bool hasDosage = tablePrice.Columns.Contains("Dosage");
+ bool hasMarkup = tablePrice.Columns.Contains("MarkupPercent");
+
foreach (DataRow row in tablePrice.Rows)
{
- using (var cmd = new SQLiteCommand(commandToInsert, sqliteCon, transaction))
- {
- cmd.Parameters.AddWithValue("@guid_es", row["guid_es"]);
- cmd.Parameters.AddWithValue("@es_code", row["es_code"]);
- cmd.Parameters.AddWithValue("@supplier_id", row["supplier_id"]);
- cmd.Parameters.AddWithValue("@DrugName", row["DrugName"]);
- cmd.Parameters.AddWithValue("@SupplierName", row["SupplierName"]);
- cmd.Parameters.AddWithValue("@Price", row["Price"]);
- cmd.Parameters.AddWithValue("@Quantity", row["Quantity"]);
- cmd.Parameters.AddWithValue("@ExpiryPeriod", row["ExpiryPeriod"]);
- cmd.Parameters.AddWithValue("@Description", row["Description"]);
- 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.Parameters.AddWithValue("@MarkupPercent", row.Table.Columns.Contains("MarkupPercent") ? row["MarkupPercent"] : DBNull.Value);
- cmd.ExecuteNonQuery();
- }
+ pGuidEs.Value = row["guid_es"] ?? DBNull.Value;
+ pEsCode.Value = row["es_code"] ?? DBNull.Value;
+ pSupplierId.Value = row["supplier_id"] ?? DBNull.Value;
+ pDrugName.Value = row["DrugName"] ?? DBNull.Value;
+ pSupplierName.Value = row["SupplierName"] ?? DBNull.Value;
+ pPrice.Value = row["Price"] ?? DBNull.Value;
+ pQuantity.Value = row["Quantity"] ?? DBNull.Value;
+ pExpiry.Value = row["ExpiryPeriod"] ?? DBNull.Value;
+ pDescription.Value = row["Description"] ?? DBNull.Value;
+ pZakaz.Value = row["Zakaz"] ?? DBNull.Value;
+ pSumma.Value = row["SummaZakaza"] ?? DBNull.Value;
+ pSupplierPriceId.Value = row["supplier_price_id"] ?? DBNull.Value;
+ pTradeName.Value = hasTradeName ? (row["TradeName"] ?? DBNull.Value) : DBNull.Value;
+ pDosage.Value = hasDosage ? (row["Dosage"] ?? DBNull.Value) : DBNull.Value;
+ pMarkup.Value = hasMarkup ? (row["MarkupPercent"] ?? DBNull.Value) : DBNull.Value;
+ cmd.ExecuteNonQuery();
saved++;
if (progress != null && (saved % 250 == 0 || saved == total))
diff --git a/src/ElectronicPharmacy/HelpForms/HF_Registration.cs b/src/ElectronicPharmacy/HelpForms/HF_Registration.cs
index cf06cb1..3185a7f 100644
--- a/src/ElectronicPharmacy/HelpForms/HF_Registration.cs
+++ b/src/ElectronicPharmacy/HelpForms/HF_Registration.cs
@@ -16,10 +16,6 @@ namespace Электронная_Фармация.HelpForms
Shown += (_, __) => WindowChromeHelper.ApplyTitleBarTheme(this, ThemeManager.Colors);
}
- string login = Settings.Default.stringLogin.ToString();
- string password = Settings.Default.stringPassword.ToString();
- string doWeHaveAToken = Settings.Default.stringToken.ToString();
-
private void btnCloseThis_Click(object sender, EventArgs e)
{
Close();
@@ -30,8 +26,8 @@ namespace Электронная_Фармация.HelpForms
btnConfirmRegistration.Enabled = false;
btnCloseThis.Enabled = false;
UseWaitCursor = true;
- string strLogin = txtLogin.Text;
- string strPassword = txtPassword.Text;
+ string strLogin = (txtLogin.Text ?? string.Empty).Trim();
+ string strPassword = txtPassword.Text ?? string.Empty;
if (strLogin.Length > 0 && strPassword.Length > 0)
{
@@ -40,7 +36,7 @@ namespace Электронная_Фармация.HelpForms
: Settings.Default.ApiBaseUrl;
LoginRequest loginRequest = new LoginRequest();
- loginRequest.Username = strLogin.Trim();
+ loginRequest.Username = strLogin;
loginRequest.Password = strPassword.Trim();
var client = new ApiClient(AppConfig.NormalizeApiBaseUrl(apiUrl));
@@ -50,8 +46,9 @@ namespace Электронная_Фармация.HelpForms
{
string token = await client.LoginAsync(loginRequest.Username, loginRequest.Password);
+ // Сохраняем только логин и токен; пароль в настройках не храним.
Settings.Default.stringLogin = strLogin;
- Settings.Default.stringPassword = strPassword;
+ Settings.Default.stringPassword = string.Empty;
Settings.Default.stringToken = token;
Settings.Default.ApiBaseUrl = client.BaseUrl;
Settings.Default.Save();
@@ -59,13 +56,24 @@ namespace Электронная_Фармация.HelpForms
AppDebugLog.Info("Auth", $"Авторизация успешна. token={AppDebugLog.MaskToken(token)}");
ToastNotification.ShowSuccess("Авторизация успешно выполнена");
- // После входа обязательно запрашиваем Location ID для отправки заказов.
- if (!HF_LocationId.PromptAndSave(this))
+ // Fallback Location ID (на аптеку задаётся у грузополучателя).
+ if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
{
- ToastNotification.ShowCustom(
- "Location ID не указан. Отправка заказов будет недоступна, пока его не заполните.",
- System.Drawing.Color.DarkOrange,
- System.Drawing.Color.White);
+ if (HF_LocationId.PromptAndSave(this))
+ {
+ ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
+ }
+ else
+ {
+ ToastNotification.ShowCustom(
+ "Location ID не указан. Задайте его у грузополучателя или в настройках.",
+ System.Drawing.Color.DarkOrange,
+ System.Drawing.Color.White);
+ }
+ }
+ else
+ {
+ ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
}
DialogResult = DialogResult.OK;
@@ -97,9 +105,10 @@ namespace Электронная_Фармация.HelpForms
{
UiThemeHelper.ApplyToControlTree(this);
WindowChromeHelper.ApplyDialogChrome(this, panel1, label3);
- txtLogin.Text = login;
- txtPassword.Text = password;
- checkTokenGained.Checked = doWeHaveAToken.Length > 0;
+ // Только последний успешный логин; пароль пользователь вводит каждый раз.
+ txtLogin.Text = Settings.Default.stringLogin ?? string.Empty;
+ txtPassword.Text = string.Empty;
+ checkTokenGained.Checked = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
}
}
}
diff --git a/src/ElectronicPharmacy/HelpForms/HF_Settings.Designer.cs b/src/ElectronicPharmacy/HelpForms/HF_Settings.Designer.cs
index d61c6aa..3b93f40 100644
--- a/src/ElectronicPharmacy/HelpForms/HF_Settings.Designer.cs
+++ b/src/ElectronicPharmacy/HelpForms/HF_Settings.Designer.cs
@@ -59,7 +59,7 @@ namespace Электронная_Фармация.HelpForms
this.lblHint.Name = "lblHint";
this.lblHint.Size = new System.Drawing.Size(512, 46);
this.lblHint.TabIndex = 1;
- this.lblHint.Text = "Здесь можно задать торговую точку, адрес API и папку для экспорта накладных.";
+ this.lblHint.Text = "Location ID ниже — запасной. Для каждой аптеки задайте свой ID в «Грузополучатели». Также укажите API и папку накладных.";
//
// lblLocationId
//