Update finance session follow-ups
This commit is contained in:
@@ -70,7 +70,8 @@ public class ConfigTransferService : IConfigTransferService
|
||||
TimerEnabled = exportSettings.TimerEnabled,
|
||||
DebugLoggingEnabled = exportSettings.DebugLoggingEnabled,
|
||||
LocalSiteExportFolder = exportSettings.LocalSiteExportFolder,
|
||||
LocalConsolidatedExportFolder = exportSettings.LocalConsolidatedExportFolder
|
||||
LocalConsolidatedExportFolder = exportSettings.LocalConsolidatedExportFolder,
|
||||
ExchangeRateDateField = SettingsPageService.NormalizeExchangeRateDateField(exportSettings.ExchangeRateDateField)
|
||||
},
|
||||
SourceSystemDefinitions = sourceSystems.Select(system => new ConfigTransferSourceSystemDefinition
|
||||
{
|
||||
@@ -283,7 +284,8 @@ public class ConfigTransferService : IConfigTransferService
|
||||
TimerEnabled = importedSettings.TimerEnabled,
|
||||
DebugLoggingEnabled = importedSettings.DebugLoggingEnabled,
|
||||
LocalSiteExportFolder = importedSettings.LocalSiteExportFolder,
|
||||
LocalConsolidatedExportFolder = importedSettings.LocalConsolidatedExportFolder
|
||||
LocalConsolidatedExportFolder = importedSettings.LocalConsolidatedExportFolder,
|
||||
ExchangeRateDateField = SettingsPageService.NormalizeExchangeRateDateField(importedSettings.ExchangeRateDateField)
|
||||
});
|
||||
|
||||
foreach (var sourceSystem in importedSourceSystems)
|
||||
|
||||
@@ -27,7 +27,8 @@ CREATE TABLE ExportSettings (
|
||||
TimerEnabled INTEGER NOT NULL,
|
||||
DebugLoggingEnabled INTEGER NOT NULL DEFAULT 0,
|
||||
LocalSiteExportFolder TEXT NOT NULL DEFAULT '',
|
||||
LocalConsolidatedExportFolder TEXT NOT NULL DEFAULT ''
|
||||
LocalConsolidatedExportFolder TEXT NOT NULL DEFAULT '',
|
||||
ExchangeRateDateField TEXT NOT NULL DEFAULT 'PostingDate'
|
||||
);";
|
||||
|
||||
internal static string GetHanaServersCreateSql() => @"
|
||||
|
||||
@@ -29,6 +29,7 @@ public class DatabaseSchemaMaintenanceService : IDatabaseSchemaMaintenanceServic
|
||||
AddColumnIfMissing(db, "ExportSettings", "DebugLoggingEnabled", "INTEGER NOT NULL DEFAULT 0");
|
||||
AddColumnIfMissing(db, "ExportSettings", "LocalSiteExportFolder", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "ExportSettings", "LocalConsolidatedExportFolder", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "ExportSettings", "ExchangeRateDateField", "TEXT NOT NULL DEFAULT 'PostingDate'");
|
||||
AddColumnIfMissing(db, "SharePointConfigs", "CentralExportFolder", "TEXT NOT NULL DEFAULT ''");
|
||||
AddColumnIfMissing(db, "ExportLogs", "FilePath", "TEXT NOT NULL DEFAULT ''");
|
||||
EnsureTransformationTable(db);
|
||||
|
||||
@@ -57,7 +57,8 @@ public class DatabaseSeedService : IDatabaseSeedService
|
||||
TimerEnabled = true,
|
||||
DebugLoggingEnabled = false,
|
||||
LocalSiteExportFolder = "",
|
||||
LocalConsolidatedExportFolder = ""
|
||||
LocalConsolidatedExportFolder = "",
|
||||
ExchangeRateDateField = ExchangeRateDateFields.PostingDate
|
||||
});
|
||||
|
||||
db.SaveChanges();
|
||||
@@ -868,7 +869,7 @@ public class DatabaseSeedService : IDatabaseSeedService
|
||||
new FinanceReference { Key = "CN", Label = "Trafag CN", Year = 2025 },
|
||||
new FinanceReference { Key = "CZ", Label = "Trafag CZ", Year = 2025, LocalCurrencyValue = 95458782m },
|
||||
new FinanceReference { Key = "DE", Label = "Trafag DE", Year = 2025, LocalCurrencyValue = 3652394.46m },
|
||||
new FinanceReference { Key = "ES", Label = "Trafag ES", Year = 2025, LocalCurrencyValue = 3102333.61m },
|
||||
new FinanceReference { Key = "ES", Label = "Trafag ES", Year = 2025, LocalCurrencyValue = 3082320.18m, Notes = "Sitzung 2026-06-01: ES-Ist 3'082'320.18 EUR fachlich bestaetigt; alter Sollwert 3'102'333.61 war Referenz-/Excel-Fehler." },
|
||||
new FinanceReference { Key = "FR", Label = "Trafag FR", Year = 2025, LocalCurrencyValue = 1450582m, CheckValue = 1471218m },
|
||||
new FinanceReference { Key = "GFS", Label = "Trafag GfS", Year = 2025, LocalCurrencyValue = 6495513m },
|
||||
new FinanceReference { Key = "IN", Label = "Trafag IN", Year = 2025, LocalCurrencyValue = 747341702m, CheckValue = 750936591m },
|
||||
@@ -904,9 +905,11 @@ public class DatabaseSeedService : IDatabaseSeedService
|
||||
}
|
||||
}
|
||||
|
||||
if (current.Key == "ES" && current.Year == 2025 && current.LocalCurrencyValue != 3102333.61m)
|
||||
if (current.Key == "ES" && current.Year == 2025 && current.LocalCurrencyValue != 3082320.18m)
|
||||
{
|
||||
current.LocalCurrencyValue = 3102333.61m;
|
||||
current.LocalCurrencyValue = 3082320.18m;
|
||||
current.CheckValue = null;
|
||||
current.Notes = "Sitzung 2026-06-01: ES-Ist 3'082'320.18 EUR fachlich bestaetigt; alter Sollwert 3'102'333.61 war Referenz-/Excel-Fehler.";
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -184,6 +184,8 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
var aggregation = ResolveAggregation(options);
|
||||
|
||||
using var db = await _dbFactory.CreateDbContextAsync();
|
||||
var settings = await db.ExportSettings.AsNoTracking().FirstOrDefaultAsync() ?? new ExportSettings();
|
||||
var exchangeRateDateField = SettingsPageService.NormalizeExchangeRateDateField(settings.ExchangeRateDateField);
|
||||
var baseRows = await db.CentralSalesRecords
|
||||
.Select(r => new CentralCockpitRow
|
||||
{
|
||||
@@ -196,10 +198,17 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
Quantity = r.Quantity,
|
||||
StandardCost = r.StandardCost,
|
||||
SalesValue = r.SalesPriceValue,
|
||||
PeriodDate = r.InvoiceDate ?? r.ExtractionDate
|
||||
PostingDate = r.PostingDate,
|
||||
InvoiceDate = r.InvoiceDate,
|
||||
ExtractionDate = r.ExtractionDate,
|
||||
PeriodDate = r.InvoiceDate ?? r.ExtractionDate,
|
||||
ExchangeRateDate = r.ExtractionDate
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var row in baseRows)
|
||||
row.ExchangeRateDate = ResolveExchangeRateDate(exchangeRateDateField, row.PostingDate, row.InvoiceDate, row.ExtractionDate);
|
||||
|
||||
if (baseRows.Count == 0)
|
||||
throw new InvalidOperationException("Die zentrale Tabelle enthält noch keine Datensätze.");
|
||||
|
||||
@@ -246,13 +255,15 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
DisplayCurrency = BuildDisplayCurrencyLabel(selectedRows.Select(x => x.DisplayCurrency)),
|
||||
ValueTotal = selectedRows.Sum(x => x.Value),
|
||||
MissingExchangeRateCount = selectedRows.Count(x => x.MissingExchangeRate),
|
||||
ExchangeRateDateField = exchangeRateDateField,
|
||||
ExchangeRateDateLabel = BuildExchangeRateDateLabel(exchangeRateDateField),
|
||||
PeriodStart = selectedRows.Min(x => x.PeriodDate),
|
||||
PeriodEnd = selectedRows.Max(x => x.PeriodDate)
|
||||
},
|
||||
AdditionalValueFields = aggregation.AdditionalValueFields
|
||||
.Select(ToValueFieldOption)
|
||||
.ToList(),
|
||||
Notices = BuildCentralNotices(aggregation, selectedRows.Count(x => x.MissingExchangeRate), options),
|
||||
Notices = BuildCentralNotices(aggregation, selectedRows.Count(x => x.MissingExchangeRate), options, exchangeRateDateField),
|
||||
YearlyTotals = yearlyRows
|
||||
.GroupBy(x => new { x.PeriodDate.Year, x.DisplayCurrency })
|
||||
.OrderBy(g => g.Key.Year)
|
||||
@@ -316,6 +327,10 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
if (financeRules.Count == 0)
|
||||
financeRules = FinanceRuleEngine.CreateDefaultRules().ToList();
|
||||
|
||||
var intercompanyRules = await db.FinanceIntercompanyRules
|
||||
.AsNoTracking()
|
||||
.Where(rule => rule.IsActive)
|
||||
.ToListAsync();
|
||||
var financeRuleEngine = new FinanceRuleEngine(financeRules);
|
||||
var records = await db.CentralSalesRecords
|
||||
.AsNoTracking()
|
||||
@@ -374,6 +389,7 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
Include = include,
|
||||
Value = value,
|
||||
RawSalesValue = record.SalesPriceValue,
|
||||
IsIntercompany = IsIntercompanyCustomer(record, intercompanyRules),
|
||||
Quantity = record.Quantity,
|
||||
InvoiceNumber = record.InvoiceNumber,
|
||||
DocumentType = record.DocumentType,
|
||||
@@ -439,7 +455,8 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
"Diese Sicht verwendet dieselbe FinanceRuleEngine wie das zentrale Excel-Blatt Finance Summary.",
|
||||
"Jahr, Land und Waehrung werden auf das Endergebnis angewendet.",
|
||||
"Finance-Jahr basiert auf PostingDate, danach InvoiceDate, danach ExtractionDate; DE-Regeln koennen das Jahr erzwingen.",
|
||||
"Include/Exclude, Gutschriften-Negierung und IT-Deduplizierung folgen den gepflegten Finance Regeln."
|
||||
"Include/Exclude, Gutschriften-Negierung und IT-Deduplizierung folgen den gepflegten Finance Regeln.",
|
||||
"Intercompany / 2nd-party wird als Diagnosebetrag ausgewiesen; der Standard-Ist bleibt inklusive dieser Positionen."
|
||||
};
|
||||
if (scopedRows.Count == 0)
|
||||
{
|
||||
@@ -461,6 +478,7 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
var countryRows = BuildFinanceCountryStatusRows(scopedRows, referenceByKey);
|
||||
var productAssignmentRows = BuildProductAssignmentRows(scopedRows, allRows);
|
||||
var productFinanceSummary = BuildProductFinanceSummary(productAssignmentRows, resultCurrencies);
|
||||
notices.AddRange(BuildProductAssignmentNotices(productAssignmentRows, productFinanceSummary));
|
||||
|
||||
return new ManagementFinanceSummaryResult
|
||||
{
|
||||
@@ -578,6 +596,7 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
var rowList = group.ToList();
|
||||
referenceByKey.TryGetValue(group.Key.CountryKey, out var referenceValue);
|
||||
var actual = rowList.Sum(row => row.Value);
|
||||
var intercompanyValue = rowList.Where(row => row.IsIntercompany).Sum(row => row.Value);
|
||||
var difference = referenceValue.HasValue ? actual - referenceValue.Value : (decimal?)null;
|
||||
return new ManagementFinanceCountryStatusRow
|
||||
{
|
||||
@@ -587,6 +606,8 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
IncludedRows = rowList.Count(row => row.Include),
|
||||
ExcludedRows = rowList.Count(row => !row.Include),
|
||||
NetSalesActual = actual,
|
||||
IntercompanyValue = intercompanyValue,
|
||||
NetSalesActualExcludingIntercompany = actual - intercompanyValue,
|
||||
SourceSystems = JoinDistinct(rowList.Select(row => row.SourceSystem)),
|
||||
Tscs = JoinDistinct(rowList.Select(row => row.Tsc)),
|
||||
ReferenceValue = referenceValue,
|
||||
@@ -871,7 +892,34 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
};
|
||||
|
||||
private static string NormalizeMaterialKey(string value)
|
||||
=> string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim().ToUpperInvariant();
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return string.Empty;
|
||||
|
||||
var normalized = new string(value
|
||||
.Trim()
|
||||
.ToUpperInvariant()
|
||||
.Where(ch => !char.IsWhiteSpace(ch))
|
||||
.ToArray());
|
||||
|
||||
var withoutLeadingZeros = normalized.TrimStart('0');
|
||||
return string.IsNullOrWhiteSpace(withoutLeadingZeros) ? "0" : withoutLeadingZeros;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> BuildProductAssignmentNotices(
|
||||
IReadOnlyCollection<ManagementProductAssignmentRow> rows,
|
||||
ManagementProductFinanceSummary summary)
|
||||
{
|
||||
if (rows.Count == 0 || summary.TotalValue == 0m)
|
||||
yield break;
|
||||
|
||||
var unresolvedValuePercent = summary.UnassignedValuePercent + summary.MissingReferenceValuePercent;
|
||||
if (unresolvedValuePercent >= 90m)
|
||||
{
|
||||
yield return $"Spartenanalyse auffaellig: {unresolvedValuePercent:N1}% des Umsatzes sind nicht zugeordnet oder nicht im TR-AG-Stamm. Das ist fachlich unplausibel und weist auf Mapping-/Referenzprobleme hin.";
|
||||
yield return "Pruefpunkte Spartenmapping: ProductDivisionRefSet-Fuellung, Join Z.Matnr = P.Matnr, fuehrende Nullen in Materialnummern, lokale Artikelnummern und letzter ZSCHWEIZ-Export.";
|
||||
}
|
||||
}
|
||||
|
||||
private static decimal PercentOf(decimal value, decimal total)
|
||||
=> total == 0m ? 0m : value * 100m / total;
|
||||
@@ -919,6 +967,43 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
return string.Join(", ", reasons.Distinct(StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool IsIntercompanyCustomer(SalesRecord record, IReadOnlyList<FinanceIntercompanyRule> rules)
|
||||
{
|
||||
var customerNumber = record.CustomerNumber?.Trim() ?? string.Empty;
|
||||
var customerName = record.CustomerName?.Trim() ?? string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(customerNumber) && string.IsNullOrWhiteSpace(customerName))
|
||||
return false;
|
||||
|
||||
var normalizedCustomerName = NormalizeRuleText(customerName);
|
||||
var referenceKey = ResolveFinanceCountryKey(record.Land, record.Tsc);
|
||||
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(rule.ScopeKey) &&
|
||||
!rule.ScopeKey.Equals(referenceKey, StringComparison.OrdinalIgnoreCase) &&
|
||||
!rule.ScopeKey.Equals(record.Tsc, StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rule.CustomerNumber) &&
|
||||
customerNumber.Equals(rule.CustomerNumber.Trim(), StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(rule.CustomerNameContains) &&
|
||||
normalizedCustomerName.Contains(NormalizeRuleText(rule.CustomerNameContains), StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string NormalizeRuleText(string value)
|
||||
=> (value ?? string.Empty)
|
||||
.Replace("\u00e4", "ae", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("\u00f6", "oe", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("\u00fc", "ue", StringComparison.OrdinalIgnoreCase)
|
||||
.Trim()
|
||||
.ToUpperInvariant();
|
||||
|
||||
private static string JoinDistinct(IEnumerable<string> values)
|
||||
{
|
||||
var distinct = values
|
||||
@@ -1019,7 +1104,7 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
.ToList();
|
||||
|
||||
var targetCurrency = (options?.TargetCurrency ?? ManagementCockpitCurrencyOptions.Native).Trim().ToUpperInvariant();
|
||||
if (targetCurrency is not ManagementCockpitCurrencyOptions.Eur and not ManagementCockpitCurrencyOptions.Usd)
|
||||
if (targetCurrency is not ManagementCockpitCurrencyOptions.Chf and not ManagementCockpitCurrencyOptions.Eur and not ManagementCockpitCurrencyOptions.Usd)
|
||||
targetCurrency = ManagementCockpitCurrencyOptions.Native;
|
||||
|
||||
return new AggregationSelection(
|
||||
@@ -1047,14 +1132,14 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
{
|
||||
var value = ResolveValue(row, aggregation.ValueField);
|
||||
var currency = ResolveCurrency(row, aggregation.ValueField);
|
||||
var converted = ConvertValue(value, currency, aggregation.ValueField, aggregation, row.PeriodDate);
|
||||
var converted = ConvertValue(value, currency, aggregation.ValueField, aggregation, row.ExchangeRateDate);
|
||||
var additionalValues = aggregation.AdditionalValueFields.ToDictionary(
|
||||
field => field.Key,
|
||||
field =>
|
||||
{
|
||||
var additionalValue = ResolveValue(row, field);
|
||||
var additionalCurrency = ResolveCurrency(row, field);
|
||||
return ConvertValue(additionalValue, additionalCurrency, field, aggregation, row.PeriodDate);
|
||||
return ConvertValue(additionalValue, additionalCurrency, field, aggregation, row.ExchangeRateDate);
|
||||
},
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
@@ -1108,6 +1193,22 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
private static string BuildRateCacheKey(string fromCurrency, string toCurrency, DateTime date)
|
||||
=> $"{fromCurrency}|{toCurrency}|{date:yyyy-MM-dd}";
|
||||
|
||||
private static DateTime ResolveExchangeRateDate(string exchangeRateDateField, DateTime? postingDate, DateTime? invoiceDate, DateTime extractionDate)
|
||||
=> exchangeRateDateField switch
|
||||
{
|
||||
ExchangeRateDateFields.InvoiceDate => invoiceDate ?? postingDate ?? extractionDate,
|
||||
ExchangeRateDateFields.ExtractionDate => extractionDate,
|
||||
_ => postingDate ?? invoiceDate ?? extractionDate
|
||||
};
|
||||
|
||||
private static string BuildExchangeRateDateLabel(string exchangeRateDateField)
|
||||
=> exchangeRateDateField switch
|
||||
{
|
||||
ExchangeRateDateFields.InvoiceDate => "InvoiceDate / Rechnungsdatum",
|
||||
ExchangeRateDateFields.ExtractionDate => "ExtractionDate / Extraktionsdatum",
|
||||
_ => "PostingDate / Buchungsdatum"
|
||||
};
|
||||
|
||||
private static decimal ResolveValue(CockpitRow row, ValueFieldDefinition field)
|
||||
=> field.Key switch
|
||||
{
|
||||
@@ -1161,7 +1262,8 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
private static List<string> BuildCentralNotices(
|
||||
AggregationSelection aggregation,
|
||||
int missingExchangeRateCount,
|
||||
ManagementCockpitAnalysisOptions? options)
|
||||
ManagementCockpitAnalysisOptions? options,
|
||||
string exchangeRateDateField)
|
||||
{
|
||||
var notices = new List<string>
|
||||
{
|
||||
@@ -1169,7 +1271,8 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
$"Summenfeld: {aggregation.ValueField.Label}.",
|
||||
"Keine Intercompany-Bereinigung angewendet.",
|
||||
"Kein Budget- und kein Spartemapping angewendet.",
|
||||
"Periodenlogik basiert auf Invoice Date, falls vorhanden, sonst auf Extraction Date."
|
||||
"Periodenlogik basiert auf Invoice Date, falls vorhanden, sonst auf Extraction Date.",
|
||||
$"Wechselkurse werden auf {BuildExchangeRateDateLabel(exchangeRateDateField)} angewendet."
|
||||
};
|
||||
|
||||
var landFilter = NormalizeOptionalFilter(options?.LandFilter);
|
||||
@@ -1561,7 +1664,11 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
public decimal Quantity { get; set; }
|
||||
public decimal StandardCost { get; set; }
|
||||
public decimal SalesValue { get; set; }
|
||||
public DateTime? PostingDate { get; set; }
|
||||
public DateTime? InvoiceDate { get; set; }
|
||||
public DateTime ExtractionDate { get; set; }
|
||||
public DateTime PeriodDate { get; set; }
|
||||
public DateTime ExchangeRateDate { get; set; }
|
||||
}
|
||||
|
||||
private class CentralAggregationRow
|
||||
@@ -1588,6 +1695,7 @@ public class ManagementCockpitService : IManagementCockpitService
|
||||
public bool Include { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
public decimal RawSalesValue { get; set; }
|
||||
public bool IsIntercompany { get; set; }
|
||||
public decimal Quantity { get; set; }
|
||||
public string InvoiceNumber { get; set; } = string.Empty;
|
||||
public string DocumentType { get; set; } = string.Empty;
|
||||
|
||||
@@ -96,6 +96,7 @@ public sealed class SettingsPageService : ISettingsPageService
|
||||
var existing = await db.ExportSettings.FirstOrDefaultAsync();
|
||||
if (existing is null)
|
||||
{
|
||||
settings.ExchangeRateDateField = NormalizeExchangeRateDateField(settings.ExchangeRateDateField);
|
||||
db.ExportSettings.Add(settings);
|
||||
}
|
||||
else
|
||||
@@ -107,6 +108,7 @@ public sealed class SettingsPageService : ISettingsPageService
|
||||
existing.DebugLoggingEnabled = settings.DebugLoggingEnabled;
|
||||
existing.LocalSiteExportFolder = settings.LocalSiteExportFolder;
|
||||
existing.LocalConsolidatedExportFolder = settings.LocalConsolidatedExportFolder;
|
||||
existing.ExchangeRateDateField = NormalizeExchangeRateDateField(settings.ExchangeRateDateField);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
@@ -281,6 +283,18 @@ public sealed class SettingsPageService : ISettingsPageService
|
||||
|
||||
public static string NormalizeConfigValue(string? value) => value?.Trim() ?? string.Empty;
|
||||
|
||||
public static string NormalizeExchangeRateDateField(string? value)
|
||||
{
|
||||
var normalized = NormalizeConfigValue(value);
|
||||
return normalized switch
|
||||
{
|
||||
ExchangeRateDateFields.PostingDate => ExchangeRateDateFields.PostingDate,
|
||||
ExchangeRateDateFields.InvoiceDate => ExchangeRateDateFields.InvoiceDate,
|
||||
ExchangeRateDateFields.ExtractionDate => ExchangeRateDateFields.ExtractionDate,
|
||||
_ => ExchangeRateDateFields.PostingDate
|
||||
};
|
||||
}
|
||||
|
||||
public static string BuildSharePointTestPreview(string tenantId, string clientId, string clientSecret, string siteUrl)
|
||||
{
|
||||
var maskedSecret = string.IsNullOrEmpty(clientSecret)
|
||||
|
||||
Reference in New Issue
Block a user