Compare commits

...

3 Commits

Author SHA1 Message Date
Loretta b6dddd6f1f Merge branch 'FruitBank_v0.0.8.0' of https://git.aycode.com/Adam/FruitBankHybridApp into FruitBank_v0.0.8.0 2026-07-10 15:38:29 +02:00
Loretta 42642b35a2 Enable server-roaming for grid layouts; EKÁER depot guard
Extended MgGridBase to support server-roaming of user grid layouts via SignalR, controlled by the new EnableServerLayouts parameter. Updated Save/Load/HasUserLayoutAsync logic and documentation. Added SignalR API methods for grid layout storage. In EKÁER, added TryDepotError to require PartnerDepot before XML generation and enforced gross weight > 0 per line. Updated docs and settings.local.json for new features and automation.
2026-07-10 15:37:26 +02:00
Loretta b129357cb6 Update config for local dev; add AyCode.Blazor consumers doc
- Added AyCode.Blazor/CONSUMERS.md to document consumer repos and usage for impact assessment.
- Switched appsettings.json URLs to localhost for local development; production URLs are now commented out.
- Commented out Blazor/DevExpress compression and optimization settings in .csproj files.
- Minor whitespace and indentation cleanups.
- No functional code changes.
2026-07-09 13:12:26 +02:00
11 changed files with 103 additions and 41 deletions

File diff suppressed because one or more lines are too long

View File

@ -45,9 +45,11 @@ public sealed class FruitBankEkaerService : IFruitBankEkaerService
// A csoport azonos Partneré (a kapu így csoportosít) → az első dokumentum pénzneme a mérvadó.
var currency = documents.First().Partner?.Currency;
// A csoport ÖSSZES dokumentuma EGY tradeCard-dá (összevont tömeg/érték): ToConsignment → BuildTradeCard.
var tradeCard = _mapper.BuildTradeCard(_mapper.ToConsignment(documents, _settings.Company));
return TryConfigError(ekaerHistory, currency) ?? Finalize(ekaerHistory, tradeCard, currency);
// Guardok a leképezés ELŐTT (nincs félrevezető XML), majd a csoport ÖSSZES dokumentuma EGY tradeCard-dá
// (összevont tömeg/érték): ToConsignment → BuildTradeCard.
return TryDepotError(ekaerHistory, documents)
?? TryConfigError(ekaerHistory, currency)
?? Finalize(ekaerHistory, _mapper.BuildTradeCard(_mapper.ToConsignment(documents, _settings.Company)), currency);
}
public EkaerHistory GenerateEkaerXmlDocument(OrderDto order, EkaerHistory? ekaerHistory = null)
@ -72,6 +74,18 @@ public sealed class FruitBankEkaerService : IFruitBankEkaerService
history.Partner = partner;
}
/// <summary>Depot-kapu (T-D8R4): a PartnerDepot az EKÁER-hez kötelező — a felrakodási hely a depot címe. Hiányzó
/// vagy be nem töltött telephelynél a leképezés ELŐTT ValidationError (nincs félrevezető, partner-központi-címes XML).
/// <c>null</c> = rendben, mehet a generálás.</summary>
private static EkaerHistory? TryDepotError(EkaerHistory ekaerHistory, IReadOnlyCollection<ShippingDocument> documents)
{
if (documents.All(d => d.PartnerDepot != null)) return null;
ekaerHistory.Status = EkaerStatus.ValidationError;
ekaerHistory.ErrorText = "A szállítólevélhez nincs telephely (PartnerDepot) rendelve — EKÁER-hez kötelező (a felrakodási hely a telephely címe); pótlás után generáld újra.";
return ekaerHistory;
}
/// <summary>Config-kapu: külföldi (nem HUF) feladónál az árfolyam kötelező — különben a leképezés ELŐTT
/// ValidationError (nincs félrevezető XML). <c>null</c> = rendben, mehet a generálás.</summary>
private EkaerHistory? TryConfigError(EkaerHistory ekaerHistory, string? currency)

View File

@ -117,4 +117,18 @@ public interface IFruitBankDataControllerCommon
public Task<GenericAttributeDto?> UpdateGenericAttributeDto(GenericAttributeDto genericAttributeDto);
/// <summary>
/// Returns the stored grid-layout JSON for the composite layout key, or null if none saved.
/// </summary>
public Task<string?> GetGridLayout(string key, int userId);
/// <summary>
/// Saves (upserts) the grid-layout JSON under the composite layout key for the given user.
/// </summary>
public Task<bool> SaveGridLayout(string key, string layoutJson, int userId);
/// <summary>
/// Deletes the stored grid layout for the composite layout key. Returns false if the user does not exist.
/// </summary>
public Task<bool> DeleteGridLayout(string key, int userId);
}

View File

@ -31,10 +31,11 @@ public sealed class EkaerObligationResult
/// is bírság), ezért: a bizonytalan adatot (érvénytelen országkód) NEM döntjük el magától → <see cref="EkaerObligation.DataError"/>.
/// </summary>
/// <remarks>
/// Sorrend: (1) az AGGREGÁLT tömeg/érték küszöb FELETT → Required, az országkódtól FÜGGETLENÜL — belföld+küszöb
/// felett és külföld egyaránt kötelező; az országkód-hibát ilyenkor a generate-validálás jelzi, a sor létrejön;
/// (2) küszöb ALATT → itt számít az ország: hiányzó/érvénytelen országkód → DataError (a foreign-vs-belföld nem
/// dönthető el); (3) küszöb alatt, érvényes országkódok: külföld (eltér) → Required, belföld (egyezik) → NotRequired.
/// Sorrend: (0) TÉTELENKÉNTI előfeltétel — bruttó súly > 0, különben DataError (a 0 súly csendben alul-számolná a
/// küszöb-summát → alul-jelentés); (1) az AGGREGÁLT tömeg/érték küszöb FELETT → Required, az országkódtól FÜGGETLENÜL
/// — belföld+küszöb felett és külföld egyaránt kötelező; az országkód-hibát ilyenkor a generate-validálás jelzi, a sor
/// létrejön; (2) küszöb ALATT → itt számít az ország: hiányzó/érvénytelen országkód → DataError (a foreign-vs-belföld
/// nem dönthető el); (3) küszöb alatt, érvényes országkódok: külföld (eltér) → Required, belföld (egyezik) → NotRequired.
/// A 13/2020. (XII. 23.) PM rendelet alapján a küszöb feladó→címzett→jármű relációra aggregált — a hívó már
/// így állítja össze a <see cref="EkaerConsignment.Lines"/>-t (bejövőnél a (Shipping, Partner) csoport tételei).
/// </remarks>
@ -45,6 +46,18 @@ public static class EkaerReportability
ArgumentNullException.ThrowIfNull(consignment);
ArgumentNullException.ThrowIfNull(settings);
var subject = Subject(consignment);
// (0) Tételenkénti előfeltétel: bruttó súly > 0. A 0 súlyú tétel (kitöltetlen dokumentum-súly / méretlen
// rendelés) csendben alul-számolná a küszöb-aggregátumot → alul-jelentési kockázat; a NAV amúgy is tételenként
// pozitív bruttó súlyt vár. Hiányzó súly → DataError (a sor nem jön létre; pótlás után a következő futás felveszi).
var weightErrors = consignment.Lines
.Where(l => l.WeightKg <= 0)
.Select(l => $"{subject} / {(string.IsNullOrWhiteSpace(l.Name) ? $"tétel #{l.ExternalId}" : l.Name)}: a tétel bruttó súlya hiányzik (0 kg) — a kötelezettség nem dönthető el.")
.ToList();
if (weightErrors.Count > 0)
return EkaerObligationResult.DataError(weightErrors);
// (1) Küszöb FELETT → kötelező, FÜGGETLENÜL az országkódtól: belföld+küszöb felett ÉS külföld egyaránt kötelező.
// Az országkód-hibát ilyenkor NEM itt blokkoljuk — a sor létrejön, a hibát a generate-validálás jelzi.
var totalWeight = consignment.Lines.Sum(l => l.WeightKg);
@ -54,7 +67,6 @@ public static class EkaerReportability
// (2) Küszöb ALATT: itt MÁR az ország dönt — csak a külföldi (eltérő országkód) reláció kötelező. Ehhez érvényes
// ISO-2 országkódok kellenek; hiányzó/érvénytelen → a foreign-vs-belföld nem dönthető el → DataError.
var subject = Subject(consignment);
var errors = new List<string>();
if (!IsValidCountry(consignment.Seller.CountryCode))
errors.Add($"{subject}: a feladó országkódja hiányzik vagy érvénytelen ('{consignment.Seller.CountryCode}').");

View File

@ -74,7 +74,7 @@ public sealed class ShippingToEkaerMapper : IShippingToEkaerMapper
// ── Adapterek: forrás → normalizált EkaerConsignment (az EGYETLEN irányfüggő rész) ────────────
/// <summary>Bejövő: egy vagy több <c>ShippingDocument</c> (a hívó (Shipping, Partner)-re csoportosít) → egy
/// <summary>Bejövő: egy vagy több <c>ShippingDocument</c> (a hívó (Shipping, Partner, PartnerDepot)-ra csoportosít) → egy
/// szállítmány a csoport ÖSSZES tételével. Egy dokumentummal a generáláshoz, a csoporttal a kötelezettség-summához.</summary>
public EkaerConsignment ToConsignment(IReadOnlyCollection<ShippingDocument> documents, EkaerCompanyInfo company)
{
@ -83,19 +83,24 @@ public sealed class ShippingToEkaerMapper : IShippingToEkaerMapper
if (documents.Count == 0) throw new ArgumentException("Legalább egy ShippingDocument szükséges.", nameof(documents));
var first = documents.First();
var partner = first.Partner; // a csoport azonos partneré (a hívó (Shipping, Partner)-re csoportosít)
var partner = first.Partner; // a csoport azonos partneré (a hívó (Shipping, Partner, PartnerDepot)-ra csoportosít)
var shipping = first.Shipping; // azonos Shipping; a kapunál lehet null (a jármű ott nem kell)
var rateToHuf = SafeRateToHuf(partner?.Currency);
// A depot TaxId-ja Partner-passthrough (a nested Partner-t nem töltjük külön query-vel) — ugyanaz a minta,
// mint a MapShipping `document.Shipping ??=` visszakötése.
if (first.PartnerDepot is { } depot) depot.Partner ??= partner;
var lines = documents
.SelectMany(d => d.ShippingItems ?? [])
.Select(item => new EkaerLine
{
ExternalId = item.Id.ToString(),
TradeReason = TradeReasonType.A, // bejövő áru = beszerzés
TradeReason = TradeReasonType.S, // S = értékesítés ÉS beszerzés (egy kód); az A kivezetett, D-nél csak S adható
Vtsz = NormalizeVtsz(item.ProductDto?.Gtin),
Name = item.ProductName,
WeightKg = item.MeasuredGrossWeight,
// A szállítólevélen szereplő bruttó — a bejelentés a kísérő dokumentum adatairól megy (mint a value),
// nem a (későbbi) mérlegelésről; méretlen tétel így nem 0 kg-mal jelenik meg.
WeightKg = item.GrossWeightOnDocument,
ValueHuf = EkaerValueCalculator.ItemValueHuf(item, rateToHuf),
})
.ToList();
@ -107,7 +112,9 @@ public sealed class ShippingToEkaerMapper : IShippingToEkaerMapper
Currency = partner?.Currency,
Seller = PartnerEndpoint(partner),
Buyer = CompanyEndpoint(company),
LoadLocation = BuildLocation(partner), // a beszállító telephelye (a PartnerDepot-bekötés külön feladat)
// A felrakodási hely a DEPOT címe (T-D8R4) — a generate-út betölti (LoadWith) és guardolja (TryDepotError).
// A kapu-útvonalon a depot nincs betöltve → partner-központi cím, ami ott rendben van: a kötelezettség-döntés címet nem használ.
LoadLocation = BuildLocation((ICompanyInfoBase?)first.PartnerDepot ?? partner),
UnloadLocation = company.Site, // a saját telephely (kész LocationType — mezővesztés nélkül átengedve)
Lines = lines,
Vehicle = BuildVehicle(shipping?.CargoTruck),

View File

@ -238,6 +238,17 @@ namespace FruitBankHybrid.Shared.Services.SignalRs
=> PostDataAsync<MgLoginModelRequest, MgLoginModelResponse>(SignalRTags.AuthenticateUser, loginModelRequest);
#endregion Authenticate
#region GridLayout
public Task<string?> GetGridLayout(string key, int userId)
=> GetByIdAsync<string>(SignalRTags.GetGridLayoutTag, [key, userId]);
public Task<bool> SaveGridLayout(string key, string layoutJson, int userId)
=> PostAsync<bool>(SignalRTags.SaveGridLayoutTag, [key, layoutJson, userId]);
public Task<bool> DeleteGridLayout(string key, int userId)
=> PostAsync<bool>(SignalRTags.DeleteGridLayoutTag, [key, userId]);
#endregion GridLayout
#region Orders
public Task<List<OrderDtoTableItem>?> GetAllOrderDtoTableItems()
=> GetAllAsync<List<OrderDtoTableItem>>(SignalRTags.GetAllOrderDtos);

View File

@ -8,10 +8,10 @@
"AyCode": {
"ProjectId": "aad53443-2ee2-4650-8a99-97e907265e4e",
"Urls": {
//"BaseUrl": "https://localhost:59579",
//"ApiBaseUrl": "https://localhost:59579"
"BaseUrl": "https://shop.fruitbank.hu",
"ApiBaseUrl": "https://shop.fruitbank.hu"
"BaseUrl": "https://localhost:59579",
"ApiBaseUrl": "https://localhost:59579"
//"BaseUrl": "https://shop.fruitbank.hu",
//"ApiBaseUrl": "https://shop.fruitbank.hu"
},
"Logger": {
"AppType": "Server",
@ -26,8 +26,8 @@
},
"AcHubConnection": {
//"Url": "https://localhost:59579/fbHub",
"Url": "https://shop.fruitbank.hu/fbHub",
"Url": "https://localhost:59579/fbHub",
//"Url": "https://shop.fruitbank.hu/fbHub",
"TransportMaxBufferSize": 30000000,
"ApplicationMaxBufferSize": 30000000,
"CloseTimeout": "00:00:10",

View File

@ -11,12 +11,10 @@
<WasmStripILAfterAOT>true</WasmStripILAfterAOT>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<BlazorEnableCompression>false</BlazorEnableCompression>
<BlazorCacheBootResources>false</BlazorCacheBootResources>
<OptimizationPreference>Speed</OptimizationPreference>
<DevExpressBrotliCompression>false</DevExpressBrotliCompression>
<!--<BlazorEnableCompression>false</BlazorEnableCompression>
<BlazorCacheBootResources>false</BlazorCacheBootResources>
<OptimizationPreference>Speed</OptimizationPreference>
<DevExpressBrotliCompression>false</DevExpressBrotliCompression>-->
</PropertyGroup>
<ItemGroup>

View File

@ -8,10 +8,10 @@
"AyCode": {
"ProjectId": "aad53443-2ee2-4650-8a99-97e907265e4e",
"Urls": {
//"BaseUrl": "https://localhost:59579",
//"ApiBaseUrl": "https://localhost:59579"
"BaseUrl": "https://shop.fruitbank.hu",
"ApiBaseUrl": "https://shop.fruitbank.hu"
"BaseUrl": "https://localhost:59579",
"ApiBaseUrl": "https://localhost:59579"
//"BaseUrl": "https://shop.fruitbank.hu",
//"ApiBaseUrl": "https://shop.fruitbank.hu"
},
"Logger": {
"AppType": "Server",
@ -26,8 +26,8 @@
},
"AcHubConnection": {
//"Url": "https://localhost:59579/fbHub",
"Url": "https://shop.fruitbank.hu/fbHub",
"Url": "https://localhost:59579/fbHub",
//"Url": "https://shop.fruitbank.hu/fbHub",
"TransportMaxBufferSize": 30000000,
"ApplicationMaxBufferSize": 30000000,
"CloseTimeout": "00:00:10",

View File

@ -9,8 +9,8 @@
<RunAOTCompilation>false</RunAOTCompilation>
<WasmStripILAfterAOT>true</WasmStripILAfterAOT>
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
<BlazorEnableCompression>false</BlazorEnableCompression>
<DevExpressBrotliCompression>false</DevExpressBrotliCompression>
<!--<BlazorEnableCompression>false</BlazorEnableCompression>
<DevExpressBrotliCompression>false</DevExpressBrotliCompression>-->
</PropertyGroup>
<ItemGroup>
@ -78,7 +78,7 @@
</Reference>
<Reference Include="Mango.Nop.Core">
<HintPath>..\..\NopCommerce.Common\4.70\Libraries\Mango.Nop.Core\bin\FruitBank\$(Configuration)\net9.0\Mango.Nop.Core.dll</HintPath>
<HintPath>..\..\NopCommerce.Common\4.70\Libraries\Mango.Nop.Core\bin\FruitBank\$(Configuration)\net9.0\Mango.Nop.Core.dll</HintPath>
</Reference>
</ItemGroup>

View File

@ -8,10 +8,10 @@
"AyCode": {
"ProjectId": "aad53443-2ee2-4650-8a99-97e907265e4e",
"Urls": {
//"BaseUrl": "https://localhost:59579",
//"ApiBaseUrl": "https://localhost:59579"
"BaseUrl": "https://shop.fruitbank.hu",
"ApiBaseUrl": "https://shop.fruitbank.hu"
"BaseUrl": "https://localhost:59579",
"ApiBaseUrl": "https://localhost:59579"
//"BaseUrl": "https://shop.fruitbank.hu",
//"ApiBaseUrl": "https://shop.fruitbank.hu"
},
"Logger": {
"AppType": "Server",
@ -26,8 +26,8 @@
},
"AcHubConnection": {
//"Url": "https://localhost:59579/fbHub",
"Url": "https://shop.fruitbank.hu/fbHub",
"Url": "https://localhost:59579/fbHub",
//"Url": "https://shop.fruitbank.hu/fbHub",
"TransportMaxBufferSize": 30000000,
"ApplicationMaxBufferSize": 30000000,
"CloseTimeout": "00:00:10",