297 lines
14 KiB
C#
297 lines
14 KiB
C#
using FruitBank.Common.Entities;
|
|
using FruitBank.Common.Enums;
|
|
using LinqToDB;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
|
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
|
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
|
using Nop.Services.Catalog;
|
|
using Nop.Services.Customers;
|
|
using Nop.Services.Security;
|
|
using Nop.Web.Framework;
|
|
using Nop.Web.Framework.Controllers;
|
|
using Nop.Web.Framework.Mvc.Filters;
|
|
using System.Text.Json;
|
|
|
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;
|
|
|
|
[AuthorizeAdmin]
|
|
[Area(AreaNames.ADMIN)]
|
|
public class OrderDraftAdminController : BasePluginController
|
|
{
|
|
private readonly IOrderDraftService _draftService;
|
|
private readonly FruitBankDbContext _dbContext;
|
|
private readonly ICustomerService _customerService;
|
|
private readonly IProductService _productService;
|
|
private readonly IPermissionService _permissionService;
|
|
|
|
public OrderDraftAdminController(
|
|
IOrderDraftService draftService,
|
|
FruitBankDbContext dbContext,
|
|
ICustomerService customerService,
|
|
IProductService productService,
|
|
IPermissionService permissionService)
|
|
{
|
|
_draftService = draftService;
|
|
_dbContext = dbContext;
|
|
_customerService = customerService;
|
|
_productService = productService;
|
|
_permissionService = permissionService;
|
|
}
|
|
|
|
// ── LISTA ─────────────────────────────────────────────────────────────────
|
|
|
|
[HttpGet]
|
|
public IActionResult List()
|
|
{
|
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/OrderDraft/List.cshtml",
|
|
new OrderDraftListModel());
|
|
}
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> OrderDraftList()
|
|
{
|
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
return Json(new { draw = 1, recordsTotal = 0, recordsFiltered = 0, data = Array.Empty<object>() });
|
|
|
|
_ = int.TryParse(Request.Form["draw"].FirstOrDefault(), out int draw); draw = Math.Max(draw, 1);
|
|
_ = int.TryParse(Request.Form["start"].FirstOrDefault(), out int start); start = Math.Max(start, 0);
|
|
_ = int.TryParse(Request.Form["length"].FirstOrDefault(), out int length); length = length < 1 ? 25 : Math.Min(length, 200);
|
|
|
|
var statusFilter = Request.Form["statusFilter"].FirstOrDefault() ?? "Pending";
|
|
var globalSearch = Request.Form["search[value]"].FirstOrDefault()?.Trim() ?? "";
|
|
|
|
// 1. Draftek lekérése
|
|
var query = _dbContext.OrderDrafts.GetAll(false);
|
|
|
|
if (Enum.TryParse<OrderDraftStatus>(statusFilter, out var statusEnum))
|
|
query = query.Where(d => d.Status == statusEnum);
|
|
|
|
var allDrafts = await query.OrderByDescending(d => d.CreatedOnUtc).ToListAsync();
|
|
|
|
// 2. Kapcsolódó customer nevek — batch
|
|
var customerIds = allDrafts.Where(d => d.CustomerId.HasValue).Select(d => d.CustomerId!.Value).Distinct().ToList();
|
|
var customers = new Dictionary<int, string>();
|
|
if (customerIds.Any())
|
|
{
|
|
var customerList = await _dbContext.Customers.Table
|
|
.Where(c => customerIds.Contains(c.Id))
|
|
.Select(c => new { c.Id, c.FirstName, c.LastName, c.Email })
|
|
.ToListAsync();
|
|
customers = customerList.ToDictionary(
|
|
c => c.Id,
|
|
c => string.IsNullOrWhiteSpace($"{c.FirstName} {c.LastName}".Trim())
|
|
? c.Email ?? c.Id.ToString()
|
|
: $"{c.FirstName} {c.LastName}".Trim());
|
|
}
|
|
|
|
// 3. Item count — batch
|
|
var draftIds = allDrafts.Select(d => d.Id).ToList();
|
|
var itemCounts = await _dbContext.OrderDraftItems.GetAll()
|
|
.Where(i => draftIds.Contains(i.DraftId))
|
|
.GroupBy(i => i.DraftId)
|
|
.Select(g => new { DraftId = g.Key, Count = g.Count() })
|
|
.ToListAsync();
|
|
var itemCountDict = itemCounts.ToDictionary(x => x.DraftId, x => x.Count);
|
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
var rows = allDrafts.Select(d => new OrderDraftListRow
|
|
{
|
|
Id = d.Id,
|
|
ExternalUsername = d.ExternalUsername,
|
|
CustomerId = d.CustomerId,
|
|
CustomerName = d.CustomerId.HasValue && customers.TryGetValue(d.CustomerId.Value, out var cn) ? cn : "— ismeretlen —",
|
|
RawMessageTextShort = d.RawMessageText.Length > 80 ? d.RawMessageText[..80] + "…" : d.RawMessageText,
|
|
ParsedDeliveryDate = d.ParsedDeliveryDate.ToString("yyyy-MM-dd"),
|
|
ItemCount = itemCountDict.TryGetValue(d.Id, out var cnt) ? cnt : 0,
|
|
Status = d.Status.ToString(),
|
|
StatusBadgeClass = d.Status switch
|
|
{
|
|
OrderDraftStatus.Pending => "badge-warning",
|
|
OrderDraftStatus.Approved => "badge-success",
|
|
OrderDraftStatus.Rejected => "badge-danger",
|
|
OrderDraftStatus.Expired => "badge-secondary",
|
|
_ => "badge-light"
|
|
},
|
|
IsUrgent = d.Status == OrderDraftStatus.Pending && (now - d.CreatedOnUtc).TotalHours > 20,
|
|
CreatedOnUtc = d.CreatedOnUtc.ToString("yyyy-MM-dd HH:mm")
|
|
}).ToList();
|
|
|
|
int recordsTotal = rows.Count;
|
|
|
|
if (!string.IsNullOrWhiteSpace(globalSearch))
|
|
rows = rows.Where(r =>
|
|
r.ExternalUsername.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
|
|
r.CustomerName.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
|
|
r.RawMessageTextShort.Contains(globalSearch, StringComparison.OrdinalIgnoreCase)
|
|
).ToList();
|
|
|
|
int recordsFiltered = rows.Count;
|
|
var page = rows.Skip(start).Take(length).ToList();
|
|
|
|
return Json(new { draw, recordsTotal, recordsFiltered, data = page });
|
|
}
|
|
|
|
// ── RÉSZLETEK / JÓVÁHAGYÓ OLDAL ──────────────────────────────────────────
|
|
|
|
[HttpGet]
|
|
public async Task<IActionResult> Detail(int id)
|
|
{
|
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
return AccessDeniedView();
|
|
|
|
var draft = await _draftService.GetByIdAsync(id, true);
|
|
if (draft == null) return NotFound();
|
|
|
|
// Customer név
|
|
string customerName = "— ismeretlen partner —";
|
|
if (draft.CustomerId.HasValue)
|
|
{
|
|
var customer = await _customerService.GetCustomerByIdAsync(draft.CustomerId.Value);
|
|
if (customer != null)
|
|
customerName = string.IsNullOrWhiteSpace($"{customer.FirstName} {customer.LastName}".Trim())
|
|
? customer.Email ?? customer.Id.ToString()
|
|
: $"{customer.FirstName} {customer.LastName}".Trim();
|
|
}
|
|
|
|
// Termék nevek és kandidátusok batch lekérése
|
|
var allCandidateIds = draft.Items
|
|
.SelectMany(i =>
|
|
{
|
|
try { return JsonSerializer.Deserialize<List<int>>(i.SuggestedProductIdsJson) ?? []; }
|
|
catch { return []; }
|
|
})
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
var productMap = new Dictionary<int, (string Name, int Stock)>();
|
|
if (allCandidateIds.Any())
|
|
{
|
|
var products = await _dbContext.ProductDtos
|
|
.GetAllByIds(allCandidateIds, false)
|
|
.ToListAsync();
|
|
productMap = products.ToDictionary(
|
|
p => p.Id,
|
|
p => (p.Name, p.StockQuantity));
|
|
}
|
|
|
|
var model = new OrderDraftDetailModel
|
|
{
|
|
Id = draft.Id,
|
|
ExternalUsername = draft.ExternalUsername,
|
|
CustomerId = draft.CustomerId,
|
|
CustomerName = customerName,
|
|
RawMessageText = draft.RawMessageText,
|
|
ParsedDeliveryDate = draft.ParsedDeliveryDate,
|
|
IsPreorder = draft.ParsedDeliveryDate.Date > DateTime.Today.AddDays(4),
|
|
Status = draft.Status.ToString(),
|
|
AdminNote = draft.AdminNote ?? string.Empty,
|
|
CreatedOnUtc = draft.CreatedOnUtc.ToString("yyyy-MM-dd HH:mm"),
|
|
CanApprove = draft.Status == OrderDraftStatus.Pending,
|
|
Items = draft.Items.OrderBy(i => i.SortOrder).Select(i =>
|
|
{
|
|
var candidateIds = new List<int>();
|
|
try { candidateIds = JsonSerializer.Deserialize<List<int>>(i.SuggestedProductIdsJson) ?? []; }
|
|
catch { /* */ }
|
|
|
|
var selectedName = i.SelectedProductId.HasValue && productMap.TryGetValue(i.SelectedProductId.Value, out var sel)
|
|
? sel.Name : string.Empty;
|
|
|
|
return new OrderDraftItemModel
|
|
{
|
|
Id = i.Id,
|
|
RawProductText = i.RawProductText,
|
|
AiResolvedProductName = i.AiResolvedProductName,
|
|
RequestedQuantity = i.RequestedQuantity,
|
|
IsPreorder = i.IsPreorder,
|
|
SortOrder = i.SortOrder,
|
|
SelectedProductId = i.SelectedProductId,
|
|
SelectedProductName = selectedName,
|
|
Candidates = candidateIds
|
|
.Where(pid => productMap.ContainsKey(pid))
|
|
.Select(pid => new OrderDraftProductCandidate
|
|
{
|
|
Id = pid,
|
|
Name = productMap[pid].Name,
|
|
Stock = productMap[pid].Stock.ToString()
|
|
}).ToList()
|
|
};
|
|
}).ToList()
|
|
};
|
|
|
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/OrderDraft/Detail.cshtml", model);
|
|
}
|
|
|
|
// ── AJAX: TÉTEL FRISSÍTÉSE ────────────────────────────────────────────────
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> UpdateItem(int draftItemId, int selectedProductId, int quantity)
|
|
{
|
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
return Json(new { success = false });
|
|
|
|
await _draftService.UpdateDraftItemAsync(draftItemId, selectedProductId, quantity);
|
|
return Json(new { success = true });
|
|
}
|
|
|
|
// ── AJAX: PARTNER HOZZÁRENDELÉSE ──────────────────────────────────────────
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> AssignCustomer(int draftId, int customerId)
|
|
{
|
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
return Json(new { success = false });
|
|
|
|
var workContext = HttpContext.RequestServices.GetService(typeof(Nop.Core.IWorkContext)) as Nop.Core.IWorkContext;
|
|
var admin = workContext != null ? await workContext.GetCurrentCustomerAsync() : null;
|
|
|
|
await _draftService.AssignCustomerAsync(draftId, customerId, admin?.Id ?? 0);
|
|
|
|
var customer = await _customerService.GetCustomerByIdAsync(customerId);
|
|
var name = customer == null ? customerId.ToString()
|
|
: string.IsNullOrWhiteSpace($"{customer.FirstName} {customer.LastName}".Trim())
|
|
? customer.Email ?? customerId.ToString()
|
|
: $"{customer.FirstName} {customer.LastName}".Trim();
|
|
|
|
return Json(new { success = true, customerName = name });
|
|
}
|
|
|
|
// ── AJAX: JÓVÁHAGYÁS ─────────────────────────────────────────────────────
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> Approve(int draftId)
|
|
{
|
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
return Json(new { success = false, error = "Hozzáférés megtagadva." });
|
|
|
|
var workContext = HttpContext.RequestServices.GetService(typeof(Nop.Core.IWorkContext)) as Nop.Core.IWorkContext;
|
|
var admin = workContext != null ? await workContext.GetCurrentCustomerAsync() : null;
|
|
|
|
var result = await _draftService.ApproveAsync(draftId, admin?.Id ?? 0);
|
|
|
|
return Json(new
|
|
{
|
|
success = result.Success,
|
|
error = result.ErrorMessage,
|
|
preOrderIds = result.PreOrderIds,
|
|
orderIds = result.OrderIds
|
|
});
|
|
}
|
|
|
|
// ── AJAX: ELUTASÍTÁS ──────────────────────────────────────────────────────
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> Reject(int draftId, string? adminNote)
|
|
{
|
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
|
return Json(new { success = false });
|
|
|
|
var workContext = HttpContext.RequestServices.GetService(typeof(Nop.Core.IWorkContext)) as Nop.Core.IWorkContext;
|
|
var admin = workContext != null ? await workContext.GetCurrentCustomerAsync() : null;
|
|
|
|
await _draftService.RejectAsync(draftId, admin?.Id ?? 0, adminNote);
|
|
return Json(new { success = true });
|
|
}
|
|
}
|