Apply available buyer/price-list/region discounts to summary.price on download, keep consignee carts separate, and harden SQLite startup/migrations. Co-authored-by: Cursor <cursoragent@cursor.com>
264 lines
11 KiB
C#
264 lines
11 KiB
C#
using System;
|
||
using System.Data;
|
||
using System.Data.SQLite;
|
||
using System.Windows.Forms;
|
||
using Электронная_Фармация.Classes;
|
||
|
||
namespace Электронная_Фармация.Forms
|
||
{
|
||
public partial class FConsignees : Form
|
||
{
|
||
public string SelectedConsigneeNameAfterSave { get; private set; } = string.Empty;
|
||
|
||
public FConsignees()
|
||
{
|
||
InitializeComponent();
|
||
}
|
||
|
||
private void FConsignees_Load(object sender, EventArgs e)
|
||
{
|
||
UiThemeHelper.ApplyToControlTree(this);
|
||
showMeContentFromConsigneesTable();
|
||
}
|
||
|
||
void showMeContentFromConsigneesTable()
|
||
{
|
||
using (SQLiteConnection conConsignees = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||
{
|
||
string selectFromConsignees = @"
|
||
select [idConsignees] as [Id],
|
||
[codeConsignees] as [Код],
|
||
[ConsigneesName] as [Наименование],
|
||
[ConsigneesAddress] as [Адрес],
|
||
ifnull([LocationId], '') as [Location ID],
|
||
[ConsigneesUseAsDefault] as [По умолчанию]
|
||
from [Consignees]
|
||
order by [ConsigneesUseAsDefault] desc, [ConsigneesName]";
|
||
try
|
||
{
|
||
conConsignees.Open();
|
||
SQLiteCommand cmdShowConsigneesList = new SQLiteCommand(selectFromConsignees, conConsignees);
|
||
DataTable tableConsignees = new DataTable();
|
||
SQLiteDataAdapter daConsigneeList = new SQLiteDataAdapter(cmdShowConsigneesList);
|
||
daConsigneeList.Fill(tableConsignees);
|
||
dgvConsignees.DataSource = tableConsignees;
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void txtInteractiveShowMeConsignees_KeyPress(object sender, KeyPressEventArgs e)
|
||
{
|
||
if (e.KeyChar == (char)Keys.Enter)
|
||
{
|
||
if (dgvConsignees.Rows.Count > 0 && dgvConsignees.DataSource is DataTable table)
|
||
{
|
||
table.DefaultView.RowFilter = $"[Наименование] like '%{txtInteractiveShowMeConsignees.Text.Replace("'", "''")}%'";
|
||
if (dgvConsignees.Rows.Count == 0)
|
||
{
|
||
showMeContentFromConsigneesTable();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
showMeContentFromConsigneesTable();
|
||
}
|
||
txtInteractiveShowMeConsignees.Text = string.Empty;
|
||
}
|
||
}
|
||
|
||
private void btnSave_Click(object sender, EventArgs e)
|
||
{
|
||
if (!(dgvConsignees.DataSource is DataTable table))
|
||
{
|
||
return;
|
||
}
|
||
|
||
dgvConsignees.EndEdit();
|
||
|
||
// Кто должен стать активной аптекой после сохранения.
|
||
// По вашему ТЗ: "аптека выбранная в этом окне" => текущая выделенная строка.
|
||
var selectedName = dgvConsignees.CurrentRow?.Cells["Наименование"]?.Value?.ToString()?.Trim()
|
||
?? string.Empty;
|
||
SelectedConsigneeNameAfterSave = selectedName;
|
||
|
||
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("Грузополучатели сохранены");
|
||
|
||
if (!string.IsNullOrWhiteSpace(selectedName))
|
||
{
|
||
ConsigneeHelper.SetActiveConsignee(selectedName);
|
||
}
|
||
|
||
DialogResult = DialogResult.OK;
|
||
Close();
|
||
}
|
||
|
||
private void btnAdd_Click(object sender, EventArgs e)
|
||
{
|
||
using (var dialog = new Form())
|
||
{
|
||
dialog.Text = "Новая аптека";
|
||
dialog.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||
dialog.StartPosition = FormStartPosition.CenterParent;
|
||
// Немного увеличиваем высоту/ширину на случай DPI/шрифтов.
|
||
dialog.ClientSize = new System.Drawing.Size(480, 310);
|
||
dialog.MaximizeBox = false;
|
||
dialog.MinimizeBox = false;
|
||
dialog.ShowInTaskbar = false;
|
||
|
||
var lblName = new Label { Text = "Наименование", Left = 16, Top = 16, AutoSize = true };
|
||
var txtName = new TextBox { Left = 16, Top = 42, Width = 440, Height = 28 };
|
||
var lblAddress = new Label { Text = "Адрес", Left = 16, Top = 82, AutoSize = true };
|
||
var txtAddress = new TextBox { Left = 16, Top = 108, Width = 440, Height = 28 };
|
||
var lblLocation = new Label { Text = "Location ID", Left = 16, Top = 148, AutoSize = true };
|
||
var txtLocation = new TextBox { Left = 16, Top = 174, Width = 440, Height = 28 };
|
||
|
||
var btnOk = new Elfisa.UI.Controls.ModernButton
|
||
{
|
||
Text = "Добавить",
|
||
DialogResult = DialogResult.OK
|
||
};
|
||
var btnCancel = new Elfisa.UI.Controls.ModernButton
|
||
{
|
||
Text = "Отмена",
|
||
DialogResult = DialogResult.Cancel
|
||
};
|
||
|
||
dialog.Controls.AddRange(new Control[]
|
||
{
|
||
lblName, txtName, lblAddress, txtAddress, lblLocation, txtLocation, btnOk, btnCancel
|
||
});
|
||
dialog.AcceptButton = btnOk;
|
||
dialog.CancelButton = btnCancel;
|
||
UiThemeHelper.ApplyToControlTree(dialog);
|
||
|
||
btnOk.Padding = new Padding(12, 4, 12, 4);
|
||
btnCancel.Padding = new Padding(12, 4, 12, 4);
|
||
Elfisa.UI.Controls.ModernButtonStyles.FitToText(btnOk, 36);
|
||
Elfisa.UI.Controls.ModernButtonStyles.FitToText(btnCancel, 36);
|
||
btnOk.Height = 36;
|
||
btnCancel.Height = 36;
|
||
|
||
const int padding = 16;
|
||
const int gap = 10;
|
||
|
||
// Минимально гарантируем ширину, чтобы обе кнопки поместились.
|
||
var requiredWidth = btnOk.Width + btnCancel.Width + gap + padding * 2;
|
||
if (dialog.ClientSize.Width < requiredWidth)
|
||
{
|
||
dialog.ClientSize = new System.Drawing.Size(requiredWidth, dialog.ClientSize.Height);
|
||
}
|
||
|
||
// Всегда зажимаем в границы формы после вычисления ширины ModernButton'ов.
|
||
btnCancel.Left = Math.Max(padding, dialog.ClientSize.Width - btnCancel.Width - padding);
|
||
btnCancel.Top = dialog.ClientSize.Height - btnCancel.Height - padding;
|
||
|
||
btnOk.Left = Math.Max(padding, btnCancel.Left - btnOk.Width - gap);
|
||
btnOk.Top = btnCancel.Top;
|
||
|
||
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();
|
||
}
|
||
}
|
||
}
|