From 438a7c85cc3e07c47dd730f4ad50b6fb61af569e Mon Sep 17 00:00:00 2001 From: Professional Date: Mon, 21 Apr 2025 21:14:48 +0700 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8C=D1=82?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.config | 18 ++ Form1.Designer.cs | 223 ++++++++++++++ Form1.cs | 500 +++++++++++++++++++++++++++++++ Form1.resx | 120 ++++++++ FormManageStores.Designer.cs | 201 +++++++++++++ FormManageStores.cs | 150 ++++++++++ FormManageStores.resx | 120 ++++++++ OzonStore.cs | 28 ++ Program.cs | 23 ++ Properties/AssemblyInfo.cs | 33 ++ Properties/Resources.Designer.cs | 71 +++++ Properties/Resources.resx | 117 ++++++++ Properties/Settings.Designer.cs | 38 +++ Properties/Settings.settings | 9 + SKLADm.csproj | 122 ++++++++ SKLADm.sln | 25 ++ packages.config | 10 + 17 files changed, 1808 insertions(+) create mode 100644 App.config create mode 100644 Form1.Designer.cs create mode 100644 Form1.cs create mode 100644 Form1.resx create mode 100644 FormManageStores.Designer.cs create mode 100644 FormManageStores.cs create mode 100644 FormManageStores.resx create mode 100644 OzonStore.cs create mode 100644 Program.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.resx create mode 100644 Properties/Settings.Designer.cs create mode 100644 Properties/Settings.settings create mode 100644 SKLADm.csproj create mode 100644 SKLADm.sln create mode 100644 packages.config diff --git a/App.config b/App.config new file mode 100644 index 0000000..4a9f8d0 --- /dev/null +++ b/App.config @@ -0,0 +1,18 @@ + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/Form1.Designer.cs b/Form1.Designer.cs new file mode 100644 index 0000000..bf0f3ca --- /dev/null +++ b/Form1.Designer.cs @@ -0,0 +1,223 @@ +//------------------------------------------------------------------------------ +// +// Этот код создан программой. +// Исполняемая версия:4.0.30319.42000 +// +// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае +// повторной генерации кода. +// +//------------------------------------------------------------------------------ + +namespace OzonInternalLabelPrinter // Убедитесь, что это ваше пространство имен +{ + partial class Form1 + { + /// + /// Обязательная переменная конструктора. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Освободить все используемые ресурсы. + /// + /// истинно, если управляемый ресурс должен быть удален; иначе ложно. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + // Освобождаем ресурсы, созданные в коде Form1.cs + _httpClient?.Dispose(); + _printDocForSetup?.Dispose(); // Добавлено освобождение PrintDocument + } + base.Dispose(disposing); + } + + #region Код, автоматически созданный конструктором форм Windows + + /// + /// Требуемый метод для поддержки конструктора — не изменяйте + /// содержимое этого метода с помощью редактора кода. + /// + private void InitializeComponent() + { + this.label3 = new System.Windows.Forms.Label(); + this.txtOfferId = new System.Windows.Forms.TextBox(); + this.btnGetData = new System.Windows.Forms.Button(); + this.lblProductName = new System.Windows.Forms.Label(); + this.lblProductSku = new System.Windows.Forms.Label(); + this.lblProductBarcode = new System.Windows.Forms.Label(); + this.btnPrintLabel = new System.Windows.Forms.Button(); + this.lblStatus = new System.Windows.Forms.Label(); + this.cmbStores = new System.Windows.Forms.ComboBox(); + this.btnManageStores = new System.Windows.Forms.Button(); + this.labelStore = new System.Windows.Forms.Label(); + this.btnPageSetup = new System.Windows.Forms.Button(); // Объявление кнопки Настройки + this.pageSetupDialog1 = new System.Windows.Forms.PageSetupDialog(); // Объявление диалога Настройки + this.SuspendLayout(); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 94); // Скорректировано + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(51, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Артикул:"; + // + // txtOfferId + // + this.txtOfferId.Enabled = false; + this.txtOfferId.Location = new System.Drawing.Point(88, 91); // Скорректировано + this.txtOfferId.Name = "txtOfferId"; + this.txtOfferId.Size = new System.Drawing.Size(176, 20); + this.txtOfferId.TabIndex = 3; // TabIndex изменен + this.txtOfferId.TextChanged += new System.EventHandler(this.txtOfferId_TextChanged); + // + // btnGetData + // + this.btnGetData.Enabled = false; + this.btnGetData.Location = new System.Drawing.Point(270, 89); // Скорректировано + this.btnGetData.Name = "btnGetData"; + this.btnGetData.Size = new System.Drawing.Size(118, 23); + this.btnGetData.TabIndex = 4; // TabIndex изменен + this.btnGetData.Text = "Получить данные"; + this.btnGetData.UseVisualStyleBackColor = true; + this.btnGetData.Click += new System.EventHandler(this.btnGetData_Click); + // + // lblProductName + // + this.lblProductName.AutoSize = true; + this.lblProductName.Location = new System.Drawing.Point(12, 133); // Скорректировано + this.lblProductName.Name = "lblProductName"; + this.lblProductName.Size = new System.Drawing.Size(60, 13); + this.lblProductName.TabIndex = 5; + this.lblProductName.Text = "Название:"; + // + // lblProductSku + // + this.lblProductSku.AutoSize = true; + this.lblProductSku.Location = new System.Drawing.Point(12, 156); // Скорректировано + this.lblProductSku.Name = "lblProductSku"; + this.lblProductSku.Size = new System.Drawing.Size(51, 13); + this.lblProductSku.TabIndex = 6; + this.lblProductSku.Text = "Артикул:"; + // + // lblProductBarcode + // + this.lblProductBarcode.AutoSize = true; + this.lblProductBarcode.Location = new System.Drawing.Point(12, 179); // Скорректировано + this.lblProductBarcode.Name = "lblProductBarcode"; + this.lblProductBarcode.Size = new System.Drawing.Size(59, 13); + this.lblProductBarcode.TabIndex = 7; + this.lblProductBarcode.Text = "Штрихкод:"; + // + // btnPrintLabel + // + this.btnPrintLabel.Enabled = false; + this.btnPrintLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.btnPrintLabel.Location = new System.Drawing.Point(16, 214); // Скорректировано + this.btnPrintLabel.Name = "btnPrintLabel"; + this.btnPrintLabel.Size = new System.Drawing.Size(373, 33); + this.btnPrintLabel.TabIndex = 8; // TabIndex изменен + this.btnPrintLabel.Text = "Печать этикетки"; + this.btnPrintLabel.UseVisualStyleBackColor = true; + this.btnPrintLabel.Click += new System.EventHandler(this.btnPrintLabel_Click); + // + // lblStatus + // + this.lblStatus.AutoSize = true; + this.lblStatus.Location = new System.Drawing.Point(13, 260); // Скорректировано + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Size = new System.Drawing.Size(44, 13); + this.lblStatus.TabIndex = 9; + this.lblStatus.Text = "Статус:"; + // + // cmbStores + // + this.cmbStores.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbStores.FormattingEnabled = true; + this.cmbStores.Location = new System.Drawing.Point(88, 24); + this.cmbStores.Name = "cmbStores"; + this.cmbStores.Size = new System.Drawing.Size(176, 21); + this.cmbStores.TabIndex = 0; + this.cmbStores.SelectedIndexChanged += new System.EventHandler(this.cmbStores_SelectedIndexChanged); + // + // btnManageStores + // + this.btnManageStores.Location = new System.Drawing.Point(270, 23); + this.btnManageStores.Name = "btnManageStores"; + this.btnManageStores.Size = new System.Drawing.Size(118, 23); + this.btnManageStores.TabIndex = 1; + this.btnManageStores.Text = "Упр. магазинами..."; + this.btnManageStores.UseVisualStyleBackColor = true; + this.btnManageStores.Click += new System.EventHandler(this.btnManageStores_Click); + // + // labelStore + // + this.labelStore.AutoSize = true; + this.labelStore.Location = new System.Drawing.Point(12, 27); + this.labelStore.Name = "labelStore"; + this.labelStore.Size = new System.Drawing.Size(54, 13); + this.labelStore.TabIndex = 15; + this.labelStore.Text = "Магазин:"; + // + // btnPageSetup + // *** Новая кнопка Настройки принтера *** + this.btnPageSetup.Location = new System.Drawing.Point(270, 53); + this.btnPageSetup.Name = "btnPageSetup"; + this.btnPageSetup.Size = new System.Drawing.Size(118, 23); + this.btnPageSetup.TabIndex = 2; // Изменен TabIndex + this.btnPageSetup.Text = "Настройка принтера"; + this.btnPageSetup.UseVisualStyleBackColor = true; + this.btnPageSetup.Click += new System.EventHandler(this.btnPageSetup_Click); + // + // pageSetupDialog1 + // *** Диалог настройки страницы *** + this.pageSetupDialog1 = new System.Windows.Forms.PageSetupDialog(); + this.pageSetupDialog1.EnableMetric = true; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(404, 288); // Скорректирован размер + this.Controls.Add(this.btnPageSetup); // Добавлена кнопка + this.Controls.Add(this.labelStore); + this.Controls.Add(this.btnManageStores); + this.Controls.Add(this.cmbStores); + this.Controls.Add(this.lblStatus); + this.Controls.Add(this.btnPrintLabel); + this.Controls.Add(this.lblProductBarcode); + this.Controls.Add(this.lblProductSku); + this.Controls.Add(this.lblProductName); + this.Controls.Add(this.btnGetData); + this.Controls.Add(this.txtOfferId); + this.Controls.Add(this.label3); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "Form1"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Генератор внутренних этикеток Ozon"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox txtOfferId; + private System.Windows.Forms.Button btnGetData; + private System.Windows.Forms.Label lblProductName; + private System.Windows.Forms.Label lblProductSku; + private System.Windows.Forms.Label lblProductBarcode; + private System.Windows.Forms.Button btnPrintLabel; + private System.Windows.Forms.Label lblStatus; + private System.Windows.Forms.ComboBox cmbStores; + private System.Windows.Forms.Button btnManageStores; + private System.Windows.Forms.Label labelStore; + private System.Windows.Forms.Button btnPageSetup; // Добавлено + private System.Windows.Forms.PageSetupDialog pageSetupDialog1; // Добавлено + } +} \ No newline at end of file diff --git a/Form1.cs b/Form1.cs new file mode 100644 index 0000000..0194b77 --- /dev/null +++ b/Form1.cs @@ -0,0 +1,500 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Printing; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Cryptography; // Для шифрования/дешифрования +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SKLADm.Properties; // Для доступа к Settings.Default +using SKLADm; +using ZXing; +using ZXing.Common; +using ZXing.Windows.Compatibility; + +namespace OzonInternalLabelPrinter // Убедитесь, что это ваше пространство имен +{ + public partial class Form1 : Form + { + private HttpClient _httpClient; + // Bitmap больше не нужен как поле класса + // private Bitmap _labelBitmap; + private string _currentProductName; + private string _currentSku; + private string _currentBarcode; + private List _availableStores; + // Поле для хранения экземпляра документа между настройкой и печатью + private PrintDocument _printDocForSetup; // Используем для PageSetup и Print + + // Конструктор формы + public Form1() + { + InitializeComponent(); // Инициализирует компоненты из ДИЗАЙНЕРА + InitializeHttpClient(); + // Инициализируем PrintDocument один раз + _printDocForSetup = new PrintDocument(); + // Устанавливаем обработчик печати один раз + _printDocForSetup.PrintPage += new PrintPageEventHandler(pd_PrintPage); + // Загружаем магазины при запуске ПОСЛЕ инициализации _printDocForSetup + LoadStoresToComboBox(); + } + + // Инициализация HTTP клиента + private void InitializeHttpClient() + { + _httpClient = new HttpClient + { + BaseAddress = new Uri("https://api-seller.ozon.ru/") + }; + _httpClient.DefaultRequestHeaders.Accept.Clear(); + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + } + + // --- Логика работы с магазинами и ключами --- + + private static readonly byte[] s_entropy = null; // Энтропия для шифрования + + // Дешифрование Api-Key + private string DecryptApiKey(string encryptedKeyBase64) + { + if (string.IsNullOrEmpty(encryptedKeyBase64)) return string.Empty; + try + { + byte[] encryptedBytes = Convert.FromBase64String(encryptedKeyBase64); + byte[] decryptedBytes = ProtectedData.Unprotect(encryptedBytes, s_entropy, DataProtectionScope.CurrentUser); + return Encoding.UTF8.GetString(decryptedBytes); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Ошибка дешифрования ключа: {ex.Message}"); + MessageBox.Show($"Не удалось дешифровать Api-Key для магазина '{cmbStores.Text}'. Возможно, настройки повреждены или созданы под другим пользователем.\n\nОшибка: {ex.Message}", + "Ошибка ключа", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; + } + } + + // Загрузка магазинов из настроек в ComboBox + private void LoadStoresToComboBox() + { + string json = Settings.Default.SavedStoresJson; + _availableStores = new List(); + if (!string.IsNullOrEmpty(json)) + { + try + { + _availableStores = JsonConvert.DeserializeObject>(json) ?? new List(); + } + catch (Exception ex) + { + MessageBox.Show($"Ошибка загрузки списка магазинов: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); + _availableStores = new List(); + } + } + + cmbStores.DataSource = null; + cmbStores.DataSource = _availableStores; + cmbStores.DisplayMember = "StoreName"; + + bool storesExist = _availableStores.Any(); + cmbStores.Enabled = storesExist; + txtOfferId.Enabled = storesExist; + btnPageSetup.Enabled = storesExist; // Включаем настройку, если есть магазины + // Кнопка GetData включится при вводе артикула + + if (storesExist) + { + cmbStores.SelectedIndex = 0; + // Установим принтер по умолчанию для _printDocForSetup, если возможно + TrySetDefaultPrinterForDocument(); + } + else + { + cmbStores.SelectedIndex = -1; + // Показываем сообщение, если нужно + // MessageBox.Show("Нет сохраненных магазинов...", "Внимание", MessageBoxButtons.OK, Information); + } + ResetProductData(); + // Обновляем состояние кнопки GetData после загрузки + txtOfferId_TextChanged(txtOfferId, EventArgs.Empty); + } + + // Попытка установить принтер по умолчанию для PrintDocument + private void TrySetDefaultPrinterForDocument() + { + string thermalPrinterName = "Xprinter XP-365B"; // Или другое имя по умолчанию + foreach (string printerName in PrinterSettings.InstalledPrinters) + { + if (printerName.Equals(thermalPrinterName, StringComparison.OrdinalIgnoreCase)) + { + try + { + _printDocForSetup.PrinterSettings.PrinterName = printerName; + System.Diagnostics.Debug.WriteLine($"Принтер по умолчанию '{printerName}' установлен для PrintDocument."); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Ошибка установки принтера по умолчанию '{printerName}': {ex.Message}"); + } + return; // Выходим после нахождения + } + } + System.Diagnostics.Debug.WriteLine($"Принтер по умолчанию '{thermalPrinterName}' не найден."); + } + + // --- Обработчики событий UI --- + + private void btnManageStores_Click(object sender, EventArgs e) + { + using (FormManageStores manageForm = new FormManageStores()) + { + manageForm.ShowDialog(this); + LoadStoresToComboBox(); // Перезагружаем список + } + } + + private void cmbStores_SelectedIndexChanged(object sender, EventArgs e) + { + ResetProductData(); + txtOfferId_TextChanged(txtOfferId, EventArgs.Empty); // Обновляем состояние кнопки GetData + // Можно попробовать обновить принтер по умолчанию для _printDocForSetup, + // если у разных магазинов могут быть разные принтеры (но это усложнит логику) + } + + private void txtOfferId_TextChanged(object sender, EventArgs e) + { + btnGetData.Enabled = cmbStores.SelectedItem != null && !string.IsNullOrEmpty(txtOfferId.Text.Trim()); + if (string.IsNullOrEmpty(txtOfferId.Text.Trim())) + { + ResetProductData(); + } + } + + // --- Кнопка "Настройка принтера" --- + private void btnPageSetup_Click(object sender, EventArgs e) + { + // Убедимся, что принтер в _printDocForSetup актуален (если не нашли Xprinter при запуске, + // или если у пользователя несколько принтеров) + if (!PrinterSettings.InstalledPrinters.Cast().Contains(_printDocForSetup.PrinterSettings.PrinterName)) + { + // Если текущий принтер недоступен, сбрасываем на принтер по умолчанию Windows + try + { + _printDocForSetup.PrinterSettings = new PrinterSettings(); // Сброс на настройки по умолчанию + System.Diagnostics.Debug.WriteLine("Текущий принтер в PrintDocument был недоступен, сброшен на принтер по умолчанию Windows."); + } + catch { } + } + + pageSetupDialog1.Document = _printDocForSetup; // Связываем диалог с нашим PrintDocument + pageSetupDialog1.MinMargins = new Margins(0, 0, 0, 0); // Минимальные поля + pageSetupDialog1.AllowMargins = true; // Разрешаем менять поля + pageSetupDialog1.AllowOrientation = false; // Запрещаем менять ориентацию + pageSetupDialog1.AllowPaper = true; // Разрешаем менять размер бумаги + pageSetupDialog1.AllowPrinter = true; // Разрешаем выбирать другой принтер + + if (pageSetupDialog1.ShowDialog(this) == DialogResult.OK) + { + // Настройки применились к _printDocForSetup + lblStatus.Text = "Настройки принтера применены."; + System.Diagnostics.Debug.WriteLine($"PageSetupDialog OK: New PaperSize={_printDocForSetup.DefaultPageSettings.PaperSize.PaperName}, Printer={_printDocForSetup.PrinterSettings.PrinterName}"); + } + else + { + lblStatus.Text = "Настройка принтера отменена."; + } + } + + // --- Получение данных из API --- + private async void btnGetData_Click(object sender, EventArgs e) + { + OzonStore selectedStore = cmbStores.SelectedItem as OzonStore; + if (selectedStore == null) { MessageBox.Show("Выберите магазин.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } + + string clientId = selectedStore.ClientId; + string apiKey = DecryptApiKey(selectedStore.EncryptedApiKey); + if (apiKey == null) { lblStatus.Text = "Ошибка ключа API."; return; } + + string rawOfferId = txtOfferId.Text.Trim(); + if (string.IsNullOrEmpty(rawOfferId)) { MessageBox.Show("Введите Артикул.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } + + string offerId = Regex.Replace(rawOfferId, @"[^a-zA-Zа-яА-Я0-9_-]", ""); + if (string.IsNullOrEmpty(offerId)) { MessageBox.Show("Артикул некорректен.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } + if (offerId != rawOfferId) { txtOfferId.Text = offerId; } + + ResetProductData(); + lblStatus.Text = "Запрос данных..."; + Application.DoEvents(); + + _httpClient.DefaultRequestHeaders.Remove("Client-Id"); + _httpClient.DefaultRequestHeaders.Remove("Api-Key"); + _httpClient.DefaultRequestHeaders.Add("Client-Id", clientId); + _httpClient.DefaultRequestHeaders.Add("Api-Key", apiKey); + + string responseBody = string.Empty; + try + { + var requestData = new { offer_id = new[] { offerId } }; + var jsonRequest = JsonConvert.SerializeObject(requestData); + var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"); + + HttpResponseMessage response = await _httpClient.PostAsync("/v3/product/info/list", content); + responseBody = await response.Content.ReadAsStringAsync(); + System.Diagnostics.Debug.WriteLine($"API Response ({response.StatusCode}): {responseBody}"); + + if (response.IsSuccessStatusCode) + { + JObject jsonResponse = JObject.Parse(responseBody); + JToken itemsArray = jsonResponse["items"]; + + if (itemsArray != null && itemsArray.Type == JTokenType.Array && itemsArray.HasValues) + { + JToken firstItem = itemsArray[0]; + if (firstItem != null) + { + _currentProductName = firstItem["name"]?.ToString() ?? "N/A"; + _currentSku = firstItem["offer_id"]?.ToString() ?? offerId; + _currentBarcode = firstItem["barcode"]?.ToString(); + if (string.IsNullOrEmpty(_currentBarcode)) + { + JToken barcodesToken = firstItem["barcodes"]; + if (barcodesToken != null && barcodesToken.Type == JTokenType.Array && barcodesToken.HasValues) + { _currentBarcode = barcodesToken.First?.ToString(); } + } + _currentBarcode = _currentBarcode ?? "N/A"; + + GenerateLabelPreview(); // Обновляем UI и включаем кнопку печати + } + else { HandleApiError("Нет данных о товаре в ответе.", responseBody); } + } + else { HandleApiError("Ответ API не содержит списка товаров.", responseBody); } + } + else { HandleApiError($"Ошибка API: {response.StatusCode}", responseBody); } + } + catch (JsonReaderException jsonEx) { MessageBox.Show($"Ошибка JSON: {jsonEx.Message}\n{responseBody}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); ResetProductData(); } + catch (HttpRequestException httpEx) { MessageBox.Show($"Ошибка сети: {httpEx.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); ResetProductData(); } + catch (Exception ex) { MessageBox.Show($"Ошибка: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); ResetProductData(); } + finally { apiKey = null; } + } + + // Обработка ошибок API + private void HandleApiError(string messagePrefix, string responseBody) + { + string errorMessage = $"{messagePrefix}"; + try + { + JObject errorJson = JObject.Parse(responseBody); + string ozonError = errorJson["message"]?.ToString() ?? errorJson["error"]?["message"]?.ToString(); + errorMessage = string.IsNullOrEmpty(ozonError) ? $"{messagePrefix}\n{responseBody}" : $"{messagePrefix}\n{ozonError}"; + } + catch { errorMessage = $"{messagePrefix}\n{responseBody}"; } + + MessageBox.Show(errorMessage, "Ошибка API Ozon", MessageBoxButtons.OK, MessageBoxIcon.Error); + lblStatus.Text = "Ошибка API."; + ResetProductData(); + } + + // Сброс данных о товаре и состояния UI + private void ResetProductData() + { + _currentProductName = null; + _currentSku = null; + _currentBarcode = null; + lblProductName.Text = "Название:"; + lblProductSku.Text = "Артикул:"; + lblProductBarcode.Text = "Штрихкод:"; + // Очищаем превью, если оно есть + if (this.Controls.ContainsKey("picLabelPreview")) + (this.Controls["picLabelPreview"] as PictureBox).Image = null; + btnPrintLabel.Enabled = false; + // lblStatus.Text = "Статус:"; + } + + // Генерация (только обновление UI) + private void GenerateLabelPreview() + { + bool dataAvailable = !string.IsNullOrEmpty(_currentSku) && + !string.IsNullOrEmpty(_currentProductName) && + !string.IsNullOrEmpty(_currentBarcode) && + _currentBarcode != "N/A"; + if (!dataAvailable) { ResetProductData(); return; } + + lblProductName.Text = $"Название: {_currentProductName}"; + lblProductSku.Text = $"Артикул: {_currentSku}"; + lblProductBarcode.Text = $"Штрихкод: {_currentBarcode}"; + + // Очистка превью, если оно есть + if (this.Controls.ContainsKey("picLabelPreview")) + (this.Controls["picLabelPreview"] as PictureBox).Image = null; + + btnPrintLabel.Enabled = true; + lblStatus.Text = "Данные получены, готово к печати."; + } + + + // --- Печать --- + + // Кнопка "Печать этикетки" + private void btnPrintLabel_Click(object sender, EventArgs e) + { + if (string.IsNullOrEmpty(_currentSku) || string.IsNullOrEmpty(_currentProductName) || string.IsNullOrEmpty(_currentBarcode) || _currentBarcode == "N/A") + { + MessageBox.Show("Нет данных для печати.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + PrintDialog printDialog = new PrintDialog(); + printDialog.Document = _printDocForSetup; // Используем настроенный документ + printDialog.AllowSomePages = false; + printDialog.AllowSelection = false; + printDialog.UseEXDialog = true; + + if (printDialog.ShowDialog(this) == DialogResult.OK) + { + // _printDocForSetup уже содержит принтер и настройки страницы, выбранные в PrintDialog или PageSetupDialog + // Переустанавливаем поля на случай, если PrintDialog их сбросил + _printDocForSetup.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0); + _printDocForSetup.OriginAtMargins = false; + + try + { + lblStatus.Text = $"Печать на {_printDocForSetup.PrinterSettings.PrinterName}..."; + _printDocForSetup.Print(); // Печатаем с текущими настройками + lblStatus.Text = "Отправлено на печать."; + } + catch (InvalidPrinterException) + { + MessageBox.Show($"Ошибка: Принтер '{_printDocForSetup.PrinterSettings.PrinterName}' недоступен или не найден.\nПроверьте подключение или выберите другой принтер в настройках.", "Ошибка принтера", MessageBoxButtons.OK, MessageBoxIcon.Error); + lblStatus.Text = "Ошибка принтера."; + } + + catch (Exception ex) + { + MessageBox.Show($"Ошибка печати: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); + lblStatus.Text = "Ошибка печати."; + } + } + else { lblStatus.Text = "Печать отменена."; } + + printDialog.Dispose(); + } + + // Метод отрисовки страницы для печати (динамический) + private void pd_PrintPage(object sender, PrintPageEventArgs e) + { + // Проверка данных + if (string.IsNullOrEmpty(_currentSku) || string.IsNullOrEmpty(_currentProductName) || string.IsNullOrEmpty(_currentBarcode) || _currentBarcode == "N/A") + { System.Diagnostics.Debug.WriteLine("PrintPage ABORTED - Missing data!"); e.Cancel = true; return; } + + Graphics g = e.Graphics; + // Получаем РЕАЛЬНЫЕ границы печатаемой области в единицах Graphics (обычно сотые дюйма) + // Не PageBounds, а PrintableArea! PageBounds - это физический размер листа. + RectangleF printArea = e.PageSettings.PrintableArea; + + // Отступы от КРАЕВ ПЕЧАТАЕМОЙ ОБЛАСТИ (в единицах Graphics) + // Подбирайте эти значения, чтобы получить рамку внутри печатаемой зоны + float marginX = 5 * g.DpiX / 100f; // Пример: 5/100 дюйма -> в единицах Graphics + float marginY = 3 * g.DpiY / 100f; // Пример: 3/100 дюйма -> в единицах Graphics + + // Область для рисования контента внутри отступов + float drawableX = printArea.Left + marginX; + float drawableY = printArea.Top + marginY; + float drawableWidth = printArea.Width - (marginX * 2); + float drawableHeight = printArea.Height - (marginY * 2); + float currentY = drawableY; // Текущая позиция по Y + + // Проверка на валидность области + if (drawableWidth <= 0 || drawableHeight <= 0) { System.Diagnostics.Debug.WriteLine("PrintPage ABORTED - Invalid drawable area!"); e.Cancel = true; return; } + + System.Diagnostics.Debug.WriteLine("--- Printing Page (Dynamic) ---"); + System.Diagnostics.Debug.WriteLine($"Paper: {e.PageSettings.PaperSize.PaperName} ({e.PageSettings.PaperSize.Width}x{e.PageSettings.PaperSize.Height}) hund.inch"); + System.Diagnostics.Debug.WriteLine($"PrintableArea ({g.PageUnit}): X={printArea.X:F2}, Y={printArea.Y:F2}, W={printArea.Width:F2}, H={printArea.Height:F2}"); + System.Diagnostics.Debug.WriteLine($"Drawable Area ({g.PageUnit}): X={drawableX:F2}, Y={drawableY:F2}, W={drawableWidth:F2}, H={drawableHeight:F2}"); + + // Шрифты (размер в пунктах) + float baseFontSizePoints = 6f; // Базовый размер, можно менять + Font titleFont = new Font("Arial", baseFontSizePoints + 1, FontStyle.Bold); + Font regularFont = new Font("Arial", baseFontSizePoints); + Font barcodeTextFont = new Font("Arial", baseFontSizePoints - 1); + + try + { + // --- Рисуем Название --- + string productName = _currentProductName ?? "N/A"; + SizeF titleSize = g.MeasureString(productName, titleFont, (int)drawableWidth); + // Ограничиваем высоту, чтобы поместилось остальное + float maxTitleHeight = drawableHeight * 0.3f; + float actualTitleHeight = Math.Min(titleSize.Height, maxTitleHeight); + g.DrawString(productName, titleFont, Brushes.Black, new RectangleF(drawableX, currentY, drawableWidth, actualTitleHeight)); + currentY += actualTitleHeight + (2 * g.DpiY / 72f); // Отступ 2 пункта + + // --- Рисуем Артикул --- + string skuText = $"Артикул: {_currentSku ?? "N/A"}"; + SizeF skuSize = g.MeasureString(skuText, regularFont, (int)drawableWidth); + if (currentY + skuSize.Height < printArea.Bottom - marginY - 15 * 100f / g.DpiY) // Оставляем ~15px под ШК + текст + { + g.DrawString(skuText, regularFont, Brushes.Black, drawableX, currentY); + currentY += skuSize.Height + (3 * g.DpiY / 72f); // Отступ 3 пункта + } + + // --- Рисуем Штрихкод --- + float remainingHeight = (printArea.Bottom - marginY) - currentY; // Оставшаяся высота + if (remainingHeight > 15 * 100f / g.DpiY) // Если осталось хотя бы ~15px + { + try + { + // --- Расчет размеров ШК --- + float barcodeTextHeight = g.MeasureString(_currentBarcode, barcodeTextFont).Height; + float barcodePrintHeight = Math.Max(10 * 100f / g.DpiY, remainingHeight - barcodeTextHeight - (2 * g.DpiY / 72f)); // Вычитаем текст и отступ + float barcodePrintWidth = drawableWidth * 0.98f; + + int barcodePixelHeight = (int)(barcodePrintHeight * g.DpiY / 100f); + int barcodePixelWidth = (int)(barcodePrintWidth * g.DpiX / 100f); + if (barcodePixelHeight < 10 || barcodePixelWidth < 40) throw new Exception("Слишком мало места для ШК."); + + BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128; + // ... (определение barcodeFormat) ... + if (_currentBarcode.Length == 13 && long.TryParse(_currentBarcode, out _)) barcodeFormat = BarcodeFormat.EAN_13; + else if (_currentBarcode.Length == 8 && long.TryParse(_currentBarcode, out _)) barcodeFormat = BarcodeFormat.EAN_8; + + var barcodeWriter = new ZXing.Windows.Compatibility.BarcodeWriter { Format = barcodeFormat, Options = new EncodingOptions { Height = barcodePixelHeight, Width = barcodePixelWidth, PureBarcode = true, Margin = 1 } }; + + using (var barcodeBitmap = barcodeWriter.Write(_currentBarcode)) + { + float barcodeX = drawableX + (drawableWidth - barcodePrintWidth) / 2.0f; // Центрируем + g.DrawImage(barcodeBitmap, barcodeX, currentY, barcodePrintWidth, barcodePrintHeight); + currentY += barcodePrintHeight + (1 * g.DpiY / 72f); // Отступ 1 пункт + } + + // --- Рисуем текст ШК --- + SizeF barcodeTextSize = g.MeasureString(_currentBarcode, barcodeTextFont); + if (currentY + barcodeTextSize.Height <= printArea.Bottom - marginY) + { + float textX = drawableX + (drawableWidth - barcodeTextSize.Width) / 2.0f; // Центрируем + g.DrawString(_currentBarcode, barcodeTextFont, Brushes.Black, textX, currentY); + } + else { System.Diagnostics.Debug.WriteLine("SKIPPING Barcode text - no space"); } + + } + catch (Exception exBarcode) + { + System.Diagnostics.Debug.WriteLine($"Error drawing barcode: {exBarcode.Message}"); + g.DrawString("Ошибка ШК", regularFont, Brushes.Red, drawableX, currentY); // Рисуем ошибку вместо ШК + } + } + else { System.Diagnostics.Debug.WriteLine("SKIPPING Barcode - not enough vertical space"); } + + } + catch (Exception exDraw) { System.Diagnostics.Debug.WriteLine($"Error drawing page: {exDraw}"); } + finally { titleFont.Dispose(); regularFont.Dispose(); barcodeTextFont.Dispose(); } + + e.HasMorePages = false; + } + + } // Конец класса Form1 +} // Конец namespace \ No newline at end of file diff --git a/Form1.resx b/Form1.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/FormManageStores.Designer.cs b/FormManageStores.Designer.cs new file mode 100644 index 0000000..09e93fd --- /dev/null +++ b/FormManageStores.Designer.cs @@ -0,0 +1,201 @@ +namespace SKLADm +{ + partial class FormManageStores + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.lstStores = new System.Windows.Forms.ListBox(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.btnAddStore = new System.Windows.Forms.Button(); + this.txtEditApiKey = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.txtEditClientId = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtStoreName = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.btnDeleteStore = new System.Windows.Forms.Button(); + this.btnClose = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // lstStores + // + this.lstStores.FormattingEnabled = true; + this.lstStores.Location = new System.Drawing.Point(12, 25); + this.lstStores.Name = "lstStores"; + this.lstStores.Size = new System.Drawing.Size(188, 199); // Adjust height as needed + this.lstStores.TabIndex = 0; + // Add event handler if you want to display details on selection: + // this.lstStores.SelectedIndexChanged += new System.EventHandler(this.lstStores_SelectedIndexChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(121, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Сохраненные магазины:"; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.btnAddStore); + this.groupBox1.Controls.Add(this.txtEditApiKey); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.txtEditClientId); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.txtStoreName); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Location = new System.Drawing.Point(219, 25); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(303, 155); // Adjust size as needed + this.groupBox1.TabIndex = 2; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Добавить новый магазин"; + // + // btnAddStore + // + this.btnAddStore.Location = new System.Drawing.Point(196, 118); + this.btnAddStore.Name = "btnAddStore"; + this.btnAddStore.Size = new System.Drawing.Size(97, 23); + this.btnAddStore.TabIndex = 6; + this.btnAddStore.Text = "Добавить"; + this.btnAddStore.UseVisualStyleBackColor = true; + this.btnAddStore.Click += new System.EventHandler(this.btnAddStore_Click); + // + // txtEditApiKey + // + this.txtEditApiKey.Location = new System.Drawing.Point(74, 86); + this.txtEditApiKey.Name = "txtEditApiKey"; + this.txtEditApiKey.PasswordChar = '*'; + this.txtEditApiKey.Size = new System.Drawing.Size(219, 20); + this.txtEditApiKey.TabIndex = 5; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(16, 89); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(48, 13); + this.label4.TabIndex = 4; + this.label4.Text = "Api-Key:"; + // + // txtEditClientId + // + this.txtEditClientId.Location = new System.Drawing.Point(74, 57); + this.txtEditClientId.Name = "txtEditClientId"; + this.txtEditClientId.Size = new System.Drawing.Size(219, 20); + this.txtEditClientId.TabIndex = 3; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(16, 60); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(50, 13); + this.label3.TabIndex = 2; + this.label3.Text = "Client ID:"; + // + // txtStoreName + // + this.txtStoreName.Location = new System.Drawing.Point(74, 28); + this.txtStoreName.Name = "txtStoreName"; + this.txtStoreName.Size = new System.Drawing.Size(219, 20); + this.txtStoreName.TabIndex = 1; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(16, 31); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(60, 13); + this.label2.TabIndex = 0; + this.label2.Text = "Название:"; + // + // btnDeleteStore + // + this.btnDeleteStore.Location = new System.Drawing.Point(12, 230); // Position below listbox + this.btnDeleteStore.Name = "btnDeleteStore"; + this.btnDeleteStore.Size = new System.Drawing.Size(188, 23); + this.btnDeleteStore.TabIndex = 3; + this.btnDeleteStore.Text = "Удалить выбранный"; + this.btnDeleteStore.UseVisualStyleBackColor = true; + this.btnDeleteStore.Click += new System.EventHandler(this.btnDeleteStore_Click); + // + // btnClose + // + this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; // Makes Esc close the form + this.btnClose.Location = new System.Drawing.Point(447, 230); // Position bottom right + this.btnClose.Name = "btnClose"; + this.btnClose.Size = new System.Drawing.Size(75, 23); + this.btnClose.TabIndex = 4; + this.btnClose.Text = "Закрыть"; + this.btnClose.UseVisualStyleBackColor = true; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // FormManageStores + // + this.AcceptButton = this.btnAddStore; // Enter in groupbox adds store + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnClose; // Esc closes form + this.ClientSize = new System.Drawing.Size(534, 267); // Adjust size as needed + this.Controls.Add(this.btnClose); + this.Controls.Add(this.btnDeleteStore); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.lstStores); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; // Prevent resizing + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FormManageStores"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; // Center over Form1 + this.Text = "Управление магазинами Ozon"; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ListBox lstStores; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button btnAddStore; + private System.Windows.Forms.TextBox txtEditApiKey; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox txtEditClientId; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox txtStoreName; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btnDeleteStore; + private System.Windows.Forms.Button btnClose; + } +} diff --git a/FormManageStores.cs b/FormManageStores.cs new file mode 100644 index 0000000..41df99e --- /dev/null +++ b/FormManageStores.cs @@ -0,0 +1,150 @@ +using Newtonsoft.Json; +using OzonInternalLabelPrinter; // Добавляем пространство имен для OzonStore +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; // Для ProtectedData +using System.Text; +using System.Windows.Forms; + +namespace SKLADm +{ + public partial class FormManageStores : Form + { + private List _stores; + // Энтропия для шифрования (можно оставить null или использовать свой секрет) + private static readonly byte[] s_entropy = null; // Или Encoding.UTF8.GetBytes("MyOptionalEntropy"); + + public FormManageStores() + { + InitializeComponent(); + LoadStoresToListBox(); + } + + // --- Шифрование Api-Key --- + private string EncryptApiKey(string apiKey) + { + if (string.IsNullOrEmpty(apiKey)) return string.Empty; + try + { + byte[] apiKeyBytes = Encoding.UTF8.GetBytes(apiKey); + byte[] encryptedBytes = ProtectedData.Protect(apiKeyBytes, s_entropy, DataProtectionScope.CurrentUser); + return Convert.ToBase64String(encryptedBytes); + } + catch (Exception ex) + { + MessageBox.Show($"Ошибка шифрования ключа: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); + return null; // Возвращаем null при ошибке + } + } + + // --- Загрузка магазинов из настроек --- + private void LoadStoresToListBox() + { + string json = Properties.Settings.Default.SavedStoresJson; + _stores = new List(); + if (!string.IsNullOrEmpty(json)) + { + try + { + _stores = JsonConvert.DeserializeObject>(json) ?? new List(); + } + catch (Exception ex) + { + MessageBox.Show($"Ошибка загрузки списка магазинов: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); + _stores = new List(); // Создаем пустой список при ошибке + } + } + UpdateListBox(); + } + + // --- Сохранение магазинов в настройки --- + private void SaveStores() + { + try + { + string json = JsonConvert.SerializeObject(_stores, Formatting.Indented); + Properties.Settings.Default.SavedStoresJson = json; + Properties.Settings.Default.Save(); + } + catch (Exception ex) + { + MessageBox.Show($"Ошибка сохранения списка магазинов: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + // --- Обновление ListBox --- + private void UpdateListBox() + { + lstStores.DataSource = null; // Сначала отвязываем источник + lstStores.DataSource = _stores; // Привязываем обновленный список + lstStores.DisplayMember = "StoreName"; // Отображаем имя + } + + // --- Кнопка "Добавить магазин" --- + private void btnAddStore_Click(object sender, EventArgs e) + { + string storeName = txtStoreName.Text.Trim(); + string clientId = txtEditClientId.Text.Trim(); + string apiKey = txtEditApiKey.Text.Trim(); // Берем ключ как есть + + if (string.IsNullOrEmpty(storeName) || string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(apiKey)) + { + MessageBox.Show("Пожалуйста, заполните все поля: Название, Client ID, Api-Key.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // Проверяем, нет ли уже магазина с таким именем + if (_stores.Any(s => s.StoreName.Equals(storeName, StringComparison.OrdinalIgnoreCase))) + { + MessageBox.Show("Магазин с таким названием уже существует.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + // Шифруем ключ + string encryptedKey = EncryptApiKey(apiKey); + if (encryptedKey == null) // Если шифрование не удалось + { + return; + } + + // Создаем и добавляем магазин + OzonStore newStore = new OzonStore(storeName, clientId, encryptedKey); + _stores.Add(newStore); + + // Сохраняем и обновляем список + SaveStores(); + UpdateListBox(); + + // Очищаем поля ввода + txtStoreName.Clear(); + txtEditClientId.Clear(); + txtEditApiKey.Clear(); + } + + // --- Кнопка "Удалить выбранный" (Опционально) --- + private void btnDeleteStore_Click(object sender, EventArgs e) + { + if (lstStores.SelectedItem is OzonStore selectedStore) + { + if (MessageBox.Show($"Вы уверены, что хотите удалить магазин '{selectedStore.StoreName}'?", "Подтверждение", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + _stores.Remove(selectedStore); + SaveStores(); + UpdateListBox(); + } + } + else + { + MessageBox.Show("Выберите магазин для удаления.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } + + // --- Кнопка "Закрыть" --- + private void btnClose_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.OK; // Указываем, что форма закрыта штатно + this.Close(); + } + } +} diff --git a/FormManageStores.resx b/FormManageStores.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/FormManageStores.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/OzonStore.cs b/OzonStore.cs new file mode 100644 index 0000000..54b28ee --- /dev/null +++ b/OzonStore.cs @@ -0,0 +1,28 @@ +using System; + +namespace OzonInternalLabelPrinter +{ + [Serializable] // Важно для возможной сериализации в будущем, хотя с JSON необязательно + public class OzonStore + { + public string StoreName { get; set; } // Имя, которое видит пользователь + public string ClientId { get; set; } + public string EncryptedApiKey { get; set; } // Храним ЗАШИФРОВАННЫЙ ключ + + // Конструктор для удобства + public OzonStore(string name, string clientId, string encryptedKey) + { + StoreName = name; + ClientId = clientId; + EncryptedApiKey = encryptedKey; + } + + public OzonStore() { } // Пустой конструктор для сериализации + + // Переопределяем ToString, чтобы в ComboBox отображалось имя + public override string ToString() + { + return StoreName ?? "Без имени"; + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..93fa4e4 --- /dev/null +++ b/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using OzonInternalLabelPrinter; // Добавляем директиву using для пространства имен с Form1 + +namespace SKLADm +{ + internal static class Program + { + /// + /// Главная точка входа для приложения. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d9343ab --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Общие сведения об этой сборке предоставляются следующим набором +// набора атрибутов. Измените значения этих атрибутов для изменения сведений, +// связанных со сборкой. +[assembly: AssemblyTitle("SKLADm")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SKLADm")] +[assembly: AssemblyCopyright("Copyright © 2025")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми +// для компонентов COM. Если необходимо обратиться к типу в этой сборке через +// COM, следует установить атрибут ComVisible в TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("17f6818e-f645-43c4-a25a-eab41e5e1be6")] + +// Сведения о версии сборки состоят из указанных ниже четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 0000000..6630341 --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Этот код создан программным средством. +// Версия среды выполнения: 4.0.30319.42000 +// +// Изменения в этом файле могут привести к неправильному поведению и будут утрачены, если +// код создан повторно. +// +//------------------------------------------------------------------------------ + +namespace SKLADm.Properties +{ + + + /// + /// Класс ресурсов со строгим типом для поиска локализованных строк и пр. + /// + // Этот класс был автоматически создан при помощи StronglyTypedResourceBuilder + // класс с помощью таких средств, как ResGen или Visual Studio. + // Для добавления или удаления члена измените файл .ResX, а затем перезапустите ResGen + // с параметром /str или заново постройте свой VS-проект. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Возврат кэшированного экземпляра ResourceManager, используемого этим классом. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SKLADm.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Переопределяет свойство CurrentUICulture текущего потока для всех + /// подстановки ресурсов с помощью этого класса ресурсов со строгим типом. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 0000000..0c10770 --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// Этот код создан программой. +// Исполняемая версия:4.0.30319.42000 +// +// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае +// повторной генерации кода. +// +//------------------------------------------------------------------------------ + +namespace SKLADm.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string SavedStoresJson { + get { + return ((string)(this["SavedStoresJson"])); + } + set { + this["SavedStoresJson"] = value; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 0000000..da36574 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/SKLADm.csproj b/SKLADm.csproj new file mode 100644 index 0000000..502966b --- /dev/null +++ b/SKLADm.csproj @@ -0,0 +1,122 @@ + + + + + Debug + AnyCPU + {17F6818E-F645-43C4-A25A-EAB41E5E1BE6} + WinExe + SKLADm + SKLADm + v4.6.2 + 512 + true + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + + + + packages\System.Drawing.Common.8.0.12\lib\net462\System.Drawing.Common.dll + + + + packages\System.Security.Cryptography.ProtectedData.9.0.4\lib\net462\System.Security.Cryptography.ProtectedData.dll + + + + + + + + + + + + packages\ZXing.Net.0.16.10\lib\net461\zxing.dll + + + packages\ZXing.Net.0.16.10\lib\net461\zxing.presentation.dll + + + packages\ZXing.Net.Bindings.Windows.Compatibility.0.16.13\lib\netstandard2.0\ZXing.Windows.Compatibility.dll + + + + + Form + + + Form1.cs + + + Form + + + FormManageStores.cs + + + + + + Form1.cs + + + FormManageStores.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}. + + + + \ No newline at end of file diff --git a/SKLADm.sln b/SKLADm.sln new file mode 100644 index 0000000..e5a9478 --- /dev/null +++ b/SKLADm.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35913.81 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SKLADm", "SKLADm.csproj", "{17F6818E-F645-43C4-A25A-EAB41E5E1BE6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {17F6818E-F645-43C4-A25A-EAB41E5E1BE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17F6818E-F645-43C4-A25A-EAB41E5E1BE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17F6818E-F645-43C4-A25A-EAB41E5E1BE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17F6818E-F645-43C4-A25A-EAB41E5E1BE6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {58FC063D-E3B8-421B-BCB0-0349A0C4DCDB} + EndGlobalSection +EndGlobal diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..c09cfa3 --- /dev/null +++ b/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file