Compare commits

...

4 Commits

Author SHA1 Message Date
Loretta 4081bddfcc Merge branch 'FruitBank_v0.0.8.0' of https://git.aycode.com/Adam/Mango.Nop.Plugins into FruitBank_v0.0.8.0 2026-07-10 15:38:22 +02:00
Loretta 9f6aa1b903 EKÁER: depot, isEkaer, grid layout, doc updates
- Switch to DEV DB and split company address street/number
- CustomerCreditWidget: isEkaer now nullable, defaults to true
- FruitBankDataController: add grid layout CRUD endpoints
- EKÁER: require depot for inbound, batch isEkaer for outbound
- Update docs: stricter foreign reporting, depot handling, isEkaer logic, mapping rules, and close depot-grouping issue
2026-07-10 15:37:05 +02:00
Loretta 36f22aa2cc AI OrderDraft intake, customer attrs, PreOrder admin UX
- Add OrderDraft (AI-assisted order intake) workflow: API, service, admin UI, and DB tables for draft creation, review, and approval into PreOrders.
- Implement customer attributes block: EKÁER flag, sites, license plates; add modals and AJAX endpoints for editing; auto-assign defaults to new orders.
- Extend order attributes (admin order page) for site/plate selection.
- Enhance PreOrder admin: inline editing, product swap, manual conversion, improved detail/list UI.
- Update localization (EN/HU) and documentation for new features.
- Register new services/routes; refactor PreOrder conversion and product swap logic.
- Improve customer and preorder UI/UX, including search and voice input.
2026-07-03 12:12:04 +02:00
Loretta 95b7fbd561 Update DB to PROD, enable NopLogWriter, cleanup usings
Switched the connection string to the production database in appsettings.json. Enabled Mango.Nop.Services.Loggers.NopLogWriter as an additional logger. Removed an unused using directive from FruitBankDataController.cs.
2026-07-03 12:10:19 +02:00
5 changed files with 218 additions and 26 deletions

View File

@ -35,8 +35,10 @@ public class CustomerCreditWidgetViewComponent : NopViewComponent
var credit = await _customerCreditService.GetByCustomerIdAsync(customerId);
var outstanding = await _customerCreditService.GetOutstandingBalanceAsync(customerId);
// Hiányzó attribútum (null) = köteles — a kapu is így dönt (CreateMissingEkaerHistories: csak az explicit
// false mentesít); a checkbox pipáltat mutat, amíg explicit ki nem veszik.
var isEkaer = await _attributeService
.GetGenericAttributeValueAsync<Customer, bool>(customerId, FruitBankPluginConst.IsEkaerAttribute, storeId: 0);
.GetGenericAttributeValueAsync<Customer, bool?>(customerId, FruitBankPluginConst.IsEkaerAttribute, storeId: 0) ?? true;
var model = new CustomerCreditWidgetModel
{

View File

@ -10,7 +10,6 @@ using DocumentFormat.OpenXml.Office2010.Excel;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using FruitBank.Common.Interfaces;
//using FruitBank.Common.Loggers;
using FruitBank.Common.Models;
using FruitBank.Common.Server;
using FruitBank.Common.Server.Interfaces;
@ -50,6 +49,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
: BasePluginController, IFruitBankDataControllerServer
{
private const int LastShippingDays = 15;
private const string GridLayoutKeyGroup = "GridLayout";
private readonly ILogger _logger = new Logger<FruitBankDataController>(logWriters.ToArray());
[SignalR(SignalRTags.ProcessAndSaveFullShippingJson)]
@ -78,6 +78,53 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
return await ctx.GenericAttributeDtos.GetByIdAsync(genericAttributeDto.Id);
}
private async Task<GenericAttributeDto?> GetGridLayoutAttributeAsync(string key, int userId)
=> (await ctx.GetGenericAttributeDtosByEntityIdAndKeyGroup(userId, GridLayoutKeyGroup, 0))
.FirstOrDefault(x => x.Key == key);
[SignalR(SignalRTags.GetGridLayoutTag)]
public async Task<string?> GetGridLayout(string key, int userId)
{
return (await GetGridLayoutAttributeAsync(key, userId))?.Value;
}
[SignalR(SignalRTags.SaveGridLayoutTag)]
public async Task<bool> SaveGridLayout(string key, string layoutJson, int userId)
{
var attribute = await GetGridLayoutAttributeAsync(key, userId);
if (attribute == null)
{
await ctx.GenericAttributeDtos.InsertAsync(new GenericAttributeDto
{
EntityId = userId,
KeyGroup = GridLayoutKeyGroup,
Key = key,
Value = layoutJson,
StoreId = 0,
CreatedOrUpdatedDateUTC = DateTime.UtcNow
});
}
else
{
attribute.Value = layoutJson;
attribute.CreatedOrUpdatedDateUTC = DateTime.UtcNow;
await ctx.GenericAttributeDtos.UpdateAsync(attribute);
}
return true;
}
[SignalR(SignalRTags.DeleteGridLayoutTag)]
public async Task<bool> DeleteGridLayout(string key, int userId)
{
var attribute = await GetGridLayoutAttributeAsync(key, userId);
if (attribute == null) return false;
await ctx.GenericAttributeDtos.DeleteAsync(attribute);
return true;
}
[SignalR(SignalRTags.GetStockQuantityHistoryDtos)]
public async Task<List<StockQuantityHistoryDto>> GetStockQuantityHistoryDtos()
{
@ -341,7 +388,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
if (!ekaerHistory.IsOutgoing)
{
// Bejövő: a csoport ÖSSZES szállítólevele EGY tradeCard-dá (összevont tömeg/érték); GetAll(true) = teljes gráf.
var documents = await ctx.ShippingDocuments.GetAll(true).Where(sd => foreignKeys.Contains(sd.Id)).ToListAsync();
// A PartnerDepot szándékosan NINCS a default loadRelations-ben — csak itt kell (felrakodási hely = depot címe).
var documents = await ctx.ShippingDocuments.GetAll(true)
.LoadWith(sd => sd.PartnerDepot)
.Where(sd => foreignKeys.Contains(sd.Id)).ToListAsync();
if (documents.Count == 0) throw new InvalidOperationException($"EkaerHistory #{ekaerHistoryId}: no ShippingDocuments found for the mapped ids.");
ekaerHistory = fruitBankEkaerService.GenerateEkaerXmlDocument(documents, ekaerHistory);
@ -434,6 +484,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
if (result.Obligation == EkaerObligation.DataError) { messages.UnionWith(result.Errors); continue; }
if (result.Obligation == EkaerObligation.NotRequired) continue;
// Kötelező, de nincs telephely → NEM jön létre sor, üzenet a popupba (T-D8R4: a PartnerDepot az
// EKÁER-hez kötelező — a felrakodási hely a depot címe); pótlás után a következő futás felveszi.
// A depot-check a kötelezettség-döntés UTÁN: küszöb alatti belföldi csoportra nem szólunk feleslegesen.
if (group.Key.PartnerDepotId is null)
{
messages.Add($"{docs[0].Partner?.Name} (Szállítás #{group.Key.ShippingId}): nincs telephely (PartnerDepot) — EKÁER-bejelentéshez kötelező, a sor nem jött létre.");
continue;
}
// Kötelező → EGY EkaerHistory a csoportra (összegző: ShippingDate + Partner — invariáns) + a dokumentumok mapping-foreignKey-ként.
var history = new EkaerHistory { IsOutgoing = false, StatusId = (int)EkaerStatus.Pending };
fruitBankEkaerService.SetSummary(history, docs[0].ShippingDate, docs[0].Partner?.Name);
@ -456,10 +515,22 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
var missingOrders = missingOrderIds.Count == 0 ? [] : await ctx.OrderDtos.GetAllByIds(missingOrderIds, true).ToListAsync();
// Vevő-szintű mentesség (isEkaer GenericAttribute — mint a Partner.IsEkaer) EGY batch-queryvel a ciklus előtt:
// ami nincs a dictionary-ben = nincs beállítva = köteles (fail-safe; a widget-checkbox is pipáltat mutat rá),
// CSAK az explicit false mentesít.
var customerIds = missingOrders.Select(o => o.CustomerId).Distinct().ToList();
Dictionary<int, bool> isEkaerByCustomer = customerIds.Count == 0 ? new() : (await ctx.GenericAttributes.Table
.Where(ga => ga.KeyGroup == nameof(Customer) && ga.Key == FruitBankPluginConst.IsEkaerAttribute
&& ga.StoreId == 0 && customerIds.Contains(ga.EntityId))
.ToListAsync())
.ToDictionary(ga => ga.EntityId, ga => CommonHelper.To<bool>(ga.Value));
foreach (var order in missingOrders)
{
try
{
if (isEkaerByCustomer.TryGetValue(order.CustomerId, out var isEkaer) && !isEkaer) continue;
var result = fruitBankEkaerService.EvaluateObligation(order);
if (result.Obligation == EkaerObligation.DataError) { messages.UnionWith(result.Errors); continue; }
if (result.Obligation == EkaerObligation.NotRequired) continue;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long