feat: Implement mfr truncation fix and enhance invoice handling
Playwright Tests / test (push) Canceled after 0s
Playwright Tests / test (push) Canceled after 0s
- Added a new SQL script to address the "String or binary data would be truncated" error in mfr sync by widening the Note fields in service objects and companies to NVARCHAR(MAX). - Updated the table types and associated stored procedures to accommodate the changes, ensuring compatibility with existing data. - Queued a re-sync for previously rejected rows due to truncation issues. - Created application logins for fuchs_fds and fuchs_web with placeholder passwords for initial setup. - Introduced functions for invoice backdating and invoice date retrieval to streamline invoice processing. - Developed a password generation function with customizable parameters for length and character types. - Added a random number generation function to support password creation.
This commit is contained in:
@@ -32,7 +32,6 @@ public class FdsReminderData
|
||||
internal string RawCustomValues => NewValues?.nz("CustomValues").Trim() ?? "";
|
||||
|
||||
// -- Computed props from registration -------------------------------------
|
||||
public DateTime? DateCreated => ReminderRegistration?.getString("DateCreated") is { Length: > 0 } d ? DateTime.Parse(d) : null;
|
||||
public string ReminderType => ReminderRegistration?.getString("type").Substr(0, 1) ?? "";
|
||||
public string UserNameFinalized => ReminderRegistration?.getString("UserNameFinalized") ?? "";
|
||||
public string UserEmailFinalized => ReminderRegistration?.getString("UserEmailFinalized") ?? "";
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using static OCORE.OCORE_dictionaries;
|
||||
|
||||
namespace Fuchs.intranet;
|
||||
|
||||
/// <summary>Result of a <c>fds__setInvoiceFinal</c> call as seen by <c>req/sconf</c>.</summary>
|
||||
public enum InvoiceFinalizeOutcome
|
||||
{
|
||||
/// <summary>The draft was finalized (one row returned).</summary>
|
||||
Finalized,
|
||||
/// <summary>SQL error 60001: no longer allowed to backdate; nothing was finalized.</summary>
|
||||
BackdateNotAllowed,
|
||||
/// <summary>No error but no row: the invoice is already final/sent (or unknown); nothing was changed.</summary>
|
||||
AlreadyFinal,
|
||||
/// <summary>Any other SQL error (authorization 60000, constraint, timeout ...).</summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The invoice date ("Rechnungsdatum") printed on the PDF and carried as the e-invoice issue date
|
||||
/// (BT-2). One source for both so they can never disagree (ADR 0032):
|
||||
/// <c>DateFinalized</c> (the finalisation — or, for a backdated invoice, the backdated — date),
|
||||
/// falling back to <c>DateCreated</c> for drafts. Both columns are written with <c>GETUTCDATE()</c>,
|
||||
/// so the value is converted to the German calendar day.
|
||||
/// </summary>
|
||||
public static class InvoiceDates
|
||||
{
|
||||
private static readonly TimeZoneInfo German = ResolveGermanZone();
|
||||
|
||||
public static DateOnly IssueDate(GenericObjectDictionary? registration, DateTime? utcNow = null)
|
||||
{
|
||||
DateTime? stored = registration?.getDate_null("DateFinalized") ?? registration?.getDate_null("DateCreated");
|
||||
DateTime utc = stored ?? utcNow ?? DateTime.UtcNow;
|
||||
return DateOnly.FromDateTime(ToGermanTime(utc));
|
||||
}
|
||||
|
||||
/// <summary>Parses the ISO date returned by <c>fds__fn_invoiceBackdate</c>; null when empty/invalid.</summary>
|
||||
public static DateOnly? ParseBackdate(string? iso)
|
||||
=> DateOnly.TryParseExact((iso ?? "").Trim(), "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture,
|
||||
System.Globalization.DateTimeStyles.None, out var d) ? d : null;
|
||||
|
||||
/// <summary>True when the finalise request asked for backdating (checkbox value "1"/"true").</summary>
|
||||
public static bool IsBackdateRequested(string? formValue)
|
||||
=> (formValue ?? "").Trim().ToLowerInvariant() is "1" or "true" or "on";
|
||||
|
||||
/// <summary>SQL error number of <c>THROW 60001, 'backdating not allowed'</c> in <c>fds__setInvoiceFinal</c>.</summary>
|
||||
public const int BackdateNotAllowedErrorNumber = 60001;
|
||||
|
||||
/// <summary>
|
||||
/// Classifies what <c>fds__setInvoiceFinal</c> did, from the OCORE result: an exception (with the SQL
|
||||
/// error number), or the number of rows the guarded <c>UPDATE ... WHERE isFinal = 0 AND isSent = 0</c>
|
||||
/// returned. Only the dedicated 60001 means "backdating no longer allowed"; a 0-row result means the
|
||||
/// invoice was already final/sent (e.g. a double submit) — reporting that as a backdate rejection
|
||||
/// would invite the user to create a duplicate.
|
||||
/// </summary>
|
||||
public static InvoiceFinalizeOutcome ClassifyFinalizeResult(string? sqlException, int? sqlErrorNumber, int rowCount)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sqlException))
|
||||
return sqlErrorNumber == BackdateNotAllowedErrorNumber
|
||||
? InvoiceFinalizeOutcome.BackdateNotAllowed
|
||||
: InvoiceFinalizeOutcome.Error;
|
||||
return rowCount > 0 ? InvoiceFinalizeOutcome.Finalized : InvoiceFinalizeOutcome.AlreadyFinal;
|
||||
}
|
||||
|
||||
/// <summary>Tag value for the <c>fuchs.invoices.backdated</c> counter's <c>outcome</c> dimension.</summary>
|
||||
public static string OutcomeTag(InvoiceFinalizeOutcome outcome) => outcome switch
|
||||
{
|
||||
InvoiceFinalizeOutcome.Finalized => "ok",
|
||||
InvoiceFinalizeOutcome.BackdateNotAllowed => "rejected",
|
||||
InvoiceFinalizeOutcome.AlreadyFinal => "already_final",
|
||||
_ => "error"
|
||||
};
|
||||
|
||||
internal static DateTime ToGermanTime(DateTime utc)
|
||||
=> TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(utc, DateTimeKind.Utc), German);
|
||||
|
||||
private static TimeZoneInfo ResolveGermanZone()
|
||||
{
|
||||
foreach (var id in new[] { "Europe/Berlin", "W. Europe Standard Time" })
|
||||
{
|
||||
try { return TimeZoneInfo.FindSystemTimeZoneById(id); }
|
||||
catch (TimeZoneNotFoundException) { }
|
||||
catch (InvalidTimeZoneException) { }
|
||||
}
|
||||
return TimeZoneInfo.Local;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user