using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace PayRollDAL.DTO.GateEntry
{
///
/// Accepts both a JSON number and a JSON string so that:
/// • single-item QR → "batchQty": 14158.6 (number)
/// • multi-item QR → "batchQty": "14158.6,14155" (string)
/// Both deserialise to a string that the DAL splits and parses.
///
internal sealed class FlexibleDecimalStringConverter : JsonConverter
{
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> reader.TokenType == JsonTokenType.Number
? reader.GetDecimal().ToString(System.Globalization.CultureInfo.InvariantCulture)
: reader.GetString();
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
=> writer.WriteStringValue(value);
}
///
/// Parses InvoiceDate from multiple formats the QR scanner may emit:
/// "01.05.2026" (DD.MM.YYYY — printed on the label)
/// "2026-03-31" (ISO 8601 — standard API format)
/// "01/05/2026" (DD/MM/YYYY)
/// Returns null when the string is absent or unparseable.
///
internal sealed class FlexibleDateOnlyConverter : JsonConverter
{
private static readonly string[] _Formats =
{
"dd.MM.yyyy", // 01.05.2026
"yyyy-MM-dd", // 2026-03-31
"dd/MM/yyyy", // 01/05/2026
"MM/dd/yyyy", // 05/01/2026
"d.M.yyyy", // 1.5.2026
};
public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var s = reader.GetString();
if (string.IsNullOrWhiteSpace(s)) return null;
if (DateOnly.TryParseExact(s, _Formats,
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out var d))
return d;
return DateOnly.TryParse(s,
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out var d2)
? d2 : (DateOnly?)null;
}
public override void Write(Utf8JsonWriter writer, DateOnly? value, JsonSerializerOptions options)
{
if (value.HasValue) writer.WriteStringValue(value.Value.ToString("yyyy-MM-dd"));
else writer.WriteNullValue();
}
}
///
/// Raw text values from a QR scanner / event sheet.
///
/// SINGLE-ITEM (normal):
/// One object → one resolved detail row.
/// All fields are plain scalar values.
///
/// MULTI-ITEM (comma-encoded):
/// One object → N resolved detail rows.
/// Per-item fields (MaterialCode, BatchNo, BatchQty, InvoiceQty,
/// AllocationName, UOMName, Remarks) carry N comma-separated values.
/// Header fields (SupplierName, PONumber, InvoiceNumber, InvoiceDate)
/// are shared across all expanded rows.
///
/// Example — 2 items in one object:
/// "materialCode": "SB/1622/C000/NA000/1270,SB/1622/C000/NA000/1271"
/// "batchNo": "15G2600104,15G2600105"
/// "batchQty": "14158.6,14155"
/// "invoiceQty": "14158.6,141544.6"
///
/// Rules:
/// • Count = max number of comma-separated tokens across all per-item fields.
/// • Fields with fewer tokens than Count reuse their last value for the
/// remaining rows (safe broadcast).
/// • Fields with exactly 1 token are broadcast to all rows unchanged.
///
public class GateEntryDetailRawInputDTO
{
// ── Header fields (shared across all expanded rows) ──────────────
/// Supplier party name — resolves to PartyId/PartyCode on the gate entry header.
public string SupplierName { get; set; }
/// PO document number (e.g. "VFG/PO-III/4076/2025-26") — resolves to DocumentId, DocumentNumber.
public string PONumber { get; set; }
/// Invoice number (e.g. "S5D25260924") — stored as ReferenceNumber on the gate entry header.
public string InvoiceNumber { get; set; }
///
/// Invoice date — stored as ReferenceDate on the gate entry header.
/// Accepts "01.05.2026" (DD.MM.YYYY), "2026-03-31" (ISO), or "01/05/2026".
///
[JsonConverter(typeof(FlexibleDateOnlyConverter))]
public DateOnly? InvoiceDate { get; set; }
// ── Per-item fields (comma-separated for multi-item QR) ──────────
///
/// Material code or name. Comma-separated for multi-item QR.
/// Resolved first by ITEMCODE, then by ITEMNAME.
///
public string MaterialCode { get; set; }
/// Batch / lot number. Comma-separated for multi-item QR.
public string BatchNo { get; set; }
///
/// Batch quantity → LotQuantity. Comma-separated for multi-item QR.
/// Accepts both JSON number (14158.6) and JSON string ("14158.6,14155").
///
[JsonConverter(typeof(FlexibleDecimalStringConverter))]
public string BatchQty { get; set; }
///
/// Invoice quantity → DocumentValue. Comma-separated for multi-item QR.
/// Accepts both JSON number and JSON string.
///
[JsonConverter(typeof(FlexibleDecimalStringConverter))]
public string InvoiceQty { get; set; }
/// Allocation name. Comma-separated for multi-item QR.
public string AllocationName { get; set; }
/// Unit of measure name. Comma-separated for multi-item QR.
public string UOMName { get; set; }
/// Free-text remarks. Comma-separated for multi-item QR.
public string Remarks { get; set; }
}
}