diff --git a/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs b/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
index cd8dcda..19e75b2 100644
--- a/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
+++ b/src/ElectronicPharmacy/Classes/ConsigneeHelper.cs
@@ -215,6 +215,113 @@ namespace Электронная_Фармация.Classes
return result;
}
+ ///
+ /// Процент наценки, который клиент задал вручную для аптеки.
+ /// null = ещё не сохраняли, брать fallback.
+ ///
+ public static decimal? GetClientMarkupPercent(string consigneeName)
+ {
+ if (string.IsNullOrWhiteSpace(consigneeName))
+ {
+ return null;
+ }
+
+ try
+ {
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"SELECT ClientMarkupPercent
+ FROM Consignees
+ WHERE ConsigneesName = @name
+ LIMIT 1",
+ connection))
+ {
+ command.Parameters.AddWithValue("@name", consigneeName.Trim());
+ var result = command.ExecuteScalar();
+ if (result == null || result == DBNull.Value)
+ {
+ return null;
+ }
+
+ if (result is decimal d)
+ {
+ return d;
+ }
+
+ if (decimal.TryParse(
+ Convert.ToString(result, System.Globalization.CultureInfo.InvariantCulture),
+ System.Globalization.NumberStyles.Any,
+ System.Globalization.CultureInfo.InvariantCulture,
+ out var parsed))
+ {
+ return parsed;
+ }
+
+ return null;
+ }
+ }
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Сохраняет процент наценки клиента для конкретной аптеки.
+ /// Не сбрасывается при обновлении прайса.
+ ///
+ public static void SetClientMarkupPercent(string consigneeName, decimal markupPercent)
+ {
+ if (string.IsNullOrWhiteSpace(consigneeName))
+ {
+ return;
+ }
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET ClientMarkupPercent = @pct
+ WHERE ConsigneesName = @name",
+ connection))
+ {
+ command.Parameters.AddWithValue("@pct", markupPercent);
+ command.Parameters.AddWithValue("@name", consigneeName.Trim());
+ command.ExecuteNonQuery();
+ }
+ }
+ }
+
+ ///
+ /// Если у аптеки ещё нет сохранённого процента — записать стартовое значение с сервера.
+ /// Уже введённые клиентом значения не трогаем.
+ ///
+ public static void SeedClientMarkupPercentIfEmpty(decimal? serverMarkupPercent)
+ {
+ if (!serverMarkupPercent.HasValue)
+ {
+ return;
+ }
+
+ using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
+ {
+ connection.Open();
+ using (var command = new SQLiteCommand(
+ @"UPDATE Consignees
+ SET ClientMarkupPercent = @pct
+ WHERE ClientMarkupPercent IS NULL",
+ connection))
+ {
+ command.Parameters.AddWithValue("@pct", serverMarkupPercent.Value);
+ command.ExecuteNonQuery();
+ }
+ }
+ }
+
///
/// Синхронизирует поля заказа в `PriceList` (Zakaz/SummaZakaza) с корзиной выбранной аптеки.
/// Данные корзины хранятся в TempOrderItems с фильтром по ConsigneeName, а PriceList обновляется
diff --git a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs
index 6c96beb..2e8d278 100644
--- a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs
+++ b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs
@@ -593,6 +593,7 @@ FROM [TempOrderItems];";
TryAddColumn(conForCheckTables, "PriceList", "TradeName", "nvarchar(256)");
TryAddColumn(conForCheckTables, "PriceList", "Dosage", "nvarchar(128)");
TryAddColumn(conForCheckTables, "PriceList", "MarkupPercent", "real");
+ TryAddColumn(conForCheckTables, "Consignees", "ClientMarkupPercent", "real");
ConsigneeHelper.MigrateGlobalLocationIdIfNeeded(conForCheckTables);
}
catch (Exception ex)
diff --git a/src/ElectronicPharmacy/HelpForms/HFConsignees.Designer.cs b/src/ElectronicPharmacy/HelpForms/HFConsignees.Designer.cs
index 3594399..41b4cc4 100644
--- a/src/ElectronicPharmacy/HelpForms/HFConsignees.Designer.cs
+++ b/src/ElectronicPharmacy/HelpForms/HFConsignees.Designer.cs
@@ -45,14 +45,14 @@
this.dgvConsignees.BackgroundColor = System.Drawing.SystemColors.ButtonFace;
this.dgvConsignees.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dgvConsignees.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
- this.dgvConsignees.Location = new System.Drawing.Point(12, 56);
+ this.dgvConsignees.Location = new System.Drawing.Point(16, 56);
this.dgvConsignees.MultiSelect = false;
this.dgvConsignees.Name = "dgvConsignees";
this.dgvConsignees.ReadOnly = true;
this.dgvConsignees.RowHeadersVisible = false;
this.dgvConsignees.RowHeadersWidth = 51;
this.dgvConsignees.RowTemplate.Height = 24;
- this.dgvConsignees.Size = new System.Drawing.Size(409, 295);
+ this.dgvConsignees.Size = new System.Drawing.Size(640, 295);
this.dgvConsignees.TabIndex = 0;
//
// label1
@@ -69,7 +69,7 @@
//
this.btnConfirmConsignee.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
this.btnConfirmConsignee.Font = new System.Drawing.Font("Segoe UI Semibold", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
- this.btnConfirmConsignee.Location = new System.Drawing.Point(336, 361);
+ this.btnConfirmConsignee.Location = new System.Drawing.Point(567, 361);
this.btnConfirmConsignee.Name = "btnConfirmConsignee";
this.btnConfirmConsignee.Size = new System.Drawing.Size(89, 43);
this.btnConfirmConsignee.TabIndex = 2;
@@ -81,7 +81,7 @@
//
this.btnCloseThis.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
this.btnCloseThis.Font = new System.Drawing.Font("Segoe UI Semibold", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
- this.btnCloseThis.Location = new System.Drawing.Point(174, 361);
+ this.btnCloseThis.Location = new System.Drawing.Point(420, 361);
this.btnCloseThis.Name = "btnCloseThis";
this.btnCloseThis.Size = new System.Drawing.Size(125, 43);
this.btnCloseThis.TabIndex = 3;
@@ -93,7 +93,7 @@
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(434, 412);
+ this.ClientSize = new System.Drawing.Size(672, 412);
this.Controls.Add(this.btnCloseThis);
this.Controls.Add(this.btnConfirmConsignee);
this.Controls.Add(this.label1);
diff --git a/src/ElectronicPharmacy/HelpForms/HFConsignees.cs b/src/ElectronicPharmacy/HelpForms/HFConsignees.cs
index 6d719c7..6e146c1 100644
--- a/src/ElectronicPharmacy/HelpForms/HFConsignees.cs
+++ b/src/ElectronicPharmacy/HelpForms/HFConsignees.cs
@@ -28,7 +28,6 @@ namespace Электронная_Фармация.HelpForms
string qShowMeConsigneesList = @"
select ConsigneesName as [Грузополучатель],
- ifnull(LocationId, '') as [Location ID],
ConsigneesAddress as [Адрес]
from Consignees
order by ConsigneesUseAsDefault desc, ConsigneesName";
@@ -38,6 +37,16 @@ order by ConsigneesUseAsDefault desc, ConsigneesName";
DataTable tableConsignees = new DataTable();
daConsigneesList.Fill(tableConsignees);
dgvConsignees.DataSource = tableConsignees;
+
+ if (dgvConsignees.Columns.Contains("Грузополучатель"))
+ {
+ dgvConsignees.Columns["Грузополучатель"].FillWeight = 45;
+ }
+
+ if (dgvConsignees.Columns.Contains("Адрес"))
+ {
+ dgvConsignees.Columns["Адрес"].FillWeight = 55;
+ }
}
catch (Exception ex)
{
diff --git a/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs b/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs
index 7b33c64..9c5babd 100644
--- a/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs
+++ b/src/ElectronicPharmacy/HelpForms/HF_DownloadDataFromServer.cs
@@ -368,15 +368,14 @@ namespace Электронная_Фармация.HelpForms
private static void ApplyClientMarkupSettings(PriceSummaryResponse response)
{
+ // Серверный % используем только как стартовое значение для аптек,
+ // у которых клиент ещё ничего не сохранил. Уже введённые проценты не трогаем.
if (response?.MarkupPercent == null)
{
return;
}
- var markup = response.MarkupPercent.Value;
- Settings.Default.ClientMarkupPercent = markup;
- Settings.Default.IntDefaultPercentMarkup = (int)Math.Round(markup, MidpointRounding.AwayFromZero);
- Settings.Default.Save();
+ ConsigneeHelper.SeedClientMarkupPercentIfEmpty(response.MarkupPercent);
}
private void ReportTransferProgress(HttpTransferProgress transfer)
diff --git a/src/ElectronicPharmacy/UserControls/UCPriceList.cs b/src/ElectronicPharmacy/UserControls/UCPriceList.cs
index f0d61ae..53ec0e0 100644
--- a/src/ElectronicPharmacy/UserControls/UCPriceList.cs
+++ b/src/ElectronicPharmacy/UserControls/UCPriceList.cs
@@ -35,6 +35,7 @@ namespace Электронная_Фармация.UserControls
DataTable tablePriceList;
string _WorkWithConsignee;
decimal _percentageAsDefault;
+ bool _suppressMarkupPersist;
string _selectedSupplierFilter = string.Empty;
bool _suppressSupplierFilterEvents;
bool _suppressPriceListSelectionEvents;
@@ -1006,34 +1007,59 @@ namespace Электронная_Фармация.UserControls
void loadInfoAboutDefaultMarkup()
{
- decimal percentageMarkupDefault = Properties.Settings.Default.ClientMarkupPercent;
- if (percentageMarkupDefault <= 0)
+ decimal percentageMarkupDefault = 0m;
+ var fromConsignee = ConsigneeHelper.GetClientMarkupPercent(_WorkWithConsignee);
+ if (fromConsignee.HasValue)
{
- percentageMarkupDefault = Properties.Settings.Default.IntDefaultPercentMarkup;
+ percentageMarkupDefault = fromConsignee.Value;
}
-
- if (percentageMarkupDefault <= 0)
+ else
{
- percentageMarkupDefault = MarkupHelper.FallbackMarkupPercent;
+ percentageMarkupDefault = Properties.Settings.Default.ClientMarkupPercent;
+ if (percentageMarkupDefault <= 0)
+ {
+ percentageMarkupDefault = Properties.Settings.Default.IntDefaultPercentMarkup;
+ }
+
+ if (percentageMarkupDefault < 0)
+ {
+ percentageMarkupDefault = MarkupHelper.FallbackMarkupPercent;
+ }
+
+ // Первый запуск для этой аптеки — сразу закрепим текущее значение за аптекой.
+ if (!string.IsNullOrWhiteSpace(_WorkWithConsignee))
+ {
+ try
+ {
+ ConsigneeHelper.SetClientMarkupPercent(_WorkWithConsignee, percentageMarkupDefault);
+ }
+ catch
+ {
+ // колонка может ещё не быть — при следующем старте миграция добавит
+ }
+ }
}
_percentageAsDefault = percentageMarkupDefault;
- numericPercent.DecimalPlaces = 2;
- numericPercent.Increment = 0.1m;
- numericPercent.Value = Math.Max(numericPercent.Minimum, Math.Min(numericPercent.Maximum, percentageMarkupDefault));
+ _suppressMarkupPersist = true;
+ try
+ {
+ numericPercent.DecimalPlaces = 2;
+ numericPercent.Increment = 0.1m;
+ numericPercent.Value = Math.Max(
+ numericPercent.Minimum,
+ Math.Min(numericPercent.Maximum, percentageMarkupDefault));
+ }
+ finally
+ {
+ _suppressMarkupPersist = false;
+ }
}
private decimal GetMarkupPercentForRow(DataGridViewRow row)
{
- if (row?.DataGridView?.Columns.Contains("Наценка %") == true)
- {
- var cellValue = row.Cells["Наценка %"]?.Value;
- if (MarkupHelper.TryParseMarkup(cellValue, out var itemMarkup) && itemMarkup >= 0)
- {
- return itemMarkup;
- }
- }
-
+ // Для превью/спиннера всегда используем процент аптеки, который задал клиент.
+ // Серверные значения в колонке «Наценка %» не перетирают ручной ввод.
return _percentageAsDefault;
}
@@ -2028,7 +2054,18 @@ and SumOrderedItems = '{SumOrder}'";
}
var markupPercentage = GetMarkupPercentForRow(currentRow);
- numericPercent.Value = Math.Max(numericPercent.Minimum, Math.Min(numericPercent.Maximum, markupPercentage));
+ _suppressMarkupPersist = true;
+ try
+ {
+ numericPercent.Value = Math.Max(
+ numericPercent.Minimum,
+ Math.Min(numericPercent.Maximum, markupPercentage));
+ }
+ finally
+ {
+ _suppressMarkupPersist = false;
+ }
+
lblPriceForGood.Text = $"= {priceForGood:0.##} руб";
}
catch
@@ -2405,6 +2442,11 @@ and SumOrderedItems = '{SumOrder}'";
private void numericPercent_ValueChanged(object sender, EventArgs e)
{
+ if (!_suppressMarkupPersist)
+ {
+ PersistClientMarkupPercent();
+ }
+
if (numericPercent.Value != _percentageAsDefault)
{
checkUsePercentageAsDefault.Checked = false;
@@ -2413,6 +2455,30 @@ and SumOrderedItems = '{SumOrder}'";
ShowMePriceWithMarkupPercentForSelectedGood();
}
+ private void PersistClientMarkupPercent()
+ {
+ var value = numericPercent.Value;
+ _percentageAsDefault = value;
+
+ if (!string.IsNullOrWhiteSpace(_WorkWithConsignee))
+ {
+ try
+ {
+ ConsigneeHelper.SetClientMarkupPercent(_WorkWithConsignee, value);
+ }
+ catch
+ {
+ // ignore transient DB errors; value stays in UI
+ }
+ }
+
+ // Глобальный fallback на случай новой аптеки без своего значения.
+ Properties.Settings.Default.ClientMarkupPercent = value;
+ Properties.Settings.Default.IntDefaultPercentMarkup =
+ Convert.ToInt32(Math.Round(value, MidpointRounding.AwayFromZero));
+ Properties.Settings.Default.Save();
+ }
+
private void UCPriceList_Paint(object sender, PaintEventArgs e)
{
if (comboSupplierFilter.DroppedDown)
@@ -2435,13 +2501,9 @@ and SumOrderedItems = '{SumOrder}'";
private void checkUsePercentageAsDefault_CheckedChanged(object sender, EventArgs e)
{
- if (_percentageAsDefault != numericPercent.Value && checkUsePercentageAsDefault.Checked == true)
+ if (checkUsePercentageAsDefault.Checked)
{
- var newDefaultPercent = numericPercent.Value;
- Properties.Settings.Default.ClientMarkupPercent = newDefaultPercent;
- Properties.Settings.Default.IntDefaultPercentMarkup = Convert.ToInt32(Math.Round(newDefaultPercent, MidpointRounding.AwayFromZero));
- Properties.Settings.Default.Save();
- _percentageAsDefault = newDefaultPercent;
+ PersistClientMarkupPercent();
}
}