xml download
This commit is contained in:
parent
0638c89989
commit
169f7fc133
|
|
@ -21,9 +21,22 @@ public sealed class PreOrder : MgEntityBase
|
||||||
[ToonDescription(Purpose = "Requested delivery date. Drives the conversion window (only preorders within PreOrderConversionWindowDays — 4 days — of this date are eligible for allocation) and the expiry sweep — once this date is past, any still-Pending items are Dropped.")]
|
[ToonDescription(Purpose = "Requested delivery date. Drives the conversion window (only preorders within PreOrderConversionWindowDays — 4 days — of this date are eligible for allocation) and the expiry sweep — once this date is past, any still-Pending items are Dropped.")]
|
||||||
public DateTime DateOfReceipt { get; set; }
|
public DateTime DateOfReceipt { get; set; }
|
||||||
|
|
||||||
|
// Persisted as an int column ("Status"). A raw enum property is NOT mapped/written by the
|
||||||
|
// nopCommerce LinqToDB stack (nop stores enums as int + a [NotMapped] wrapper — cf. Order.OrderStatusId),
|
||||||
|
// so writing the enum directly left the column stuck at 0. StatusId is the real mapped column;
|
||||||
|
// the Status wrapper below is the typed API.
|
||||||
|
[Column("Status")]
|
||||||
|
[ToonDescription(Purpose = "Header lifecycle status, stored as int. See the Status wrapper for the enum semantics.")]
|
||||||
|
public int StatusId { get; set; }
|
||||||
|
|
||||||
|
[NotColumn, System.ComponentModel.DataAnnotations.Schema.NotMapped]
|
||||||
[ToonDescription(Purpose = "Header lifecycle: Pending / Confirmed (all items Fulfilled) / PartiallyFulfilled (some Dropped or partial, none left Pending) / Cancelled.",
|
[ToonDescription(Purpose = "Header lifecycle: Pending / Confirmed (all items Fulfilled) / PartiallyFulfilled (some Dropped or partial, none left Pending) / Cancelled.",
|
||||||
BusinessRule = "set by RefreshPreOrderStatusAsync from item states: items.All(Fulfilled) => Confirmed; (any Dropped or PartiallyFulfilled) && none Pending => PartiallyFulfilled; else Pending. Cancelled is set explicitly.")]
|
BusinessRule = "set by RefreshPreOrderStatusAsync from item states: items.All(Fulfilled) => Confirmed; (any Dropped or PartiallyFulfilled) && none Pending => PartiallyFulfilled; else Pending. Cancelled is set explicitly. Persisted via StatusId.")]
|
||||||
public PreOrderStatus Status { get; set; }
|
public PreOrderStatus Status
|
||||||
|
{
|
||||||
|
get => (PreOrderStatus)StatusId;
|
||||||
|
set => StatusId = (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
[ToonDescription(Purpose = "Optional free-text note entered by the customer when placing the preorder.")]
|
[ToonDescription(Purpose = "Optional free-text note entered by the customer when placing the preorder.")]
|
||||||
public string? CustomerNote { get; set; }
|
public string? CustomerNote { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,19 @@ public sealed class PreOrderItem : MgEntityBase
|
||||||
Constraints = "non-negative")]
|
Constraints = "non-negative")]
|
||||||
public decimal UnitPriceInclTax { get; set; }
|
public decimal UnitPriceInclTax { get; set; }
|
||||||
|
|
||||||
|
// Persisted as an int column ("Status"). A raw enum property is NOT mapped/written by the
|
||||||
|
// nopCommerce LinqToDB stack, so writing the enum directly left the column stuck at 0.
|
||||||
|
// StatusId is the real mapped column; the Status wrapper below is the typed API.
|
||||||
|
[Column("Status")]
|
||||||
|
[ToonDescription(Purpose = "Item lifecycle status, stored as int. See the Status wrapper for the enum semantics.")]
|
||||||
|
public int StatusId { get; set; }
|
||||||
|
|
||||||
|
[NotColumn, System.ComponentModel.DataAnnotations.Schema.NotMapped]
|
||||||
[ToonDescription(Purpose = "Item lifecycle: Pending / Fulfilled (fully allocated) / PartiallyFulfilled (partly allocated) / Dropped (expired or no incoming stock).",
|
[ToonDescription(Purpose = "Item lifecycle: Pending / Fulfilled (fully allocated) / PartiallyFulfilled (partly allocated) / Dropped (expired or no incoming stock).",
|
||||||
BusinessRule = "set during conversion: FulfilledQuantity >= RequestedQuantity ? Fulfilled : FulfilledQuantity > 0 ? PartiallyFulfilled : Dropped; stays Pending until first allocation")]
|
BusinessRule = "set during conversion: FulfilledQuantity >= RequestedQuantity ? Fulfilled : FulfilledQuantity > 0 ? PartiallyFulfilled : Dropped; stays Pending until first allocation. Persisted via StatusId.")]
|
||||||
public PreOrderItemStatus Status { get; set; }
|
public PreOrderItemStatus Status
|
||||||
|
{
|
||||||
|
get => (PreOrderItemStatus)StatusId;
|
||||||
|
set => StatusId = (int)value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,12 +83,12 @@
|
||||||
Enabled="@(CanGenerate(row))"
|
Enabled="@(CanGenerate(row))"
|
||||||
Attributes="@(new Dictionary<string, object> { ["title"] = row.ErrorText ?? "EKÁER XML generálása és validálása" })"
|
Attributes="@(new Dictionary<string, object> { ["title"] = row.ErrorText ?? "EKÁER XML generálása és validálása" })"
|
||||||
Click="async () => await OnGenerateClick(row)" />
|
Click="async () => await OnGenerateClick(row)" />
|
||||||
<DxButton Text="Copy"
|
<DxButton Text="Letöltés"
|
||||||
SizeMode="SizeMode.Small"
|
SizeMode="SizeMode.Small"
|
||||||
RenderStyle="ButtonRenderStyle.Secondary"
|
RenderStyle="ButtonRenderStyle.Secondary"
|
||||||
Enabled="@(CanCopy(row))"
|
Enabled="@(CanDownload(row))"
|
||||||
Attributes="@(new Dictionary<string, object> { ["title"] = CanCopy(row) ? "A generált XML vágólapra másolása (kézi NAV-beadáshoz)" : "Hibás bejelentés nem másolható — javítsd az adatokat és generáld újra." })"
|
Attributes="@(new Dictionary<string, object> { ["title"] = CanDownload(row) ? "A generált XML letöltése fájlként (kézi NAV-beadáshoz)" : "Hibás bejelentés nem tölthető le — javítsd az adatokat és generáld újra." })"
|
||||||
Click="async () => await OnCopyClick(row)" />
|
Click="async () => await OnDownloadClick(row)" />
|
||||||
}
|
}
|
||||||
</CellDisplayTemplate>
|
</CellDisplayTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
|
|
@ -215,8 +215,8 @@
|
||||||
private bool CanGenerate(EkaerHistory ekaerHistory)
|
private bool CanGenerate(EkaerHistory ekaerHistory)
|
||||||
=> !ekaerHistory.Status.IsSent() && !_generatingIds.Contains(ekaerHistory.Id);
|
=> !ekaerHistory.Status.IsSent() && !_generatingIds.Contains(ekaerHistory.Id);
|
||||||
|
|
||||||
// Csak BLOKKOLÓ hiba (ValidationError) esetén NEM másolható; a warning (pl. hiányzó rendszám) küldhető → másolható.
|
// Csak BLOKKOLÓ hiba (ValidationError) esetén NEM tölthető le; a warning (pl. hiányzó rendszám) küldhető → letölthető.
|
||||||
private static bool CanCopy(EkaerHistory ekaerHistory)
|
private static bool CanDownload(EkaerHistory ekaerHistory)
|
||||||
=> ekaerHistory.Status.IsSubmittable() && !string.IsNullOrWhiteSpace(ekaerHistory.XmlDoc);
|
=> ekaerHistory.Status.IsSubmittable() && !string.IsNullOrWhiteSpace(ekaerHistory.XmlDoc);
|
||||||
|
|
||||||
// A szülő-oldal (fül-számlálók) értesítése egy státuszt érintő művelet után. Üres delegáltnál no-op.
|
// A szülő-oldal (fül-számlálók) értesítése egy státuszt érintő művelet után. Üres delegáltnál no-op.
|
||||||
|
|
@ -297,17 +297,25 @@
|
||||||
await DialogService.ShowMessageBoxAsync("EKÁER — kihagyott tételek", string.Join(Environment.NewLine, skipped), MessageBoxRenderStyle.Warning);
|
await DialogService.ShowMessageBoxAsync("EKÁER — kihagyott tételek", string.Join(Environment.NewLine, skipped), MessageBoxRenderStyle.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnCopyClick(EkaerHistory ekaerHistory)
|
private async Task OnDownloadClick(EkaerHistory ekaerHistory)
|
||||||
{
|
{
|
||||||
if (!CanCopy(ekaerHistory)) return;
|
if (!CanDownload(ekaerHistory)) return;
|
||||||
|
|
||||||
|
// Fájlnév: "yyyy.MM.dd-Partner.xml" — a partnernévből az érvénytelen fájlnév-karaktereket kicseréljük.
|
||||||
|
var datePart = ekaerHistory.ShippingDate?.ToString("yyyy.MM.dd") ?? "nincs-datum";
|
||||||
|
var partner = string.IsNullOrWhiteSpace(ekaerHistory.Partner) ? "partner" : ekaerHistory.Partner;
|
||||||
|
var invalid = System.IO.Path.GetInvalidFileNameChars();
|
||||||
|
var safePartner = new string(partner.Select(c => invalid.Contains(c) ? '_' : c).ToArray()).Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(safePartner)) safePartner = "partner";
|
||||||
|
var fileName = $"{datePart}-{safePartner}.xml";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await JS.InvokeVoidAsync("navigator.clipboard.writeText", ekaerHistory.XmlDoc);
|
await JS.InvokeVoidAsync("downloadFileFromText", fileName, ekaerHistory.XmlDoc, "application/xml");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error($"XmlDoc vágólapra másolása sikertelen; EkaerHistory.Id: {ekaerHistory.Id}", ex);
|
_logger.Error($"XmlDoc letöltése sikertelen; EkaerHistory.Id: {ekaerHistory.Id}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Triggers a client-side file download from in-memory text (no server round-trip).
|
||||||
|
// Used e.g. by the EKÁER grid to save the generated trade-card XML as a file.
|
||||||
|
window.downloadFileFromText = function (filename, content, mime) {
|
||||||
|
try {
|
||||||
|
const blob = new Blob([content], { type: mime || 'application/octet-stream' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename || 'download';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(function () {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('downloadFileFromText failed', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||||
</script>
|
</script>
|
||||||
<script src="_content/FruitBankHybrid.Shared/js/pdfViewer.js"></script>
|
<script src="_content/FruitBankHybrid.Shared/js/pdfViewer.js"></script>
|
||||||
|
<script src="_content/FruitBankHybrid.Shared/js/fileDownload.js"></script>
|
||||||
<script src="@Assets["/_framework/blazor.web.js"]"></script>
|
<script src="@Assets["/_framework/blazor.web.js"]"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||||
</script>
|
</script>
|
||||||
<script src="_content/FruitBankHybrid.Shared/js/pdfViewer.js"></script>
|
<script src="_content/FruitBankHybrid.Shared/js/pdfViewer.js"></script>
|
||||||
|
<script src="_content/FruitBankHybrid.Shared/js/fileDownload.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue