From e35e67849f87e5bb1b7e92e38cd74385018050f1 Mon Sep 17 00:00:00 2001 From: Exest Date: Tue, 28 Jul 2026 11:41:00 +0300 Subject: [PATCH] =?UTF-8?q?v1.0.4:=20=D0=B0=D0=B4=D0=B0=D0=BF=D1=82=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20desktop=20=D0=BA=20=D1=82=D0=B5=D0=BA?= =?UTF-8?q?=D1=83=D1=89=D0=B5=D0=BC=D1=83=20buyer=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Перевести desktop-клиент на работу с текущими buyer orders и локальным SQLite-кешем, чтобы отправка заказов и синхронизация документов работали с уже развернутым сервером без старых MSSQL-зависимостей. Co-authored-by: Cursor --- src/ElectronicPharmacy/Classes/ApiClient.cs | 274 +++++++++++++++++- src/ElectronicPharmacy/Classes/DataSender.cs | 26 +- .../Classes/InvoiceSyncService.cs | 249 ++++++++++++++-- .../ElectronicPharmacy.csproj | 3 - .../Forms/FCheckDatabaseIntegrary.cs | 4 +- .../HelpForms/HF_Comment.cs | 25 +- .../UserControls/FOrders.cs | 22 +- .../UserControls/UCInvoices.cs | 22 +- .../UserControls/UCRefusals.cs | 48 +-- 9 files changed, 570 insertions(+), 103 deletions(-) diff --git a/src/ElectronicPharmacy/Classes/ApiClient.cs b/src/ElectronicPharmacy/Classes/ApiClient.cs index 1706e0b..e1e9894 100644 --- a/src/ElectronicPharmacy/Classes/ApiClient.cs +++ b/src/ElectronicPharmacy/Classes/ApiClient.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; +using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using System.Text.Json; +using System.Text.Json.Serialization; namespace Электронная_Фармация.Classes { @@ -155,20 +158,101 @@ namespace Электронная_Фармация.Classes } public async Task> GetInvoicesAsync() + { + var orders = await GetBuyerOrdersAsync(status: "Placed", limit: 200); + var result = new List(); + + foreach (var order in orders) + { + var detail = await GetBuyerOrderByIdAsync(order.OrderID); + if (detail == null) + { + continue; + } + + result.Add(InvoiceApiItem.FromBuyerOrder(detail)); + } + + AppDebugLog.Info("ApiClient", $"Накладные (через buyer/orders) получены: {result.Count} шт."); + return result; + } + + public async Task> GetBuyerOrdersAsync(string status = null, int limit = 200, int offset = 0) { EnsureAuthenticated(); - var responseBody = await SendAuthorizedGetAsync("/api/buyer/invoices", "загрузку накладных"); + var query = new List(); + if (!string.IsNullOrWhiteSpace(status)) + { + query.Add($"status={Uri.EscapeDataString(status)}"); + } + + if (limit > 0) + { + query.Add($"limit={limit}"); + } + + if (offset > 0) + { + query.Add($"offset={offset}"); + } + + var path = "/api/buyer/orders"; + if (query.Count > 0) + { + path += "?" + string.Join("&", query); + } + + var responseBody = await SendAuthorizedGetAsync(path, "загрузку заказов"); try { - var items = JsonSerializer.Deserialize>(responseBody, JsonOptions) ?? new List(); - AppDebugLog.Info("ApiClient", $"Накладные получены: {items.Count} шт."); - return items; + var response = JsonSerializer.Deserialize(responseBody, JsonOptions); + return response?.Orders ?? new List(); } catch (Exception ex) { - AppDebugLog.Error("ApiClient", "Не удалось разобрать ответ накладных", ex); - throw new HttpRequestException($"Некорректный ответ сервера при загрузке накладных: {ex.Message}", ex); + AppDebugLog.Error("ApiClient", "Не удалось разобрать список заказов", ex); + throw new HttpRequestException($"Некорректный ответ сервера при загрузке заказов: {ex.Message}", ex); + } + } + + public async Task GetBuyerOrderByIdAsync(string orderId) + { + EnsureAuthenticated(); + if (string.IsNullOrWhiteSpace(orderId)) + { + throw new ArgumentException("Не указан ID заказа.", nameof(orderId)); + } + + var responseBody = await SendAuthorizedGetAsync($"/api/buyer/orders/{Uri.EscapeDataString(orderId)}", "загрузку заказа"); + try + { + return JsonSerializer.Deserialize(responseBody, JsonOptions); + } + catch (Exception ex) + { + AppDebugLog.Error("ApiClient", $"Не удалось разобрать заказ {orderId}", ex); + throw new HttpRequestException($"Некорректный ответ сервера при загрузке заказа: {ex.Message}", ex); + } + } + + public async Task PlaceBuyerOrderAsync(string orderId) + { + EnsureAuthenticated(); + if (string.IsNullOrWhiteSpace(orderId)) + { + throw new ArgumentException("Не указан ID заказа.", nameof(orderId)); + } + + var responseBody = await SendAuthorizedPostAsync($"/api/buyer/orders/{Uri.EscapeDataString(orderId)}/place", string.Empty, "оформление заказа"); + try + { + return JsonSerializer.Deserialize(responseBody, JsonOptions); + } + catch (Exception ex) + { + AppDebugLog.Error("ApiClient", $"Не удалось разобрать ответ оформления заказа {orderId}", ex); + throw new HttpRequestException($"Некорректный ответ сервера при оформлении заказа: {ex.Message}", ex); } } @@ -187,6 +271,63 @@ namespace Электронная_Фармация.Classes return await SendAuthorizedGetWithProgressAsync(path, operationName, null); } + private async Task SendAuthorizedPostAsync(string path, string requestBody, string operationName) + { + var fullUrl = BuildFullUrl(path); + var stopwatch = Stopwatch.StartNew(); + + AppDebugLog.ApiRequest("POST", fullUrl, requestBody, $"token={AppDebugLog.MaskToken(_token)}"); + + try + { + using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, fullUrl)) + { + httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); + httpRequest.Content = new StringContent(requestBody ?? string.Empty, Encoding.UTF8, "application/json"); + var response = await _httpClient.SendAsync(httpRequest); + var responseBody = await response.Content.ReadAsStringAsync(); + stopwatch.Stop(); + + AppDebugLog.ApiResponse("POST", fullUrl, (int)response.StatusCode, stopwatch.ElapsedMilliseconds, responseBody); + + if (response.IsSuccessStatusCode) + { + return responseBody; + } + + if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) + { + _token = null; + var error = TryDeserializeError(responseBody); + var message = $"Сессия истекла: {error?.Error ?? "Необходимо войти заново"}"; + AppDebugLog.ApiHttpError("POST", fullUrl, (int)response.StatusCode, responseBody, operationName); + throw new UnauthorizedAccessException(message); + } + + var httpError = TryDeserializeError(responseBody); + var httpMessage = $"Ошибка HTTP {response.StatusCode}: {httpError?.Error ?? responseBody}"; + AppDebugLog.ApiHttpError("POST", fullUrl, (int)response.StatusCode, responseBody, operationName); + throw new HttpRequestException(httpMessage); + } + } + catch (UnauthorizedAccessException) + { + throw; + } + catch (HttpRequestException ex) when (!ex.Message.StartsWith("Ошибка HTTP")) + { + stopwatch.Stop(); + AppDebugLog.ApiFailure("POST", fullUrl, stopwatch.ElapsedMilliseconds, ex); + throw WrapNetworkError(ex, operationName); + } + catch (TaskCanceledException ex) + { + stopwatch.Stop(); + AppDebugLog.ApiFailure("POST", fullUrl, stopwatch.ElapsedMilliseconds, ex); + throw WrapNetworkError(ex, operationName); + } + } + private async Task SendAuthorizedGetWithProgressAsync( string path, string operationName, @@ -372,6 +513,127 @@ namespace Электронная_Фармация.Classes public string ConsigneeName { get; set; } public string InvoiceSum { get; set; } public string RefuseSum { get; set; } + + public string SourceOrderId { get; set; } + public List Items { get; set; } = new List(); + + public static InvoiceApiItem FromBuyerOrder(BuyerOrderDetailDto detail) + { + var supplierNames = detail.Items == null + ? string.Empty + : string.Join(", ", detail.Items + .Select(i => i.Supplier) + .Where(s => !string.IsNullOrWhiteSpace(s)) + .Distinct()); + + var invoiceDate = detail.PlacedAt ?? detail.CreatedAt; + var invoice = new InvoiceApiItem + { + InvoiceNumber = string.IsNullOrWhiteSpace(detail.LocalOrderNumber) + ? detail.OrderID + : detail.LocalOrderNumber, + InvoiceDate = invoiceDate?.ToString("yyyy-MM-dd") ?? string.Empty, + SupplierName = supplierNames, + ConsigneeName = detail.LocationAddress ?? string.Empty, + InvoiceSum = (detail.TotalAmount ?? 0m).ToString(System.Globalization.CultureInfo.InvariantCulture), + RefuseSum = "0", + SourceOrderId = detail.OrderID + }; + + if (detail.Items != null) + { + foreach (var item in detail.Items) + { + invoice.Items.Add(new InvoiceApiOrderItem + { + GoodCode = item.ItemCode ?? string.Empty, + GoodName = item.Name ?? string.Empty, + SupplierName = item.Supplier ?? string.Empty, + Qty = item.Qty, + Price = item.UnitPrice, + Total = item.Total + }); + } + } + + return invoice; + } + } + + public sealed class InvoiceApiOrderItem + { + public string GoodCode { get; set; } + public string GoodName { get; set; } + public string SupplierName { get; set; } + public decimal Qty { get; set; } + public decimal Price { get; set; } + public decimal Total { get; set; } + } + + public sealed class BuyerOrdersResponse + { + [JsonPropertyName("orders")] + public List Orders { get; set; } + } + + public sealed class BuyerOrderListItemDto + { + [JsonPropertyName("order_id")] + public string OrderID { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + } + + public sealed class BuyerOrderDetailDto + { + [JsonPropertyName("order_id")] + public string OrderID { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("total_amount")] + public decimal? TotalAmount { get; set; } + + [JsonPropertyName("created_at")] + public DateTime? CreatedAt { get; set; } + + [JsonPropertyName("placed_at")] + public DateTime? PlacedAt { get; set; } + + [JsonPropertyName("comment")] + public string Comment { get; set; } + + [JsonPropertyName("location_address")] + public string LocationAddress { get; set; } + + [JsonPropertyName("items")] + public List Items { get; set; } + + [JsonIgnore] + public string LocalOrderNumber { get; set; } + } + + public sealed class BuyerOrderItemDetailDto + { + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("supplier")] + public string Supplier { get; set; } + + [JsonPropertyName("qty")] + public decimal Qty { get; set; } + + [JsonPropertyName("unit_price")] + public decimal UnitPrice { get; set; } + + [JsonPropertyName("total")] + public decimal Total { get; set; } + + [JsonPropertyName("item_code")] + public string ItemCode { get; set; } } public sealed class HttpTransferProgress diff --git a/src/ElectronicPharmacy/Classes/DataSender.cs b/src/ElectronicPharmacy/Classes/DataSender.cs index 827a5e1..941e0f3 100644 --- a/src/ElectronicPharmacy/Classes/DataSender.cs +++ b/src/ElectronicPharmacy/Classes/DataSender.cs @@ -272,7 +272,13 @@ WHERE Orders.OrderState = 'НОВЫЙ' return false; } - MarkOrderSent(order.LocalOrderId); + var createdOrder = JsonSerializer.Deserialize(responseString, options); + if (createdOrder == null || string.IsNullOrWhiteSpace(createdOrder.OrderID)) + { + throw new InvalidOperationException("Сервер не вернул order_id созданного заказа."); + } + + MarkOrderSent(order.LocalOrderId, createdOrder.OrderID, createdOrder.Status); AppDebugLog.Info("DataSender", $"Заказ {order.LocalOrderId} отправлен"); return true; } @@ -286,7 +292,7 @@ WHERE Orders.OrderState = 'НОВЫЙ' } } - private static void MarkOrderSent(string localOrderId) + private static void MarkOrderSent(string localOrderId, string remoteOrderId, string remoteStatus) { if (string.IsNullOrWhiteSpace(localOrderId)) { @@ -297,9 +303,14 @@ WHERE Orders.OrderState = 'НОВЫЙ' { con.Open(); using (var cmd = new SQLiteCommand( - "UPDATE [Orders] SET [OrderState] = 'ОТПРАВЛЕН' WHERE [Id_Order] = @id AND [OrderState] = 'НОВЫЙ'", + @"UPDATE [Orders] + SET [OrderState] = @state, + [RemoteOrderId] = @remoteOrderId + WHERE [Id_Order] = @id AND [OrderState] = 'НОВЫЙ'", con)) { + cmd.Parameters.AddWithValue("@state", "ОТПРАВЛЕН"); + cmd.Parameters.AddWithValue("@remoteOrderId", remoteOrderId ?? string.Empty); cmd.Parameters.AddWithValue("@id", localOrderId); cmd.ExecuteNonQuery(); } @@ -357,4 +368,13 @@ WHERE Orders.OrderState = 'НОВЫЙ' [JsonPropertyName("item_code")] public string ItemCode { get; set; } } + + public sealed class BuyerOrderCreateResult + { + [JsonPropertyName("order_id")] + public string OrderID { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + } } diff --git a/src/ElectronicPharmacy/Classes/InvoiceSyncService.cs b/src/ElectronicPharmacy/Classes/InvoiceSyncService.cs index d8a7107..35cba7f 100644 --- a/src/ElectronicPharmacy/Classes/InvoiceSyncService.cs +++ b/src/ElectronicPharmacy/Classes/InvoiceSyncService.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Data.SQLite; +using System.Globalization; +using System.Linq; using System.Threading.Tasks; using Электронная_Фармация.Properties; @@ -26,7 +28,29 @@ namespace Электронная_Фармация.Classes var client = new ApiClient(); client.SetToken(token); AppDebugLog.Info("InvoiceSync", $"Старт синхронизации накладных. Сервер={client.BaseUrl}"); - var items = await client.GetInvoicesAsync(); + var orders = await client.GetBuyerOrdersAsync(limit: 200); + var items = new List(); + using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString)) + { + con.Open(); + foreach (var order in orders) + { + if (string.Equals(order.Status, "Cancelled", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var detail = await client.GetBuyerOrderByIdAsync(order.OrderID); + if (detail == null) + { + continue; + } + + detail.LocalOrderNumber = TryGetLocalOrderNumber(con, detail.OrderID); + items.Add(InvoiceApiItem.FromBuyerOrder(detail)); + } + } + var saved = SaveInvoices(items); AppDebugLog.Info("InvoiceSync", $"Накладные сохранены локально: {saved} шт."); return saved; @@ -40,30 +64,32 @@ namespace Электронная_Фармация.Classes private static int SaveInvoices(IReadOnlyList items) { - if (items.Count == 0) - { - return 0; - } - using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString)) { con.Open(); using (var tx = con.BeginTransaction()) { + using (var cleanupItems = new SQLiteCommand(@" +DELETE FROM InvoiceItem +WHERE idInvoice IN ( + SELECT idInvoice FROM Invoice WHERE ifnull(IsRequestedInvoice, 0) = 0 +);", con, tx)) + { + cleanupItems.ExecuteNonQuery(); + } + + using (var cleanupInvoices = new SQLiteCommand( + "DELETE FROM Invoice WHERE ifnull(IsRequestedInvoice, 0) = 0;", + con, + tx)) + { + cleanupInvoices.ExecuteNonQuery(); + } + foreach (var item in items) { - using (var cmd = new SQLiteCommand(@" -INSERT OR REPLACE INTO Invoice (InvoiceNumber, invoiceDate, SupplierName, ConsigneesName, InvoiceSum, RefuseSum) -VALUES (@number, @date, @supplier, @consignee, @sum, @refuseSum)", con, tx)) - { - cmd.Parameters.AddWithValue("@number", item.InvoiceNumber ?? string.Empty); - cmd.Parameters.AddWithValue("@date", item.InvoiceDate ?? string.Empty); - cmd.Parameters.AddWithValue("@supplier", item.SupplierName ?? string.Empty); - cmd.Parameters.AddWithValue("@consignee", item.ConsigneeName ?? string.Empty); - cmd.Parameters.AddWithValue("@sum", item.InvoiceSum ?? string.Empty); - cmd.Parameters.AddWithValue("@refuseSum", item.RefuseSum ?? string.Empty); - cmd.ExecuteNonQuery(); - } + var invoiceId = UpsertInvoice(con, tx, item); + SaveInvoiceItems(con, tx, invoiceId, item); } tx.Commit(); @@ -72,5 +98,192 @@ VALUES (@number, @date, @supplier, @consignee, @sum, @refuseSum)", con, tx)) return items.Count; } + + private static int UpsertInvoice(SQLiteConnection con, SQLiteTransaction tx, InvoiceApiItem item) + { + using (var findCmd = new SQLiteCommand(@" +SELECT [idInvoice] +FROM [Invoice] +WHERE cast([SourceOrderId] as text) = @sourceOrderId +LIMIT 1;", con, tx)) + { + findCmd.Parameters.AddWithValue("@sourceOrderId", item.SourceOrderId ?? string.Empty); + var existing = findCmd.ExecuteScalar(); + if (existing != null && existing != DBNull.Value) + { + var invoiceId = Convert.ToInt32(existing, CultureInfo.InvariantCulture); + using (var updateCmd = new SQLiteCommand(@" +UPDATE [Invoice] +SET [InvoiceNumber] = @number, + [InvoiceDate] = @date, + [SupplierName] = @supplier, + [ConsigneesName] = @consignee, + [InvoiceSum] = @sum, + [RefuseSum] = @refuseSum, + [SourceOrderNumber] = @sourceOrderNumber, + [IsRequestedInvoice] = 0 +WHERE [idInvoice] = @invoiceId;", con, tx)) + { + updateCmd.Parameters.AddWithValue("@number", item.InvoiceNumber ?? string.Empty); + updateCmd.Parameters.AddWithValue("@date", item.InvoiceDate ?? string.Empty); + updateCmd.Parameters.AddWithValue("@supplier", item.SupplierName ?? string.Empty); + updateCmd.Parameters.AddWithValue("@consignee", item.ConsigneeName ?? string.Empty); + updateCmd.Parameters.AddWithValue("@sum", item.InvoiceSum ?? "0"); + updateCmd.Parameters.AddWithValue("@refuseSum", item.RefuseSum ?? "0"); + updateCmd.Parameters.AddWithValue("@sourceOrderNumber", item.InvoiceNumber ?? string.Empty); + updateCmd.Parameters.AddWithValue("@invoiceId", invoiceId); + updateCmd.ExecuteNonQuery(); + } + + using (var deleteItems = new SQLiteCommand("DELETE FROM [InvoiceItem] WHERE [idInvoice] = @invoiceId;", con, tx)) + { + deleteItems.Parameters.AddWithValue("@invoiceId", invoiceId); + deleteItems.ExecuteNonQuery(); + } + + return invoiceId; + } + } + + using (var insertCmd = new SQLiteCommand(@" +INSERT INTO [Invoice] +( + [InvoiceNumber], + [InvoiceDate], + [CodeSupplier], + [CodeConsignees], + [SumWithoutNDS], + [SumWithNDS], + [SumNDS], + [SupplierName], + [ConsigneesName], + [InvoiceSum], + [RefuseSum], + [SourceOrderId], + [SourceOrderNumber], + [IsRequestedInvoice] +) +VALUES +( + @number, + @date, + 0, + 0, + @sum, + @sum, + 0, + @supplier, + @consignee, + @sum, + @refuseSum, + @sourceOrderId, + @sourceOrderNumber, + 0 +); +SELECT last_insert_rowid();", con, tx)) + { + insertCmd.Parameters.AddWithValue("@number", item.InvoiceNumber ?? string.Empty); + insertCmd.Parameters.AddWithValue("@date", item.InvoiceDate ?? string.Empty); + insertCmd.Parameters.AddWithValue("@sum", item.InvoiceSum ?? "0"); + insertCmd.Parameters.AddWithValue("@supplier", item.SupplierName ?? string.Empty); + insertCmd.Parameters.AddWithValue("@consignee", item.ConsigneeName ?? string.Empty); + insertCmd.Parameters.AddWithValue("@refuseSum", item.RefuseSum ?? "0"); + insertCmd.Parameters.AddWithValue("@sourceOrderId", item.SourceOrderId ?? string.Empty); + insertCmd.Parameters.AddWithValue("@sourceOrderNumber", item.InvoiceNumber ?? string.Empty); + return Convert.ToInt32(insertCmd.ExecuteScalar(), CultureInfo.InvariantCulture); + } + } + + private static void SaveInvoiceItems(SQLiteConnection con, SQLiteTransaction tx, int invoiceId, InvoiceApiItem item) + { + foreach (var invoiceItem in item.Items ?? Enumerable.Empty()) + { + using (var cmd = new SQLiteCommand(@" +INSERT INTO [InvoiceItem] +( + [idGood], + [GoodCode], + [Good], + [ProducerName], + [CountryName], + [idInvoice], + [GoodsGount], + [PriceSupplierWithoutNDS], + [PriceSupplierWithNDS], + [PriceProducerWithoutNDS], + [PriceProducerWithNDS], + [NDS], + [JNVLS], + [PriceReestr], + [SumWithoutNDS], + [SumWithNDS], + [SumNDS], + [Serial], + [BestBefore], + [Sertificate], + [Marked], + [GTIN] +) +VALUES +( + null, + @goodCode, + @good, + '', + '', + @invoiceId, + @qty, + @price, + @price, + null, + null, + 0, + 0, + null, + @sum, + @sum, + 0, + '', + '', + '', + 0, + '' +)", con, tx)) + { + cmd.Parameters.AddWithValue("@goodCode", ParseGoodCode(invoiceItem.GoodCode)); + cmd.Parameters.AddWithValue("@good", invoiceItem.GoodName ?? string.Empty); + cmd.Parameters.AddWithValue("@invoiceId", invoiceId); + cmd.Parameters.AddWithValue("@qty", invoiceItem.Qty); + cmd.Parameters.AddWithValue("@price", invoiceItem.Price); + cmd.Parameters.AddWithValue("@sum", invoiceItem.Total); + cmd.ExecuteNonQuery(); + } + } + } + + private static int ParseGoodCode(string value) + { + int parsed; + return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsed) ? parsed : 0; + } + + private static string TryGetLocalOrderNumber(SQLiteConnection con, string remoteOrderId) + { + if (string.IsNullOrWhiteSpace(remoteOrderId)) + { + return null; + } + + using (var cmd = new SQLiteCommand(@" +SELECT [OrderNumber] +FROM [Orders] +WHERE [RemoteOrderId] = @remoteOrderId +LIMIT 1;", con)) + { + cmd.Parameters.AddWithValue("@remoteOrderId", remoteOrderId); + var value = cmd.ExecuteScalar(); + return value == null || value == DBNull.Value ? null : value.ToString(); + } + } } } diff --git a/src/ElectronicPharmacy/ElectronicPharmacy.csproj b/src/ElectronicPharmacy/ElectronicPharmacy.csproj index f961d0c..47d2d62 100644 --- a/src/ElectronicPharmacy/ElectronicPharmacy.csproj +++ b/src/ElectronicPharmacy/ElectronicPharmacy.csproj @@ -301,9 +301,6 @@ - - PreserveNewest - diff --git a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs index 2ff4df3..289cfd6 100644 --- a/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs +++ b/src/ElectronicPharmacy/Forms/FCheckDatabaseIntegrary.cs @@ -250,7 +250,8 @@ create table if not exists [Orders] ( [SummaZakaza] nvarchar(32), [OrderState] nvarchar(32), [Comment] nvarchar(1024), -[ConsigneeName] nvarchar(128) +[ConsigneeName] nvarchar(128), +[RemoteOrderId] nvarchar(36) ) "; @@ -400,6 +401,7 @@ create table if not exists [OrderItems] ( TryAddColumn(conForCheckTables, "Invoice", "SourceOrderId", "integer"); TryAddColumn(conForCheckTables, "Invoice", "SourceOrderNumber", "nvarchar(36)"); TryAddColumn(conForCheckTables, "Invoice", "IsRequestedInvoice", "int(1)"); + TryAddColumn(conForCheckTables, "Orders", "RemoteOrderId", "nvarchar(36)"); TryAddColumn(conForCheckTables, "PriceList", "TradeName", "nvarchar(256)"); TryAddColumn(conForCheckTables, "PriceList", "Dosage", "nvarchar(128)"); TryAddColumn(conForCheckTables, "PriceList", "MarkupPercent", "real"); diff --git a/src/ElectronicPharmacy/HelpForms/HF_Comment.cs b/src/ElectronicPharmacy/HelpForms/HF_Comment.cs index e447bd9..c903ecb 100644 --- a/src/ElectronicPharmacy/HelpForms/HF_Comment.cs +++ b/src/ElectronicPharmacy/HelpForms/HF_Comment.cs @@ -43,22 +43,25 @@ using Электронная_Фармация.Classes; using (SQLiteConnection conAddComment = new SQLiteConnection(connectionStringToLocalDB)) { - - string qShowMeStatusOrder = $"select OrderState from Orders where Id_Order = '{_idOrder}'"; - - string qAddComment = $"update Orders set Comment = '{rtxtComment.Text}' where Id_Order = '{_idOrder}'"; try { conAddComment.Open(); - SQLiteCommand cmdShowMeOrderStatus = new SQLiteCommand(qShowMeStatusOrder, conAddComment); - - string OrderState = cmdShowMeOrderStatus.ExecuteScalar().ToString(); - - if (OrderState == "НОВЫЙ") + string orderState; + using (var cmdShowMeOrderStatus = new SQLiteCommand("select OrderState from Orders where Id_Order = @id", conAddComment)) { - SQLiteCommand cmdAddComment = new SQLiteCommand(qAddComment, conAddComment); - cmdAddComment.ExecuteNonQuery(); + cmdShowMeOrderStatus.Parameters.AddWithValue("@id", _idOrder); + orderState = Convert.ToString(cmdShowMeOrderStatus.ExecuteScalar()) ?? string.Empty; + } + + if (orderState == "НОВЫЙ") + { + using (var cmdAddComment = new SQLiteCommand("update Orders set Comment = @comment where Id_Order = @id", conAddComment)) + { + cmdAddComment.Parameters.AddWithValue("@comment", rtxtComment.Text); + cmdAddComment.Parameters.AddWithValue("@id", _idOrder); + cmdAddComment.ExecuteNonQuery(); + } } else { diff --git a/src/ElectronicPharmacy/UserControls/FOrders.cs b/src/ElectronicPharmacy/UserControls/FOrders.cs index 6baa273..eb733ac 100644 --- a/src/ElectronicPharmacy/UserControls/FOrders.cs +++ b/src/ElectronicPharmacy/UserControls/FOrders.cs @@ -394,9 +394,9 @@ namespace Электронная_Фармация.UserControls try { - var service = new InvoiceRequestService(); - service.CreateOrUpdateFromOrder(orderId); - ToastNotification.ShowSuccess("Накладная добавлена в раздел \"Накладные\"."); + var service = new InvoiceSyncService(); + service.SyncFromServerAsync().GetAwaiter().GetResult(); + ToastNotification.ShowSuccess("Накладные обновлены с сервера."); var shell = FindForm() as ElectroPharmacy; shell?.OpenInvoicesTab(orderId); @@ -508,7 +508,11 @@ and OrderDate <= '{dateTo}' void loadConsignees() { string connectionStringToLocalDB = AppConfig.SqliteConnectionString; - string qShowMeConsigneesList = "select ConsigneesName from Consignees order by [ConsigneesUseAsDefault] desc"; + string qShowMeConsigneesList = @" +select distinct ConsigneeName +from Orders +where trim(ifnull(ConsigneeName, '')) <> '' +order by ConsigneeName"; using (SQLiteConnection conConsignees = new SQLiteConnection(connectionStringToLocalDB)) { @@ -527,7 +531,7 @@ and OrderDate <= '{dateTo}' var items = new List { "Грузополучатель" }; items.AddRange(tableConsignees.Rows.Cast() - .Select(r => r["ConsigneesName"]?.ToString()) + .Select(r => r["ConsigneeName"]?.ToString()) .Where(n => !string.IsNullOrWhiteSpace(n))); _comboConsignees.DataSource = items; _comboConsignees.SelectedIndex = 0; @@ -547,7 +551,11 @@ and OrderDate <= '{dateTo}' { string connectionStringToLocalDB = AppConfig.SqliteConnectionString; - string qShowMeSuppliersList = "select SuppliersName from suppliers order by [SuppliersName] asc"; + string qShowMeSuppliersList = @" +select distinct SupplierName +from Orders +where trim(ifnull(SupplierName, '')) <> '' +order by SupplierName asc"; using (SQLiteConnection conSuppliers = new SQLiteConnection(connectionStringToLocalDB)) { @@ -559,7 +567,7 @@ and OrderDate <= '{dateTo}' DataTable tableSuppliers = new DataTable(); daSuppliersList.Fill(tableSuppliers); comboSuppliers.DataSource = tableSuppliers; - comboSuppliers.DisplayMember = "SuppliersName"; + comboSuppliers.DisplayMember = "SupplierName"; comboSuppliers.Text = "Поставщик"; } catch (Exception ex) diff --git a/src/ElectronicPharmacy/UserControls/UCInvoices.cs b/src/ElectronicPharmacy/UserControls/UCInvoices.cs index ba51fdc..9ea5b2e 100644 --- a/src/ElectronicPharmacy/UserControls/UCInvoices.cs +++ b/src/ElectronicPharmacy/UserControls/UCInvoices.cs @@ -57,7 +57,11 @@ namespace Электронная_Фармация.UserControls { string connectionStringToLocalDB = AppConfig.SqliteConnectionString; - string qShowMeSuppliersList = "select SuppliersName from suppliers order by [SuppliersName] asc"; + string qShowMeSuppliersList = @" +select distinct SupplierName +from Invoice +where trim(ifnull(SupplierName, '')) <> '' +order by SupplierName asc"; using (SQLiteConnection conSuppliers = new SQLiteConnection(connectionStringToLocalDB)) { @@ -69,7 +73,7 @@ namespace Электронная_Фармация.UserControls DataTable tableSuppliers = new DataTable(); daSuppliersList.Fill(tableSuppliers); comboSuppliers.DataSource = tableSuppliers; - comboSuppliers.DisplayMember = "SuppliersName"; + comboSuppliers.DisplayMember = "SupplierName"; comboSuppliers.Text = "Поставщик"; } catch(Exception ex) @@ -87,7 +91,11 @@ namespace Электронная_Фармация.UserControls { string connectionStringToLocalDB = AppConfig.SqliteConnectionString; - string qShowMeConsigneesList = "select ConsigneesName from Consignees order by [ConsigneesUseAsDefault] desc"; + string qShowMeConsigneesList = @" +select distinct ConsigneesName +from Invoice +where trim(ifnull(ConsigneesName, '')) <> '' +order by ConsigneesName asc"; using (SQLiteConnection conConsignees = new SQLiteConnection(connectionStringToLocalDB)) { @@ -127,8 +135,8 @@ select i.[idInvoice] as [ИД накладной], i.[InvoiceNumber] as [Номер], i.[InvoiceDate] as [Дата], - coalesce(nullif(i.[SupplierName], ''), (select s.[SuppliersName] from [Suppliers] s where s.[codeSuppliers] = i.[CodeSupplier])) as [Поставщик], - coalesce(nullif(i.[ConsigneesName], ''), (select c.[ConsigneesName] from [Consignees] c where c.[codeConsignees] = i.[CodeConsignees])) as [Грузополучатель], + ifnull(i.[SupplierName], '') as [Поставщик], + ifnull(i.[ConsigneesName], '') as [Грузополучатель], i.[SourceOrderNumber] as [Номер заказа], coalesce(i.[InvoiceSum], i.[SumWithNDS], i.[SumWithoutNDS], 0) as [Сумма], coalesce(i.[RefuseSum], 0) as [Сумма отказа] @@ -142,12 +150,12 @@ where date(i.[InvoiceDate]) between date(@dateFrom) and date(@dateTo)"; if (comboSuppliers.Text != "" && comboSuppliers.Text != "Поставщик") { - sql += " and coalesce(nullif(i.[SupplierName], ''), (select s.[SuppliersName] from [Suppliers] s where s.[codeSuppliers] = i.[CodeSupplier])) like @supplierName"; + sql += " and ifnull(i.[SupplierName], '') like @supplierName"; } if (comboConsignees.Text != "" && comboConsignees.Text != "Грузополучатель") { - sql += " and coalesce(nullif(i.[ConsigneesName], ''), (select c.[ConsigneesName] from [Consignees] c where c.[codeConsignees] = i.[CodeConsignees])) like @consigneeName"; + sql += " and ifnull(i.[ConsigneesName], '') like @consigneeName"; } if (!string.IsNullOrWhiteSpace(_initialSourceOrderId)) diff --git a/src/ElectronicPharmacy/UserControls/UCRefusals.cs b/src/ElectronicPharmacy/UserControls/UCRefusals.cs index df42bdd..3185ecd 100644 --- a/src/ElectronicPharmacy/UserControls/UCRefusals.cs +++ b/src/ElectronicPharmacy/UserControls/UCRefusals.cs @@ -124,12 +124,7 @@ namespace Электронная_Фармация.UserControls using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString)) { con.Open(); - - var orderColumns = GetColumns(con, "Orders"); - var usesNewSchema = orderColumns.Contains("Id_Order") && orderColumns.Contains("OrderedCountItems"); - - var q = usesNewSchema ? GetQueryForNewSchema() : GetQueryForOldSchema(); - using (var cmd = new SQLiteCommand(q, con)) + using (var cmd = new SQLiteCommand(GetQueryForNewSchema(), con)) using (var da = new SQLiteDataAdapter(cmd)) { var table = new DataTable(); @@ -186,26 +181,6 @@ order by oi.[DrugName]", con)) } } } - - private static HashSet GetColumns(SQLiteConnection con, string tableName) - { - var columns = new HashSet(StringComparer.OrdinalIgnoreCase); - using (var cmd = new SQLiteCommand($"pragma table_info([{tableName}]);", con)) - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - var name = reader["name"] as string; - if (!string.IsNullOrWhiteSpace(name)) - { - columns.Add(name); - } - } - } - - return columns; - } - private static string GetQueryForNewSchema() { return @" @@ -237,26 +212,5 @@ group by having sum(ifnull(oi.[RefuseItemsCount], 0)) > 0 order by o.[OrderDate] desc;"; } - - private static string GetQueryForOldSchema() - { - return @" -select - o.[OrderNumber] as [Номер заказа], - o.[OrderDate] as [Дата], - supp.[SuppliersName] as [Поставщик], - consign.[ConsigneesName] as [Грузополучатель], - o.[OrderedItemsCount] as [К-во товара в заказе], - o.[OrderSum] as [Сумма заказа], - o.[RefuseItemsCount] as [К-во отказанного товара], - o.[RefuseSum] as [Сумма отказа], - o.[Comment] as [Комментарий], - o.[OrderState] as [Статус] -from Orders as o -inner join Consignees consign on consign.codeConsignees = o.codeConsignees -inner join Suppliers supp on supp.codeSuppliers = o.codeSuppliers -where ifnull(o.[RefuseItemsCount], 0) > 0 or ifnull(o.[RefuseSum], 0) > 0 -order by [OrderDate] desc;"; - } } }