Add per-pharmacy carts and client-side summed discount pricing.
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>
This commit is contained in:
parent
458b70e036
commit
fd5bd18c66
@ -157,6 +157,164 @@ namespace Электронная_Фармация.Classes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<BuyerDto>> GetBuyersAsync()
|
||||||
|
{
|
||||||
|
EnsureAuthenticated();
|
||||||
|
var responseBody = await SendAuthorizedGetAsync("/api/buyers", "загрузку покупателей");
|
||||||
|
return DeserializeBuyers(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BuyerDto> GetBuyerByIdAsync(string buyerId)
|
||||||
|
{
|
||||||
|
EnsureAuthenticated();
|
||||||
|
if (string.IsNullOrWhiteSpace(buyerId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Не указан buyer_id.", nameof(buyerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseBody = await SendAuthorizedGetAsync(
|
||||||
|
$"/api/buyers/{Uri.EscapeDataString(buyerId.Trim())}",
|
||||||
|
"загрузку покупателя");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<BuyerDto>(responseBody, JsonOptions);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Error("ApiClient", "Не удалось разобрать покупателя", ex);
|
||||||
|
throw new HttpRequestException($"Некорректный ответ сервера при загрузке покупателя: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<BuyerPriceListAssignmentDto>> GetBuyerPriceListsAsync(string buyerId)
|
||||||
|
{
|
||||||
|
EnsureAuthenticated();
|
||||||
|
if (string.IsNullOrWhiteSpace(buyerId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Не указан buyer_id.", nameof(buyerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseBody = await SendAuthorizedGetAsync(
|
||||||
|
$"/api/buyers/{Uri.EscapeDataString(buyerId.Trim())}/price-lists",
|
||||||
|
"загрузку назначений прайсов покупателя");
|
||||||
|
return DeserializeBuyerPriceLists(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<PriceListDto>> GetPriceListsAsync()
|
||||||
|
{
|
||||||
|
EnsureAuthenticated();
|
||||||
|
var responseBody = await SendAuthorizedGetAsync("/api/price-lists", "загрузку прайс-листов");
|
||||||
|
return DeserializePriceLists(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<PriceListRegionDto>> GetPriceListRegionsAsync(string priceListId)
|
||||||
|
{
|
||||||
|
EnsureAuthenticated();
|
||||||
|
if (string.IsNullOrWhiteSpace(priceListId))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Не указан price_list_id.", nameof(priceListId));
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = "/api/sc/price-list-regions?price_list_id=" + Uri.EscapeDataString(priceListId.Trim());
|
||||||
|
var responseBody = await SendAuthorizedGetAsync(path, "загрузку региональных наценок прайса");
|
||||||
|
return DeserializePriceListRegions(responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BuyerDto> DeserializeBuyers(string responseBody)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var doc = JsonDocument.Parse(responseBody))
|
||||||
|
{
|
||||||
|
if (doc.RootElement.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<List<BuyerDto>>(responseBody, JsonOptions)
|
||||||
|
?? new List<BuyerDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapped = JsonSerializer.Deserialize<BuyersResponse>(responseBody, JsonOptions);
|
||||||
|
return wrapped?.Buyers ?? wrapped?.Items ?? new List<BuyerDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Error("ApiClient", "Не удалось разобрать список покупателей", ex);
|
||||||
|
throw new HttpRequestException($"Некорректный ответ сервера при загрузке покупателей: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BuyerPriceListAssignmentDto> DeserializeBuyerPriceLists(string responseBody)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var doc = JsonDocument.Parse(responseBody))
|
||||||
|
{
|
||||||
|
if (doc.RootElement.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<List<BuyerPriceListAssignmentDto>>(responseBody, JsonOptions)
|
||||||
|
?? new List<BuyerPriceListAssignmentDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapped = JsonSerializer.Deserialize<BuyerPriceListsResponse>(responseBody, JsonOptions);
|
||||||
|
return wrapped?.Items ?? wrapped?.PriceLists ?? new List<BuyerPriceListAssignmentDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Error("ApiClient", "Не удалось разобрать назначения прайсов покупателя", ex);
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"Некорректный ответ сервера при загрузке назначений прайсов: {ex.Message}",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PriceListDto> DeserializePriceLists(string responseBody)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var doc = JsonDocument.Parse(responseBody))
|
||||||
|
{
|
||||||
|
if (doc.RootElement.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<List<PriceListDto>>(responseBody, JsonOptions)
|
||||||
|
?? new List<PriceListDto>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapped = JsonSerializer.Deserialize<PriceListsResponse>(responseBody, JsonOptions);
|
||||||
|
return wrapped?.PriceLists ?? wrapped?.Items ?? new List<PriceListDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Error("ApiClient", "Не удалось разобрать прайс-листы", ex);
|
||||||
|
throw new HttpRequestException($"Некорректный ответ сервера при загрузке прайс-листов: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<PriceListRegionDto> DeserializePriceListRegions(string responseBody)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var doc = JsonDocument.Parse(responseBody))
|
||||||
|
{
|
||||||
|
if (doc.RootElement.ValueKind == JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<List<PriceListRegionDto>>(responseBody, JsonOptions)
|
||||||
|
?? new List<PriceListRegionDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<PriceListRegionDto>();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Error("ApiClient", "Не удалось разобрать региональные наценки прайса", ex);
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"Некорректный ответ сервера при загрузке региональных наценок: {ex.Message}",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<InvoiceApiItem>> GetInvoicesAsync()
|
public async Task<List<InvoiceApiItem>> GetInvoicesAsync()
|
||||||
{
|
{
|
||||||
var orders = await GetBuyerOrdersAsync(status: "Placed", limit: 200);
|
var orders = await GetBuyerOrdersAsync(status: "Placed", limit: 200);
|
||||||
|
|||||||
@ -12,6 +12,8 @@ namespace Электронная_Фармация.Classes
|
|||||||
public const string DefaultApiBaseUrl = "https://24pharmdata.ru";
|
public const string DefaultApiBaseUrl = "https://24pharmdata.ru";
|
||||||
private const string LegacyApiBaseUrl = "http://195.34.241.84:9988";
|
private const string LegacyApiBaseUrl = "http://195.34.241.84:9988";
|
||||||
private const string ApiBaseUrlEnvVar = "ELF_API_BASE_URL";
|
private const string ApiBaseUrlEnvVar = "ELF_API_BASE_URL";
|
||||||
|
private static bool _sqliteInteropPrepared;
|
||||||
|
private static readonly object _sqliteInteropLock = new object();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replaces known dead API URLs in saved user settings.
|
/// Replaces known dead API URLs in saved user settings.
|
||||||
@ -95,7 +97,11 @@ namespace Электронная_Фармация.Classes
|
|||||||
|
|
||||||
public static string SqliteConnectionString
|
public static string SqliteConnectionString
|
||||||
{
|
{
|
||||||
get { return $"Data Source={SqliteDbPath};Version=3;New=False;"; }
|
get
|
||||||
|
{
|
||||||
|
EnsureSQLiteInteropInBaseDir();
|
||||||
|
return $"Data Source={SqliteDbPath};Version=3;New=False;";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LocationId
|
public static string LocationId
|
||||||
@ -119,5 +125,57 @@ namespace Электронная_Фармация.Classes
|
|||||||
Settings.Default.InvoiceExportPath = (path ?? string.Empty).Trim();
|
Settings.Default.InvoiceExportPath = (path ?? string.Empty).Trim();
|
||||||
Settings.Default.Save();
|
Settings.Default.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EnsureSQLiteInteropInBaseDir()
|
||||||
|
{
|
||||||
|
// System.Data.SQLite ожидает SQLite.Interop.dll рядом с exe.
|
||||||
|
// В релизной поставке DLL часто лежит в подпапках x86/x64.
|
||||||
|
if (_sqliteInteropPrepared)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_sqliteInteropLock)
|
||||||
|
{
|
||||||
|
if (_sqliteInteropPrepared)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
var targetDll = Path.Combine(baseDir, "SQLite.Interop.dll");
|
||||||
|
if (File.Exists(targetDll))
|
||||||
|
{
|
||||||
|
_sqliteInteropPrepared = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyCPU в .NET Framework обычно выбирает разрядность процесса автоматически.
|
||||||
|
var archFolder = Environment.Is64BitProcess ? "x64" : "x86";
|
||||||
|
var sourceDll = Path.Combine(baseDir, archFolder, "SQLite.Interop.dll");
|
||||||
|
var sourcePdb = Path.Combine(baseDir, archFolder, "SQLite.Interop.pdb");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(sourceDll))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(baseDir);
|
||||||
|
File.Copy(sourceDll, targetDll, overwrite: true);
|
||||||
|
|
||||||
|
var targetPdb = Path.Combine(baseDir, "SQLite.Interop.pdb");
|
||||||
|
if (File.Exists(sourcePdb))
|
||||||
|
{
|
||||||
|
File.Copy(sourcePdb, targetPdb, overwrite: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Если копирование не удалось (права/readonly/и т.п.), дальше будет явное исключение от SQLite.
|
||||||
|
}
|
||||||
|
|
||||||
|
_sqliteInteropPrepared = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
src/ElectronicPharmacy/Classes/BuyerDiscountDtos.cs
Normal file
137
src/ElectronicPharmacy/Classes/BuyerDiscountDtos.cs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Электронная_Фармация.Classes
|
||||||
|
{
|
||||||
|
public sealed class BuyerDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("buyer_id")]
|
||||||
|
public string BuyerId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("username")]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("login")]
|
||||||
|
public string Login { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string ResolvedId =>
|
||||||
|
!string.IsNullOrWhiteSpace(BuyerId) ? BuyerId.Trim() :
|
||||||
|
!string.IsNullOrWhiteSpace(Id) ? Id.Trim() :
|
||||||
|
string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class BuyersResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("buyers")]
|
||||||
|
public List<BuyerDto> Buyers { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("items")]
|
||||||
|
public List<BuyerDto> Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class BuyerPriceListAssignmentDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("price_list_id")]
|
||||||
|
public string PriceListId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("supplier_id")]
|
||||||
|
public string SupplierId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("supplier_name")]
|
||||||
|
public string SupplierName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Клиентская скидка на этот прайс.</summary>
|
||||||
|
[JsonPropertyName("markup_pct")]
|
||||||
|
public decimal? MarkupPct { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Базовая скидка самого прайса.</summary>
|
||||||
|
[JsonPropertyName("default_markup_pct")]
|
||||||
|
public decimal? DefaultMarkupPct { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class BuyerPriceListsResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("items")]
|
||||||
|
public List<BuyerPriceListAssignmentDto> Items { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("price_lists")]
|
||||||
|
public List<BuyerPriceListAssignmentDto> PriceLists { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PriceListDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("price_list_id")]
|
||||||
|
public string PriceListId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("supplier_id")]
|
||||||
|
public string SupplierId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("supplier_name")]
|
||||||
|
public string SupplierName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("default_markup_pct")]
|
||||||
|
public decimal? DefaultMarkupPct { get; set; }
|
||||||
|
|
||||||
|
public string ResolvedId =>
|
||||||
|
!string.IsNullOrWhiteSpace(PriceListId) ? PriceListId.Trim() :
|
||||||
|
!string.IsNullOrWhiteSpace(Id) ? Id.Trim() :
|
||||||
|
string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PriceListsResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("price_lists")]
|
||||||
|
public List<PriceListDto> PriceLists { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("items")]
|
||||||
|
public List<PriceListDto> Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PriceListRegionDto
|
||||||
|
{
|
||||||
|
[JsonPropertyName("region_id")]
|
||||||
|
public string RegionId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("region_name")]
|
||||||
|
public string RegionName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("in_work")]
|
||||||
|
public bool? InWork { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("markup_pct")]
|
||||||
|
public decimal? MarkupPct { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("base_price_type")]
|
||||||
|
public string BasePriceType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Скидки, доступные для одной позиции прайса.
|
||||||
|
/// Отсутствующий источник = 0.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ItemDiscountParts
|
||||||
|
{
|
||||||
|
public string PriceListId { get; set; }
|
||||||
|
public decimal PriceListDiscountPct { get; set; }
|
||||||
|
public decimal RegionDiscountPct { get; set; }
|
||||||
|
public decimal ClientDiscountPct { get; set; }
|
||||||
|
|
||||||
|
public decimal TotalDiscountPct =>
|
||||||
|
PriceListDiscountPct + RegionDiscountPct + ClientDiscountPct;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -214,5 +214,61 @@ namespace Электронная_Фармация.Classes
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Синхронизирует поля заказа в `PriceList` (Zakaz/SummaZakaza) с корзиной выбранной аптеки.
|
||||||
|
/// Данные корзины хранятся в TempOrderItems с фильтром по ConsigneeName, а PriceList обновляется
|
||||||
|
/// только для текущего активного просмотра.
|
||||||
|
/// </summary>
|
||||||
|
public static void SyncCartToPriceList(string consigneeName)
|
||||||
|
{
|
||||||
|
var consignee = (consigneeName ?? string.Empty).Trim().Replace("'", "''");
|
||||||
|
using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||||||
|
{
|
||||||
|
connection.Open();
|
||||||
|
|
||||||
|
using (var tx = connection.BeginTransaction())
|
||||||
|
{
|
||||||
|
using (var clear = new SQLiteCommand(
|
||||||
|
@"UPDATE [PriceList]
|
||||||
|
SET [Zakaz] = NULL, [SummaZakaza] = NULL;", connection, tx))
|
||||||
|
{
|
||||||
|
clear.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var sync = new SQLiteCommand(
|
||||||
|
@"UPDATE [PriceList]
|
||||||
|
SET
|
||||||
|
[Zakaz] = (
|
||||||
|
SELECT t.[Zakaz]
|
||||||
|
FROM [TempOrderItems] t
|
||||||
|
WHERE t.[ConsigneeName] = @consignee
|
||||||
|
AND t.[id_PriceList_Item] = [PriceList].[id_PriceList_Item]
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
[SummaZakaza] = (
|
||||||
|
SELECT t.[SummaZakaza]
|
||||||
|
FROM [TempOrderItems] t
|
||||||
|
WHERE t.[ConsigneeName] = @consignee
|
||||||
|
AND t.[id_PriceList_Item] = [PriceList].[id_PriceList_Item]
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM [TempOrderItems] t
|
||||||
|
WHERE t.[ConsigneeName] = @consignee
|
||||||
|
AND t.[id_PriceList_Item] = [PriceList].[id_PriceList_Item]
|
||||||
|
);",
|
||||||
|
connection,
|
||||||
|
tx))
|
||||||
|
{
|
||||||
|
sync.Parameters.AddWithValue("@consignee", consignee);
|
||||||
|
sync.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
321
src/ElectronicPharmacy/Classes/DiscountResolver.cs
Normal file
321
src/ElectronicPharmacy/Classes/DiscountResolver.cs
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Электронная_Фармация.Properties;
|
||||||
|
|
||||||
|
namespace Электронная_Фармация.Classes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Собирает доступные скидки покупателя/прайса/региона и вешает на позицию то, что пришло.
|
||||||
|
/// Отсутствующий источник = 0.
|
||||||
|
/// </summary>
|
||||||
|
public static class DiscountResolver
|
||||||
|
{
|
||||||
|
public static async Task<DiscountLookup> LoadAsync(ApiClient client, string loginHint)
|
||||||
|
{
|
||||||
|
var lookup = new DiscountLookup();
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
var regionId = (Settings.Default.RegionId ?? string.Empty).Trim();
|
||||||
|
lookup.RegionId = regionId;
|
||||||
|
|
||||||
|
string buyerId = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buyers = await client.GetBuyersAsync();
|
||||||
|
buyerId = ResolveBuyerId(buyers, loginHint);
|
||||||
|
lookup.BuyerId = buyerId;
|
||||||
|
AppDebugLog.Info(
|
||||||
|
"Discount",
|
||||||
|
string.IsNullOrWhiteSpace(buyerId)
|
||||||
|
? "buyer_id не найден — клиентская скидка будет 0"
|
||||||
|
: $"buyer_id={buyerId}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Warning("Discount", $"Не удалось загрузить покупателей: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<BuyerPriceListAssignmentDto> assignments = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(buyerId))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
assignments = await client.GetBuyerPriceListsAsync(buyerId);
|
||||||
|
AppDebugLog.Info("Discount", $"Назначений прайсов покупателя: {assignments?.Count ?? 0}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Warning("Discount", $"Не удалось загрузить buyer price-lists: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PriceListDto> priceLists = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
priceLists = await client.GetPriceListsAsync();
|
||||||
|
AppDebugLog.Info("Discount", $"Прайс-листов: {priceLists?.Count ?? 0}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Warning("Discount", $"Не удалось загрузить price-lists: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
EnrichAssignmentsFromPriceLists(assignments, priceLists);
|
||||||
|
|
||||||
|
if (assignments != null)
|
||||||
|
{
|
||||||
|
foreach (var a in assignments)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(a?.PriceListId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var plId = a.PriceListId.Trim();
|
||||||
|
lookup.ByPriceListId[plId] = new ItemDiscountParts
|
||||||
|
{
|
||||||
|
PriceListId = plId,
|
||||||
|
ClientDiscountPct = PositiveOrZero(a.MarkupPct),
|
||||||
|
PriceListDiscountPct = PositiveOrZero(a.DefaultMarkupPct),
|
||||||
|
RegionDiscountPct = 0m
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexSupplierKeys(lookup, a.SupplierId, a.SupplierName, plId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priceLists != null)
|
||||||
|
{
|
||||||
|
foreach (var pl in priceLists)
|
||||||
|
{
|
||||||
|
var plId = pl?.ResolvedId;
|
||||||
|
if (string.IsNullOrWhiteSpace(plId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lookup.ByPriceListId.TryGetValue(plId, out var parts))
|
||||||
|
{
|
||||||
|
parts = new ItemDiscountParts { PriceListId = plId };
|
||||||
|
lookup.ByPriceListId[plId] = parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.PriceListDiscountPct <= 0m && pl.DefaultMarkupPct.HasValue && pl.DefaultMarkupPct.Value > 0m)
|
||||||
|
{
|
||||||
|
parts.PriceListDiscountPct = pl.DefaultMarkupPct.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexSupplierKeys(lookup, pl.SupplierId, pl.SupplierName, plId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var plId in lookup.ByPriceListId.Keys.ToList())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var regions = await client.GetPriceListRegionsAsync(plId);
|
||||||
|
var regionPct = ResolveRegionDiscount(regions, regionId);
|
||||||
|
lookup.ByPriceListId[plId].RegionDiscountPct = regionPct;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppDebugLog.Warning("Discount", $"Регионы для прайса {plId}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDebugLog.Info(
|
||||||
|
"Discount",
|
||||||
|
$"Готово: прайсов={lookup.ByPriceListId.Count}, привязок по поставщику={lookup.BySupplierKey.Count}, region={regionId}");
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemDiscountParts ResolveForItem(DiscountLookup lookup, PriceSummaryItem item)
|
||||||
|
{
|
||||||
|
if (lookup == null || item == null)
|
||||||
|
{
|
||||||
|
return new ItemDiscountParts();
|
||||||
|
}
|
||||||
|
|
||||||
|
var priceListId = (item.PriceListId ?? string.Empty).Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(priceListId) &&
|
||||||
|
lookup.ByPriceListId.TryGetValue(priceListId, out var byId))
|
||||||
|
{
|
||||||
|
return Clone(byId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var supplierId = (item.SupplierId ?? string.Empty).Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(supplierId) &&
|
||||||
|
lookup.BySupplierKey.TryGetValue(NormalizeKey(supplierId), out var plFromSupplier) &&
|
||||||
|
lookup.ByPriceListId.TryGetValue(plFromSupplier, out var bySupplier))
|
||||||
|
{
|
||||||
|
return Clone(bySupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
var supplierName = (item.SupplierName ?? string.Empty).Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(supplierName) &&
|
||||||
|
lookup.BySupplierKey.TryGetValue(NormalizeKey(supplierName), out var plFromName) &&
|
||||||
|
lookup.ByPriceListId.TryGetValue(plFromName, out var byName))
|
||||||
|
{
|
||||||
|
return Clone(byName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ItemDiscountParts();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveBuyerId(List<BuyerDto> buyers, string loginHint)
|
||||||
|
{
|
||||||
|
if (buyers == null || buyers.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var login = (loginHint ?? string.Empty).Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(login))
|
||||||
|
{
|
||||||
|
var matched = buyers.FirstOrDefault(b =>
|
||||||
|
string.Equals((b.Username ?? string.Empty).Trim(), login, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals((b.Login ?? string.Empty).Trim(), login, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
string.Equals((b.Name ?? string.Empty).Trim(), login, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (!string.IsNullOrWhiteSpace(matched?.ResolvedId))
|
||||||
|
{
|
||||||
|
return matched.ResolvedId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buyers.Count == 1 && !string.IsNullOrWhiteSpace(buyers[0].ResolvedId))
|
||||||
|
{
|
||||||
|
return buyers[0].ResolvedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buyers
|
||||||
|
.Select(b => b.ResolvedId)
|
||||||
|
.FirstOrDefault(id => !string.IsNullOrWhiteSpace(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnrichAssignmentsFromPriceLists(
|
||||||
|
List<BuyerPriceListAssignmentDto> assignments,
|
||||||
|
List<PriceListDto> priceLists)
|
||||||
|
{
|
||||||
|
if (assignments == null || priceLists == null || priceLists.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var byId = priceLists
|
||||||
|
.Where(p => !string.IsNullOrWhiteSpace(p?.ResolvedId))
|
||||||
|
.GroupBy(p => p.ResolvedId, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
foreach (var a in assignments)
|
||||||
|
{
|
||||||
|
if (a == null || string.IsNullOrWhiteSpace(a.PriceListId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!byId.TryGetValue(a.PriceListId.Trim(), out var pl))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(a.SupplierId))
|
||||||
|
{
|
||||||
|
a.SupplierId = pl.SupplierId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(a.SupplierName))
|
||||||
|
{
|
||||||
|
a.SupplierName = pl.SupplierName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!a.DefaultMarkupPct.HasValue || a.DefaultMarkupPct.Value <= 0m) &&
|
||||||
|
pl.DefaultMarkupPct.HasValue)
|
||||||
|
{
|
||||||
|
a.DefaultMarkupPct = pl.DefaultMarkupPct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal ResolveRegionDiscount(List<PriceListRegionDto> regions, string regionId)
|
||||||
|
{
|
||||||
|
if (regions == null || regions.Count == 0)
|
||||||
|
{
|
||||||
|
return 0m;
|
||||||
|
}
|
||||||
|
|
||||||
|
PriceListRegionDto match = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(regionId))
|
||||||
|
{
|
||||||
|
match = regions.FirstOrDefault(r =>
|
||||||
|
string.Equals((r.RegionId ?? string.Empty).Trim(), regionId, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
(r.InWork == null || r.InWork.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match == null)
|
||||||
|
{
|
||||||
|
match = regions.FirstOrDefault(r => r.InWork == true) ?? regions.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PositiveOrZero(match?.MarkupPct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void IndexSupplierKeys(DiscountLookup lookup, string supplierId, string supplierName, string priceListId)
|
||||||
|
{
|
||||||
|
if (lookup == null || string.IsNullOrWhiteSpace(priceListId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(supplierId))
|
||||||
|
{
|
||||||
|
lookup.BySupplierKey[NormalizeKey(supplierId)] = priceListId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(supplierName))
|
||||||
|
{
|
||||||
|
lookup.BySupplierKey[NormalizeKey(supplierName)] = priceListId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ItemDiscountParts Clone(ItemDiscountParts source)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
return new ItemDiscountParts();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ItemDiscountParts
|
||||||
|
{
|
||||||
|
PriceListId = source.PriceListId,
|
||||||
|
PriceListDiscountPct = source.PriceListDiscountPct,
|
||||||
|
RegionDiscountPct = source.RegionDiscountPct,
|
||||||
|
ClientDiscountPct = source.ClientDiscountPct
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal PositiveOrZero(decimal? value)
|
||||||
|
{
|
||||||
|
return value.HasValue && value.Value > 0m ? value.Value : 0m;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeKey(string value)
|
||||||
|
{
|
||||||
|
return (value ?? string.Empty).Trim().ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DiscountLookup
|
||||||
|
{
|
||||||
|
public string BuyerId { get; set; }
|
||||||
|
public string RegionId { get; set; }
|
||||||
|
public Dictionary<string, ItemDiscountParts> ByPriceListId { get; } =
|
||||||
|
new Dictionary<string, ItemDiscountParts>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
public Dictionary<string, string> BySupplierKey { get; } =
|
||||||
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,29 +1,57 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Электронная_Фармация.Classes
|
namespace Электронная_Фармация.Classes
|
||||||
{
|
{
|
||||||
public static class MarkupHelper
|
public static class MarkupHelper
|
||||||
{
|
{
|
||||||
public const decimal FallbackMarkupPercent = 30m;
|
public const decimal FallbackMarkupPercent = 0m;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Сумма доступных скидок. null / отрицательные значения считаются 0.
|
||||||
|
/// </summary>
|
||||||
|
public static decimal SumDiscountPercents(params decimal?[] parts)
|
||||||
|
{
|
||||||
|
decimal total = 0m;
|
||||||
|
if (parts == null)
|
||||||
|
{
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
if (part.HasValue && part.Value > 0m)
|
||||||
|
{
|
||||||
|
total += part.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
public static decimal ResolveMarkupPercent(decimal? itemMarkup, decimal? clientMarkup, decimal fallbackMarkup)
|
public static decimal ResolveMarkupPercent(decimal? itemMarkup, decimal? clientMarkup, decimal fallbackMarkup)
|
||||||
{
|
{
|
||||||
if (itemMarkup.HasValue && itemMarkup.Value >= 0)
|
var total = SumDiscountPercents(itemMarkup, clientMarkup);
|
||||||
|
if (total > 0m)
|
||||||
{
|
{
|
||||||
return itemMarkup.Value;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientMarkup.HasValue && clientMarkup.Value >= 0)
|
return fallbackMarkup > 0m ? fallbackMarkup : FallbackMarkupPercent;
|
||||||
{
|
|
||||||
return clientMarkup.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallbackMarkup > 0 ? fallbackMarkup : FallbackMarkupPercent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static decimal ApplyMarkup(decimal basePrice, decimal markupPercent)
|
public static decimal ApplyMarkup(decimal basePrice, decimal markupPercent)
|
||||||
{
|
{
|
||||||
return basePrice + (basePrice * markupPercent / 100m);
|
return ApplyDiscount(basePrice, markupPercent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// итоговая = base * (1 - discount/100), минимум 0. Без округления.
|
||||||
|
/// </summary>
|
||||||
|
public static decimal ApplyDiscount(decimal basePrice, decimal discountPercent)
|
||||||
|
{
|
||||||
|
var effectiveDiscount = Math.Max(0m, discountPercent);
|
||||||
|
var result = basePrice * (1m - effectiveDiscount / 100m);
|
||||||
|
return result < 0m ? 0m : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParseMarkup(object value, out decimal markupPercent)
|
public static bool TryParseMarkup(object value, out decimal markupPercent)
|
||||||
@ -59,7 +87,12 @@ namespace Электронная_Фармация.Classes
|
|||||||
}
|
}
|
||||||
|
|
||||||
var text = value.ToString()?.Trim().Replace(',', '.');
|
var text = value.ToString()?.Trim().Replace(',', '.');
|
||||||
return !string.IsNullOrWhiteSpace(text) && decimal.TryParse(text, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out markupPercent);
|
return !string.IsNullOrWhiteSpace(text) &&
|
||||||
|
decimal.TryParse(
|
||||||
|
text,
|
||||||
|
System.Globalization.NumberStyles.Any,
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture,
|
||||||
|
out markupPercent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,9 @@ namespace Электронная_Фармация.Classes
|
|||||||
[JsonPropertyName("supplier_price_id")]
|
[JsonPropertyName("supplier_price_id")]
|
||||||
public string SupplierPriceID { get; set; }
|
public string SupplierPriceID { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("price_list_id")]
|
||||||
|
public string PriceListId { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("guid_es")]
|
[JsonPropertyName("guid_es")]
|
||||||
public string GuidEs { get; set; }
|
public string GuidEs { get; set; }
|
||||||
|
|
||||||
|
|||||||
@ -114,6 +114,8 @@
|
|||||||
<Compile Include="Classes\LoginRequest.cs" />
|
<Compile Include="Classes\LoginRequest.cs" />
|
||||||
<Compile Include="Classes\LoginResponse.cs" />
|
<Compile Include="Classes\LoginResponse.cs" />
|
||||||
<Compile Include="Classes\MarkupHelper.cs" />
|
<Compile Include="Classes\MarkupHelper.cs" />
|
||||||
|
<Compile Include="Classes\BuyerDiscountDtos.cs" />
|
||||||
|
<Compile Include="Classes\DiscountResolver.cs" />
|
||||||
<Compile Include="Classes\AppServices.cs" />
|
<Compile Include="Classes\AppServices.cs" />
|
||||||
<Compile Include="Classes\UiDialogs.cs" />
|
<Compile Include="Classes\UiDialogs.cs" />
|
||||||
<Compile Include="Classes\UiThemeHelper.cs" />
|
<Compile Include="Classes\UiThemeHelper.cs" />
|
||||||
|
|||||||
@ -108,6 +108,11 @@ namespace Электронная_Фармация
|
|||||||
_statusFooter.Controls.Add(_lblCurrentUser);
|
_statusFooter.Controls.Add(_lblCurrentUser);
|
||||||
_statusFooter.Controls.Add(_btnSettings);
|
_statusFooter.Controls.Add(_btnSettings);
|
||||||
|
|
||||||
|
// Клик по строке "аптека" открывает выбор грузополучателя без перезапуска приложения.
|
||||||
|
// Т.к. метка у нас одна (Dock=Fill), обработчик навешиваем на весь Label.
|
||||||
|
_lblCurrentUser.Cursor = Cursors.Hand;
|
||||||
|
_lblCurrentUser.Click += (_, __) => SwitchConsignee();
|
||||||
|
|
||||||
Controls.Add(_loadingOverlay);
|
Controls.Add(_loadingOverlay);
|
||||||
Controls.Add(_documentHost);
|
Controls.Add(_documentHost);
|
||||||
Controls.Add(_statusFooter);
|
Controls.Add(_statusFooter);
|
||||||
@ -206,7 +211,8 @@ namespace Электронная_Фармация
|
|||||||
var login = Settings.Default.stringLogin?.Trim();
|
var login = Settings.Default.stringLogin?.Trim();
|
||||||
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
||||||
var pharmacy = ConsigneeName?.Trim();
|
var pharmacy = ConsigneeName?.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(pharmacy) || pharmacy == "Прайс-Лист")
|
if (hasToken &&
|
||||||
|
(string.IsNullOrWhiteSpace(pharmacy) || pharmacy == "Прайс-Лист"))
|
||||||
{
|
{
|
||||||
pharmacy = ConsigneeHelper.GetDefaultConsigneeName();
|
pharmacy = ConsigneeHelper.GetDefaultConsigneeName();
|
||||||
if (!string.IsNullOrWhiteSpace(pharmacy) &&
|
if (!string.IsNullOrWhiteSpace(pharmacy) &&
|
||||||
@ -230,7 +236,7 @@ namespace Электронная_Фармация
|
|||||||
userPart = "Пользователь не авторизован";
|
userPart = "Пользователь не авторизован";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(pharmacy) && pharmacy != "Прайс-Лист")
|
if (hasToken && !string.IsNullOrWhiteSpace(pharmacy) && pharmacy != "Прайс-Лист")
|
||||||
{
|
{
|
||||||
_lblCurrentUser.Text = $"{userPart} | Аптека: {pharmacy}";
|
_lblCurrentUser.Text = $"{userPart} | Аптека: {pharmacy}";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Elfisa.UI.Theming;
|
using Elfisa.UI.Theming;
|
||||||
using Электронная_Фармация.Classes;
|
using Электронная_Фармация.Classes;
|
||||||
|
using Электронная_Фармация.Properties;
|
||||||
using Электронная_Фармация.Forms;
|
using Электронная_Фармация.Forms;
|
||||||
using Электронная_Фармация.HelpForms;
|
using Электронная_Фармация.HelpForms;
|
||||||
using Электронная_Фармация.UserControls;
|
using Электронная_Фармация.UserControls;
|
||||||
@ -70,9 +71,26 @@ namespace Электронная_Фармация
|
|||||||
UiThemeHelper.ApplyToControlTree(fCheckDatabaseIntegrary);
|
UiThemeHelper.ApplyToControlTree(fCheckDatabaseIntegrary);
|
||||||
fCheckDatabaseIntegrary.ShowDialog();
|
fCheckDatabaseIntegrary.ShowDialog();
|
||||||
|
|
||||||
var consigneeDialog = new HFConsignees { ParentForm = this };
|
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
||||||
UiThemeHelper.ApplyToControlTree(consigneeDialog);
|
|
||||||
consigneeDialog.ShowDialog();
|
// Окно выбора грузополучателя открываем только после авторизации.
|
||||||
|
// Если аптек одна — не открываем окно вообще.
|
||||||
|
int consigneeCount = 0;
|
||||||
|
using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||||||
|
{
|
||||||
|
con.Open();
|
||||||
|
using (var cmd = new SQLiteCommand("SELECT COUNT(*) FROM Consignees", con))
|
||||||
|
{
|
||||||
|
consigneeCount = Convert.ToInt32(cmd.ExecuteScalar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasToken && consigneeCount > 1)
|
||||||
|
{
|
||||||
|
var consigneeDialog = new HFConsignees { ParentForm = this };
|
||||||
|
UiThemeHelper.ApplyToControlTree(consigneeDialog);
|
||||||
|
consigneeDialog.ShowDialog();
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(ConsigneeName))
|
if (string.IsNullOrWhiteSpace(ConsigneeName))
|
||||||
{
|
{
|
||||||
@ -109,8 +127,17 @@ namespace Электронная_Фармация
|
|||||||
var title = consignee == "Прайс-Лист"
|
var title = consignee == "Прайс-Лист"
|
||||||
? "Прайс-лист"
|
? "Прайс-лист"
|
||||||
: $"Прайс-лист ({consignee})";
|
: $"Прайс-лист ({consignee})";
|
||||||
|
|
||||||
|
// На смене аптеки не должно появляться много вкладок прайса.
|
||||||
|
// Поэтому перед открытием закрываем все вкладки с navKey="price".
|
||||||
|
ConsigneeHelper.SyncCartToPriceList(consignee);
|
||||||
|
foreach (var tab in _documentTabStrip.Tabs.Where(t => t.NavKey == "price").ToList())
|
||||||
|
{
|
||||||
|
CloseDocumentTab(tab.Id);
|
||||||
|
}
|
||||||
|
|
||||||
var ucPriceList = new UCPriceList(consignee);
|
var ucPriceList = new UCPriceList(consignee);
|
||||||
OpenDocumentTab(title, "price", ucPriceList, allowDuplicate: true);
|
OpenDocumentTab(title, "price", ucPriceList, allowDuplicate: false);
|
||||||
ucPriceList.dgvPriceList.Focus();
|
ucPriceList.dgvPriceList.Focus();
|
||||||
RefreshCurrentUserStatus();
|
RefreshCurrentUserStatus();
|
||||||
}
|
}
|
||||||
@ -167,9 +194,21 @@ namespace Электронная_Фармация
|
|||||||
|
|
||||||
private void tsConsignee_Click(object sender, EventArgs e)
|
private void tsConsignee_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
||||||
|
if (!hasToken)
|
||||||
|
{
|
||||||
|
UiDialogs.ShowInfo("Сначала выполните авторизацию, чтобы выбрать грузополучателя.", "Авторизация", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var fConsignees = new FConsignees();
|
var fConsignees = new FConsignees();
|
||||||
UiThemeHelper.ApplyToControlTree(fConsignees);
|
UiThemeHelper.ApplyToControlTree(fConsignees);
|
||||||
fConsignees.ShowDialog();
|
var dr = fConsignees.ShowDialog(this);
|
||||||
|
if (dr == DialogResult.OK &&
|
||||||
|
!string.IsNullOrWhiteSpace(fConsignees.SelectedConsigneeNameAfterSave))
|
||||||
|
{
|
||||||
|
showMePriceList(fConsignees.SelectedConsigneeNameAfterSave);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tsItemInvoices_Click(object sender, EventArgs e)
|
private void tsItemInvoices_Click(object sender, EventArgs e)
|
||||||
@ -197,14 +236,29 @@ namespace Электронная_Фармация
|
|||||||
ConsigneeHelper.SetActiveConsignee(ConsigneeName);
|
ConsigneeHelper.SetActiveConsignee(ConsigneeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Перед открытием прайс-листа синхронизируем Zakaz/SummaZakaza с корзиной активной аптеки.
|
||||||
|
ConsigneeHelper.SyncCartToPriceList(ConsigneeName);
|
||||||
|
|
||||||
var ucPriceList = new UCPriceList(consignee);
|
var ucPriceList = new UCPriceList(consignee);
|
||||||
OpenDocumentTab($"Прайс-лист ({consignee})", "price", ucPriceList, allowDuplicate: true);
|
foreach (var tab in _documentTabStrip.Tabs.Where(t => t.NavKey == "price").ToList())
|
||||||
|
{
|
||||||
|
CloseDocumentTab(tab.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenDocumentTab($"Прайс-лист ({consignee})", "price", ucPriceList, allowDuplicate: false);
|
||||||
ucPriceList.dgvPriceList.Focus();
|
ucPriceList.dgvPriceList.Focus();
|
||||||
RefreshCurrentUserStatus();
|
RefreshCurrentUserStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwitchConsignee()
|
private void SwitchConsignee()
|
||||||
{
|
{
|
||||||
|
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
||||||
|
if (!hasToken)
|
||||||
|
{
|
||||||
|
UiDialogs.ShowInfo("Сначала выполните авторизацию, чтобы сменить грузополучателя.", "Авторизация", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var consigneeDialog = new HFConsignees { ParentForm = this };
|
var consigneeDialog = new HFConsignees { ParentForm = this };
|
||||||
UiThemeHelper.ApplyToControlTree(consigneeDialog);
|
UiThemeHelper.ApplyToControlTree(consigneeDialog);
|
||||||
consigneeDialog.ShowDialog(this);
|
consigneeDialog.ShowDialog(this);
|
||||||
@ -226,7 +280,9 @@ namespace Электронная_Фармация
|
|||||||
private void tsExchangeLoad_Click(object sender, EventArgs e)
|
private void tsExchangeLoad_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
||||||
string qShowMeCoountDataFromTempOrder = "select count(*) from TempOrderItems";
|
var consigneeSafe = (string.IsNullOrWhiteSpace(ConsigneeName) ? ConsigneeHelper.GetDefaultConsigneeName() : ConsigneeName)
|
||||||
|
?.Replace("'", "''");
|
||||||
|
string qShowMeCoountDataFromTempOrder = $"select count(*) from TempOrderItems where ConsigneeName = '{consigneeSafe}'";
|
||||||
int countDataTempOrder = 0;
|
int countDataTempOrder = 0;
|
||||||
|
|
||||||
using (SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
using (SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
||||||
@ -250,6 +306,8 @@ namespace Электронная_Фармация
|
|||||||
ClearDocumentTabs();
|
ClearDocumentTabs();
|
||||||
|
|
||||||
HF_DownloadDataFromServer hF_DownloadDataFromServer = new HF_DownloadDataFromServer();
|
HF_DownloadDataFromServer hF_DownloadDataFromServer = new HF_DownloadDataFromServer();
|
||||||
|
hF_DownloadDataFromServer.ActiveConsigneeName =
|
||||||
|
string.IsNullOrWhiteSpace(ConsigneeName) ? ConsigneeHelper.GetDefaultConsigneeName() : ConsigneeName;
|
||||||
UiThemeHelper.ApplyToControlTree(hF_DownloadDataFromServer);
|
UiThemeHelper.ApplyToControlTree(hF_DownloadDataFromServer);
|
||||||
DialogResult downloadResult = DialogResult.Cancel;
|
DialogResult downloadResult = DialogResult.Cancel;
|
||||||
if (countDataTempOrder > 0)
|
if (countDataTempOrder > 0)
|
||||||
@ -627,6 +685,75 @@ namespace Электронная_Фармация
|
|||||||
UiThemeHelper.ApplyToControlTree(hF_Registration);
|
UiThemeHelper.ApplyToControlTree(hF_Registration);
|
||||||
if (hF_Registration.ShowDialog() == DialogResult.OK)
|
if (hF_Registration.ShowDialog() == DialogResult.OK)
|
||||||
{
|
{
|
||||||
|
// После успешной авторизации сначала выбираем грузополучателя,
|
||||||
|
// и только потом (если нужно) запрашиваем Location ID.
|
||||||
|
int consigneeCount;
|
||||||
|
using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||||||
|
{
|
||||||
|
con.Open();
|
||||||
|
using (var cmd = new SQLiteCommand("SELECT COUNT(*) FROM Consignees", con))
|
||||||
|
{
|
||||||
|
consigneeCount = Convert.ToInt32(cmd.ExecuteScalar());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consigneeCount <= 1)
|
||||||
|
{
|
||||||
|
var defaultName = ConsigneeHelper.GetDefaultConsigneeName();
|
||||||
|
if (!string.IsNullOrWhiteSpace(defaultName))
|
||||||
|
{
|
||||||
|
ConsigneeName = defaultName;
|
||||||
|
ConsigneeHelper.SetActiveConsignee(defaultName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var consigneeDialog = new HFConsignees { ParentForm = this };
|
||||||
|
UiThemeHelper.ApplyToControlTree(consigneeDialog);
|
||||||
|
if (consigneeDialog.ShowDialog(this) != DialogResult.OK)
|
||||||
|
{
|
||||||
|
// Без выбора грузополучателя дальше не идём.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
|
||||||
|
{
|
||||||
|
var consigneeName = ConsigneeName ?? string.Empty;
|
||||||
|
var consigneeAddress = string.Empty;
|
||||||
|
if (!string.IsNullOrWhiteSpace(consigneeName))
|
||||||
|
{
|
||||||
|
using (var con = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||||||
|
{
|
||||||
|
con.Open();
|
||||||
|
using (var cmd = new SQLiteCommand(
|
||||||
|
@"SELECT ifnull(ConsigneesAddress, '')
|
||||||
|
FROM Consignees
|
||||||
|
WHERE ConsigneesName = @name
|
||||||
|
LIMIT 1",
|
||||||
|
con))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@name", consigneeName.Trim());
|
||||||
|
consigneeAddress = cmd.ExecuteScalar()?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HF_LocationId.PromptAndSave(this, consigneeName, consigneeAddress))
|
||||||
|
{
|
||||||
|
ToastNotification.ShowCustom(
|
||||||
|
"Location ID не указан. Укажите его перед отправкой заказов.",
|
||||||
|
Color.DarkOrange,
|
||||||
|
Color.White);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(AppConfig.LocationId))
|
||||||
|
{
|
||||||
|
ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
|
||||||
|
}
|
||||||
|
|
||||||
RefreshCurrentUserStatus();
|
RefreshCurrentUserStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -643,10 +770,38 @@ namespace Электронная_Фармация
|
|||||||
|
|
||||||
private async void tsBtnSentData_Click(object sender, EventArgs e)
|
private async void tsBtnSentData_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
||||||
|
if (!hasToken)
|
||||||
|
{
|
||||||
|
UiDialogs.ShowInfo("Сначала выполните авторизацию, чтобы отправить заказы.", "Авторизация", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DialogResult drUpdateData = UiDialogs.ConfirmYesNoCancel("Будут отправлены все сохранённые заказы. Вы уверены?", "Отправка заказов", this);
|
DialogResult drUpdateData = UiDialogs.ConfirmYesNoCancel("Будут отправлены все сохранённые заказы. Вы уверены?", "Отправка заказов", this);
|
||||||
|
|
||||||
if (drUpdateData == DialogResult.Yes)
|
if (drUpdateData == DialogResult.Yes)
|
||||||
{
|
{
|
||||||
|
var nowUtc = DateTime.UtcNow;
|
||||||
|
var lastPriceUpdateUtc = Settings.Default.LastPriceUpdateUtc;
|
||||||
|
var lastPriceReminderUtc = Settings.Default.LastPriceReminderUtc;
|
||||||
|
|
||||||
|
var priceIsStale = lastPriceUpdateUtc == DateTime.MinValue ||
|
||||||
|
(nowUtc - lastPriceUpdateUtc) > TimeSpan.FromHours(24);
|
||||||
|
var canRemind = lastPriceReminderUtc == DateTime.MinValue ||
|
||||||
|
(nowUtc - lastPriceReminderUtc) > TimeSpan.FromHours(24);
|
||||||
|
|
||||||
|
if (priceIsStale && canRemind)
|
||||||
|
{
|
||||||
|
// Напоминание не блокирует отправку.
|
||||||
|
UiDialogs.ShowInfo(
|
||||||
|
"Прайс-лист не обновлялся более 24 часов. Рекомендуем обновить прайс перед отправкой заказов.",
|
||||||
|
"Прайс-лист",
|
||||||
|
this);
|
||||||
|
|
||||||
|
Settings.Default.LastPriceReminderUtc = nowUtc;
|
||||||
|
Settings.Default.Save();
|
||||||
|
}
|
||||||
|
|
||||||
ShowShellLoading("Отправка заказов...");
|
ShowShellLoading("Отправка заказов...");
|
||||||
ToastNotification.ShowCustom("Отправляем данные", Color.DarkOrange, Color.White);
|
ToastNotification.ShowCustom("Отправляем данные", Color.DarkOrange, Color.White);
|
||||||
|
|
||||||
@ -670,7 +825,9 @@ namespace Электронная_Фармация
|
|||||||
private void tsBtnDownloadData_Click(object sender, EventArgs e)
|
private void tsBtnDownloadData_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
||||||
string qShowMeCoountDataFromTempOrder = "select count(*) from TempOrderItems";
|
var consigneeSafe = (string.IsNullOrWhiteSpace(ConsigneeName) ? ConsigneeHelper.GetDefaultConsigneeName() : ConsigneeName)
|
||||||
|
?.Replace("'", "''");
|
||||||
|
string qShowMeCoountDataFromTempOrder = $"select count(*) from TempOrderItems where ConsigneeName = '{consigneeSafe}'";
|
||||||
int countDataTempOrder = 0;
|
int countDataTempOrder = 0;
|
||||||
|
|
||||||
using (SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
using (SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
||||||
@ -694,6 +851,8 @@ namespace Электронная_Фармация
|
|||||||
ClearDocumentTabs();
|
ClearDocumentTabs();
|
||||||
|
|
||||||
HF_DownloadDataFromServer hF_DownloadDataFromServer = new HF_DownloadDataFromServer();
|
HF_DownloadDataFromServer hF_DownloadDataFromServer = new HF_DownloadDataFromServer();
|
||||||
|
hF_DownloadDataFromServer.ActiveConsigneeName =
|
||||||
|
string.IsNullOrWhiteSpace(ConsigneeName) ? ConsigneeHelper.GetDefaultConsigneeName() : ConsigneeName;
|
||||||
UiThemeHelper.ApplyToControlTree(hF_DownloadDataFromServer);
|
UiThemeHelper.ApplyToControlTree(hF_DownloadDataFromServer);
|
||||||
DialogResult downloadResult = DialogResult.Cancel;
|
DialogResult downloadResult = DialogResult.Cancel;
|
||||||
if (countDataTempOrder > 0)
|
if (countDataTempOrder > 0)
|
||||||
|
|||||||
@ -309,6 +309,7 @@ create table if not exists [OrderItems] (
|
|||||||
|
|
||||||
string strCheckExistsTableTempOrderItems = @"
|
string strCheckExistsTableTempOrderItems = @"
|
||||||
create table if not exists [TempOrderItems] (
|
create table if not exists [TempOrderItems] (
|
||||||
|
[ConsigneeName] nvarchar(128) not null,
|
||||||
[guid_es] nvarchar(36) not null,
|
[guid_es] nvarchar(36) not null,
|
||||||
[es_code] integer not null,
|
[es_code] integer not null,
|
||||||
[supplier_id] nvarchar(36) not null,
|
[supplier_id] nvarchar(36) not null,
|
||||||
@ -320,8 +321,9 @@ create table if not exists [OrderItems] (
|
|||||||
[Description] text,
|
[Description] text,
|
||||||
[Zakaz] real,
|
[Zakaz] real,
|
||||||
[SummaZakaza] nvarchar(64),
|
[SummaZakaza] nvarchar(64),
|
||||||
[id_PriceList_Item] integer primary key not null,
|
[id_PriceList_Item] integer not null,
|
||||||
[supplier_price_id] nvarchar(36)
|
[supplier_price_id] nvarchar(36),
|
||||||
|
primary key ([ConsigneeName], [id_PriceList_Item])
|
||||||
)
|
)
|
||||||
";
|
";
|
||||||
|
|
||||||
@ -394,6 +396,191 @@ create table if not exists [OrderItems] (
|
|||||||
cmdCreateNotExistingsTables.CommandText = strCheckExistsTableTempOrderItems;
|
cmdCreateNotExistingsTables.CommandText = strCheckExistsTableTempOrderItems;
|
||||||
cmdCreateNotExistingsTables.ExecuteNonQuery();
|
cmdCreateNotExistingsTables.ExecuteNonQuery();
|
||||||
|
|
||||||
|
// Миграция TempOrderItems под раздельную корзину по аптекам.
|
||||||
|
// Проверяем наличие ConsigneeName и то, что id_PriceList_Item не является одинарным PK.
|
||||||
|
bool hasConsigneeName = false;
|
||||||
|
bool idPriceListItemIsSinglePk = false;
|
||||||
|
using (var infoCmd = new SQLiteCommand("PRAGMA table_info([TempOrderItems]);", conForCheckTables))
|
||||||
|
using (var infoReader = infoCmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (infoReader.Read())
|
||||||
|
{
|
||||||
|
var colName = infoReader["name"]?.ToString();
|
||||||
|
var pk = 0;
|
||||||
|
try { pk = Convert.ToInt32(infoReader["pk"]); } catch { pk = 0; }
|
||||||
|
|
||||||
|
if (string.Equals(colName, "ConsigneeName", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
hasConsigneeName = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals(colName, "id_PriceList_Item", StringComparison.OrdinalIgnoreCase) && pk == 1)
|
||||||
|
{
|
||||||
|
idPriceListItemIsSinglePk = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasConsigneeName || idPriceListItemIsSinglePk)
|
||||||
|
{
|
||||||
|
string defaultConsignee;
|
||||||
|
using (var defCmd = new SQLiteCommand(
|
||||||
|
@"SELECT ifnull((SELECT ConsigneesName
|
||||||
|
FROM Consignees
|
||||||
|
WHERE ConsigneesUseAsDefault = 1
|
||||||
|
LIMIT 1), '')",
|
||||||
|
conForCheckTables))
|
||||||
|
{
|
||||||
|
defaultConsignee = defCmd.ExecuteScalar()?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var tx = conForCheckTables.BeginTransaction())
|
||||||
|
{
|
||||||
|
using (var cmd = new SQLiteCommand(conForCheckTables))
|
||||||
|
{
|
||||||
|
cmd.Transaction = tx;
|
||||||
|
|
||||||
|
cmd.CommandText = @"
|
||||||
|
CREATE TABLE IF NOT EXISTS [TempOrderItems_new] (
|
||||||
|
[ConsigneeName] nvarchar(128) not null,
|
||||||
|
[guid_es] nvarchar(36) not null,
|
||||||
|
[es_code] integer not null,
|
||||||
|
[supplier_id] nvarchar(36) not null,
|
||||||
|
[DrugName] nvarchar(256) not null COLLATE NOCASE,
|
||||||
|
[SupplierName] nvarchar(256) not null,
|
||||||
|
[Price] nvarchar(32),
|
||||||
|
[Quantity] integer,
|
||||||
|
[ExpiryPeriod] nvarchar(64),
|
||||||
|
[Description] text,
|
||||||
|
[Zakaz] real,
|
||||||
|
[SummaZakaza] nvarchar(64),
|
||||||
|
[id_PriceList_Item] integer not null,
|
||||||
|
[supplier_price_id] nvarchar(36),
|
||||||
|
primary key ([ConsigneeName], [id_PriceList_Item])
|
||||||
|
);";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
cmd.Parameters.Clear();
|
||||||
|
cmd.Parameters.AddWithValue("@defaultConsignee", defaultConsignee.Trim());
|
||||||
|
cmd.CommandText = @"
|
||||||
|
INSERT INTO [TempOrderItems_new] (
|
||||||
|
[ConsigneeName],
|
||||||
|
[guid_es],
|
||||||
|
[es_code],
|
||||||
|
[supplier_id],
|
||||||
|
[DrugName],
|
||||||
|
[SupplierName],
|
||||||
|
[Price],
|
||||||
|
[Quantity],
|
||||||
|
[ExpiryPeriod],
|
||||||
|
[Description],
|
||||||
|
[Zakaz],
|
||||||
|
[SummaZakaza],
|
||||||
|
[id_PriceList_Item],
|
||||||
|
[supplier_price_id]
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
@defaultConsignee,
|
||||||
|
[guid_es],
|
||||||
|
[es_code],
|
||||||
|
[supplier_id],
|
||||||
|
[DrugName],
|
||||||
|
[SupplierName],
|
||||||
|
[Price],
|
||||||
|
[Quantity],
|
||||||
|
[ExpiryPeriod],
|
||||||
|
[Description],
|
||||||
|
[Zakaz],
|
||||||
|
[SummaZakaza],
|
||||||
|
[id_PriceList_Item],
|
||||||
|
[supplier_price_id]
|
||||||
|
FROM [TempOrderItems];";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
cmd.Parameters.Clear();
|
||||||
|
cmd.CommandText = @"DROP TABLE [TempOrderItems];";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
cmd.CommandText = @"ALTER TABLE [TempOrderItems_new] RENAME TO [TempOrderItems];";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Перед seed-вставкой грузополучателей нужно гарантировать наличие LocationId,
|
||||||
|
// иначе INSERT в Consignees (LocationId) упадёт на старых версиях БД.
|
||||||
|
TryAddColumn(conForCheckTables, "Consignees", "LocationId", "nvarchar(64)");
|
||||||
|
|
||||||
|
// Seed-тестовые грузополучатели (аптеки) для удобства ручного тестирования.
|
||||||
|
// Вставляем только если таблица пустая.
|
||||||
|
using (var countConsigneesCmd = new SQLiteCommand(
|
||||||
|
@"SELECT COUNT(*) FROM Consignees;",
|
||||||
|
conForCheckTables))
|
||||||
|
{
|
||||||
|
var countObj = countConsigneesCmd.ExecuteScalar();
|
||||||
|
var count = 0;
|
||||||
|
try { count = Convert.ToInt32(countObj); } catch { count = 0; }
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
var testLocationIds = new[]
|
||||||
|
{
|
||||||
|
"87368f6d-488b-41a9-b170-edc629522844",
|
||||||
|
"c4cd845f-820d-45ac-a473-3df1a0b96feb",
|
||||||
|
"d6187013-00f5-4c1b-b258-c670cf71d115"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Тестовые имена/адреса — можно менять под вашу тестовую среду.
|
||||||
|
var testNames = new[]
|
||||||
|
{
|
||||||
|
"Тестовая аптека 1",
|
||||||
|
"Тестовая аптека 2",
|
||||||
|
"Тестовая аптека 3"
|
||||||
|
};
|
||||||
|
|
||||||
|
var testAddresses = new[]
|
||||||
|
{
|
||||||
|
"Тестовый адрес 1",
|
||||||
|
"Тестовый адрес 2",
|
||||||
|
"Тестовый адрес 3"
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var tx = conForCheckTables.BeginTransaction())
|
||||||
|
{
|
||||||
|
int nextCode;
|
||||||
|
using (var nextCodeCmd = new SQLiteCommand(
|
||||||
|
@"SELECT ifnull(MAX(codeConsignees), 0) + 1 FROM Consignees;",
|
||||||
|
conForCheckTables,
|
||||||
|
tx))
|
||||||
|
{
|
||||||
|
nextCode = Convert.ToInt32(nextCodeCmd.ExecuteScalar());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
using (var insertCmd = new SQLiteCommand(
|
||||||
|
@"INSERT INTO Consignees
|
||||||
|
(codeConsignees, ConsigneesName, ConsigneesAddress, ConsigneesUseAsDefault, LocationId)
|
||||||
|
VALUES (@code, @name, @address, @isDefault, @loc);",
|
||||||
|
conForCheckTables,
|
||||||
|
tx))
|
||||||
|
{
|
||||||
|
insertCmd.Parameters.AddWithValue("@code", nextCode + i);
|
||||||
|
insertCmd.Parameters.AddWithValue("@name", testNames[i]);
|
||||||
|
insertCmd.Parameters.AddWithValue("@address", testAddresses[i]);
|
||||||
|
insertCmd.Parameters.AddWithValue("@isDefault", i == 0 ? 1 : 0);
|
||||||
|
insertCmd.Parameters.AddWithValue("@loc", testLocationIds[i]);
|
||||||
|
insertCmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TryAddColumn(conForCheckTables, "Orders", "ConsigneeName", "nvarchar(128)");
|
TryAddColumn(conForCheckTables, "Orders", "ConsigneeName", "nvarchar(128)");
|
||||||
TryAddColumn(conForCheckTables, "Invoice", "SupplierName", "nvarchar(256)");
|
TryAddColumn(conForCheckTables, "Invoice", "SupplierName", "nvarchar(256)");
|
||||||
TryAddColumn(conForCheckTables, "Invoice", "ConsigneesName", "nvarchar(128)");
|
TryAddColumn(conForCheckTables, "Invoice", "ConsigneesName", "nvarchar(128)");
|
||||||
@ -406,11 +593,28 @@ create table if not exists [OrderItems] (
|
|||||||
TryAddColumn(conForCheckTables, "PriceList", "TradeName", "nvarchar(256)");
|
TryAddColumn(conForCheckTables, "PriceList", "TradeName", "nvarchar(256)");
|
||||||
TryAddColumn(conForCheckTables, "PriceList", "Dosage", "nvarchar(128)");
|
TryAddColumn(conForCheckTables, "PriceList", "Dosage", "nvarchar(128)");
|
||||||
TryAddColumn(conForCheckTables, "PriceList", "MarkupPercent", "real");
|
TryAddColumn(conForCheckTables, "PriceList", "MarkupPercent", "real");
|
||||||
TryAddColumn(conForCheckTables, "Consignees", "LocationId", "nvarchar(64)");
|
|
||||||
ConsigneeHelper.MigrateGlobalLocationIdIfNeeded(conForCheckTables);
|
ConsigneeHelper.MigrateGlobalLocationIdIfNeeded(conForCheckTables);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||||
|
Directory.CreateDirectory(logDir);
|
||||||
|
var logPath = Path.Combine(
|
||||||
|
logDir,
|
||||||
|
$"db-integrity-err-{DateTime.Now:yyyy-MM-dd}.log");
|
||||||
|
|
||||||
|
File.AppendAllText(
|
||||||
|
logPath,
|
||||||
|
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {ex}{Environment.NewLine}{Environment.NewLine}",
|
||||||
|
Encoding.UTF8);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Логирование не должно скрывать исходную ошибку.
|
||||||
|
}
|
||||||
|
|
||||||
UiDialogs.ShowError($"Возникла ошибка при проверке локальной базы на целостность.\n{ex}", "Ошибка целостности", this);
|
UiDialogs.ShowError($"Возникла ошибка при проверке локальной базы на целостность.\n{ex}", "Ошибка целостности", this);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@ -8,6 +8,8 @@ namespace Электронная_Фармация.Forms
|
|||||||
{
|
{
|
||||||
public partial class FConsignees : Form
|
public partial class FConsignees : Form
|
||||||
{
|
{
|
||||||
|
public string SelectedConsigneeNameAfterSave { get; private set; } = string.Empty;
|
||||||
|
|
||||||
public FConsignees()
|
public FConsignees()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -86,6 +88,12 @@ order by [ConsigneesUseAsDefault] desc, [ConsigneesName]";
|
|||||||
|
|
||||||
dgvConsignees.EndEdit();
|
dgvConsignees.EndEdit();
|
||||||
|
|
||||||
|
// Кто должен стать активной аптекой после сохранения.
|
||||||
|
// По вашему ТЗ: "аптека выбранная в этом окне" => текущая выделенная строка.
|
||||||
|
var selectedName = dgvConsignees.CurrentRow?.Cells["Наименование"]?.Value?.ToString()?.Trim()
|
||||||
|
?? string.Empty;
|
||||||
|
SelectedConsigneeNameAfterSave = selectedName;
|
||||||
|
|
||||||
using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
using (var connection = new SQLiteConnection(AppConfig.SqliteConnectionString))
|
||||||
{
|
{
|
||||||
connection.Open();
|
connection.Open();
|
||||||
@ -119,7 +127,14 @@ order by [ConsigneesUseAsDefault] desc, [ConsigneesName]";
|
|||||||
|
|
||||||
table.AcceptChanges();
|
table.AcceptChanges();
|
||||||
ToastNotification.ShowSuccess("Грузополучатели сохранены");
|
ToastNotification.ShowSuccess("Грузополучатели сохранены");
|
||||||
showMeContentFromConsigneesTable();
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(selectedName))
|
||||||
|
{
|
||||||
|
ConsigneeHelper.SetActiveConsignee(selectedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnAdd_Click(object sender, EventArgs e)
|
private void btnAdd_Click(object sender, EventArgs e)
|
||||||
@ -129,23 +144,62 @@ order by [ConsigneesUseAsDefault] desc, [ConsigneesName]";
|
|||||||
dialog.Text = "Новая аптека";
|
dialog.Text = "Новая аптека";
|
||||||
dialog.FormBorderStyle = FormBorderStyle.FixedDialog;
|
dialog.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
dialog.StartPosition = FormStartPosition.CenterParent;
|
dialog.StartPosition = FormStartPosition.CenterParent;
|
||||||
dialog.ClientSize = new System.Drawing.Size(420, 220);
|
// Немного увеличиваем высоту/ширину на случай DPI/шрифтов.
|
||||||
|
dialog.ClientSize = new System.Drawing.Size(480, 310);
|
||||||
dialog.MaximizeBox = false;
|
dialog.MaximizeBox = false;
|
||||||
dialog.MinimizeBox = false;
|
dialog.MinimizeBox = false;
|
||||||
|
dialog.ShowInTaskbar = false;
|
||||||
|
|
||||||
var lblName = new Label { Text = "Наименование", Left = 16, Top = 16, Width = 380 };
|
var lblName = new Label { Text = "Наименование", Left = 16, Top = 16, AutoSize = true };
|
||||||
var txtName = new TextBox { Left = 16, Top = 40, Width = 380 };
|
var txtName = new TextBox { Left = 16, Top = 42, Width = 440, Height = 28 };
|
||||||
var lblAddress = new Label { Text = "Адрес", Left = 16, Top = 76, Width = 380 };
|
var lblAddress = new Label { Text = "Адрес", Left = 16, Top = 82, AutoSize = true };
|
||||||
var txtAddress = new TextBox { Left = 16, Top = 100, Width = 380 };
|
var txtAddress = new TextBox { Left = 16, Top = 108, Width = 440, Height = 28 };
|
||||||
var lblLocation = new Label { Text = "Location ID", Left = 16, Top = 136, Width = 380 };
|
var lblLocation = new Label { Text = "Location ID", Left = 16, Top = 148, AutoSize = true };
|
||||||
var txtLocation = new TextBox { Left = 16, Top = 160, Width = 380 };
|
var txtLocation = new TextBox { Left = 16, Top = 174, Width = 440, Height = 28 };
|
||||||
var btnOk = new Button { Text = "Добавить", DialogResult = DialogResult.OK, Left = 220, Top = 188, Width = 80 };
|
|
||||||
var btnCancel = new Button { Text = "Отмена", DialogResult = DialogResult.Cancel, Left = 310, Top = 188, Width = 80 };
|
var btnOk = new Elfisa.UI.Controls.ModernButton
|
||||||
dialog.Controls.AddRange(new Control[] { lblName, txtName, lblAddress, txtAddress, lblLocation, txtLocation, btnOk, btnCancel });
|
{
|
||||||
|
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.AcceptButton = btnOk;
|
||||||
dialog.CancelButton = btnCancel;
|
dialog.CancelButton = btnCancel;
|
||||||
UiThemeHelper.ApplyToControlTree(dialog);
|
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)
|
if (dialog.ShowDialog(this) != DialogResult.OK)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -24,6 +24,10 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Чтобы при обновлении прайса не терять корзины других аптек,
|
||||||
|
// чистим TempOrderItems только для активного грузополучателя.
|
||||||
|
public string ActiveConsigneeName { get; set; } = string.Empty;
|
||||||
|
|
||||||
private readonly string _login = Settings.Default.stringLogin ?? string.Empty;
|
private readonly string _login = Settings.Default.stringLogin ?? string.Empty;
|
||||||
private readonly string _password = Settings.Default.stringPassword ?? string.Empty;
|
private readonly string _password = Settings.Default.stringPassword ?? string.Empty;
|
||||||
private readonly string _connectionStringToLocalDb = AppConfig.SqliteConnectionString;
|
private readonly string _connectionStringToLocalDb = AppConfig.SqliteConnectionString;
|
||||||
@ -54,10 +58,23 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
AppendStatus("Загружаю сводный прайс...");
|
AppendStatus("Загружаю сводный прайс...");
|
||||||
var allSuppliersSummary = await GetFullPriceSummaryPagedAsync(client);
|
var allSuppliersSummary = await GetFullPriceSummaryPagedAsync(client);
|
||||||
int totalItems = allSuppliersSummary.Summary?.Count ?? 0;
|
int totalItems = allSuppliersSummary.Summary?.Count ?? 0;
|
||||||
|
|
||||||
|
AppendStatus("Загружаю скидки покупателя / прайса / региона...");
|
||||||
|
var discountLookup = await DiscountResolver.LoadAsync(client, _login);
|
||||||
|
if (!string.IsNullOrWhiteSpace(discountLookup.BuyerId))
|
||||||
|
{
|
||||||
|
AppendStatus($"buyer_id: {discountLookup.BuyerId}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendStatus("buyer_id не найден — клиентская скидка будет 0, если не пришла иначе");
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendStatus($"Прайс-листов со скидками: {discountLookup.ByPriceListId.Count}");
|
||||||
ApplyClientMarkupSettings(allSuppliersSummary);
|
ApplyClientMarkupSettings(allSuppliersSummary);
|
||||||
if (allSuppliersSummary.MarkupPercent.HasValue)
|
if (allSuppliersSummary.MarkupPercent.HasValue)
|
||||||
{
|
{
|
||||||
AppendStatus($"Наценка клиента: {allSuppliersSummary.MarkupPercent.Value:0.##}%");
|
AppendStatus($"Скидка из summary: {allSuppliersSummary.MarkupPercent.Value:0.##}%");
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendStatus($"Получено {totalItems} позиций");
|
AppendStatus($"Получено {totalItems} позиций");
|
||||||
@ -71,7 +88,7 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
phasePercent,
|
phasePercent,
|
||||||
$"Обработка: {p.Current} / {p.Total} ({PercentOf(p.Current, p.Total)}%)");
|
$"Обработка: {p.Current} / {p.Total} ({PercentOf(p.Current, p.Total)}%)");
|
||||||
});
|
});
|
||||||
var tablePrice = await Task.Run(() => BuildPriceTable(allSuppliersSummary, buildProgress));
|
var tablePrice = await Task.Run(() => BuildPriceTable(allSuppliersSummary, discountLookup, buildProgress));
|
||||||
|
|
||||||
SetOverallProgress(BuildPhaseEnd, "Сохранение в локальную базу...");
|
SetOverallProgress(BuildPhaseEnd, "Сохранение в локальную базу...");
|
||||||
var saveProgress = new Progress<CountProgress>(p =>
|
var saveProgress = new Progress<CountProgress>(p =>
|
||||||
@ -87,6 +104,10 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
AppendStatus($"Готово. Сохранено {tablePrice.Rows.Count} позиций в локальную базу.");
|
AppendStatus($"Готово. Сохранено {tablePrice.Rows.Count} позиций в локальную базу.");
|
||||||
ToastNotification.ShowSuccess($"Прайс обновлён: {tablePrice.Rows.Count} позиций");
|
ToastNotification.ShowSuccess($"Прайс обновлён: {tablePrice.Rows.Count} позиций");
|
||||||
|
|
||||||
|
Settings.Default.LastPriceUpdateUtc = DateTime.UtcNow;
|
||||||
|
Settings.Default.LastPriceReminderUtc = DateTime.MinValue;
|
||||||
|
Settings.Default.Save();
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException ex)
|
catch (UnauthorizedAccessException ex)
|
||||||
{
|
{
|
||||||
@ -377,6 +398,7 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
|
|
||||||
private static DataTable BuildPriceTable(
|
private static DataTable BuildPriceTable(
|
||||||
PriceSummaryResponse allSuppliersSummary,
|
PriceSummaryResponse allSuppliersSummary,
|
||||||
|
DiscountLookup discountLookup,
|
||||||
IProgress<CountProgress> progress = null)
|
IProgress<CountProgress> progress = null)
|
||||||
{
|
{
|
||||||
var tablePrice = new DataTable();
|
var tablePrice = new DataTable();
|
||||||
@ -426,7 +448,7 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
return tablePrice;
|
return tablePrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientMarkup = allSuppliersSummary.MarkupPercent;
|
var summaryClientDiscount = allSuppliersSummary.MarkupPercent;
|
||||||
int total = allSuppliersSummary.Summary.Count;
|
int total = allSuppliersSummary.Summary.Count;
|
||||||
int current = 0;
|
int current = 0;
|
||||||
foreach (var item in allSuppliersSummary.Summary)
|
foreach (var item in allSuppliersSummary.Summary)
|
||||||
@ -438,7 +460,20 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
row["supplier_id"] = item.SupplierId ?? string.Empty;
|
row["supplier_id"] = item.SupplierId ?? string.Empty;
|
||||||
row["DrugName"] = item.DrugName ?? string.Empty;
|
row["DrugName"] = item.DrugName ?? string.Empty;
|
||||||
row["SupplierName"] = item.SupplierName ?? string.Empty;
|
row["SupplierName"] = item.SupplierName ?? string.Empty;
|
||||||
row["Price"] = item.Price?.ToString() ?? string.Empty;
|
|
||||||
|
var parts = DiscountResolver.ResolveForItem(discountLookup, item);
|
||||||
|
// Вешаем только то, что пришло. Нет значения = 0.
|
||||||
|
// summary.markup_percent / item.markup_percent — доп. источник, если API ещё отдаёт.
|
||||||
|
var totalDiscount = MarkupHelper.SumDiscountPercents(
|
||||||
|
parts.PriceListDiscountPct,
|
||||||
|
parts.RegionDiscountPct,
|
||||||
|
parts.ClientDiscountPct > 0m ? parts.ClientDiscountPct : (decimal?)null,
|
||||||
|
parts.ClientDiscountPct <= 0m ? summaryClientDiscount : null,
|
||||||
|
item.MarkupPercent);
|
||||||
|
|
||||||
|
var basePrice = item.Price ?? 0m;
|
||||||
|
var finalPrice = MarkupHelper.ApplyDiscount(basePrice, totalDiscount);
|
||||||
|
row["Price"] = finalPrice.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
row["Quantity"] = item.Quantity?.ToString() ?? string.Empty;
|
row["Quantity"] = item.Quantity?.ToString() ?? string.Empty;
|
||||||
row["ExpiryPeriod"] = item.ExpiryPeriod == null ? (object)DBNull.Value : item.ExpiryPeriod;
|
row["ExpiryPeriod"] = item.ExpiryPeriod == null ? (object)DBNull.Value : item.ExpiryPeriod;
|
||||||
row["Description"] = item.Description == null ? (object)DBNull.Value : item.Description;
|
row["Description"] = item.Description == null ? (object)DBNull.Value : item.Description;
|
||||||
@ -446,8 +481,7 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
row["SummaZakaza"] = string.Empty;
|
row["SummaZakaza"] = string.Empty;
|
||||||
row["TradeName"] = string.IsNullOrWhiteSpace(item.TradeName) ? (object)DBNull.Value : item.TradeName.Trim();
|
row["TradeName"] = string.IsNullOrWhiteSpace(item.TradeName) ? (object)DBNull.Value : item.TradeName.Trim();
|
||||||
row["Dosage"] = string.IsNullOrWhiteSpace(item.Dosage) ? (object)DBNull.Value : item.Dosage.Trim();
|
row["Dosage"] = string.IsNullOrWhiteSpace(item.Dosage) ? (object)DBNull.Value : item.Dosage.Trim();
|
||||||
var markupPercent = MarkupHelper.ResolveMarkupPercent(item.MarkupPercent, clientMarkup, Settings.Default.ClientMarkupPercent);
|
row["MarkupPercent"] = totalDiscount;
|
||||||
row["MarkupPercent"] = markupPercent;
|
|
||||||
tablePrice.Rows.Add(row);
|
tablePrice.Rows.Add(row);
|
||||||
|
|
||||||
current++;
|
current++;
|
||||||
@ -463,7 +497,9 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
private void SavePriceTable(DataTable tablePrice, IProgress<CountProgress> progress = null)
|
private void SavePriceTable(DataTable tablePrice, IProgress<CountProgress> progress = null)
|
||||||
{
|
{
|
||||||
const string commandToDelete = "delete from [PriceList]";
|
const string commandToDelete = "delete from [PriceList]";
|
||||||
const string commandToDeleteTempOrder = "delete from [TempOrderItems]";
|
var commandToDeleteTempOrder = string.IsNullOrWhiteSpace(ActiveConsigneeName)
|
||||||
|
? "delete from [TempOrderItems]"
|
||||||
|
: "delete from [TempOrderItems] where [ConsigneeName] = @consignee";
|
||||||
const string commandToInsert = @"INSERT INTO [PriceList] (
|
const string commandToInsert = @"INSERT INTO [PriceList] (
|
||||||
[guid_es],
|
[guid_es],
|
||||||
[es_code],
|
[es_code],
|
||||||
@ -509,6 +545,13 @@ VALUES
|
|||||||
using (var cmdDeleteTempOrder = new SQLiteCommand(commandToDeleteTempOrder, sqliteCon))
|
using (var cmdDeleteTempOrder = new SQLiteCommand(commandToDeleteTempOrder, sqliteCon))
|
||||||
{
|
{
|
||||||
cmdDeleteBeforeInsert.ExecuteNonQuery();
|
cmdDeleteBeforeInsert.ExecuteNonQuery();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(ActiveConsigneeName) &&
|
||||||
|
commandToDeleteTempOrder.Contains("@consignee"))
|
||||||
|
{
|
||||||
|
cmdDeleteTempOrder.Parameters.AddWithValue("@consignee", ActiveConsigneeName.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
cmdDeleteTempOrder.ExecuteNonQuery();
|
cmdDeleteTempOrder.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,3 +778,5 @@ VALUES
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -55,40 +55,58 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
this.lblHint.Size = new System.Drawing.Size(420, 48);
|
this.lblHint.Size = new System.Drawing.Size(420, 48);
|
||||||
this.lblHint.TabIndex = 1;
|
this.lblHint.TabIndex = 1;
|
||||||
this.lblHint.Text = "Укажите UUID торговой точки. Он будет использоваться при отправке заказов на сервер.";
|
this.lblHint.Text = "Укажите UUID торговой точки. Он будет использоваться при отправке заказов на сервер.";
|
||||||
|
|
||||||
|
// lblConsigneeName
|
||||||
|
this.lblConsigneeName.AutoSize = true;
|
||||||
|
this.lblConsigneeName.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold);
|
||||||
|
this.lblConsigneeName.Location = new System.Drawing.Point(20, 128);
|
||||||
|
this.lblConsigneeName.Name = "lblConsigneeName";
|
||||||
|
this.lblConsigneeName.Size = new System.Drawing.Size(0, 0);
|
||||||
|
this.lblConsigneeName.TabIndex = 2;
|
||||||
|
this.lblConsigneeName.Text = "";
|
||||||
|
|
||||||
|
// lblConsigneeAddress
|
||||||
|
this.lblConsigneeAddress.AutoSize = true;
|
||||||
|
this.lblConsigneeAddress.Font = new System.Drawing.Font("Segoe UI", 10F);
|
||||||
|
this.lblConsigneeAddress.Location = new System.Drawing.Point(20, 150);
|
||||||
|
this.lblConsigneeAddress.Name = "lblConsigneeAddress";
|
||||||
|
this.lblConsigneeAddress.Size = new System.Drawing.Size(0, 0);
|
||||||
|
this.lblConsigneeAddress.TabIndex = 3;
|
||||||
|
this.lblConsigneeAddress.Text = "";
|
||||||
//
|
//
|
||||||
// lblLocationId
|
// lblLocationId
|
||||||
//
|
//
|
||||||
this.lblLocationId.AutoSize = true;
|
this.lblLocationId.AutoSize = true;
|
||||||
this.lblLocationId.Font = new System.Drawing.Font("Segoe UI", 12F);
|
this.lblLocationId.Font = new System.Drawing.Font("Segoe UI", 12F);
|
||||||
this.lblLocationId.Location = new System.Drawing.Point(20, 128);
|
this.lblLocationId.Location = new System.Drawing.Point(20, 182);
|
||||||
this.lblLocationId.Name = "lblLocationId";
|
this.lblLocationId.Name = "lblLocationId";
|
||||||
this.lblLocationId.Size = new System.Drawing.Size(108, 28);
|
this.lblLocationId.Size = new System.Drawing.Size(108, 28);
|
||||||
this.lblLocationId.TabIndex = 2;
|
this.lblLocationId.TabIndex = 4;
|
||||||
this.lblLocationId.Text = "Location ID";
|
this.lblLocationId.Text = "Location ID";
|
||||||
//
|
//
|
||||||
// txtLocationId
|
// txtLocationId
|
||||||
//
|
//
|
||||||
this.txtLocationId.Font = new System.Drawing.Font("Segoe UI", 12F);
|
this.txtLocationId.Font = new System.Drawing.Font("Segoe UI", 12F);
|
||||||
this.txtLocationId.Location = new System.Drawing.Point(24, 160);
|
this.txtLocationId.Location = new System.Drawing.Point(24, 214);
|
||||||
this.txtLocationId.Name = "txtLocationId";
|
this.txtLocationId.Name = "txtLocationId";
|
||||||
this.txtLocationId.Size = new System.Drawing.Size(412, 34);
|
this.txtLocationId.Size = new System.Drawing.Size(412, 34);
|
||||||
this.txtLocationId.TabIndex = 3;
|
this.txtLocationId.TabIndex = 5;
|
||||||
//
|
//
|
||||||
// btnCancel
|
// btnCancel
|
||||||
//
|
//
|
||||||
this.btnCancel.Location = new System.Drawing.Point(24, 220);
|
this.btnCancel.Location = new System.Drawing.Point(24, 275);
|
||||||
this.btnCancel.Name = "btnCancel";
|
this.btnCancel.Name = "btnCancel";
|
||||||
this.btnCancel.Size = new System.Drawing.Size(125, 40);
|
this.btnCancel.Size = new System.Drawing.Size(125, 40);
|
||||||
this.btnCancel.TabIndex = 4;
|
this.btnCancel.TabIndex = 6;
|
||||||
this.btnCancel.Text = "Отмена";
|
this.btnCancel.Text = "Отмена";
|
||||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||||
//
|
//
|
||||||
// btnSave
|
// btnSave
|
||||||
//
|
//
|
||||||
this.btnSave.Location = new System.Drawing.Point(221, 220);
|
this.btnSave.Location = new System.Drawing.Point(221, 275);
|
||||||
this.btnSave.Name = "btnSave";
|
this.btnSave.Name = "btnSave";
|
||||||
this.btnSave.Size = new System.Drawing.Size(215, 40);
|
this.btnSave.Size = new System.Drawing.Size(215, 40);
|
||||||
this.btnSave.TabIndex = 5;
|
this.btnSave.TabIndex = 7;
|
||||||
this.btnSave.Text = "Сохранить";
|
this.btnSave.Text = "Сохранить";
|
||||||
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
|
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
|
||||||
//
|
//
|
||||||
@ -98,10 +116,12 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.btnCancel;
|
this.CancelButton = this.btnCancel;
|
||||||
this.ClientSize = new System.Drawing.Size(460, 280);
|
this.ClientSize = new System.Drawing.Size(460, 330);
|
||||||
this.Controls.Add(this.btnSave);
|
this.Controls.Add(this.btnSave);
|
||||||
this.Controls.Add(this.btnCancel);
|
this.Controls.Add(this.btnCancel);
|
||||||
this.Controls.Add(this.txtLocationId);
|
this.Controls.Add(this.txtLocationId);
|
||||||
|
this.Controls.Add(this.lblConsigneeAddress);
|
||||||
|
this.Controls.Add(this.lblConsigneeName);
|
||||||
this.Controls.Add(this.lblLocationId);
|
this.Controls.Add(this.lblLocationId);
|
||||||
this.Controls.Add(this.lblHint);
|
this.Controls.Add(this.lblHint);
|
||||||
this.Controls.Add(this.panelHeader);
|
this.Controls.Add(this.panelHeader);
|
||||||
@ -123,6 +143,8 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
private System.Windows.Forms.Panel panelHeader;
|
private System.Windows.Forms.Panel panelHeader;
|
||||||
private System.Windows.Forms.Label lblTitle;
|
private System.Windows.Forms.Label lblTitle;
|
||||||
private System.Windows.Forms.Label lblHint;
|
private System.Windows.Forms.Label lblHint;
|
||||||
|
private System.Windows.Forms.Label lblConsigneeName;
|
||||||
|
private System.Windows.Forms.Label lblConsigneeAddress;
|
||||||
private System.Windows.Forms.Label lblLocationId;
|
private System.Windows.Forms.Label lblLocationId;
|
||||||
private Elfisa.UI.Controls.ModernTextBox txtLocationId;
|
private Elfisa.UI.Controls.ModernTextBox txtLocationId;
|
||||||
private Elfisa.UI.Controls.ModernButton btnCancel;
|
private Elfisa.UI.Controls.ModernButton btnCancel;
|
||||||
|
|||||||
@ -10,6 +10,9 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
{
|
{
|
||||||
public partial class HF_LocationId : Form
|
public partial class HF_LocationId : Form
|
||||||
{
|
{
|
||||||
|
private string _consigneeName = string.Empty;
|
||||||
|
private string _consigneeAddress = string.Empty;
|
||||||
|
|
||||||
public HF_LocationId()
|
public HF_LocationId()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -28,6 +31,20 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Показывает Location ID с контекстом: выбранная аптека (имя/адрес).
|
||||||
|
/// </summary>
|
||||||
|
public static bool PromptAndSave(IWin32Window owner, string consigneeName, string consigneeAddress)
|
||||||
|
{
|
||||||
|
using (var form = new HF_LocationId())
|
||||||
|
{
|
||||||
|
form._consigneeName = consigneeName ?? string.Empty;
|
||||||
|
form._consigneeAddress = consigneeAddress ?? string.Empty;
|
||||||
|
UiThemeHelper.ApplyToControlTree(form);
|
||||||
|
return form.ShowDialog(owner) == DialogResult.OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void HF_LocationId_Load(object sender, EventArgs e)
|
private void HF_LocationId_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
UiThemeHelper.ApplyToControlTree(this);
|
UiThemeHelper.ApplyToControlTree(this);
|
||||||
@ -35,6 +52,18 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
txtLocationId.Text = Settings.Default.LocationId ?? string.Empty;
|
txtLocationId.Text = Settings.Default.LocationId ?? string.Empty;
|
||||||
txtLocationId.SelectAll();
|
txtLocationId.SelectAll();
|
||||||
txtLocationId.Focus();
|
txtLocationId.Focus();
|
||||||
|
|
||||||
|
if (lblConsigneeName != null)
|
||||||
|
{
|
||||||
|
lblConsigneeName.Text = _consigneeName;
|
||||||
|
lblConsigneeName.Visible = !string.IsNullOrWhiteSpace(_consigneeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lblConsigneeAddress != null)
|
||||||
|
{
|
||||||
|
lblConsigneeAddress.Text = _consigneeAddress;
|
||||||
|
lblConsigneeAddress.Visible = !string.IsNullOrWhiteSpace(_consigneeAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnSave_Click(object sender, EventArgs e)
|
private void btnSave_Click(object sender, EventArgs e)
|
||||||
|
|||||||
@ -56,26 +56,6 @@ namespace Электронная_Фармация.HelpForms
|
|||||||
AppDebugLog.Info("Auth", $"Авторизация успешна. token={AppDebugLog.MaskToken(token)}");
|
AppDebugLog.Info("Auth", $"Авторизация успешна. token={AppDebugLog.MaskToken(token)}");
|
||||||
ToastNotification.ShowSuccess("Авторизация успешно выполнена");
|
ToastNotification.ShowSuccess("Авторизация успешно выполнена");
|
||||||
|
|
||||||
// Fallback Location ID (на аптеку задаётся у грузополучателя).
|
|
||||||
if (string.IsNullOrWhiteSpace(AppConfig.LocationId))
|
|
||||||
{
|
|
||||||
if (HF_LocationId.PromptAndSave(this))
|
|
||||||
{
|
|
||||||
ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ToastNotification.ShowCustom(
|
|
||||||
"Location ID не указан. Задайте его у грузополучателя или в настройках.",
|
|
||||||
System.Drawing.Color.DarkOrange,
|
|
||||||
System.Drawing.Color.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ConsigneeHelper.ApplyLocationIdToEmptyConsignees(AppConfig.LocationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close();
|
Close();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -143,5 +143,29 @@ namespace Электронная_Фармация.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("01.01.0001 00:00:00")]
|
||||||
|
public global::System.DateTime LastPriceUpdateUtc {
|
||||||
|
get {
|
||||||
|
return ((global::System.DateTime)(this["LastPriceUpdateUtc"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["LastPriceUpdateUtc"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("01.01.0001 00:00:00")]
|
||||||
|
public global::System.DateTime LastPriceReminderUtc {
|
||||||
|
get {
|
||||||
|
return ((global::System.DateTime)(this["LastPriceReminderUtc"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["LastPriceReminderUtc"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,5 +32,11 @@
|
|||||||
<Setting Name="InvoiceExportPath" Type="System.String" Scope="User">
|
<Setting Name="InvoiceExportPath" Type="System.String" Scope="User">
|
||||||
<Value Profile="(Default)" />
|
<Value Profile="(Default)" />
|
||||||
</Setting>
|
</Setting>
|
||||||
|
<Setting Name="LastPriceUpdateUtc" Type="System.DateTime" Scope="User">
|
||||||
|
<Value Profile="(Default)">01.01.0001 00:00:00</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="LastPriceReminderUtc" Type="System.DateTime" Scope="User">
|
||||||
|
<Value Profile="(Default)">01.01.0001 00:00:00</Value>
|
||||||
|
</Setting>
|
||||||
</Settings>
|
</Settings>
|
||||||
</SettingsFile>
|
</SettingsFile>
|
||||||
@ -11,6 +11,7 @@ using System.Data.SQLite;
|
|||||||
using Электронная_Фармация.UserControls;
|
using Электронная_Фармация.UserControls;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using Электронная_Фармация.Classes;
|
using Электронная_Фармация.Classes;
|
||||||
|
using Электронная_Фармация.Properties;
|
||||||
using Elfisa.UI.Theming;
|
using Elfisa.UI.Theming;
|
||||||
|
|
||||||
namespace Электронная_Фармация.UserControls
|
namespace Электронная_Фармация.UserControls
|
||||||
@ -39,7 +40,24 @@ namespace Электронная_Фармация.UserControls
|
|||||||
|
|
||||||
public void loadDataAboutOrderV2(string Supplier)
|
public void loadDataAboutOrderV2(string Supplier)
|
||||||
{
|
{
|
||||||
string qShowMeOrderItems = "select * from TempOrderItems";
|
var consigneeSafe = (_consigneeName ?? string.Empty).Replace("'", "''");
|
||||||
|
string qShowMeOrderItems = $@"
|
||||||
|
SELECT
|
||||||
|
[guid_es],
|
||||||
|
[es_code],
|
||||||
|
[supplier_id],
|
||||||
|
[DrugName],
|
||||||
|
[SupplierName],
|
||||||
|
[Price],
|
||||||
|
[Quantity],
|
||||||
|
[ExpiryPeriod],
|
||||||
|
[Description],
|
||||||
|
[Zakaz],
|
||||||
|
[SummaZakaza],
|
||||||
|
[id_PriceList_Item],
|
||||||
|
[supplier_price_id]
|
||||||
|
FROM [TempOrderItems]
|
||||||
|
WHERE [ConsigneeName] = '{consigneeSafe}'";
|
||||||
|
|
||||||
//string qShowMeSumOfOrder = "select cast(sum(Price * Zakaz) as real) from TempOrderItems";
|
//string qShowMeSumOfOrder = "select cast(sum(Price * Zakaz) as real) from TempOrderItems";
|
||||||
|
|
||||||
@ -53,7 +71,8 @@ namespace Электронная_Фармация.UserControls
|
|||||||
|
|
||||||
if (Supplier != string.Empty && Supplier != "Корзина")
|
if (Supplier != string.Empty && Supplier != "Корзина")
|
||||||
{
|
{
|
||||||
qShowMeOrderItems += $" where [SupplierName] = '{Supplier}'";
|
var supplierSafe = Supplier.Replace("'", "''");
|
||||||
|
qShowMeOrderItems += $" and [SupplierName] = '{supplierSafe}'";
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLiteCommand cmdShowMeTempOrderItems = new SQLiteCommand(qShowMeOrderItems, conTempOrder);
|
SQLiteCommand cmdShowMeTempOrderItems = new SQLiteCommand(qShowMeOrderItems, conTempOrder);
|
||||||
@ -286,8 +305,9 @@ and ConsigneesName = '{consigneeName}'
|
|||||||
private void DeleteCartItem(string idPriceListItem, string drugName)
|
private void DeleteCartItem(string idPriceListItem, string drugName)
|
||||||
{
|
{
|
||||||
var safeId = idPriceListItem.Replace("'", "''");
|
var safeId = idPriceListItem.Replace("'", "''");
|
||||||
|
var consigneeSafe = (_consigneeName ?? string.Empty).Replace("'", "''");
|
||||||
string qClearPriceList = $"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null where id_PriceList_Item = '{safeId}'";
|
string qClearPriceList = $"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null where id_PriceList_Item = '{safeId}'";
|
||||||
string qDeleteFromTempOrder = $"delete from [TempOrderItems] where [id_PriceList_Item] = '{safeId}'";
|
string qDeleteFromTempOrder = $"delete from [TempOrderItems] where [id_PriceList_Item] = '{safeId}' and [ConsigneeName] = '{consigneeSafe}'";
|
||||||
|
|
||||||
using (var sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
using (var sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
||||||
{
|
{
|
||||||
@ -324,18 +344,26 @@ and ConsigneesName = '{consigneeName}'
|
|||||||
{
|
{
|
||||||
using(SQLiteConnection sqlConForDeleteTempOrder = new SQLiteConnection(connectionStringToLocalDB))
|
using(SQLiteConnection sqlConForDeleteTempOrder = new SQLiteConnection(connectionStringToLocalDB))
|
||||||
{
|
{
|
||||||
|
var supplierSafe = (Supplier ?? string.Empty).Replace("'", "''");
|
||||||
|
var consigneeSafe = (_consigneeName ?? string.Empty).Replace("'", "''");
|
||||||
string qDeletePriceListItemForASupplier;
|
string qDeletePriceListItemForASupplier;
|
||||||
string qDeleteTempOrderForASupplier;
|
string qDeleteTempOrderForASupplier;
|
||||||
|
|
||||||
if (Supplier != string.Empty)
|
if (supplierSafe != string.Empty)
|
||||||
{
|
{
|
||||||
qDeletePriceListItemForASupplier = $"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null where [id_PriceList_Item] in (select [id_PriceList_Item] from [TempOrderItems] where [SupplierName] = '{Supplier}')";
|
qDeletePriceListItemForASupplier =
|
||||||
qDeleteTempOrderForASupplier = $"delete from [TempOrderItems] where [SupplierName] = '{Supplier}'";
|
$"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null where [id_PriceList_Item] in " +
|
||||||
|
$"(select [id_PriceList_Item] from [TempOrderItems] where [SupplierName] = '{supplierSafe}' and [ConsigneeName] = '{consigneeSafe}')";
|
||||||
|
qDeleteTempOrderForASupplier =
|
||||||
|
$"delete from [TempOrderItems] where [SupplierName] = '{supplierSafe}' and [ConsigneeName] = '{consigneeSafe}'";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qDeletePriceListItemForASupplier = $"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null";
|
qDeletePriceListItemForASupplier =
|
||||||
qDeleteTempOrderForASupplier = $"delete from [TempOrderItems]";
|
$"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null " +
|
||||||
|
$"where [id_PriceList_Item] in (select [id_PriceList_Item] from [TempOrderItems] where [ConsigneeName] = '{consigneeSafe}')";
|
||||||
|
qDeleteTempOrderForASupplier =
|
||||||
|
$"delete from [TempOrderItems] where [ConsigneeName] = '{consigneeSafe}'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -423,10 +451,18 @@ ConsigneesName = '{consigneeName}'
|
|||||||
|
|
||||||
private void btnSendOrder_Click(object sender, EventArgs e)
|
private void btnSendOrder_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
var hasToken = !string.IsNullOrWhiteSpace(Settings.Default.stringToken);
|
||||||
|
if (!hasToken)
|
||||||
|
{
|
||||||
|
UiDialogs.ShowInfo("Сначала выполните авторизацию, чтобы создать заказ.", "Авторизация", FindForm());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
||||||
|
|
||||||
string SupplierName = _supplierName;
|
string SupplierName = _supplierName;
|
||||||
string consigneeName = _consigneeName;
|
string consigneeName = _consigneeName;
|
||||||
|
var consigneeSafe = (consigneeName ?? string.Empty).Replace("'", "''");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -465,6 +501,7 @@ select
|
|||||||
SupplierName
|
SupplierName
|
||||||
from TempOrderItems
|
from TempOrderItems
|
||||||
where zakaz > 0
|
where zakaz > 0
|
||||||
|
and [ConsigneeName] = '{consigneeSafe}'
|
||||||
{filterAddon}
|
{filterAddon}
|
||||||
group by SupplierName
|
group by SupplierName
|
||||||
";
|
";
|
||||||
@ -520,6 +557,8 @@ group by SupplierName
|
|||||||
string OrderNumber = $"EP-{DateTime.Now:yyyyMMdd-HHmmss}-{Guid.NewGuid().ToString("N").Substring(0, 6)}";
|
string OrderNumber = $"EP-{DateTime.Now:yyyyMMdd-HHmmss}-{Guid.NewGuid().ToString("N").Substring(0, 6)}";
|
||||||
string OrderDate = DateTime.Now.ToString("dd.MM.yyyy");
|
string OrderDate = DateTime.Now.ToString("dd.MM.yyyy");
|
||||||
_consigneeName = ParentForm?.ConsigneeName ?? _consigneeName ?? string.Empty;
|
_consigneeName = ParentForm?.ConsigneeName ?? _consigneeName ?? string.Empty;
|
||||||
|
var consigneeSafe = (_consigneeName ?? string.Empty).Replace("'", "''");
|
||||||
|
var supNameSafe = (SupName ?? string.Empty).Replace("'", "''");
|
||||||
|
|
||||||
if (HasStockIssues(SupName, out var stockIssue))
|
if (HasStockIssues(SupName, out var stockIssue))
|
||||||
{
|
{
|
||||||
@ -532,14 +571,16 @@ select
|
|||||||
count(*)
|
count(*)
|
||||||
from TempOrderItems
|
from TempOrderItems
|
||||||
where Zakaz > 0
|
where Zakaz > 0
|
||||||
and SupplierName = '{SupName.Replace("'", "''")}'
|
and SupplierName = '{supNameSafe}'
|
||||||
|
and [ConsigneeName] = '{consigneeSafe}'
|
||||||
";
|
";
|
||||||
string qShowMeSumOrderedItems = $@"
|
string qShowMeSumOrderedItems = $@"
|
||||||
select
|
select
|
||||||
ifnull(sum(cast(replace(SummaZakaza, ',', '.') as real)), 0)
|
ifnull(sum(cast(replace(SummaZakaza, ',', '.') as real)), 0)
|
||||||
from TempOrderItems
|
from TempOrderItems
|
||||||
where Zakaz > 0
|
where Zakaz > 0
|
||||||
and SupplierName = '{SupName.Replace("'", "''")}'
|
and SupplierName = '{supNameSafe}'
|
||||||
|
and [ConsigneeName] = '{consigneeSafe}'
|
||||||
";
|
";
|
||||||
string qShowMeMinimalSumOfOrder = $@"
|
string qShowMeMinimalSumOfOrder = $@"
|
||||||
select
|
select
|
||||||
@ -549,7 +590,7 @@ where SuppliersName = '{SupName.Replace("'", "''")}'
|
|||||||
";
|
";
|
||||||
|
|
||||||
string qDisposeTempOrder = $@"
|
string qDisposeTempOrder = $@"
|
||||||
delete from TempOrderItems where SupplierName = '{SupName}'
|
delete from TempOrderItems where SupplierName = '{supNameSafe}' and [ConsigneeName] = '{consigneeSafe}'
|
||||||
";
|
";
|
||||||
|
|
||||||
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
string connectionStringToLocalDB = AppConfig.SqliteConnectionString;
|
||||||
@ -594,12 +635,13 @@ select
|
|||||||
'{OrderDate}',
|
'{OrderDate}',
|
||||||
supplier_id,
|
supplier_id,
|
||||||
SupplierName,
|
SupplierName,
|
||||||
(select count(*) from TempOrderItems where SupplierName = '{SupName.Replace("'", "''")}'),
|
(select count(*) from TempOrderItems where SupplierName = '{supNameSafe}' and [ConsigneeName] = '{consigneeSafe}'),
|
||||||
'{sumOrder.ToString(System.Globalization.CultureInfo.InvariantCulture)}',
|
'{sumOrder.ToString(System.Globalization.CultureInfo.InvariantCulture)}',
|
||||||
'НОВЫЙ',
|
'НОВЫЙ',
|
||||||
'{_consigneeName.Replace("'", "''")}'
|
'{_consigneeName.Replace("'", "''")}'
|
||||||
from TempOrderItems
|
from TempOrderItems
|
||||||
where SupplierName = '{SupName.Replace("'", "''")}'
|
where SupplierName = '{supNameSafe}'
|
||||||
|
and [ConsigneeName] = '{consigneeSafe}'
|
||||||
group by supplier_id, SupplierName
|
group by supplier_id, SupplierName
|
||||||
";
|
";
|
||||||
|
|
||||||
@ -616,7 +658,7 @@ select
|
|||||||
[ExpiryPeriod],
|
[ExpiryPeriod],
|
||||||
[supplier_price_id]
|
[supplier_price_id]
|
||||||
from TempOrderItems
|
from TempOrderItems
|
||||||
where SupplierName = '{SupName}'
|
where SupplierName = '{supNameSafe}' and [ConsigneeName] = '{consigneeSafe}'
|
||||||
";
|
";
|
||||||
|
|
||||||
#region старыеЗапросыСозданияЗаказа
|
#region старыеЗапросыСозданияЗаказа
|
||||||
|
|||||||
@ -1072,10 +1072,12 @@ namespace Электронная_Фармация.UserControls
|
|||||||
|
|
||||||
List<String> SuppliersFromOrder = new List<String>();
|
List<String> SuppliersFromOrder = new List<String>();
|
||||||
|
|
||||||
|
var consigneeSafe = (_WorkWithConsignee ?? string.Empty).Replace("'", "''");
|
||||||
string qShowMeSuppliersFromOrder = $@"
|
string qShowMeSuppliersFromOrder = $@"
|
||||||
select
|
select
|
||||||
[SupplierName]
|
[SupplierName]
|
||||||
from [TempOrderItems]
|
from [TempOrderItems]
|
||||||
|
where [ConsigneeName] = '{consigneeSafe}'
|
||||||
group by [SupplierName]
|
group by [SupplierName]
|
||||||
";
|
";
|
||||||
|
|
||||||
@ -1759,9 +1761,10 @@ and tempOrder.GoodName = PriceList.GoodName
|
|||||||
|
|
||||||
void creatingTempOrder(string idPriceListItem, string zakaz, string summaZakaza)
|
void creatingTempOrder(string idPriceListItem, string zakaz, string summaZakaza)
|
||||||
{
|
{
|
||||||
|
var consigneeSafe = (_WorkWithConsignee ?? string.Empty).Replace("'", "''");
|
||||||
string qUpdatePriceList = $"update [PriceList] set [Zakaz] = '{zakaz}', [SummaZakaza] = '{summaZakaza}' where [id_PriceList_Item] = '{idPriceListItem}'";
|
string qUpdatePriceList = $"update [PriceList] set [Zakaz] = '{zakaz}', [SummaZakaza] = '{summaZakaza}' where [id_PriceList_Item] = '{idPriceListItem}'";
|
||||||
string qDeleteFromTempOrder = $"delete from [TempOrderItems] where [id_PriceList_Item] = '{idPriceListItem}'";
|
string qDeleteFromTempOrder = $"delete from [TempOrderItems] where [id_PriceList_Item] = '{idPriceListItem}' and [ConsigneeName] = '{consigneeSafe}'";
|
||||||
string qCreateNewTempOrder = $"insert into TempOrderItems([guid_es],[es_code],[supplier_id],[DrugName],[SupplierName],[Price],[Quantity],[ExpiryPeriod],[Description],[Zakaz],[SummaZakaza],[id_PriceList_Item],[supplier_price_id]) select [guid_es],[es_code],[supplier_id],[DrugName],[SupplierName],[Price],[Quantity],[ExpiryPeriod],[Description],[Zakaz],[SummaZakaza],[id_PriceList_Item],[supplier_price_id] from [PriceList] where [id_PriceList_Item] = '{idPriceListItem}'";
|
string qCreateNewTempOrder = $"insert into TempOrderItems([ConsigneeName],[guid_es],[es_code],[supplier_id],[DrugName],[SupplierName],[Price],[Quantity],[ExpiryPeriod],[Description],[Zakaz],[SummaZakaza],[id_PriceList_Item],[supplier_price_id]) select '{consigneeSafe}',[guid_es],[es_code],[supplier_id],[DrugName],[SupplierName],[Price],[Quantity],[ExpiryPeriod],[Description],[Zakaz],[SummaZakaza],[id_PriceList_Item],[supplier_price_id] from [PriceList] where [id_PriceList_Item] = '{idPriceListItem}'";
|
||||||
|
|
||||||
using(SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
using(SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
||||||
{
|
{
|
||||||
@ -1795,13 +1798,14 @@ and tempOrder.GoodName = PriceList.GoodName
|
|||||||
|
|
||||||
void deleteGood(int index, string IdPriceListItem)
|
void deleteGood(int index, string IdPriceListItem)
|
||||||
{
|
{
|
||||||
|
var consigneeSafe = (_WorkWithConsignee ?? string.Empty).Replace("'", "''");
|
||||||
dgvPriceList.Rows[index].Cells[9].Value = string.Empty;
|
dgvPriceList.Rows[index].Cells[9].Value = string.Empty;
|
||||||
dgvPriceList.Rows[index].Cells[10].Value = string.Empty;
|
dgvPriceList.Rows[index].Cells[10].Value = string.Empty;
|
||||||
|
|
||||||
string IdPLI = IdPriceListItem;
|
string IdPLI = IdPriceListItem;
|
||||||
|
|
||||||
string qClearPriceList = $"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null where id_PriceList_Item = '{IdPLI}'";
|
string qClearPriceList = $"update [PriceList] set [Zakaz] = null, [SummaZakaza] = null where id_PriceList_Item = '{IdPLI}'";
|
||||||
string qDeleteFromTempOrder = $"delete from [TempOrderItems] where [id_PriceList_Item] = '{IdPLI}'";
|
string qDeleteFromTempOrder = $"delete from [TempOrderItems] where [id_PriceList_Item] = '{IdPLI}' and [ConsigneeName] = '{consigneeSafe}'";
|
||||||
|
|
||||||
using(SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
using(SQLiteConnection sqlCon = new SQLiteConnection(connectionStringToLocalDB))
|
||||||
{
|
{
|
||||||
@ -2025,8 +2029,7 @@ and SumOrderedItems = '{SumOrder}'";
|
|||||||
|
|
||||||
var markupPercentage = GetMarkupPercentForRow(currentRow);
|
var markupPercentage = GetMarkupPercentForRow(currentRow);
|
||||||
numericPercent.Value = Math.Max(numericPercent.Minimum, Math.Min(numericPercent.Maximum, markupPercentage));
|
numericPercent.Value = Math.Max(numericPercent.Minimum, Math.Min(numericPercent.Maximum, markupPercentage));
|
||||||
var priceWithPercentage = MarkupHelper.ApplyMarkup((decimal)priceForGood, markupPercentage);
|
lblPriceForGood.Text = $"= {priceForGood:0.##} руб";
|
||||||
lblPriceForGood.Text = $"= {priceWithPercentage:0.##} руб";
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -2493,3 +2496,5 @@ and SumOrderedItems = '{SumOrder}'";
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user