Add multi-pharmacy LocationId, batched price download, and SQL debug tools.
Persist last successful login only, show active pharmacy in the status bar, and keep settings compact at the bottom-right. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
babc067863
commit
458b70e036
218
src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
Normal file
218
src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
Normal file
@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
|
||||
namespace Электронная_Фармация.Classes
|
||||
{
|
||||
/// <summary>
|
||||
/// Аптека = грузополучатель + LocationId. Прайс общий, заказы уходят с location_id выбранной аптеки.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Помечает аптеку активной и синхронизирует AppConfig.LocationId.
|
||||
/// </summary>
|
||||
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<string> GetKnownSupplierIds()
|
||||
{
|
||||
var result = new List<string>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<BuyerOrderRequest> 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<BuyerOrderRequest> 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<BuyerOrderRequest> FetchDataFromSql(string onlyOrderId)
|
||||
{
|
||||
var orders = new Dictionary<string, BuyerOrderRequest>();
|
||||
string locationId = AppConfig.LocationId;
|
||||
var locationCache = new Dictionary<string, string>(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<string, string> 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<BuyerOrderRequest> 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<bool> SendOrderAsync(BuyerOrderRequest order)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
|
||||
153
src/ElectronicPharmacy/Classes/SqlSyntaxHighlighter.cs
Normal file
153
src/ElectronicPharmacy/Classes/SqlSyntaxHighlighter.cs
Normal file
@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Электронная_Фармация.Classes
|
||||
{
|
||||
/// <summary>
|
||||
/// Лёгкая подсветка SQL в RichTextBox (ключевые слова, строки, комментарии, числа, параметры).
|
||||
/// </summary>
|
||||
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<string> Keywords =
|
||||
new System.Collections.Generic.HashSet<string>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,6 +104,8 @@
|
||||
<Compile Include="Classes\ApiClient.cs" />
|
||||
<Compile Include="Classes\AppConfig.cs" />
|
||||
<Compile Include="Classes\AppDebugLog.cs" />
|
||||
<Compile Include="Classes\ConsigneeHelper.cs" />
|
||||
<Compile Include="Classes\SqlSyntaxHighlighter.cs" />
|
||||
<Compile Include="Classes\DataSender.cs" />
|
||||
<Compile Include="Classes\ErrorResponse.cs" />
|
||||
<Compile Include="Classes\InvoiceExportService.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} | Аптека: не выбрана";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
97
src/ElectronicPharmacy/Forms/FConsignees.Designer.cs
generated
97
src/ElectronicPharmacy/Forms/FConsignees.Designer.cs
generated
@ -2,15 +2,8 @@
|
||||
{
|
||||
partial class FConsignees
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
@ -22,13 +15,12 @@
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
293
src/ElectronicPharmacy/Forms/FDebug.Designer.cs
generated
293
src/ElectronicPharmacy/Forms/FDebug.Designer.cs
generated
@ -2,15 +2,8 @@
|
||||
{
|
||||
partial class FDebug
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
@ -22,115 +15,267 @@
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -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<SqlTemplate> _templates = new List<SqlTemplate>();
|
||||
private readonly Dictionary<string, TextBox> _paramBoxes = new Dictionary<string, TextBox>(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)
|
||||
{
|
||||
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
|
||||
{
|
||||
debugCon.Open();
|
||||
SQLiteCommand cmdDebug = new SQLiteCommand(sqlScript, debugCon);
|
||||
cmdDebug.ExecuteNonQuery();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"При выполнении запроса возникла ошибка. Текст ошибки:\n{ex.ToString()}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
SqlSyntaxHighlighter.Apply(RTXT_Sql, ThemeManager.IsDarkProductivityActive);
|
||||
}
|
||||
finally
|
||||
{
|
||||
debugCon.Close();
|
||||
_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
|
||||
{
|
||||
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
|
||||
{
|
||||
Text = "@" + name,
|
||||
AutoSize = true,
|
||||
Location = new Point(8, y + 4)
|
||||
};
|
||||
var box = new TextBox
|
||||
{
|
||||
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)
|
||||
{
|
||||
MessageBox.Show($"Возникла ошибка при заполнении таблицы данными.\nТекст ошибки:\n{ex.ToString()}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
sw.Stop();
|
||||
SetStatus("Ошибка: " + ex.Message);
|
||||
MessageBox.Show($"При выполнении запроса возникла ошибка.\n{ex}", "SQL-отладка", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
}
|
||||
}
|
||||
|
||||
private static bool LooksLikeSelect(string sql)
|
||||
{
|
||||
debugSelectCon.Close();
|
||||
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<string>(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<Match>()
|
||||
.Select(m => m.Groups[1].Value)
|
||||
.Concat(Regex.Matches(sql, @"\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}")
|
||||
.Cast<Match>()
|
||||
.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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -149,27 +149,182 @@ 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<PriceSummaryItem>() };
|
||||
var transferProgress = new Progress<HttpTransferProgress>(ReportTransferProgress);
|
||||
|
||||
for (int page = 0; page < maxPages; page++)
|
||||
var supplierIds = new List<string>(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<string>(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(
|
||||
chunk = await GetPriceSummaryWithAuthRetryAsync(
|
||||
client,
|
||||
supplierId: supplierId,
|
||||
regionId: regionId,
|
||||
limit: supplierPageLimit,
|
||||
offset: 0,
|
||||
transferProgress: transferProgress);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendStatus($"Ошибка поставщика {supplierId}: {ex.Message}");
|
||||
AppDebugLog.Error("Download", $"Ошибка загрузки прайса поставщика {supplierId}", ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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: pageSize,
|
||||
limit: 5000,
|
||||
offset: 0,
|
||||
transferProgress: null);
|
||||
var known = new HashSet<string>(supplierIds, StringComparer.OrdinalIgnoreCase);
|
||||
var newcomers = new List<string>();
|
||||
if (probe?.Summary != null)
|
||||
{
|
||||
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<PriceSummaryResponse> GetPriceSummaryWithAuthRetryAsync(
|
||||
ApiClient client,
|
||||
string supplierId,
|
||||
string regionId,
|
||||
int limit,
|
||||
int offset,
|
||||
IProgress<HttpTransferProgress> transferProgress)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await client.GetPriceSummaryAsync(
|
||||
supplierId: supplierId,
|
||||
regionId: regionId,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transferProgress: transferProgress);
|
||||
}
|
||||
@ -181,34 +336,13 @@ namespace Электронная_Фармация.HelpForms
|
||||
Settings.Default.Save();
|
||||
client.SetToken(null);
|
||||
await LoginAndSaveTokenAsync(client);
|
||||
SetOverallProgress(3, "Повторная загрузка прайса...");
|
||||
chunk = await client.GetPriceSummaryAsync(
|
||||
supplierId: null,
|
||||
return await client.GetPriceSummaryAsync(
|
||||
supplierId: supplierId,
|
||||
regionId: regionId,
|
||||
limit: pageSize,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
transferProgress: transferProgress);
|
||||
}
|
||||
|
||||
int got = chunk?.Summary?.Count ?? 0;
|
||||
if (page == 0 && chunk?.MarkupPercent.HasValue == true)
|
||||
{
|
||||
combined.MarkupPercent = chunk.MarkupPercent;
|
||||
}
|
||||
|
||||
if (got > 0)
|
||||
{
|
||||
combined.Summary.AddRange(chunk.Summary);
|
||||
AppendStatus($"Загружено {combined.Summary.Count} позиций...");
|
||||
}
|
||||
|
||||
if (got < pageSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
private static void ApplyClientMarkupSettings(PriceSummaryResponse response)
|
||||
@ -379,29 +513,47 @@ VALUES
|
||||
}
|
||||
|
||||
using (var transaction = sqliteCon.BeginTransaction())
|
||||
{
|
||||
int saved = 0;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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))
|
||||
|
||||
@ -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,14 +56,25 @@ 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))
|
||||
{
|
||||
if (HF_LocationId.PromptAndSave(this))
|
||||
{
|
||||
ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToastNotification.ShowCustom(
|
||||
"Location ID не указан. Отправка заказов будет недоступна, пока его не заполните.",
|
||||
"Location ID не указан. Задайте его у грузополучателя или в настройках.",
|
||||
System.Drawing.Color.DarkOrange,
|
||||
System.Drawing.Color.White);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
|
||||
}
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user