Rename Preorder domain to PreOrder across codebase
Comprehensively renamed all "Preorder" domain types, services, controllers, models, enums, database contexts, routes, views, JS, localization resources, and documentation to "PreOrder" (PascalCase, capital "O"). Updated all references in backend, frontend, localization, and schema docs for consistency. No business logic changes; this is a naming and normalization refactor.
This commit is contained in:
parent
2a8dfeaca9
commit
1468aa651c
|
|
@ -176,7 +176,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-muted small">
|
<div class="card-footer text-muted small">
|
||||||
<a href="/Admin/Preorders">Összes előrendelés →</a>
|
<a href="/Admin/PreOrders">Összes előrendelés →</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -277,7 +277,7 @@
|
||||||
if (alert.type === 'credit_exceeded')
|
if (alert.type === 'credit_exceeded')
|
||||||
return '<a href="/Admin/CustomerCredit/Details/' + alert.customerId + '" class="btn btn-xs btn-outline-warning">Hitelkeret</a>';
|
return '<a href="/Admin/CustomerCredit/Details/' + alert.customerId + '" class="btn btn-xs btn-outline-warning">Hitelkeret</a>';
|
||||||
if (alert.type === 'old_preorder')
|
if (alert.type === 'old_preorder')
|
||||||
return '<a href="/Admin/Preorders" class="btn btn-xs btn-outline-info">Előrendelések</a>';
|
return '<a href="/Admin/PreOrders" class="btn btn-xs btn-outline-info">Előrendelések</a>';
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -374,10 +374,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Pending preorders ────────────────────────────────────────────
|
// ── Pending preorders ────────────────────────────────────────────
|
||||||
if (data.pendingPreorders && data.pendingPreorders.length > 0) {
|
if (data.pendingPreOrders && data.pendingPreOrders.length > 0) {
|
||||||
$('#fb-preorders-count').text(data.pendingPreorders.length);
|
$('#fb-preorders-count').text(data.pendingPreOrders.length);
|
||||||
var $pb = $('#fb-preorders-body').empty();
|
var $pb = $('#fb-preorders-body').empty();
|
||||||
data.pendingPreorders.forEach(function (p) {
|
data.pendingPreOrders.forEach(function (p) {
|
||||||
$pb.append(
|
$pb.append(
|
||||||
'<tr>' +
|
'<tr>' +
|
||||||
'<td>' + p.company + '</td>' +
|
'<td>' + p.company + '</td>' +
|
||||||
|
|
@ -385,7 +385,7 @@
|
||||||
'<td class="text-center">' + p.itemCount + '</td>' +
|
'<td class="text-center">' + p.itemCount + '</td>' +
|
||||||
'<td class="text-center">' + p.fulfilledCount + ' / ' + p.itemCount + '</td>' +
|
'<td class="text-center">' + p.fulfilledCount + ' / ' + p.itemCount + '</td>' +
|
||||||
'<td class="text-center">' + preorderStatusBadge(p.status) + '</td>' +
|
'<td class="text-center">' + preorderStatusBadge(p.status) + '</td>' +
|
||||||
'<td><a href="/Admin/Preorders/Details/' + p.id + '" class="btn btn-xs btn-outline-secondary">Részletek</a></td>' +
|
'<td><a href="/Admin/PreOrders/Details/' + p.id + '" class="btn btn-xs btn-outline-secondary">Részletek</a></td>' +
|
||||||
'</tr>'
|
'</tr>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
protected readonly IWorkContext _workContext;
|
protected readonly IWorkContext _workContext;
|
||||||
protected readonly AICalculationService _aiCalculationService;
|
protected readonly AICalculationService _aiCalculationService;
|
||||||
protected readonly FruitBankDbContext _fruitBankDbContext;
|
protected readonly FruitBankDbContext _fruitBankDbContext;
|
||||||
protected readonly PreorderDbContext _preorderDbContext;
|
protected readonly PreOrderDbContext _preorderDbContext;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
IWorkContext workContext,
|
IWorkContext workContext,
|
||||||
AICalculationService aiCalculationService,
|
AICalculationService aiCalculationService,
|
||||||
FruitBankDbContext fruitBankDbContext,
|
FruitBankDbContext fruitBankDbContext,
|
||||||
PreorderDbContext preorderDbContext)
|
PreOrderDbContext preorderDbContext)
|
||||||
{
|
{
|
||||||
_adminAreaSettings = adminAreaSettings;
|
_adminAreaSettings = adminAreaSettings;
|
||||||
_commonModelFactory = commonModelFactory;
|
_commonModelFactory = commonModelFactory;
|
||||||
|
|
@ -186,32 +186,32 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
var today = DateTime.Now.Date;
|
var today = DateTime.Now.Date;
|
||||||
|
|
||||||
// ── Batch 1: parallel queries ─────────────────────────────────────
|
// ── Batch 1: parallel queries ─────────────────────────────────────
|
||||||
// NOTE: PreorderItems is not a LinqToDB [Association] — never use LoadWith on it.
|
// NOTE: PreOrderItems is not a LinqToDB [Association] — never use LoadWith on it.
|
||||||
// Preorders and items are loaded separately and joined in memory below.
|
// PreOrders and items are loaded separately and joined in memory below.
|
||||||
var allOrdersTask = _fruitBankDbContext.OrderDtos.GetAll(true).ToListAsync();
|
var allOrdersTask = _fruitBankDbContext.OrderDtos.GetAll(true).ToListAsync();
|
||||||
var allCreditsTask = _fruitBankDbContext.CustomerCredits.GetAll().ToListAsync();
|
var allCreditsTask = _fruitBankDbContext.CustomerCredits.GetAll().ToListAsync();
|
||||||
var allPreordersTask = _preorderDbContext.Preorders.GetAll().ToListAsync();
|
var allPreOrdersTask = _preorderDbContext.PreOrders.GetAll().ToListAsync();
|
||||||
var unprocessedDocsTask = _fruitBankDbContext.ShippingDocuments.GetAllNotMeasured(true).ToListAsync();
|
var unprocessedDocsTask = _fruitBankDbContext.ShippingDocuments.GetAllNotMeasured(true).ToListAsync();
|
||||||
|
|
||||||
await Task.WhenAll(allOrdersTask, allCreditsTask, allPreordersTask, unprocessedDocsTask);
|
await Task.WhenAll(allOrdersTask, allCreditsTask, allPreOrdersTask, unprocessedDocsTask);
|
||||||
|
|
||||||
var allOrders = await allOrdersTask;
|
var allOrders = await allOrdersTask;
|
||||||
var credits = await allCreditsTask;
|
var credits = await allCreditsTask;
|
||||||
var unprocessedDocs = await unprocessedDocsTask;
|
var unprocessedDocs = await unprocessedDocsTask;
|
||||||
|
|
||||||
// Filter pending preorders in memory — LinqToDB cannot translate enum comparisons to SQL
|
// Filter pending preorders in memory — LinqToDB cannot translate enum comparisons to SQL
|
||||||
var pendingStatuses = new[] { PreorderStatus.Pending, PreorderStatus.PartiallyFulfilled };
|
var pendingStatuses = new[] { PreOrderStatus.Pending, PreOrderStatus.PartiallyFulfilled };
|
||||||
var pendingPreorders = (await allPreordersTask)
|
var pendingPreOrders = (await allPreOrdersTask)
|
||||||
.Where(p => pendingStatuses.Contains(p.Status))
|
.Where(p => pendingStatuses.Contains(p.Status))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// ── Batch 1b: preorder items for pending preorders only ────────────
|
// ── Batch 1b: preorder items for pending preorders only ────────────
|
||||||
var pendingPreorderIds = pendingPreorders.Select(p => p.Id).ToList();
|
var pendingPreOrderIds = pendingPreOrders.Select(p => p.Id).ToList();
|
||||||
var pendingItems = pendingPreorderIds.Any()
|
var pendingItems = pendingPreOrderIds.Any()
|
||||||
? await _preorderDbContext.PreorderItems.GetAll()
|
? await _preorderDbContext.PreOrderItems.GetAll()
|
||||||
.Where(i => pendingPreorderIds.Contains(i.PreorderId))
|
.Where(i => pendingPreOrderIds.Contains(i.PreOrderId))
|
||||||
.ToListAsync()
|
.ToListAsync()
|
||||||
: new List<PreorderItem>();
|
: new List<PreOrderItem>();
|
||||||
|
|
||||||
// ── Today's orders (in-memory filter, same pattern as AICalculationService)
|
// ── Today's orders (in-memory filter, same pattern as AICalculationService)
|
||||||
var todaysOrders = allOrders
|
var todaysOrders = allOrders
|
||||||
|
|
@ -249,8 +249,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
return $"#{customerId}";
|
return $"#{customerId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Preorder customer names (may not appear in allOrders) ──────────
|
// ── PreOrder customer names (may not appear in allOrders) ──────────
|
||||||
var preorderCustomerIds = pendingPreorders
|
var preorderCustomerIds = pendingPreOrders
|
||||||
.Select(p => p.CustomerId).Distinct()
|
.Select(p => p.CustomerId).Distinct()
|
||||||
.Where(id => allOrders.All(o => o.CustomerId != id))
|
.Where(id => allOrders.All(o => o.CustomerId != id))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
@ -267,7 +267,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
preorderCustomerLookup[c.Id] = !string.IsNullOrEmpty(c.Company) ? c.Company : c.Email;
|
preorderCustomerLookup[c.Id] = !string.IsNullOrEmpty(c.Company) ? c.Company : c.Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
string PreorderCustomerName(int customerId)
|
string PreOrderCustomerName(int customerId)
|
||||||
=> preorderCustomerLookup.TryGetValue(customerId, out var name)
|
=> preorderCustomerLookup.TryGetValue(customerId, out var name)
|
||||||
? name
|
? name
|
||||||
: CustomerName(customerId);
|
: CustomerName(customerId);
|
||||||
|
|
@ -361,15 +361,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
|
|
||||||
// Pending preorders older than 7 days
|
// Pending preorders older than 7 days
|
||||||
var sevenDaysAgo = DateTime.UtcNow.AddDays(-7);
|
var sevenDaysAgo = DateTime.UtcNow.AddDays(-7);
|
||||||
foreach (var p in pendingPreorders.Where(p =>
|
foreach (var p in pendingPreOrders.Where(p =>
|
||||||
p.Status == PreorderStatus.Pending &&
|
p.Status == PreOrderStatus.Pending &&
|
||||||
p.CreatedOnUtc < sevenDaysAgo))
|
p.CreatedOnUtc < sevenDaysAgo))
|
||||||
{
|
{
|
||||||
alerts.Add(new
|
alerts.Add(new
|
||||||
{
|
{
|
||||||
type = "old_preorder",
|
type = "old_preorder",
|
||||||
preorderId = p.Id,
|
preorderId = p.Id,
|
||||||
company = PreorderCustomerName(p.CustomerId),
|
company = PreOrderCustomerName(p.CustomerId),
|
||||||
createdAt = p.CreatedOnUtc.ToLocalTime().ToString("yyyy.MM.dd"),
|
createdAt = p.CreatedOnUtc.ToLocalTime().ToString("yyyy.MM.dd"),
|
||||||
message = "Régi, nyitott előrendelés"
|
message = "Régi, nyitott előrendelés"
|
||||||
});
|
});
|
||||||
|
|
@ -404,20 +404,20 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
// Section 4: Pending preorders (items joined in memory)
|
// Section 4: Pending preorders (items joined in memory)
|
||||||
// ─────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────
|
||||||
var preorderRows = pendingPreorders
|
var preorderRows = pendingPreOrders
|
||||||
.OrderBy(p => p.CreatedOnUtc)
|
.OrderBy(p => p.CreatedOnUtc)
|
||||||
.Select(p =>
|
.Select(p =>
|
||||||
{
|
{
|
||||||
var items = pendingItems.Where(i => i.PreorderId == p.Id).ToList();
|
var items = pendingItems.Where(i => i.PreOrderId == p.Id).ToList();
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
id = p.Id,
|
id = p.Id,
|
||||||
customerId = p.CustomerId,
|
customerId = p.CustomerId,
|
||||||
company = PreorderCustomerName(p.CustomerId),
|
company = PreOrderCustomerName(p.CustomerId),
|
||||||
status = p.Status.ToString(),
|
status = p.Status.ToString(),
|
||||||
createdAt = p.CreatedOnUtc.ToLocalTime().ToString("yyyy.MM.dd"),
|
createdAt = p.CreatedOnUtc.ToLocalTime().ToString("yyyy.MM.dd"),
|
||||||
itemCount = items.Count,
|
itemCount = items.Count,
|
||||||
fulfilledCount = items.Count(i => i.Status == PreorderItemStatus.Fulfilled)
|
fulfilledCount = items.Count(i => i.Status == PreOrderItemStatus.Fulfilled)
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
@ -443,7 +443,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
pipeline,
|
pipeline,
|
||||||
alerts,
|
alerts,
|
||||||
creditStatus = creditRows,
|
creditStatus = creditRows,
|
||||||
pendingPreorders = preorderRows,
|
pendingPreOrders = preorderRows,
|
||||||
unprocessedDocs = docRows
|
unprocessedDocs = docRows
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1076,7 +1076,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
|
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
|
||||||
public virtual async Task<IActionResult> PreorderProductSearchAutoComplete(string term)
|
public virtual async Task<IActionResult> PreOrderProductSearchAutoComplete(string term)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
|
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
|
||||||
return Json(new List<object>());
|
return Json(new List<object>());
|
||||||
|
|
@ -1088,13 +1088,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
// Load preorder window attributes in two batch queries
|
// Load preorder window attributes in two batch queries
|
||||||
var gaStart = await _dbContext.GenericAttributes.Table
|
var gaStart = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowStart
|
&& ga.Key == FruitBankConst.PreOrderWindowStart
|
||||||
&& ga.StoreId == store.Id)
|
&& ga.StoreId == store.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var gaEnd = await _dbContext.GenericAttributes.Table
|
var gaEnd = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowEnd
|
&& ga.Key == FruitBankConst.PreOrderWindowEnd
|
||||||
&& ga.StoreId == store.Id)
|
&& ga.StoreId == store.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers
|
||||||
private readonly FileStorageService _fileStorageService;
|
private readonly FileStorageService _fileStorageService;
|
||||||
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||||
private readonly IStoreContext _storeContext;
|
private readonly IStoreContext _storeContext;
|
||||||
private readonly PreorderConversionService _preorderConversionService;
|
private readonly PreOrderConversionService _preorderConversionService;
|
||||||
|
|
||||||
public FileManagerController(
|
public FileManagerController(
|
||||||
IPermissionService permissionService,
|
IPermissionService permissionService,
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers
|
||||||
FileStorageService fileStorageService,
|
FileStorageService fileStorageService,
|
||||||
FruitBankAttributeService fruitBankAttributeService,
|
FruitBankAttributeService fruitBankAttributeService,
|
||||||
IStoreContext storeContext,
|
IStoreContext storeContext,
|
||||||
PreorderConversionService preorderConversionService)
|
PreOrderConversionService preorderConversionService)
|
||||||
{
|
{
|
||||||
_permissionService = permissionService;
|
_permissionService = permissionService;
|
||||||
_aiApiService = aiApiService;
|
_aiApiService = aiApiService;
|
||||||
|
|
@ -1140,12 +1140,12 @@ namespace Nop.Plugin.Misc.FruitBank.Controllers
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _preorderConversionService
|
await _preorderConversionService
|
||||||
.ConvertPreordersForProductsAsync(productIdsWithIncoming, shippingDocument.Id);
|
.ConvertPreOrdersForProductsAsync(productIdsWithIncoming, shippingDocument.Id);
|
||||||
}
|
}
|
||||||
catch (Exception convEx)
|
catch (Exception convEx)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[PreorderConversion] Error during conversion for document #{shippingDocument.Id}: {convEx.Message}");
|
$"[PreOrderConversion] Error during conversion for document #{shippingDocument.Id}: {convEx.Message}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,36 +16,36 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;
|
||||||
[AuthorizeAdmin]
|
[AuthorizeAdmin]
|
||||||
[Area(AreaNames.ADMIN)]
|
[Area(AreaNames.ADMIN)]
|
||||||
[AutoValidateAntiforgeryToken]
|
[AutoValidateAntiforgeryToken]
|
||||||
public class PreorderAdminController : BasePluginController
|
public class PreOrderAdminController : BasePluginController
|
||||||
{
|
{
|
||||||
private readonly IPermissionService _permissionService;
|
private readonly IPermissionService _permissionService;
|
||||||
private readonly PreorderDbContext _preorderDbContext;
|
private readonly PreOrderDbContext _preorderDbContext;
|
||||||
private readonly FruitBankDbContext _dbContext;
|
private readonly FruitBankDbContext _dbContext;
|
||||||
private readonly ICustomerService _customerService;
|
private readonly ICustomerService _customerService;
|
||||||
private readonly PreorderConversionService _preorderConversionService;
|
private readonly PreOrderConversionService _preorderConversionService;
|
||||||
|
|
||||||
private static readonly Dictionary<PreorderStatus, string> StatusLabels = new()
|
private static readonly Dictionary<PreOrderStatus, string> StatusLabels = new()
|
||||||
{
|
{
|
||||||
{ PreorderStatus.Pending, "Függőben" },
|
{ PreOrderStatus.Pending, "Függőben" },
|
||||||
{ PreorderStatus.Confirmed, "Megerősítve" },
|
{ PreOrderStatus.Confirmed, "Megerősítve" },
|
||||||
{ PreorderStatus.PartiallyFulfilled, "Részben teljesítve" },
|
{ PreOrderStatus.PartiallyFulfilled, "Részben teljesítve" },
|
||||||
{ PreorderStatus.Cancelled, "Törölve" }
|
{ PreOrderStatus.Cancelled, "Törölve" }
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<PreorderItemStatus, string> ItemStatusLabels = new()
|
private static readonly Dictionary<PreOrderItemStatus, string> ItemStatusLabels = new()
|
||||||
{
|
{
|
||||||
{ PreorderItemStatus.Pending, "Függőben" },
|
{ PreOrderItemStatus.Pending, "Függőben" },
|
||||||
{ PreorderItemStatus.Fulfilled, "Teljesítve" },
|
{ PreOrderItemStatus.Fulfilled, "Teljesítve" },
|
||||||
{ PreorderItemStatus.PartiallyFulfilled, "Részben" },
|
{ PreOrderItemStatus.PartiallyFulfilled, "Részben" },
|
||||||
{ PreorderItemStatus.Dropped, "Ejtve" }
|
{ PreOrderItemStatus.Dropped, "Ejtve" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public PreorderAdminController(
|
public PreOrderAdminController(
|
||||||
IPermissionService permissionService,
|
IPermissionService permissionService,
|
||||||
PreorderDbContext preorderDbContext,
|
PreOrderDbContext preorderDbContext,
|
||||||
FruitBankDbContext dbContext,
|
FruitBankDbContext dbContext,
|
||||||
ICustomerService customerService,
|
ICustomerService customerService,
|
||||||
PreorderConversionService preorderConversionService)
|
PreOrderConversionService preorderConversionService)
|
||||||
{
|
{
|
||||||
_permissionService = permissionService;
|
_permissionService = permissionService;
|
||||||
_preorderDbContext = preorderDbContext;
|
_preorderDbContext = preorderDbContext;
|
||||||
|
|
@ -57,20 +57,20 @@ public class PreorderAdminController : BasePluginController
|
||||||
// ── LIST PAGE ─────────────────────────────────────────────────────────────
|
// ── LIST PAGE ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Admin/Preorders")]
|
[Route("Admin/PreOrders")]
|
||||||
public async Task<IActionResult> List()
|
public async Task<IActionResult> List()
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
return AccessDeniedView();
|
return AccessDeniedView();
|
||||||
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Preorder/List.cshtml");
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/PreOrder/List.cshtml");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DATATABLES SERVER-SIDE ────────────────────────────────────────────────
|
// ── DATATABLES SERVER-SIDE ────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/Preorders/PreorderList")]
|
[Route("Admin/PreOrders/PreOrderList")]
|
||||||
public async Task<IActionResult> PreorderList()
|
public async Task<IActionResult> PreOrderList()
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
return Forbid();
|
return Forbid();
|
||||||
|
|
@ -87,11 +87,11 @@ public class PreorderAdminController : BasePluginController
|
||||||
var statusFilter = Request.Form["statusFilter"].FirstOrDefault()?.Trim() ?? "";
|
var statusFilter = Request.Form["statusFilter"].FirstOrDefault()?.Trim() ?? "";
|
||||||
|
|
||||||
// 1. All preorders with items — two queries
|
// 1. All preorders with items — two queries
|
||||||
var preorders = await _preorderDbContext.Preorders.GetAll(false).ToListAsync();
|
var preorders = await _preorderDbContext.PreOrders.GetAll(false).ToListAsync();
|
||||||
var allItems = await _preorderDbContext.PreorderItems.GetAll().ToListAsync();
|
var allItems = await _preorderDbContext.PreOrderItems.GetAll().ToListAsync();
|
||||||
|
|
||||||
var itemsByPreorder = allItems
|
var itemsByPreOrder = allItems
|
||||||
.GroupBy(i => i.PreorderId)
|
.GroupBy(i => i.PreOrderId)
|
||||||
.ToDictionary(g => g.Key, g => g.ToList());
|
.ToDictionary(g => g.Key, g => g.ToList());
|
||||||
|
|
||||||
// 2. Customers — batch
|
// 2. Customers — batch
|
||||||
|
|
@ -111,7 +111,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
var rows = preorders.Select(p =>
|
var rows = preorders.Select(p =>
|
||||||
{
|
{
|
||||||
customerById.TryGetValue(p.CustomerId, out var c);
|
customerById.TryGetValue(p.CustomerId, out var c);
|
||||||
var items = itemsByPreorder.TryGetValue(p.Id, out var its) ? its : new();
|
var items = itemsByPreOrder.TryGetValue(p.Id, out var its) ? its : new();
|
||||||
|
|
||||||
// Derive status from quantities rather than relying on the enum read
|
// Derive status from quantities rather than relying on the enum read
|
||||||
var fulfilledCount = items.Count(i => i.FulfilledQuantity > 0);
|
var fulfilledCount = items.Count(i => i.FulfilledQuantity > 0);
|
||||||
|
|
@ -123,13 +123,13 @@ public class PreorderAdminController : BasePluginController
|
||||||
// otherwise infer from quantities
|
// otherwise infer from quantities
|
||||||
var effectiveStatus = (int)p.Status != 0
|
var effectiveStatus = (int)p.Status != 0
|
||||||
? p.Status
|
? p.Status
|
||||||
: allFulfilled ? PreorderStatus.Confirmed
|
: allFulfilled ? PreOrderStatus.Confirmed
|
||||||
: anyFulfilled ? PreorderStatus.PartiallyFulfilled
|
: anyFulfilled ? PreOrderStatus.PartiallyFulfilled
|
||||||
: PreorderStatus.Pending;
|
: PreOrderStatus.Pending;
|
||||||
|
|
||||||
return new PreorderListRow
|
return new PreOrderListRow
|
||||||
{
|
{
|
||||||
PreorderId = p.Id,
|
PreOrderId = p.Id,
|
||||||
CustomerId = p.CustomerId,
|
CustomerId = p.CustomerId,
|
||||||
CustomerName = c != null ? $"{c.FirstName} {c.LastName}".Trim() : $"#{p.CustomerId}",
|
CustomerName = c != null ? $"{c.FirstName} {c.LastName}".Trim() : $"#{p.CustomerId}",
|
||||||
CustomerEmail = c?.Email ?? string.Empty,
|
CustomerEmail = c?.Email ?? string.Empty,
|
||||||
|
|
@ -146,7 +146,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
int recordsTotal = rows.Count;
|
int recordsTotal = rows.Count;
|
||||||
|
|
||||||
// 5. Filter by status
|
// 5. Filter by status
|
||||||
if (!string.IsNullOrWhiteSpace(statusFilter) && Enum.TryParse<PreorderStatus>(statusFilter, out var statusEnum))
|
if (!string.IsNullOrWhiteSpace(statusFilter) && Enum.TryParse<PreOrderStatus>(statusFilter, out var statusEnum))
|
||||||
rows = rows.Where(r => r.Status == statusEnum).ToList();
|
rows = rows.Where(r => r.Status == statusEnum).ToList();
|
||||||
|
|
||||||
// 6. Global search
|
// 6. Global search
|
||||||
|
|
@ -154,7 +154,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
rows = rows.Where(r =>
|
rows = rows.Where(r =>
|
||||||
r.CustomerName.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
|
r.CustomerName.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
|
||||||
r.CustomerEmail.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
|
r.CustomerEmail.Contains(globalSearch, StringComparison.OrdinalIgnoreCase) ||
|
||||||
r.PreorderId.ToString().Contains(globalSearch)
|
r.PreOrderId.ToString().Contains(globalSearch)
|
||||||
).ToList();
|
).ToList();
|
||||||
|
|
||||||
int recordsFiltered = rows.Count;
|
int recordsFiltered = rows.Count;
|
||||||
|
|
@ -178,17 +178,17 @@ public class PreorderAdminController : BasePluginController
|
||||||
// ── DETAIL PAGE ───────────────────────────────────────────────────────────
|
// ── DETAIL PAGE ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Admin/Preorders/Detail/{id:int}")]
|
[Route("Admin/PreOrders/Detail/{id:int}")]
|
||||||
public async Task<IActionResult> Detail(int id)
|
public async Task<IActionResult> Detail(int id)
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
return AccessDeniedView();
|
return AccessDeniedView();
|
||||||
|
|
||||||
var preorder = await _preorderDbContext.Preorders.GetByIdAsync(id, loadRelations: false);
|
var preorder = await _preorderDbContext.PreOrders.GetByIdAsync(id, loadRelations: false);
|
||||||
if (preorder == null) return NotFound();
|
if (preorder == null) return NotFound();
|
||||||
|
|
||||||
var items = await _preorderDbContext.PreorderItems
|
var items = await _preorderDbContext.PreOrderItems
|
||||||
.GetAllByPreorderIdAsync(id)
|
.GetAllByPreOrderIdAsync(id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var customer = await _customerService.GetCustomerByIdAsync(preorder.CustomerId);
|
var customer = await _customerService.GetCustomerByIdAsync(preorder.CustomerId);
|
||||||
|
|
@ -204,9 +204,9 @@ public class PreorderAdminController : BasePluginController
|
||||||
// Use preorder.OrderId directly — stored on the entity at conversion time
|
// Use preorder.OrderId directly — stored on the entity at conversion time
|
||||||
int? linkedOrderId = preorder.OrderId;
|
int? linkedOrderId = preorder.OrderId;
|
||||||
|
|
||||||
var model = new PreorderDetailModel
|
var model = new PreOrderDetailModel
|
||||||
{
|
{
|
||||||
PreorderId = preorder.Id,
|
PreOrderId = preorder.Id,
|
||||||
CustomerId = preorder.CustomerId,
|
CustomerId = preorder.CustomerId,
|
||||||
CustomerName = customer != null ? $"{customer.FirstName} {customer.LastName}".Trim() : $"#{preorder.CustomerId}",
|
CustomerName = customer != null ? $"{customer.FirstName} {customer.LastName}".Trim() : $"#{preorder.CustomerId}",
|
||||||
CustomerEmail = customer?.Email ?? string.Empty,
|
CustomerEmail = customer?.Email ?? string.Empty,
|
||||||
|
|
@ -222,15 +222,15 @@ public class PreorderAdminController : BasePluginController
|
||||||
|
|
||||||
// Derive item status from quantities — enum reads unreliable in LinqToDB
|
// Derive item status from quantities — enum reads unreliable in LinqToDB
|
||||||
var derivedStatus = i.FulfilledQuantity == 0
|
var derivedStatus = i.FulfilledQuantity == 0
|
||||||
? PreorderItemStatus.Pending
|
? PreOrderItemStatus.Pending
|
||||||
: i.FulfilledQuantity >= i.RequestedQuantity
|
: i.FulfilledQuantity >= i.RequestedQuantity
|
||||||
? PreorderItemStatus.Fulfilled
|
? PreOrderItemStatus.Fulfilled
|
||||||
: PreorderItemStatus.PartiallyFulfilled;
|
: PreOrderItemStatus.PartiallyFulfilled;
|
||||||
|
|
||||||
// If DB enum read as non-zero, prefer it; otherwise use derived
|
// If DB enum read as non-zero, prefer it; otherwise use derived
|
||||||
var effectiveItemStatus = (int)i.Status != 0 ? i.Status : derivedStatus;
|
var effectiveItemStatus = (int)i.Status != 0 ? i.Status : derivedStatus;
|
||||||
|
|
||||||
return new PreorderDetailItemRow
|
return new PreOrderDetailItemRow
|
||||||
{
|
{
|
||||||
ItemId = i.Id,
|
ItemId = i.Id,
|
||||||
ProductId = i.ProductId,
|
ProductId = i.ProductId,
|
||||||
|
|
@ -245,14 +245,14 @@ public class PreorderAdminController : BasePluginController
|
||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Preorder/Detail.cshtml", model);
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/PreOrder/Detail.cshtml", model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── CREATE (admin phone order) ───────────────────────────────────────────
|
// ── CREATE (admin phone order) ───────────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/Preorders/CreatePreorder")]
|
[Route("Admin/PreOrders/CreatePreOrder")]
|
||||||
public async Task<IActionResult> CreatePreorder(
|
public async Task<IActionResult> CreatePreOrder(
|
||||||
int customerId,
|
int customerId,
|
||||||
string deliveryDateTime,
|
string deliveryDateTime,
|
||||||
string? customerNote,
|
string? customerNote,
|
||||||
|
|
@ -288,7 +288,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
.Select(g => g.StoreId).FirstOrDefaultAsync();
|
.Select(g => g.StoreId).FirstOrDefaultAsync();
|
||||||
storeId = gaStore > 0 ? gaStore : 1;
|
storeId = gaStore > 0 ? gaStore : 1;
|
||||||
|
|
||||||
var preorder = new Preorder
|
var preorder = new PreOrder
|
||||||
{
|
{
|
||||||
CustomerId = customerId,
|
CustomerId = customerId,
|
||||||
StoreId = storeId,
|
StoreId = storeId,
|
||||||
|
|
@ -296,13 +296,13 @@ public class PreorderAdminController : BasePluginController
|
||||||
CustomerNote = customerNote?.Trim()
|
CustomerNote = customerNote?.Trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
var items = new List<PreorderItem>();
|
var items = new List<PreOrderItem>();
|
||||||
foreach (var pi in productItems.Where(p => p.quantity > 0))
|
foreach (var pi in productItems.Where(p => p.quantity > 0))
|
||||||
{
|
{
|
||||||
var product = await _dbContext.Products.GetByIdAsync(pi.id);
|
var product = await _dbContext.Products.GetByIdAsync(pi.id);
|
||||||
if (product == null || product.Deleted || !product.Published) continue;
|
if (product == null || product.Deleted || !product.Published) continue;
|
||||||
|
|
||||||
items.Add(new PreorderItem
|
items.Add(new PreOrderItem
|
||||||
{
|
{
|
||||||
ProductId = pi.id,
|
ProductId = pi.id,
|
||||||
RequestedQuantity = pi.quantity,
|
RequestedQuantity = pi.quantity,
|
||||||
|
|
@ -313,18 +313,18 @@ public class PreorderAdminController : BasePluginController
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return Json(new { success = false, error = "Nincs érvényes termék az előrendelésben" });
|
return Json(new { success = false, error = "Nincs érvényes termék az előrendelésben" });
|
||||||
|
|
||||||
var saved = await _preorderDbContext.InsertPreorderAsync(preorder, items);
|
var saved = await _preorderDbContext.InsertPreOrderAsync(preorder, items);
|
||||||
|
|
||||||
Console.WriteLine($"[Admin] Created preorder #{saved.Id} for customer #{customerId} " +
|
Console.WriteLine($"[Admin] Created preorder #{saved.Id} for customer #{customerId} " +
|
||||||
$"by admin, {items.Count} items, delivery {deliveryDate:u}");
|
$"by admin, {items.Count} items, delivery {deliveryDate:u}");
|
||||||
|
|
||||||
// Immediately check if any items can be fulfilled from current stock —
|
// Immediately check if any items can be fulfilled from current stock —
|
||||||
// same inline conversion as the customer-facing PlacePreorder endpoint.
|
// same inline conversion as the customer-facing PlacePreOrder endpoint.
|
||||||
var productIds = items.Select(i => i.ProductId).Distinct().ToList();
|
var productIds = items.Select(i => i.ProductId).Distinct().ToList();
|
||||||
await _preorderConversionService.ConvertPreordersForProductsAsync(productIds, 0);
|
await _preorderConversionService.ConvertPreOrdersForProductsAsync(productIds, 0);
|
||||||
|
|
||||||
// Re-read to pick up OrderId if conversion created a real order
|
// Re-read to pick up OrderId if conversion created a real order
|
||||||
var refreshed = await _preorderDbContext.Preorders.GetByIdAsync(saved.Id);
|
var refreshed = await _preorderDbContext.PreOrders.GetByIdAsync(saved.Id);
|
||||||
|
|
||||||
return Json(new { success = true, preorderId = saved.Id, orderId = refreshed?.OrderId });
|
return Json(new { success = true, preorderId = saved.Id, orderId = refreshed?.OrderId });
|
||||||
}
|
}
|
||||||
|
|
@ -347,27 +347,27 @@ public class PreorderAdminController : BasePluginController
|
||||||
// ── CANCEL ───────────────────────────────────────────────────────────
|
// ── CANCEL ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/Preorders/Cancel/{id:int}")]
|
[Route("Admin/PreOrders/Cancel/{id:int}")]
|
||||||
public async Task<IActionResult> Cancel(int id)
|
public async Task<IActionResult> Cancel(int id)
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
return Json(new { success = false, error = "Access denied" });
|
return Json(new { success = false, error = "Access denied" });
|
||||||
|
|
||||||
var preorder = await _preorderDbContext.Preorders.GetByIdAsync(id);
|
var preorder = await _preorderDbContext.PreOrders.GetByIdAsync(id);
|
||||||
if (preorder == null)
|
if (preorder == null)
|
||||||
return Json(new { success = false, error = "Preorder not found" });
|
return Json(new { success = false, error = "PreOrder not found" });
|
||||||
|
|
||||||
if (preorder.Status != PreorderStatus.Pending)
|
if (preorder.Status != PreOrderStatus.Pending)
|
||||||
return Json(new { success = false, error = "Only pending preorders can be cancelled" });
|
return Json(new { success = false, error = "Only pending preorders can be cancelled" });
|
||||||
|
|
||||||
await _preorderDbContext.CancelPreorderAsync(id);
|
await _preorderDbContext.CancelPreOrderAsync(id);
|
||||||
return Json(new { success = true });
|
return Json(new { success = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DEMAND LIST ───────────────────────────────────────────────────────────
|
// ── DEMAND LIST ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/Preorders/DemandList")]
|
[Route("Admin/PreOrders/DemandList")]
|
||||||
public async Task<IActionResult> DemandList(bool openOnly = true)
|
public async Task<IActionResult> DemandList(bool openOnly = true)
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
|
|
@ -380,24 +380,24 @@ public class PreorderAdminController : BasePluginController
|
||||||
openOnly = openOnlyParam != "false";
|
openOnly = openOnlyParam != "false";
|
||||||
|
|
||||||
// Fetch all preorder items + preorders in two queries
|
// Fetch all preorder items + preorders in two queries
|
||||||
var allItems = await _preorderDbContext.PreorderItems.GetAll().ToListAsync();
|
var allItems = await _preorderDbContext.PreOrderItems.GetAll().ToListAsync();
|
||||||
var allPreorders = await _preorderDbContext.Preorders.GetAll(false).ToListAsync();
|
var allPreOrders = await _preorderDbContext.PreOrders.GetAll(false).ToListAsync();
|
||||||
|
|
||||||
// For "open only": include only items from preorders that still have
|
// For "open only": include only items from preorders that still have
|
||||||
// unfulfilled demand (FulfilledQuantity < RequestedQuantity).
|
// unfulfilled demand (FulfilledQuantity < RequestedQuantity).
|
||||||
// We use quantities rather than Status enum (enum reads unreliable).
|
// We use quantities rather than Status enum (enum reads unreliable).
|
||||||
IEnumerable<PreorderItem> items = allItems;
|
IEnumerable<PreOrderItem> items = allItems;
|
||||||
if (openOnly)
|
if (openOnly)
|
||||||
{
|
{
|
||||||
// Open preorders: those where at least one item still needs fulfillment
|
// Open preorders: those where at least one item still needs fulfillment
|
||||||
var openPreorderIds = allPreorders
|
var openPreOrderIds = allPreOrders
|
||||||
.Where(p => allItems
|
.Where(p => allItems
|
||||||
.Where(i => i.PreorderId == p.Id)
|
.Where(i => i.PreOrderId == p.Id)
|
||||||
.Any(i => i.FulfilledQuantity < i.RequestedQuantity))
|
.Any(i => i.FulfilledQuantity < i.RequestedQuantity))
|
||||||
.Select(p => p.Id)
|
.Select(p => p.Id)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
items = allItems.Where(i => openPreorderIds.Contains(i.PreorderId));
|
items = allItems.Where(i => openPreOrderIds.Contains(i.PreOrderId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group by product
|
// Group by product
|
||||||
|
|
@ -409,7 +409,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
TotalRequested = g.Sum(i => i.RequestedQuantity),
|
TotalRequested = g.Sum(i => i.RequestedQuantity),
|
||||||
TotalFulfilled = g.Sum(i => i.FulfilledQuantity),
|
TotalFulfilled = g.Sum(i => i.FulfilledQuantity),
|
||||||
TotalUnfulfilled = g.Sum(i => i.RequestedQuantity - i.FulfilledQuantity),
|
TotalUnfulfilled = g.Sum(i => i.RequestedQuantity - i.FulfilledQuantity),
|
||||||
PreorderCount = g.Select(i => i.PreorderId).Distinct().Count(),
|
PreOrderCount = g.Select(i => i.PreOrderId).Distinct().Count(),
|
||||||
AvgUnitPrice = g.Where(i => i.UnitPriceInclTax > 0).Any()
|
AvgUnitPrice = g.Where(i => i.UnitPriceInclTax > 0).Any()
|
||||||
? g.Where(i => i.UnitPriceInclTax > 0).Average(i => i.UnitPriceInclTax)
|
? g.Where(i => i.UnitPriceInclTax > 0).Average(i => i.UnitPriceInclTax)
|
||||||
: 0m
|
: 0m
|
||||||
|
|
@ -429,7 +429,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
var rows = grouped.Select(g =>
|
var rows = grouped.Select(g =>
|
||||||
{
|
{
|
||||||
productById.TryGetValue(g.ProductId, out var dto);
|
productById.TryGetValue(g.ProductId, out var dto);
|
||||||
return new PreorderDemandRow
|
return new PreOrderDemandRow
|
||||||
{
|
{
|
||||||
ProductId = g.ProductId,
|
ProductId = g.ProductId,
|
||||||
ProductName = dto?.Name ?? $"Product #{g.ProductId}",
|
ProductName = dto?.Name ?? $"Product #{g.ProductId}",
|
||||||
|
|
@ -438,7 +438,7 @@ public class PreorderAdminController : BasePluginController
|
||||||
TotalRequested = g.TotalRequested,
|
TotalRequested = g.TotalRequested,
|
||||||
TotalFulfilled = g.TotalFulfilled,
|
TotalFulfilled = g.TotalFulfilled,
|
||||||
TotalUnfulfilled = g.TotalUnfulfilled,
|
TotalUnfulfilled = g.TotalUnfulfilled,
|
||||||
PreorderCount = g.PreorderCount,
|
PreOrderCount = g.PreOrderCount,
|
||||||
AvgUnitPrice = Math.Round(g.AvgUnitPrice, 0)
|
AvgUnitPrice = Math.Round(g.AvgUnitPrice, 0)
|
||||||
};
|
};
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;
|
||||||
[AuthorizeAdmin]
|
[AuthorizeAdmin]
|
||||||
[Area(AreaNames.ADMIN)]
|
[Area(AreaNames.ADMIN)]
|
||||||
[AutoValidateAntiforgeryToken]
|
[AutoValidateAntiforgeryToken]
|
||||||
public class PreorderAvailabilityController : BasePluginController
|
public class PreOrderAvailabilityController : BasePluginController
|
||||||
{
|
{
|
||||||
private readonly IPermissionService _permissionService;
|
private readonly IPermissionService _permissionService;
|
||||||
private readonly FruitBankDbContext _dbContext;
|
private readonly FruitBankDbContext _dbContext;
|
||||||
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||||
private readonly IStoreContext _storeContext;
|
private readonly IStoreContext _storeContext;
|
||||||
|
|
||||||
public PreorderAvailabilityController(
|
public PreOrderAvailabilityController(
|
||||||
IPermissionService permissionService,
|
IPermissionService permissionService,
|
||||||
FruitBankDbContext dbContext,
|
FruitBankDbContext dbContext,
|
||||||
FruitBankAttributeService fruitBankAttributeService,
|
FruitBankAttributeService fruitBankAttributeService,
|
||||||
|
|
@ -38,19 +38,19 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
// ── INDEX ─────────────────────────────────────────────────────────────────
|
// ── INDEX ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Admin/PreorderAvailability")]
|
[Route("Admin/PreOrderAvailability")]
|
||||||
public async Task<IActionResult> Index()
|
public async Task<IActionResult> Index()
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
return AccessDeniedView();
|
return AccessDeniedView();
|
||||||
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/PreorderAvailability/Index.cshtml");
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/PreOrderAvailability/Index.cshtml");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── ALL PRODUCTS — DataTables server-side ─────────────────────────────────
|
// ── ALL PRODUCTS — DataTables server-side ─────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/PreorderAvailability/ProductList")]
|
[Route("Admin/PreOrderAvailability/ProductList")]
|
||||||
public async Task<IActionResult> ProductList()
|
public async Task<IActionResult> ProductList()
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
|
|
@ -74,13 +74,13 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
// 2. All preorder window generic attributes — two queries, no N+1
|
// 2. All preorder window generic attributes — two queries, no N+1
|
||||||
var gaStart = await _dbContext.GenericAttributes.Table
|
var gaStart = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowStart
|
&& ga.Key == FruitBankConst.PreOrderWindowStart
|
||||||
&& ga.StoreId == storeId)
|
&& ga.StoreId == storeId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var gaEnd = await _dbContext.GenericAttributes.Table
|
var gaEnd = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowEnd
|
&& ga.Key == FruitBankConst.PreOrderWindowEnd
|
||||||
&& ga.StoreId == storeId)
|
&& ga.StoreId == storeId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
var hasStart = startByProduct.ContainsKey(p.Id);
|
var hasStart = startByProduct.ContainsKey(p.Id);
|
||||||
var hasEnd = endByProduct.ContainsKey(p.Id);
|
var hasEnd = endByProduct.ContainsKey(p.Id);
|
||||||
|
|
||||||
return new PreorderAvailabilityRow
|
return new PreOrderAvailabilityRow
|
||||||
{
|
{
|
||||||
ProductId = p.Id,
|
ProductId = p.Id,
|
||||||
ProductName = p.Name,
|
ProductName = p.Name,
|
||||||
|
|
@ -131,7 +131,7 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
// ── AVAILABLE TODAY — DataTables server-side ──────────────────────────────
|
// ── AVAILABLE TODAY — DataTables server-side ──────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/PreorderAvailability/AvailableTodayList")]
|
[Route("Admin/PreOrderAvailability/AvailableTodayList")]
|
||||||
public async Task<IActionResult> AvailableTodayList()
|
public async Task<IActionResult> AvailableTodayList()
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
|
|
@ -153,13 +153,13 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
|
|
||||||
var gaStart = await _dbContext.GenericAttributes.Table
|
var gaStart = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowStart
|
&& ga.Key == FruitBankConst.PreOrderWindowStart
|
||||||
&& ga.StoreId == storeId)
|
&& ga.StoreId == storeId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var gaEnd = await _dbContext.GenericAttributes.Table
|
var gaEnd = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowEnd
|
&& ga.Key == FruitBankConst.PreOrderWindowEnd
|
||||||
&& ga.StoreId == storeId)
|
&& ga.StoreId == storeId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
@ -179,7 +179,7 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
{
|
{
|
||||||
DateTime.TryParse(startByProduct[p.Id], out var ws);
|
DateTime.TryParse(startByProduct[p.Id], out var ws);
|
||||||
DateTime.TryParse(endByProduct[p.Id], out var we);
|
DateTime.TryParse(endByProduct[p.Id], out var we);
|
||||||
return new PreorderAvailabilityRow
|
return new PreOrderAvailabilityRow
|
||||||
{
|
{
|
||||||
ProductId = p.Id,
|
ProductId = p.Id,
|
||||||
ProductName = p.Name,
|
ProductName = p.Name,
|
||||||
|
|
@ -201,7 +201,7 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
// ── SAVE WINDOW DATES for a product ───────────────────────────────────────
|
// ── SAVE WINDOW DATES for a product ───────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Admin/PreorderAvailability/SaveWindow")]
|
[Route("Admin/PreOrderAvailability/SaveWindow")]
|
||||||
public async Task<IActionResult> SaveWindow(int productId, string? windowStart, string? windowEnd)
|
public async Task<IActionResult> SaveWindow(int productId, string? windowStart, string? windowEnd)
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||||
|
|
@ -215,13 +215,13 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
if (string.IsNullOrWhiteSpace(windowStart))
|
if (string.IsNullOrWhiteSpace(windowStart))
|
||||||
{
|
{
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
.DeleteGenericAttributeAsync<Product>(productId, FruitBankConst.PreorderWindowStart, storeId);
|
.DeleteGenericAttributeAsync<Product>(productId, FruitBankConst.PreOrderWindowStart, storeId);
|
||||||
}
|
}
|
||||||
else if (DateTime.TryParse(windowStart, out var ws))
|
else if (DateTime.TryParse(windowStart, out var ws))
|
||||||
{
|
{
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
.InsertOrUpdateGenericAttributeAsync<Product, DateTime>(
|
.InsertOrUpdateGenericAttributeAsync<Product, DateTime>(
|
||||||
productId, FruitBankConst.PreorderWindowStart, ws.Date, storeId);
|
productId, FruitBankConst.PreOrderWindowStart, ws.Date, storeId);
|
||||||
}
|
}
|
||||||
else return Json(new { success = false, error = $"Invalid start date: {windowStart}" });
|
else return Json(new { success = false, error = $"Invalid start date: {windowStart}" });
|
||||||
|
|
||||||
|
|
@ -229,13 +229,13 @@ public class PreorderAvailabilityController : BasePluginController
|
||||||
if (string.IsNullOrWhiteSpace(windowEnd))
|
if (string.IsNullOrWhiteSpace(windowEnd))
|
||||||
{
|
{
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
.DeleteGenericAttributeAsync<Product>(productId, FruitBankConst.PreorderWindowEnd, storeId);
|
.DeleteGenericAttributeAsync<Product>(productId, FruitBankConst.PreOrderWindowEnd, storeId);
|
||||||
}
|
}
|
||||||
else if (DateTime.TryParse(windowEnd, out var we))
|
else if (DateTime.TryParse(windowEnd, out var we))
|
||||||
{
|
{
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
.InsertOrUpdateGenericAttributeAsync<Product, DateTime>(
|
.InsertOrUpdateGenericAttributeAsync<Product, DateTime>(
|
||||||
productId, FruitBankConst.PreorderWindowEnd, we.Date, storeId);
|
productId, FruitBankConst.PreOrderWindowEnd, we.Date, storeId);
|
||||||
}
|
}
|
||||||
else return Json(new { success = false, error = $"Invalid end date: {windowEnd}" });
|
else return Json(new { success = false, error = $"Invalid end date: {windowEnd}" });
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,37 +2,37 @@ using FruitBank.Common.Enums;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
||||||
|
|
||||||
public class PreorderListRow
|
public class PreOrderListRow
|
||||||
{
|
{
|
||||||
public int PreorderId { get; set; }
|
public int PreOrderId { get; set; }
|
||||||
public int CustomerId { get; set; }
|
public int CustomerId { get; set; }
|
||||||
public string CustomerName { get; set; } = string.Empty;
|
public string CustomerName { get; set; } = string.Empty;
|
||||||
public string CustomerEmail { get; set; } = string.Empty;
|
public string CustomerEmail { get; set; } = string.Empty;
|
||||||
public string DateOfReceipt { get; set; } = string.Empty; // formatted
|
public string DateOfReceipt { get; set; } = string.Empty; // formatted
|
||||||
public string CreatedOnUtc { get; set; } = string.Empty; // formatted
|
public string CreatedOnUtc { get; set; } = string.Empty; // formatted
|
||||||
public PreorderStatus Status { get; set; }
|
public PreOrderStatus Status { get; set; }
|
||||||
public string StatusLabel { get; set; } = string.Empty;
|
public string StatusLabel { get; set; } = string.Empty;
|
||||||
public int ItemCount { get; set; }
|
public int ItemCount { get; set; }
|
||||||
public int FulfilledCount { get; set; }
|
public int FulfilledCount { get; set; }
|
||||||
public int? OrderId { get; set; } // linked real order, if created
|
public int? OrderId { get; set; } // linked real order, if created
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PreorderDetailModel
|
public class PreOrderDetailModel
|
||||||
{
|
{
|
||||||
public int PreorderId { get; set; }
|
public int PreOrderId { get; set; }
|
||||||
public int CustomerId { get; set; }
|
public int CustomerId { get; set; }
|
||||||
public string CustomerName { get; set; } = string.Empty;
|
public string CustomerName { get; set; } = string.Empty;
|
||||||
public string CustomerEmail { get; set; } = string.Empty;
|
public string CustomerEmail { get; set; } = string.Empty;
|
||||||
public string DateOfReceipt { get; set; } = string.Empty;
|
public string DateOfReceipt { get; set; } = string.Empty;
|
||||||
public string CreatedOnUtc { get; set; } = string.Empty;
|
public string CreatedOnUtc { get; set; } = string.Empty;
|
||||||
public string UpdatedOnUtc { get; set; } = string.Empty;
|
public string UpdatedOnUtc { get; set; } = string.Empty;
|
||||||
public PreorderStatus Status { get; set; }
|
public PreOrderStatus Status { get; set; }
|
||||||
public string? CustomerNote { get; set; }
|
public string? CustomerNote { get; set; }
|
||||||
public int? OrderId { get; set; }
|
public int? OrderId { get; set; }
|
||||||
public List<PreorderDetailItemRow> Items { get; set; } = new();
|
public List<PreOrderDetailItemRow> Items { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PreorderDetailItemRow
|
public class PreOrderDetailItemRow
|
||||||
{
|
{
|
||||||
public int ItemId { get; set; }
|
public int ItemId { get; set; }
|
||||||
public int ProductId { get; set; }
|
public int ProductId { get; set; }
|
||||||
|
|
@ -41,11 +41,11 @@ public class PreorderDetailItemRow
|
||||||
public int RequestedQuantity { get; set; }
|
public int RequestedQuantity { get; set; }
|
||||||
public int FulfilledQuantity { get; set; }
|
public int FulfilledQuantity { get; set; }
|
||||||
public decimal UnitPriceInclTax { get; set; }
|
public decimal UnitPriceInclTax { get; set; }
|
||||||
public PreorderItemStatus Status { get; set; }
|
public PreOrderItemStatus Status { get; set; }
|
||||||
public string StatusLabel { get; set; } = string.Empty;
|
public string StatusLabel { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PreorderDemandRow
|
public class PreOrderDemandRow
|
||||||
{
|
{
|
||||||
public int ProductId { get; set; }
|
public int ProductId { get; set; }
|
||||||
public string ProductName { get; set; } = string.Empty;
|
public string ProductName { get; set; } = string.Empty;
|
||||||
|
|
@ -54,6 +54,6 @@ public class PreorderDemandRow
|
||||||
public int TotalRequested { get; set; } // sum of RequestedQuantity
|
public int TotalRequested { get; set; } // sum of RequestedQuantity
|
||||||
public int TotalFulfilled { get; set; } // sum of FulfilledQuantity
|
public int TotalFulfilled { get; set; } // sum of FulfilledQuantity
|
||||||
public int TotalUnfulfilled { get; set; } // TotalRequested - TotalFulfilled
|
public int TotalUnfulfilled { get; set; } // TotalRequested - TotalFulfilled
|
||||||
public int PreorderCount { get; set; } // distinct preorders containing this product
|
public int PreOrderCount { get; set; } // distinct preorders containing this product
|
||||||
public decimal AvgUnitPrice { get; set; } // average snapshot price
|
public decimal AvgUnitPrice { get; set; } // average snapshot price
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models;
|
||||||
|
|
||||||
public class PreorderAvailabilityRow
|
public class PreOrderAvailabilityRow
|
||||||
{
|
{
|
||||||
public int ProductId { get; set; }
|
public int ProductId { get; set; }
|
||||||
public string ProductName { get; set; } = string.Empty;
|
public string ProductName { get; set; } = string.Empty;
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.PreorderDetailModel
|
@model Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.PreOrderDetailModel
|
||||||
@using FruitBank.Common.Enums
|
@using FruitBank.Common.Enums
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewBag.PageTitle = $"Előrendelés #{Model.PreorderId}";
|
ViewBag.PageTitle = $"Előrendelés #{Model.PreOrderId}";
|
||||||
Layout = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/_FruitBankAdminLayout.cshtml";
|
Layout = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/_FruitBankAdminLayout.cshtml";
|
||||||
|
|
||||||
var statusClass = Model.Status switch
|
var statusClass = Model.Status switch
|
||||||
{
|
{
|
||||||
PreorderStatus.Confirmed => "po-status-confirmed",
|
PreOrderStatus.Confirmed => "po-status-confirmed",
|
||||||
PreorderStatus.PartiallyFulfilled => "po-status-partial",
|
PreOrderStatus.PartiallyFulfilled => "po-status-partial",
|
||||||
PreorderStatus.Cancelled => "po-status-cancelled",
|
PreOrderStatus.Cancelled => "po-status-cancelled",
|
||||||
_ => "po-status-pending"
|
_ => "po-status-pending"
|
||||||
};
|
};
|
||||||
|
|
||||||
var statusLabel = Model.Status switch
|
var statusLabel = Model.Status switch
|
||||||
{
|
{
|
||||||
PreorderStatus.Confirmed => "Megerősítve",
|
PreOrderStatus.Confirmed => "Megerősítve",
|
||||||
PreorderStatus.PartiallyFulfilled => "Részben teljesítve",
|
PreOrderStatus.PartiallyFulfilled => "Részben teljesítve",
|
||||||
PreorderStatus.Cancelled => "Törölve",
|
PreOrderStatus.Cancelled => "Törölve",
|
||||||
_ => "Függőben"
|
_ => "Függőben"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -46,14 +46,14 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Back link -->
|
<!-- Back link -->
|
||||||
<a href="/Admin/Preorders" class="btn btn-default btn-sm mb-3">
|
<a href="/Admin/PreOrders" class="btn btn-default btn-sm mb-3">
|
||||||
<i class="fas fa-arrow-left"></i> Vissza a listához
|
<i class="fas fa-arrow-left"></i> Vissza a listához
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="content-header clearfix">
|
<div class="content-header clearfix">
|
||||||
<h1 class="float-left">
|
<h1 class="float-left">
|
||||||
<i class="fas fa-calendar-plus" style="color:#2d7a3a;"></i>
|
<i class="fas fa-calendar-plus" style="color:#2d7a3a;"></i>
|
||||||
Előrendelés <strong>#@Model.PreorderId</strong>
|
Előrendelés <strong>#@Model.PreOrderId</strong>
|
||||||
<span class="@statusClass ml-2">@statusLabel</span>
|
<span class="@statusClass ml-2">@statusLabel</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
<i class="fas fa-external-link-alt"></i> Rendelés #@Model.OrderId
|
<i class="fas fa-external-link-alt"></i> Rendelés #@Model.OrderId
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@if (Model.Status == PreorderStatus.Pending)
|
@if (Model.Status == PreOrderStatus.Pending)
|
||||||
{
|
{
|
||||||
<button id="cancelBtn" class="btn btn-danger btn-sm ml-2">
|
<button id="cancelBtn" class="btn btn-danger btn-sm ml-2">
|
||||||
<i class="fas fa-times"></i> Visszavonás
|
<i class="fas fa-times"></i> Visszavonás
|
||||||
|
|
@ -112,10 +112,10 @@
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<strong>Tételek (@Model.Items.Count)</strong>
|
<strong>Tételek (@Model.Items.Count)</strong>
|
||||||
@{
|
@{
|
||||||
var fulfilled = Model.Items.Count(i => i.Status == PreorderItemStatus.Fulfilled);
|
var fulfilled = Model.Items.Count(i => i.Status == PreOrderItemStatus.Fulfilled);
|
||||||
var partial = Model.Items.Count(i => i.Status == PreorderItemStatus.PartiallyFulfilled);
|
var partial = Model.Items.Count(i => i.Status == PreOrderItemStatus.PartiallyFulfilled);
|
||||||
var dropped = Model.Items.Count(i => i.Status == PreorderItemStatus.Dropped);
|
var dropped = Model.Items.Count(i => i.Status == PreOrderItemStatus.Dropped);
|
||||||
var pending = Model.Items.Count(i => i.Status == PreorderItemStatus.Pending);
|
var pending = Model.Items.Count(i => i.Status == PreOrderItemStatus.Pending);
|
||||||
}
|
}
|
||||||
<span class="ml-2 text-muted" style="font-size:13px;">
|
<span class="ml-2 text-muted" style="font-size:13px;">
|
||||||
@if (fulfilled > 0) { <span class="badge badge-success">@fulfilled teljesítve</span> }
|
@if (fulfilled > 0) { <span class="badge badge-success">@fulfilled teljesítve</span> }
|
||||||
|
|
@ -142,9 +142,9 @@
|
||||||
{
|
{
|
||||||
var rowClass = item.Status switch
|
var rowClass = item.Status switch
|
||||||
{
|
{
|
||||||
PreorderItemStatus.Fulfilled => "item-fulfilled",
|
PreOrderItemStatus.Fulfilled => "item-fulfilled",
|
||||||
PreorderItemStatus.PartiallyFulfilled => "item-partial",
|
PreOrderItemStatus.PartiallyFulfilled => "item-partial",
|
||||||
PreorderItemStatus.Dropped => "item-dropped",
|
PreOrderItemStatus.Dropped => "item-dropped",
|
||||||
_ => "item-pending"
|
_ => "item-pending"
|
||||||
};
|
};
|
||||||
var pct = item.RequestedQuantity > 0
|
var pct = item.RequestedQuantity > 0
|
||||||
|
|
@ -188,7 +188,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
@{
|
@{
|
||||||
var totalEstimated = Model.Items
|
var totalEstimated = Model.Items
|
||||||
.Where(i => !i.IsMeasurable && (i.Status == PreorderItemStatus.Fulfilled || i.Status == PreorderItemStatus.PartiallyFulfilled))
|
.Where(i => !i.IsMeasurable && (i.Status == PreOrderItemStatus.Fulfilled || i.Status == PreOrderItemStatus.PartiallyFulfilled))
|
||||||
.Sum(i => i.UnitPriceInclTax * i.FulfilledQuantity);
|
.Sum(i => i.UnitPriceInclTax * i.FulfilledQuantity);
|
||||||
}
|
}
|
||||||
<tfoot>
|
<tfoot>
|
||||||
|
|
@ -205,19 +205,19 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@if (Model.Status == PreorderStatus.Pending)
|
@if (Model.Status == PreOrderStatus.Pending)
|
||||||
{
|
{
|
||||||
<script>
|
<script>
|
||||||
$(function () {
|
$(function () {
|
||||||
$('#cancelBtn').click(function () {
|
$('#cancelBtn').click(function () {
|
||||||
if (!confirm('Biztosan visszavonod ezt az előrendelést? Ez a művelet nem visszafordítható.')) return;
|
if (!confirm('Biztosan visszavonod ezt az előrendelést? Ez a művelet nem visszafordítható.')) return;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : '/Admin/Preorders/Cancel/@Model.PreorderId',
|
url : '/Admin/PreOrders/Cancel/@Model.PreOrderId',
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
data : { __RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val() },
|
data : { __RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val() },
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
location.href = '/Admin/Preorders';
|
location.href = '/Admin/PreOrders';
|
||||||
} else {
|
} else {
|
||||||
alert('Hiba: ' + (res.error || 'Ismeretlen hiba'));
|
alert('Hiba: ' + (res.error || 'Ismeretlen hiba'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
@{
|
@{
|
||||||
ViewBag.PageTitle = "Előrendelések";
|
ViewBag.PageTitle = "Előrendelések";
|
||||||
NopHtml.SetActiveMenuItemSystemName("Preorders.List");
|
NopHtml.SetActiveMenuItemSystemName("PreOrders.List");
|
||||||
Layout = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/_FruitBankAdminLayout.cshtml";
|
Layout = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/_FruitBankAdminLayout.cshtml";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,7 +194,7 @@ $(function () {
|
||||||
return diff >= 0 && diff <= 4;
|
return diff >= 0 && diff <= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Preorder list grid ──────────────────────────────────────────────────
|
// ── PreOrder list grid ──────────────────────────────────────────────────
|
||||||
var poTable = $('#po-grid').DataTable({
|
var poTable = $('#po-grid').DataTable({
|
||||||
serverSide: true, processing: true, pageLength: 25,
|
serverSide: true, processing: true, pageLength: 25,
|
||||||
lengthMenu: [[25,50,100],[25,50,100]], order: [[3,'desc']],
|
lengthMenu: [[25,50,100],[25,50,100]], order: [[3,'desc']],
|
||||||
|
|
@ -202,13 +202,13 @@ $(function () {
|
||||||
info:'_START_–_END_ / _TOTAL_ előrendelés', infoEmpty:'0 előrendelés',
|
info:'_START_–_END_ / _TOTAL_ előrendelés', infoEmpty:'0 előrendelés',
|
||||||
infoFiltered:'(szűrve _MAX_-ból)', emptyTable:'Nincs előrendelés', zeroRecords:'Nincs találat',
|
infoFiltered:'(szűrve _MAX_-ból)', emptyTable:'Nincs előrendelés', zeroRecords:'Nincs találat',
|
||||||
paginate:{first:'««',previous:'«',next:'»',last:'»»'} },
|
paginate:{first:'««',previous:'«',next:'»',last:'»»'} },
|
||||||
ajax: { url:'/Admin/Preorders/PreorderList', type:'POST',
|
ajax: { url:'/Admin/PreOrders/PreOrderList', type:'POST',
|
||||||
data: function(d){ d.__RequestVerificationToken=_token; d.statusFilter=activeStatus; } },
|
data: function(d){ d.__RequestVerificationToken=_token; d.statusFilter=activeStatus; } },
|
||||||
createdRow: function(row, data) {
|
createdRow: function(row, data) {
|
||||||
if (isUrgentRow(data)) $(row).addClass('po-urgent-row');
|
if (isUrgentRow(data)) $(row).addClass('po-urgent-row');
|
||||||
},
|
},
|
||||||
columns: [
|
columns: [
|
||||||
{ data:'PreorderId', name:'PreorderId', render:function(d){ return '<strong>#'+d+'</strong>'; } },
|
{ data:'PreOrderId', name:'PreOrderId', render:function(d){ return '<strong>#'+d+'</strong>'; } },
|
||||||
{ data:'CustomerName', name:'CustomerName', render:function(d,t,row){ return '<div>'+d+'</div><small class="text-muted">'+row.CustomerEmail+'</small>'; } },
|
{ data:'CustomerName', name:'CustomerName', render:function(d,t,row){ return '<div>'+d+'</div><small class="text-muted">'+row.CustomerEmail+'</small>'; } },
|
||||||
{ data:'DateOfReceipt',name:'DateOfReceipt',render:function(d,t,row){
|
{ data:'DateOfReceipt',name:'DateOfReceipt',render:function(d,t,row){
|
||||||
var icon = '<i class="fas fa-calendar-day text-muted mr-1"></i>';
|
var icon = '<i class="fas fa-calendar-day text-muted mr-1"></i>';
|
||||||
|
|
@ -218,8 +218,8 @@ $(function () {
|
||||||
{ data:'CreatedOnUtc', name:'CreatedOnUtc', render:function(d){ return '<small>'+d+'</small>'; } },
|
{ data:'CreatedOnUtc', name:'CreatedOnUtc', render:function(d){ return '<small>'+d+'</small>'; } },
|
||||||
{ data:'Status', name:'Status', orderable:false, render:function(d,t,row){ return statusBadge(row); } },
|
{ data:'Status', name:'Status', orderable:false, render:function(d,t,row){ return statusBadge(row); } },
|
||||||
{ data:'ItemCount', orderable:false, className:'text-center', render:function(d,t,row){ return itemProgress(row); } },
|
{ data:'ItemCount', orderable:false, className:'text-center', render:function(d,t,row){ return itemProgress(row); } },
|
||||||
{ data:'PreorderId', orderable:false, searchable:false, className:'text-center', width:'60px',
|
{ data:'PreOrderId', orderable:false, searchable:false, className:'text-center', width:'60px',
|
||||||
render:function(d){ return '<a href="/Admin/Preorders/Detail/'+d+'" class="btn btn-xs btn-default" title="Részletek"><i class="fas fa-eye"></i></a>'; } }
|
render:function(d){ return '<a href="/Admin/PreOrders/Detail/'+d+'" class="btn btn-xs btn-default" title="Részletek"><i class="fas fa-eye"></i></a>'; } }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
$(document).on('click','.po-filter',function(){
|
$(document).on('click','.po-filter',function(){
|
||||||
|
|
@ -235,7 +235,7 @@ $(function () {
|
||||||
info:'_START_–_END_ / _TOTAL_ termék', infoEmpty:'Nincs adat',
|
info:'_START_–_END_ / _TOTAL_ termék', infoEmpty:'Nincs adat',
|
||||||
infoFiltered:'(szűrve _MAX_-ból)', emptyTable:'Nincs előrendelési igény', zeroRecords:'Nincs találat',
|
infoFiltered:'(szűrve _MAX_-ból)', emptyTable:'Nincs előrendelési igény', zeroRecords:'Nincs találat',
|
||||||
paginate:{first:'««',previous:'«',next:'»',last:'»»'} },
|
paginate:{first:'««',previous:'«',next:'»',last:'»»'} },
|
||||||
ajax:{ url:'/Admin/Preorders/DemandList', type:'POST',
|
ajax:{ url:'/Admin/PreOrders/DemandList', type:'POST',
|
||||||
data:function(d){ d.__RequestVerificationToken=_token; d.openOnly=demandOpenOnly?'true':'false'; },
|
data:function(d){ d.__RequestVerificationToken=_token; d.openOnly=demandOpenOnly?'true':'false'; },
|
||||||
dataSrc:function(json){
|
dataSrc:function(json){
|
||||||
var n=(json.data||[]).filter(function(r){return r.TotalUnfulfilled>0;}).length;
|
var n=(json.data||[]).filter(function(r){return r.TotalUnfulfilled>0;}).length;
|
||||||
|
|
@ -253,7 +253,7 @@ $(function () {
|
||||||
var cls=pct===100?'bg-success':pct>0?'bg-warning':'bg-secondary';
|
var cls=pct===100?'bg-success':pct>0?'bg-warning':'bg-secondary';
|
||||||
return '<div>'+fmtQty(d)+'</div><div class="progress mt-1" style="height:4px;"><div class="progress-bar '+cls+'" style="width:'+pct+'%"></div></div>'; } },
|
return '<div>'+fmtQty(d)+'</div><div class="progress mt-1" style="height:4px;"><div class="progress-bar '+cls+'" style="width:'+pct+'%"></div></div>'; } },
|
||||||
{ data:'TotalUnfulfilled',orderable:false, className:'text-center', render:function(d){ return unfulfilledCell(d); } },
|
{ data:'TotalUnfulfilled',orderable:false, className:'text-center', render:function(d){ return unfulfilledCell(d); } },
|
||||||
{ data:'PreorderCount', orderable:false, className:'text-center', render:function(d){ return '<span class="badge badge-secondary">'+d+'</span>'; } },
|
{ data:'PreOrderCount', orderable:false, className:'text-center', render:function(d){ return '<span class="badge badge-secondary">'+d+'</span>'; } },
|
||||||
{ data:'AvgUnitPrice', orderable:false, className:'text-right', render:function(d){ return fmtPrice(d); } }
|
{ data:'AvgUnitPrice', orderable:false, className:'text-right', render:function(d){ return fmtPrice(d); } }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
@ -270,7 +270,7 @@ $(function () {
|
||||||
demandTable.ajax.reload();
|
demandTable.ajax.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Create Order / Preorder Modal (mode-aware) ──────────────────────────
|
// ── Create Order / PreOrder Modal (mode-aware) ──────────────────────────
|
||||||
var cpProducts = [];
|
var cpProducts = [];
|
||||||
var cpMode = null;
|
var cpMode = null;
|
||||||
var CP_CUTOFF = 4; // ≤4 days → order, >4 days → preorder
|
var CP_CUTOFF = 4; // ≤4 days → order, >4 days → preorder
|
||||||
|
|
@ -332,7 +332,7 @@ $(function () {
|
||||||
if (!cpMode){ resp([]); return; }
|
if (!cpMode){ resp([]); return; }
|
||||||
$.get(cpMode==='order'
|
$.get(cpMode==='order'
|
||||||
?'/Admin/CustomOrder/ProductSearchAutoComplete'
|
?'/Admin/CustomOrder/ProductSearchAutoComplete'
|
||||||
:'/Admin/CustomOrder/PreorderProductSearchAutoComplete',
|
:'/Admin/CustomOrder/PreOrderProductSearchAutoComplete',
|
||||||
{term:req.term}, resp);
|
{term:req.term}, resp);
|
||||||
},
|
},
|
||||||
select:function(e,ui){ addCpProduct(ui.item); $('#cp-product-search').val(''); return false; }
|
select:function(e,ui){ addCpProduct(ui.item); $('#cp-product-search').val(''); return false; }
|
||||||
|
|
@ -402,7 +402,7 @@ $(function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$.ajax({url:'/Admin/Preorders/CreatePreorder', type:'POST',
|
$.ajax({url:'/Admin/PreOrders/CreatePreOrder', type:'POST',
|
||||||
data:{ customerId:$('#cp-customer-id').val(), deliveryDateTime:$('#cp-delivery').val(),
|
data:{ customerId:$('#cp-customer-id').val(), deliveryDateTime:$('#cp-delivery').val(),
|
||||||
customerNote:$('#cp-note').val().trim(), productsJson:$('#cp-products-json').val(),
|
customerNote:$('#cp-note').val().trim(), productsJson:$('#cp-products-json').val(),
|
||||||
__RequestVerificationToken:_token },
|
__RequestVerificationToken:_token },
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
@{
|
@{
|
||||||
ViewBag.PageTitle = "Előrendelés — termékelérhetőség";
|
ViewBag.PageTitle = "Előrendelés — termékelérhetőség";
|
||||||
NopHtml.SetActiveMenuItemSystemName("PreorderAvailability");
|
NopHtml.SetActiveMenuItemSystemName("PreOrderAvailability");
|
||||||
Layout = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/_FruitBankAdminLayout.cshtml";
|
Layout = "~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/_FruitBankAdminLayout.cshtml";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@ $(function () {
|
||||||
zeroRecords : 'Nincs találat'
|
zeroRecords : 'Nincs találat'
|
||||||
},
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
url : '/Admin/PreorderAvailability/ProductList',
|
url : '/Admin/PreOrderAvailability/ProductList',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: function (d) { d.__RequestVerificationToken = _token; }
|
data: function (d) { d.__RequestVerificationToken = _token; }
|
||||||
},
|
},
|
||||||
|
|
@ -197,7 +197,7 @@ $(function () {
|
||||||
else rowData.WindowEnd = newVal || null;
|
else rowData.WindowEnd = newVal || null;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : '/Admin/PreorderAvailability/SaveWindow',
|
url : '/Admin/PreOrderAvailability/SaveWindow',
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
data : {
|
data : {
|
||||||
__RequestVerificationToken : _token,
|
__RequestVerificationToken : _token,
|
||||||
|
|
@ -243,7 +243,7 @@ $(function () {
|
||||||
zeroRecords : 'Nincs találat'
|
zeroRecords : 'Nincs találat'
|
||||||
},
|
},
|
||||||
ajax: {
|
ajax: {
|
||||||
url : '/Admin/PreorderAvailability/AvailableTodayList',
|
url : '/Admin/PreOrderAvailability/AvailableTodayList',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: function (d) { d.__RequestVerificationToken = _token; },
|
data: function (d) { d.__RequestVerificationToken = _token; },
|
||||||
dataSrc: function (json) {
|
dataSrc: function (json) {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ using Nop.Web.Framework.Components;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Components;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Components;
|
||||||
|
|
||||||
public class CustomerPreorderNavViewComponent : NopViewComponent
|
public class CustomerPreOrderNavViewComponent : NopViewComponent
|
||||||
{
|
{
|
||||||
public IViewComponentResult Invoke(string widgetZone, object additionalData)
|
public IViewComponentResult Invoke(string widgetZone, object additionalData)
|
||||||
{
|
{
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Views/CustomerPreorder/NavItem.cshtml");
|
return View("~/Plugins/Misc.FruitBankPlugin/Views/CustomerPreOrder/NavItem.cshtml");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,21 @@ using Nop.Core;
|
||||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||||
using Nop.Services.Customers;
|
using Nop.Services.Customers;
|
||||||
using Nop.Web.Framework.Controllers;
|
using Nop.Web.Framework.Controllers;
|
||||||
|
using static Nop.Plugin.Misc.FruitBankPlugin.Controllers.CustomerPreOrderController;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers;
|
||||||
|
|
||||||
public class CustomerPreorderController : BasePluginController
|
public class CustomerPreOrderController : BasePluginController
|
||||||
{
|
{
|
||||||
private readonly IWorkContext _workContext;
|
private readonly IWorkContext _workContext;
|
||||||
private readonly ICustomerService _customerService;
|
private readonly ICustomerService _customerService;
|
||||||
private readonly PreorderDbContext _preorderDbContext;
|
private readonly PreOrderDbContext _preorderDbContext;
|
||||||
private readonly FruitBankDbContext _dbContext;
|
private readonly FruitBankDbContext _dbContext;
|
||||||
|
|
||||||
public CustomerPreorderController(
|
public CustomerPreOrderController(
|
||||||
IWorkContext workContext,
|
IWorkContext workContext,
|
||||||
ICustomerService customerService,
|
ICustomerService customerService,
|
||||||
PreorderDbContext preorderDbContext,
|
PreOrderDbContext preorderDbContext,
|
||||||
FruitBankDbContext dbContext)
|
FruitBankDbContext dbContext)
|
||||||
{
|
{
|
||||||
_workContext = workContext;
|
_workContext = workContext;
|
||||||
|
|
@ -35,13 +36,13 @@ public class CustomerPreorderController : BasePluginController
|
||||||
return Challenge();
|
return Challenge();
|
||||||
|
|
||||||
// Load this customer's preorders, newest first
|
// Load this customer's preorders, newest first
|
||||||
var preorders = await _preorderDbContext.Preorders
|
var preorders = await _preorderDbContext.PreOrders
|
||||||
.GetAllByCustomerIdAsync(customer.Id, false)
|
.GetAllByCustomerIdAsync(customer.Id, false)
|
||||||
.OrderByDescending(p => p.CreatedOnUtc)
|
.OrderByDescending(p => p.CreatedOnUtc)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var allItems = await _preorderDbContext.PreorderItems.GetAll()
|
var allItems = await _preorderDbContext.PreOrderItems.GetAll()
|
||||||
.Where(i => preorders.Select(p => p.Id).Contains(i.PreorderId))
|
.Where(i => preorders.Select(p => p.Id).Contains(i.PreOrderId))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
// Resolve product names
|
// Resolve product names
|
||||||
|
|
@ -54,7 +55,7 @@ public class CustomerPreorderController : BasePluginController
|
||||||
|
|
||||||
var rows = preorders.Select(p =>
|
var rows = preorders.Select(p =>
|
||||||
{
|
{
|
||||||
var items = allItems.Where(i => i.PreorderId == p.Id).ToList();
|
var items = allItems.Where(i => i.PreOrderId == p.Id).ToList();
|
||||||
|
|
||||||
// Derive status from quantities (enum reads unreliable in LinqToDB)
|
// Derive status from quantities (enum reads unreliable in LinqToDB)
|
||||||
var allFulfilled = items.Any() && items.All(i => i.FulfilledQuantity >= i.RequestedQuantity);
|
var allFulfilled = items.Any() && items.All(i => i.FulfilledQuantity >= i.RequestedQuantity);
|
||||||
|
|
@ -63,13 +64,13 @@ public class CustomerPreorderController : BasePluginController
|
||||||
i.RequestedQuantity > 0);
|
i.RequestedQuantity > 0);
|
||||||
|
|
||||||
var effectiveStatus = (int)p.Status != 0 ? p.Status
|
var effectiveStatus = (int)p.Status != 0 ? p.Status
|
||||||
: allFulfilled ? PreorderStatus.Confirmed
|
: allFulfilled ? PreOrderStatus.Confirmed
|
||||||
: anyFulfilled ? PreorderStatus.PartiallyFulfilled
|
: anyFulfilled ? PreOrderStatus.PartiallyFulfilled
|
||||||
: PreorderStatus.Pending;
|
: PreOrderStatus.Pending;
|
||||||
|
|
||||||
return new CustomerPreorderRow
|
return new CustomerPreOrderRow
|
||||||
{
|
{
|
||||||
PreorderId = p.Id,
|
PreOrderId = p.Id,
|
||||||
OrderId = p.OrderId,
|
OrderId = p.OrderId,
|
||||||
DateOfReceipt = p.DateOfReceipt,
|
DateOfReceipt = p.DateOfReceipt,
|
||||||
CreatedOnUtc = p.CreatedOnUtc,
|
CreatedOnUtc = p.CreatedOnUtc,
|
||||||
|
|
@ -78,7 +79,7 @@ public class CustomerPreorderController : BasePluginController
|
||||||
Items = items.Select(i =>
|
Items = items.Select(i =>
|
||||||
{
|
{
|
||||||
productById.TryGetValue(i.ProductId, out var dto);
|
productById.TryGetValue(i.ProductId, out var dto);
|
||||||
return new CustomerPreorderItemRow
|
return new CustomerPreOrderItemRow
|
||||||
{
|
{
|
||||||
ProductName = dto?.Name ?? $"Termék #{i.ProductId}",
|
ProductName = dto?.Name ?? $"Termék #{i.ProductId}",
|
||||||
IsMeasurable = dto?.IsMeasurable ?? false,
|
IsMeasurable = dto?.IsMeasurable ?? false,
|
||||||
|
|
@ -86,38 +87,38 @@ public class CustomerPreorderController : BasePluginController
|
||||||
FulfilledQuantity = i.FulfilledQuantity,
|
FulfilledQuantity = i.FulfilledQuantity,
|
||||||
UnitPriceInclTax = i.UnitPriceInclTax,
|
UnitPriceInclTax = i.UnitPriceInclTax,
|
||||||
Status = i.FulfilledQuantity == 0
|
Status = i.FulfilledQuantity == 0
|
||||||
? PreorderItemStatus.Pending
|
? PreOrderItemStatus.Pending
|
||||||
: i.FulfilledQuantity >= i.RequestedQuantity
|
: i.FulfilledQuantity >= i.RequestedQuantity
|
||||||
? PreorderItemStatus.Fulfilled
|
? PreOrderItemStatus.Fulfilled
|
||||||
: PreorderItemStatus.PartiallyFulfilled
|
: PreOrderItemStatus.PartiallyFulfilled
|
||||||
};
|
};
|
||||||
}).ToList()
|
}).ToList()
|
||||||
};
|
};
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Views/CustomerPreorder/List.cshtml", rows);
|
return View("~/Plugins/Misc.FruitBankPlugin/Views/CustomerPreOrder/List.cshtml", rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Inner models ──────────────────────────────────────────────────────────
|
// ── Inner models ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
public class CustomerPreorderRow
|
public class CustomerPreOrderRow
|
||||||
{
|
{
|
||||||
public int PreorderId { get; set; }
|
public int PreOrderId { get; set; }
|
||||||
public int? OrderId { get; set; }
|
public int? OrderId { get; set; }
|
||||||
public DateTime DateOfReceipt { get; set; }
|
public DateTime DateOfReceipt { get; set; }
|
||||||
public DateTime CreatedOnUtc { get; set; }
|
public DateTime CreatedOnUtc { get; set; }
|
||||||
public PreorderStatus Status { get; set; }
|
public PreOrderStatus Status { get; set; }
|
||||||
public string? CustomerNote { get; set; }
|
public string? CustomerNote { get; set; }
|
||||||
public List<CustomerPreorderItemRow> Items { get; set; } = new();
|
public List<CustomerPreOrderItemRow> Items { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CustomerPreorderItemRow
|
public class CustomerPreOrderItemRow
|
||||||
{
|
{
|
||||||
public string ProductName { get; set; } = string.Empty;
|
public string ProductName { get; set; } = string.Empty;
|
||||||
public bool IsMeasurable { get; set; }
|
public bool IsMeasurable { get; set; }
|
||||||
public int RequestedQuantity { get; set; }
|
public int RequestedQuantity { get; set; }
|
||||||
public int FulfilledQuantity { get; set; }
|
public int FulfilledQuantity { get; set; }
|
||||||
public decimal UnitPriceInclTax { get; set; }
|
public decimal UnitPriceInclTax { get; set; }
|
||||||
public PreorderItemStatus Status { get; set; }
|
public PreOrderItemStatus Status { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
ICustomerService customerService,
|
ICustomerService customerService,
|
||||||
ICustomerRegistrationService customerRegistrationService,
|
ICustomerRegistrationService customerRegistrationService,
|
||||||
ILocalizationService localizationService,
|
ILocalizationService localizationService,
|
||||||
PreorderConversionService preorderConversionService,
|
PreOrderConversionService preorderConversionService,
|
||||||
IEnumerable<IAcLogWriterBase> logWriters)
|
IEnumerable<IAcLogWriterBase> logWriters)
|
||||||
: BasePluginController, IFruitBankDataControllerServer
|
: BasePluginController, IFruitBankDataControllerServer
|
||||||
{
|
{
|
||||||
|
|
@ -187,7 +187,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
public async Task<CargoTruck> GetCargoTruckById(int id)
|
public async Task<CargoTruck> GetCargoTruckById(int id)
|
||||||
{
|
{
|
||||||
_logger.Detail($"GetCargoTruckById invoked; id: {id}");
|
_logger.Detail($"GetCargoTruckById invoked; id: {id}");
|
||||||
return await ctx.CargoTrucks.GetByIdAsync(id);
|
return await ctx.CargoTrucks.GetByIdAsync(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SignalR(SignalRTags.GetCargoTrucksByCargoPartnerId)]
|
||||||
|
public async Task<List<CargoTruck>> GetCargoTrucksByCargoPartnerId(int cargoPartnerId)
|
||||||
|
{
|
||||||
|
_logger.Detail($"GetCargoTrucksByCargoPartnerId invoked; cargoPartnerId: {cargoPartnerId}");
|
||||||
|
return await ctx.CargoTrucks.GetByCargoPartnerId(cargoPartnerId, true).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SignalR(SignalRTags.AddCargoTruck)]
|
[SignalR(SignalRTags.AddCargoTruck)]
|
||||||
|
|
@ -198,7 +205,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
_logger.Detail($"AddCargoTruck invoked; id: {cargoTruck.Id}");
|
_logger.Detail($"AddCargoTruck invoked; id: {cargoTruck.Id}");
|
||||||
|
|
||||||
await ctx.CargoTrucks.InsertAsync(cargoTruck);
|
await ctx.CargoTrucks.InsertAsync(cargoTruck);
|
||||||
return await ctx.CargoTrucks.GetByIdAsync(cargoTruck.Id);
|
return await ctx.CargoTrucks.GetByIdAsync(cargoTruck.Id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SignalR(SignalRTags.UpdateCargoTruck)]
|
[SignalR(SignalRTags.UpdateCargoTruck)]
|
||||||
|
|
@ -209,7 +216,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
_logger.Detail($"UpdateCargoTruck invoked; id: {cargoTruck.Id}");
|
_logger.Detail($"UpdateCargoTruck invoked; id: {cargoTruck.Id}");
|
||||||
|
|
||||||
await ctx.CargoTrucks.UpdateAsync(cargoTruck);
|
await ctx.CargoTrucks.UpdateAsync(cargoTruck);
|
||||||
return await ctx.CargoTrucks.GetByIdAsync(cargoTruck.Id);
|
return await ctx.CargoTrucks.GetByIdAsync(cargoTruck.Id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SignalR(SignalRTags.GetShippings)]
|
[SignalR(SignalRTags.GetShippings)]
|
||||||
|
|
@ -360,7 +367,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
// (EventConsumer also fires this, double-call is idempotent)
|
// (EventConsumer also fires this, double-call is idempotent)
|
||||||
if (shippingItem.QuantityOnDocument > oldItem.QuantityOnDocument)
|
if (shippingItem.QuantityOnDocument > oldItem.QuantityOnDocument)
|
||||||
_ = Task.Run(async () => await preorderConversionService
|
_ = Task.Run(async () => await preorderConversionService
|
||||||
.ConvertPreordersForProductsAsync(
|
.ConvertPreOrdersForProductsAsync(
|
||||||
new List<int> { shippingItem.ProductId.Value },
|
new List<int> { shippingItem.ProductId.Value },
|
||||||
shippingItem.ShippingDocumentId));
|
shippingItem.ShippingDocumentId));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ public class OrderController : BasePluginController
|
||||||
private readonly ICustomerService _customerService;
|
private readonly ICustomerService _customerService;
|
||||||
private readonly ILocalizationService _localizationService;
|
private readonly ILocalizationService _localizationService;
|
||||||
private readonly FruitBankDbContext _dbContext;
|
private readonly FruitBankDbContext _dbContext;
|
||||||
private readonly PreorderDbContext _preorderDbContext;
|
private readonly PreOrderDbContext _preorderDbContext;
|
||||||
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||||
private readonly CustomPriceCalculationService _customPriceCalculationService;
|
private readonly CustomPriceCalculationService _customPriceCalculationService;
|
||||||
private readonly IShoppingCartService _shoppingCartService;
|
private readonly IShoppingCartService _shoppingCartService;
|
||||||
private readonly IProductService _productService;
|
private readonly IProductService _productService;
|
||||||
private readonly OpenAIApiService _aiApiService;
|
private readonly OpenAIApiService _aiApiService;
|
||||||
private readonly CerebrasAPIService _cerebrasApiService;
|
private readonly CerebrasAPIService _cerebrasApiService;
|
||||||
private readonly PreorderConversionService _preorderConversionService;
|
private readonly PreOrderConversionService _preorderConversionService;
|
||||||
|
|
||||||
private const string PendingDeliveryKey = "OrderFlowPendingDeliveryDateTime";
|
private const string PendingDeliveryKey = "OrderFlowPendingDeliveryDateTime";
|
||||||
|
|
||||||
|
|
@ -45,14 +45,14 @@ public class OrderController : BasePluginController
|
||||||
ICustomerService customerService,
|
ICustomerService customerService,
|
||||||
ILocalizationService localizationService,
|
ILocalizationService localizationService,
|
||||||
FruitBankDbContext dbContext,
|
FruitBankDbContext dbContext,
|
||||||
PreorderDbContext preorderDbContext,
|
PreOrderDbContext preorderDbContext,
|
||||||
FruitBankAttributeService fruitBankAttributeService,
|
FruitBankAttributeService fruitBankAttributeService,
|
||||||
IPriceCalculationService priceCalculationService,
|
IPriceCalculationService priceCalculationService,
|
||||||
IShoppingCartService shoppingCartService,
|
IShoppingCartService shoppingCartService,
|
||||||
IProductService productService,
|
IProductService productService,
|
||||||
OpenAIApiService aiApiService,
|
OpenAIApiService aiApiService,
|
||||||
CerebrasAPIService cerebrasApiService,
|
CerebrasAPIService cerebrasApiService,
|
||||||
PreorderConversionService preorderConversionService)
|
PreOrderConversionService preorderConversionService)
|
||||||
{
|
{
|
||||||
_workContext = workContext;
|
_workContext = workContext;
|
||||||
_storeContext = storeContext;
|
_storeContext = storeContext;
|
||||||
|
|
@ -105,7 +105,7 @@ public class OrderController : BasePluginController
|
||||||
|
|
||||||
// Quick Order: delivery needs current stock (before Thursday)
|
// Quick Order: delivery needs current stock (before Thursday)
|
||||||
// OR goods already arrived (Thu-Sun) and delivery still this week
|
// OR goods already arrived (Thu-Sun) and delivery still this week
|
||||||
// Preorder: delivery is Thursday+ but today is still Mon/Tue/Wed (goods not yet here)
|
// PreOrder: delivery is Thursday+ but today is still Mon/Tue/Wed (goods not yet here)
|
||||||
return (deliveryBeforeThursday || (isLateWeek && deliveryThisWeek))
|
return (deliveryBeforeThursday || (isLateWeek && deliveryThisWeek))
|
||||||
? "quickorder"
|
? "quickorder"
|
||||||
: "preorder";
|
: "preorder";
|
||||||
|
|
@ -216,10 +216,10 @@ public class OrderController : BasePluginController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── PRODUCTS — Preorder flow (curated window list) ────────────────────────
|
// ── PRODUCTS — PreOrder flow (curated window list) ────────────────────────
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetPreorderProducts()
|
public async Task<IActionResult> GetPreOrderProducts()
|
||||||
{
|
{
|
||||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||||
if (await _customerService.IsGuestAsync(customer))
|
if (await _customerService.IsGuestAsync(customer))
|
||||||
|
|
@ -232,12 +232,12 @@ public class OrderController : BasePluginController
|
||||||
|
|
||||||
var gaStart = await _dbContext.GenericAttributes.Table
|
var gaStart = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowStart
|
&& ga.Key == FruitBankConst.PreOrderWindowStart
|
||||||
&& ga.StoreId == store.Id)
|
&& ga.StoreId == store.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
var gaEnd = await _dbContext.GenericAttributes.Table
|
var gaEnd = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowEnd
|
&& ga.Key == FruitBankConst.PreOrderWindowEnd
|
||||||
&& ga.StoreId == store.Id)
|
&& ga.StoreId == store.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
@ -403,10 +403,10 @@ public class OrderController : BasePluginController
|
||||||
return Json(new { success = true, cartItems = await GetCartItemsJson(customer, store) });
|
return Json(new { success = true, cartItems = await GetCartItemsJson(customer, store) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── PLACE PREORDER (Preorder flow) ────────────────────────────────────────
|
// ── PLACE PREORDER (PreOrder flow) ────────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> PlacePreorder([FromBody] PlacePreorderRequest request)
|
public async Task<IActionResult> PlacePreOrder([FromBody] PlacePreOrderRequest request)
|
||||||
{
|
{
|
||||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||||
if (await _customerService.IsGuestAsync(customer))
|
if (await _customerService.IsGuestAsync(customer))
|
||||||
|
|
@ -422,7 +422,7 @@ public class OrderController : BasePluginController
|
||||||
{
|
{
|
||||||
var store = await _storeContext.GetCurrentStoreAsync();
|
var store = await _storeContext.GetCurrentStoreAsync();
|
||||||
|
|
||||||
var preorder = new Preorder
|
var preorder = new PreOrder
|
||||||
{
|
{
|
||||||
CustomerId = customer.Id,
|
CustomerId = customer.Id,
|
||||||
StoreId = store.Id,
|
StoreId = store.Id,
|
||||||
|
|
@ -430,7 +430,7 @@ public class OrderController : BasePluginController
|
||||||
CustomerNote = request.CustomerNote?.Trim()
|
CustomerNote = request.CustomerNote?.Trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
var items = new List<PreorderItem>();
|
var items = new List<PreOrderItem>();
|
||||||
foreach (var req in request.Items.Where(i => i.Quantity > 0))
|
foreach (var req in request.Items.Where(i => i.Quantity > 0))
|
||||||
{
|
{
|
||||||
var product = await _dbContext.Products.GetByIdAsync(req.ProductId);
|
var product = await _dbContext.Products.GetByIdAsync(req.ProductId);
|
||||||
|
|
@ -444,7 +444,7 @@ public class OrderController : BasePluginController
|
||||||
unitPrice = pr.finalPrice;
|
unitPrice = pr.finalPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
items.Add(new PreorderItem
|
items.Add(new PreOrderItem
|
||||||
{
|
{
|
||||||
ProductId = req.ProductId,
|
ProductId = req.ProductId,
|
||||||
RequestedQuantity = req.Quantity,
|
RequestedQuantity = req.Quantity,
|
||||||
|
|
@ -455,7 +455,7 @@ public class OrderController : BasePluginController
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return Json(new { success = false, message = "Nincs érvényes termék az előrendelésben" });
|
return Json(new { success = false, message = "Nincs érvényes termék az előrendelésben" });
|
||||||
|
|
||||||
var saved = await _preorderDbContext.InsertPreorderAsync(preorder, items);
|
var saved = await _preorderDbContext.InsertPreOrderAsync(preorder, items);
|
||||||
|
|
||||||
// Clean up the pending datetime attribute
|
// Clean up the pending datetime attribute
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
|
|
@ -466,12 +466,12 @@ public class OrderController : BasePluginController
|
||||||
// Awaited inline (not fire-and-forget) so we can return the order ID if one is created.
|
// Awaited inline (not fire-and-forget) so we can return the order ID if one is created.
|
||||||
// shippingDocumentId = 0 signals this was triggered at preorder placement, not by a document.
|
// shippingDocumentId = 0 signals this was triggered at preorder placement, not by a document.
|
||||||
var productIds = items.Select(i => i.ProductId).Distinct().ToList();
|
var productIds = items.Select(i => i.ProductId).Distinct().ToList();
|
||||||
await _preorderConversionService.ConvertPreordersForProductsAsync(productIds, 0);
|
await _preorderConversionService.ConvertPreOrdersForProductsAsync(productIds, 0);
|
||||||
|
|
||||||
// Re-read to pick up OrderId if conversion created a real order
|
// Re-read to pick up OrderId if conversion created a real order
|
||||||
var refreshed = await _preorderDbContext.Preorders.GetByIdAsync(saved.Id);
|
var refreshed = await _preorderDbContext.PreOrders.GetByIdAsync(saved.Id);
|
||||||
|
|
||||||
Console.WriteLine($"[OrderFlow] PlacePreorder #{saved.Id} — orderId={refreshed?.OrderId}");
|
Console.WriteLine($"[OrderFlow] PlacePreOrder #{saved.Id} — orderId={refreshed?.OrderId}");
|
||||||
|
|
||||||
return Json(new
|
return Json(new
|
||||||
{
|
{
|
||||||
|
|
@ -607,14 +607,14 @@ OUTPUT FORMAT: [{""product"": ""narancs"", ""quantity"": 100}]";
|
||||||
|
|
||||||
// ── Inner models ──────────────────────────────────────────────────────────
|
// ── Inner models ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
public class PlacePreorderRequest
|
public class PlacePreOrderRequest
|
||||||
{
|
{
|
||||||
public string? DeliveryDateTime { get; set; }
|
public string? DeliveryDateTime { get; set; }
|
||||||
public string? CustomerNote { get; set; }
|
public string? CustomerNote { get; set; }
|
||||||
public List<PreorderItemRequest> Items { get; set; } = new();
|
public List<PreOrderItemRequest> Items { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PreorderItemRequest
|
public class PreOrderItemRequest
|
||||||
{
|
{
|
||||||
public int ProductId { get; set; }
|
public int ProductId { get; set; }
|
||||||
public int Quantity { get; set; }
|
public int Quantity { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -10,31 +10,32 @@ using Nop.Services.Catalog;
|
||||||
using Nop.Services.Customers;
|
using Nop.Services.Customers;
|
||||||
using Nop.Services.Localization;
|
using Nop.Services.Localization;
|
||||||
using Nop.Web.Framework.Controllers;
|
using Nop.Web.Framework.Controllers;
|
||||||
|
using static Nop.Plugin.Misc.FruitBankPlugin.Controllers.OrderController;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers;
|
||||||
|
|
||||||
[AutoValidateAntiforgeryToken]
|
[AutoValidateAntiforgeryToken]
|
||||||
public class PreorderController : BasePluginController
|
public class PreOrderController : BasePluginController
|
||||||
{
|
{
|
||||||
private readonly IWorkContext _workContext;
|
private readonly IWorkContext _workContext;
|
||||||
private readonly IStoreContext _storeContext;
|
private readonly IStoreContext _storeContext;
|
||||||
private readonly ICustomerService _customerService;
|
private readonly ICustomerService _customerService;
|
||||||
private readonly ILocalizationService _localizationService;
|
private readonly ILocalizationService _localizationService;
|
||||||
private readonly FruitBankDbContext _dbContext;
|
private readonly FruitBankDbContext _dbContext;
|
||||||
private readonly PreorderDbContext _preorderDbContext;
|
private readonly PreOrderDbContext _preorderDbContext;
|
||||||
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||||
private readonly CustomPriceCalculationService _customPriceCalculationService;
|
private readonly CustomPriceCalculationService _customPriceCalculationService;
|
||||||
|
|
||||||
private const string PendingDeliveryDateTimeKey = "PreorderPendingDeliveryDateTime";
|
private const string PendingDeliveryDateTimeKey = "PreOrderPendingDeliveryDateTime";
|
||||||
private const string Prefix = "Plugins.Misc.FruitBankPlugin.Preorder.";
|
private const string Prefix = "Plugins.Misc.FruitBankPlugin.PreOrder.";
|
||||||
|
|
||||||
public PreorderController(
|
public PreOrderController(
|
||||||
IWorkContext workContext,
|
IWorkContext workContext,
|
||||||
IStoreContext storeContext,
|
IStoreContext storeContext,
|
||||||
ICustomerService customerService,
|
ICustomerService customerService,
|
||||||
ILocalizationService localizationService,
|
ILocalizationService localizationService,
|
||||||
FruitBankDbContext dbContext,
|
FruitBankDbContext dbContext,
|
||||||
PreorderDbContext preorderDbContext,
|
PreOrderDbContext preorderDbContext,
|
||||||
FruitBankAttributeService fruitBankAttributeService,
|
FruitBankAttributeService fruitBankAttributeService,
|
||||||
IPriceCalculationService priceCalculationService)
|
IPriceCalculationService priceCalculationService)
|
||||||
{
|
{
|
||||||
|
|
@ -60,7 +61,7 @@ public class PreorderController : BasePluginController
|
||||||
if (await _customerService.IsGuestAsync(customer))
|
if (await _customerService.IsGuestAsync(customer))
|
||||||
return Challenge();
|
return Challenge();
|
||||||
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Views/Preorder/Index.cshtml");
|
return View("~/Plugins/Misc.FruitBankPlugin/Views/PreOrder/Index.cshtml");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── GET SAVED DELIVERY DATETIME (page restore) ────────────────────────────
|
// ── GET SAVED DELIVERY DATETIME (page restore) ────────────────────────────
|
||||||
|
|
@ -129,13 +130,13 @@ public class PreorderController : BasePluginController
|
||||||
// Load preorder window generic attributes — two queries, no N+1
|
// Load preorder window generic attributes — two queries, no N+1
|
||||||
var gaStart = await _dbContext.GenericAttributes.Table
|
var gaStart = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowStart
|
&& ga.Key == FruitBankConst.PreOrderWindowStart
|
||||||
&& ga.StoreId == store.Id)
|
&& ga.StoreId == store.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var gaEnd = await _dbContext.GenericAttributes.Table
|
var gaEnd = await _dbContext.GenericAttributes.Table
|
||||||
.Where(ga => ga.KeyGroup == nameof(Product)
|
.Where(ga => ga.KeyGroup == nameof(Product)
|
||||||
&& ga.Key == FruitBankConst.PreorderWindowEnd
|
&& ga.Key == FruitBankConst.PreOrderWindowEnd
|
||||||
&& ga.StoreId == store.Id)
|
&& ga.StoreId == store.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
@ -192,7 +193,7 @@ public class PreorderController : BasePluginController
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[Preorder] GetAvailableProducts error: {ex.Message}");
|
Console.WriteLine($"[PreOrder] GetAvailableProducts error: {ex.Message}");
|
||||||
return Json(new { success = false, message = $"Hiba: {ex.Message}" });
|
return Json(new { success = false, message = $"Hiba: {ex.Message}" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +201,7 @@ public class PreorderController : BasePluginController
|
||||||
// ── PLACE PREORDER ────────────────────────────────────────────────────────
|
// ── PLACE PREORDER ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> PlacePreorder([FromBody] PlacePreorderRequest request)
|
public async Task<IActionResult> PlacePreOrder([FromBody] PlacePreOrderRequest request)
|
||||||
{
|
{
|
||||||
var customer = await _workContext.GetCurrentCustomerAsync();
|
var customer = await _workContext.GetCurrentCustomerAsync();
|
||||||
if (await _customerService.IsGuestAsync(customer))
|
if (await _customerService.IsGuestAsync(customer))
|
||||||
|
|
@ -216,7 +217,7 @@ public class PreorderController : BasePluginController
|
||||||
{
|
{
|
||||||
var store = await _storeContext.GetCurrentStoreAsync();
|
var store = await _storeContext.GetCurrentStoreAsync();
|
||||||
|
|
||||||
var preorder = new Preorder
|
var preorder = new PreOrder
|
||||||
{
|
{
|
||||||
CustomerId = customer.Id,
|
CustomerId = customer.Id,
|
||||||
StoreId = store.Id,
|
StoreId = store.Id,
|
||||||
|
|
@ -224,7 +225,7 @@ public class PreorderController : BasePluginController
|
||||||
CustomerNote = request.CustomerNote?.Trim()
|
CustomerNote = request.CustomerNote?.Trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
var items = new List<PreorderItem>();
|
var items = new List<PreOrderItem>();
|
||||||
foreach (var req in request.Items.Where(i => i.Quantity > 0))
|
foreach (var req in request.Items.Where(i => i.Quantity > 0))
|
||||||
{
|
{
|
||||||
var product = await _dbContext.Products.GetByIdAsync(req.ProductId);
|
var product = await _dbContext.Products.GetByIdAsync(req.ProductId);
|
||||||
|
|
@ -239,7 +240,7 @@ public class PreorderController : BasePluginController
|
||||||
unitPrice = pr.finalPrice;
|
unitPrice = pr.finalPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
items.Add(new PreorderItem
|
items.Add(new PreOrderItem
|
||||||
{
|
{
|
||||||
ProductId = req.ProductId,
|
ProductId = req.ProductId,
|
||||||
RequestedQuantity = req.Quantity,
|
RequestedQuantity = req.Quantity,
|
||||||
|
|
@ -250,14 +251,14 @@ public class PreorderController : BasePluginController
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return Json(new { success = false, message = await L("NoValidItems") });
|
return Json(new { success = false, message = await L("NoValidItems") });
|
||||||
|
|
||||||
var saved = await _preorderDbContext.InsertPreorderAsync(preorder, items);
|
var saved = await _preorderDbContext.InsertPreOrderAsync(preorder, items);
|
||||||
|
|
||||||
// Clean up the pending delivery datetime attribute
|
// Clean up the pending delivery datetime attribute
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
.DeleteGenericAttributeAsync<Nop.Core.Domain.Customers.Customer>(
|
.DeleteGenericAttributeAsync<Nop.Core.Domain.Customers.Customer>(
|
||||||
customer.Id, PendingDeliveryDateTimeKey, store.Id);
|
customer.Id, PendingDeliveryDateTimeKey, store.Id);
|
||||||
|
|
||||||
Console.WriteLine($"[Preorder] Placed #{saved.Id} — customer #{customer.Id}, {items.Count} items, delivery {deliveryDateTime:u}");
|
Console.WriteLine($"[PreOrder] Placed #{saved.Id} — customer #{customer.Id}, {items.Count} items, delivery {deliveryDateTime:u}");
|
||||||
|
|
||||||
return Json(new
|
return Json(new
|
||||||
{
|
{
|
||||||
|
|
@ -268,21 +269,21 @@ public class PreorderController : BasePluginController
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[Preorder] PlacePreorder error: {ex.Message}");
|
Console.WriteLine($"[PreOrder] PlacePreOrder error: {ex.Message}");
|
||||||
return Json(new { success = false, message = $"Hiba: {ex.Message}" });
|
return Json(new { success = false, message = $"Hiba: {ex.Message}" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── INNER MODELS ──────────────────────────────────────────────────────────
|
// ── INNER MODELS ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
public class PlacePreorderRequest
|
public class PlacePreOrderRequest
|
||||||
{
|
{
|
||||||
public string? DeliveryDateTime { get; set; }
|
public string? DeliveryDateTime { get; set; }
|
||||||
public string? CustomerNote { get; set; }
|
public string? CustomerNote { get; set; }
|
||||||
public List<PreorderItemRequest> Items { get; set; } = new();
|
public List<PreOrderItemRequest> Items { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PreorderItemRequest
|
public class PreOrderItemRequest
|
||||||
{
|
{
|
||||||
public int ProductId { get; set; }
|
public int ProductId { get; set; }
|
||||||
public int Quantity { get; set; }
|
public int Quantity { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using FruitBank.Common.Entities;
|
using FruitBank.Common.Entities;
|
||||||
|
using LinqToDB;
|
||||||
using Mango.Nop.Data.Repositories;
|
using Mango.Nop.Data.Repositories;
|
||||||
using Nop.Core.Caching;
|
using Nop.Core.Caching;
|
||||||
using Nop.Core.Configuration;
|
using Nop.Core.Configuration;
|
||||||
|
|
@ -18,9 +19,9 @@ public class CargoTruckDbTable : MgDbTableBase<CargoTruck>
|
||||||
|
|
||||||
public IQueryable<CargoTruck> GetAll(bool loadRelations)
|
public IQueryable<CargoTruck> GetAll(bool loadRelations)
|
||||||
{
|
{
|
||||||
return GetAll();
|
return loadRelations ? GetAll().LoadWith(sd => sd.CargoPartner) : GetAll();
|
||||||
//return loadRelations ? GetAll().LoadWith(sd => sd.CargoTrucks) : GetAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<CargoTruck> GetByIdAsync(int id, bool loadRelations) => GetAll(loadRelations).FirstOrDefaultAsync(p => p.Id == id);
|
public Task<CargoTruck> GetByIdAsync(int id, bool loadRelations) => GetAll(loadRelations).FirstOrDefaultAsync(p => p.Id == id);
|
||||||
|
public IQueryable<CargoTruck> GetByCargoPartnerId(int cargoPartnerId, bool loadRelations) => GetAll(loadRelations).Where(p => p.CargoPartnerId == cargoPartnerId);
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ using Nop.Data;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces;
|
||||||
|
|
||||||
public interface IPreorderDbSet<TDbTable> : IMgDbTableBase where TDbTable : IRepository<Preorder>
|
public interface IPreOrderDbSet<TDbTable> : IMgDbTableBase where TDbTable : IRepository<PreOrder>
|
||||||
{
|
{
|
||||||
public TDbTable Preorders { get; set; }
|
public TDbTable PreOrders { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using Nop.Data;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces;
|
||||||
|
|
||||||
public interface IPreorderItemDbSet<TDbTable> : IMgDbTableBase where TDbTable : IRepository<PreorderItem>
|
public interface IPreOrderItemDbSet<TDbTable> : IMgDbTableBase where TDbTable : IRepository<PreOrderItem>
|
||||||
{
|
{
|
||||||
public TDbTable PreorderItems { get; set; }
|
public TDbTable PreOrderItems { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,14 @@ using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||||
|
|
||||||
public class PreorderDbContext :
|
public class PreOrderDbContext :
|
||||||
IPreorderDbSet<PreorderDbTable>,
|
IPreOrderDbSet<PreOrderDbTable>,
|
||||||
IPreorderItemDbSet<PreorderItemDbTable>
|
IPreOrderItemDbSet<PreOrderItemDbTable>
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public PreorderDbTable Preorders { get; set; }
|
public PreOrderDbTable PreOrders { get; set; }
|
||||||
public PreorderItemDbTable PreorderItems { get; set; }
|
public PreOrderItemDbTable PreOrderItems { get; set; }
|
||||||
|
|
||||||
// Read-only access to related NopCommerce repositories needed during conversion
|
// Read-only access to related NopCommerce repositories needed during conversion
|
||||||
public IRepository<Customer> Customers { get; set; }
|
public IRepository<Customer> Customers { get; set; }
|
||||||
|
|
@ -30,112 +30,112 @@ public class PreorderDbContext :
|
||||||
public IRepository<Order> Orders { get; set; }
|
public IRepository<Order> Orders { get; set; }
|
||||||
public IRepository<OrderItem> OrderItems { get; set; }
|
public IRepository<OrderItem> OrderItems { get; set; }
|
||||||
|
|
||||||
public PreorderDbContext(
|
public PreOrderDbContext(
|
||||||
PreorderDbTable preorderDbTable,
|
PreOrderDbTable preorderDbTable,
|
||||||
PreorderItemDbTable preorderItemDbTable,
|
PreOrderItemDbTable preorderItemDbTable,
|
||||||
IRepository<Customer> customerRepository,
|
IRepository<Customer> customerRepository,
|
||||||
IRepository<Product> productRepository,
|
IRepository<Product> productRepository,
|
||||||
IRepository<Order> orderRepository,
|
IRepository<Order> orderRepository,
|
||||||
IRepository<OrderItem> orderItemRepository,
|
IRepository<OrderItem> orderItemRepository,
|
||||||
IEnumerable<IAcLogWriterBase> logWriters)
|
IEnumerable<IAcLogWriterBase> logWriters)
|
||||||
{
|
{
|
||||||
Preorders = preorderDbTable;
|
PreOrders = preorderDbTable;
|
||||||
PreorderItems = preorderItemDbTable;
|
PreOrderItems = preorderItemDbTable;
|
||||||
Customers = customerRepository;
|
Customers = customerRepository;
|
||||||
Products = productRepository;
|
Products = productRepository;
|
||||||
Orders = orderRepository;
|
Orders = orderRepository;
|
||||||
OrderItems = orderItemRepository;
|
OrderItems = orderItemRepository;
|
||||||
_logger = new Logger<PreorderDbContext>(logWriters.ToArray());
|
_logger = new Logger<PreOrderDbContext>(logWriters.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Insert a complete preorder with all its items in one operation.
|
/// Insert a complete preorder with all its items in one operation.
|
||||||
/// Returns the saved preorder (with Id populated).
|
/// Returns the saved preorder (with Id populated).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<Preorder> InsertPreorderAsync(Preorder preorder, IList<PreorderItem> items)
|
public async Task<PreOrder> InsertPreOrderAsync(PreOrder preorder, IList<PreOrderItem> items)
|
||||||
{
|
{
|
||||||
preorder.CreatedOnUtc = DateTime.UtcNow;
|
preorder.CreatedOnUtc = DateTime.UtcNow;
|
||||||
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
||||||
preorder.Status = PreorderStatus.Pending;
|
preorder.Status = PreOrderStatus.Pending;
|
||||||
|
|
||||||
await Preorders.InsertAsync(preorder);
|
await PreOrders.InsertAsync(preorder);
|
||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
item.PreorderId = preorder.Id;
|
item.PreOrderId = preorder.Id;
|
||||||
item.FulfilledQuantity = 0;
|
item.FulfilledQuantity = 0;
|
||||||
item.Status = PreorderItemStatus.Pending;
|
item.Status = PreOrderItemStatus.Pending;
|
||||||
await PreorderItems.InsertAsync(item);
|
await PreOrderItems.InsertAsync(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Info($"PreorderDbContext: inserted Preorder #{preorder.Id} with {items.Count} items for customer #{preorder.CustomerId}");
|
_logger.Info($"PreOrderDbContext: inserted PreOrder #{preorder.Id} with {items.Count} items for customer #{preorder.CustomerId}");
|
||||||
return preorder;
|
return preorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all pending preorder items for a set of productIds, ordered by PreorderId (FCFS).
|
/// Returns all pending preorder items for a set of productIds, ordered by PreOrderId (FCFS).
|
||||||
/// Used by PreorderConversionService after IncomingQuantity is written.
|
/// Used by PreOrderConversionService after IncomingQuantity is written.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<PreorderItem>> GetPendingItemsForProductsAsync(IList<int> productIds)
|
public async Task<List<PreOrderItem>> GetPendingItemsForProductsAsync(IList<int> productIds)
|
||||||
{
|
{
|
||||||
// Fetch all items for these products first, then filter by status in memory
|
// Fetch all items for these products first, then filter by status in memory
|
||||||
// LinqToDB cannot translate enum comparisons to SQL in this codebase
|
// LinqToDB cannot translate enum comparisons to SQL in this codebase
|
||||||
var all = await PreorderItems.Table
|
var all = await PreOrderItems.Table
|
||||||
.Where(i => productIds.Contains(i.ProductId))
|
.Where(i => productIds.Contains(i.ProductId))
|
||||||
.OrderBy(i => i.PreorderId)
|
.OrderBy(i => i.PreOrderId)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return all.Where(i =>
|
return all.Where(i =>
|
||||||
i.Status == PreorderItemStatus.Pending ||
|
i.Status == PreOrderItemStatus.Pending ||
|
||||||
i.Status == PreorderItemStatus.PartiallyFulfilled)
|
i.Status == PreOrderItemStatus.PartiallyFulfilled)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// After conversion: check if all items in a preorder are resolved and update the preorder's status.
|
/// After conversion: check if all items in a preorder are resolved and update the preorder's status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task RefreshPreorderStatusAsync(int preorderId)
|
public async Task RefreshPreOrderStatusAsync(int preorderId)
|
||||||
{
|
{
|
||||||
var preorder = await Preorders.GetByIdAsync(preorderId);
|
var preorder = await PreOrders.GetByIdAsync(preorderId);
|
||||||
if (preorder == null) return;
|
if (preorder == null) return;
|
||||||
|
|
||||||
var items = await PreorderItems.GetAllByPreorderIdAsync(preorderId).ToListAsync();
|
var items = await PreOrderItems.GetAllByPreOrderIdAsync(preorderId).ToListAsync();
|
||||||
|
|
||||||
var hasDropped = items.Any(i => i.Status == PreorderItemStatus.Dropped);
|
var hasDropped = items.Any(i => i.Status == PreOrderItemStatus.Dropped);
|
||||||
var hasPartial = items.Any(i => i.Status == PreorderItemStatus.PartiallyFulfilled);
|
var hasPartial = items.Any(i => i.Status == PreOrderItemStatus.PartiallyFulfilled);
|
||||||
var hasPending = items.Any(i => i.Status == PreorderItemStatus.Pending);
|
var hasPending = items.Any(i => i.Status == PreOrderItemStatus.Pending);
|
||||||
var allFulfilled = items.All(i => i.Status == PreorderItemStatus.Fulfilled);
|
var allFulfilled = items.All(i => i.Status == PreOrderItemStatus.Fulfilled);
|
||||||
|
|
||||||
preorder.Status = (hasDropped || hasPartial) && !hasPending ? PreorderStatus.PartiallyFulfilled
|
preorder.Status = (hasDropped || hasPartial) && !hasPending ? PreOrderStatus.PartiallyFulfilled
|
||||||
: allFulfilled ? PreorderStatus.Confirmed
|
: allFulfilled ? PreOrderStatus.Confirmed
|
||||||
: PreorderStatus.Pending;
|
: PreOrderStatus.Pending;
|
||||||
|
|
||||||
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
||||||
await Preorders.UpdateAsync(preorder);
|
await PreOrders.UpdateAsync(preorder);
|
||||||
|
|
||||||
_logger.Info($"PreorderDbContext: Preorder #{preorderId} status → {preorder.Status}");
|
_logger.Info($"PreOrderDbContext: PreOrder #{preorderId} status → {preorder.Status}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Mark a preorder as cancelled (customer or admin action).
|
/// Mark a preorder as cancelled (customer or admin action).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task CancelPreorderAsync(int preorderId)
|
public async Task CancelPreOrderAsync(int preorderId)
|
||||||
{
|
{
|
||||||
var preorder = await Preorders.GetByIdAsync(preorderId);
|
var preorder = await PreOrders.GetByIdAsync(preorderId);
|
||||||
if (preorder == null) return;
|
if (preorder == null) return;
|
||||||
|
|
||||||
preorder.Status = PreorderStatus.Cancelled;
|
preorder.Status = PreOrderStatus.Cancelled;
|
||||||
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
||||||
await Preorders.UpdateAsync(preorder);
|
await PreOrders.UpdateAsync(preorder);
|
||||||
|
|
||||||
var items = await PreorderItems.GetAllByPreorderIdAsync(preorderId).ToListAsync();
|
var items = await PreOrderItems.GetAllByPreOrderIdAsync(preorderId).ToListAsync();
|
||||||
var cancellableStatuses = new[] { PreorderItemStatus.Pending, PreorderItemStatus.PartiallyFulfilled };
|
var cancellableStatuses = new[] { PreOrderItemStatus.Pending, PreOrderItemStatus.PartiallyFulfilled };
|
||||||
foreach (var item in items.Where(i => cancellableStatuses.Contains(i.Status)))
|
foreach (var item in items.Where(i => cancellableStatuses.Contains(i.Status)))
|
||||||
{
|
{
|
||||||
item.Status = PreorderItemStatus.Dropped;
|
item.Status = PreOrderItemStatus.Dropped;
|
||||||
await PreorderItems.UpdateAsync(item);
|
await PreOrderItems.UpdateAsync(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Info($"PreorderDbContext: Preorder #{preorderId} cancelled");
|
_logger.Info($"PreOrderDbContext: PreOrder #{preorderId} cancelled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ using Nop.Data;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||||
|
|
||||||
public class PreorderDbTable : MgDbTableBase<Preorder>
|
public class PreOrderDbTable : MgDbTableBase<PreOrder>
|
||||||
{
|
{
|
||||||
public PreorderDbTable(
|
public PreOrderDbTable(
|
||||||
IEventPublisher eventPublisher,
|
IEventPublisher eventPublisher,
|
||||||
INopDataProvider dataProvider,
|
INopDataProvider dataProvider,
|
||||||
IShortTermCacheManager shortTermCacheManager,
|
IShortTermCacheManager shortTermCacheManager,
|
||||||
|
|
@ -22,23 +22,23 @@ public class PreorderDbTable : MgDbTableBase<Preorder>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQueryable<Preorder> GetAll(bool loadRelations)
|
public IQueryable<PreOrder> GetAll(bool loadRelations)
|
||||||
{
|
{
|
||||||
return loadRelations
|
return loadRelations
|
||||||
? GetAll()
|
? GetAll()
|
||||||
.LoadWith(p => p.PreorderItems)
|
.LoadWith(p => p.PreOrderItems)
|
||||||
: GetAll();
|
: GetAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Preorder?> GetByIdAsync(int id, bool loadRelations)
|
public Task<PreOrder?> GetByIdAsync(int id, bool loadRelations)
|
||||||
=> GetAll(loadRelations).FirstOrDefaultAsync(p => p.Id == id);
|
=> GetAll(loadRelations).FirstOrDefaultAsync(p => p.Id == id);
|
||||||
|
|
||||||
public IQueryable<Preorder> GetAllByCustomerIdAsync(int customerId, bool loadRelations)
|
public IQueryable<PreOrder> GetAllByCustomerIdAsync(int customerId, bool loadRelations)
|
||||||
=> GetAll(loadRelations).Where(p => p.CustomerId == customerId);
|
=> GetAll(loadRelations).Where(p => p.CustomerId == customerId);
|
||||||
|
|
||||||
public IQueryable<Preorder> GetAllPendingAsync(bool loadRelations)
|
public IQueryable<PreOrder> GetAllPendingAsync(bool loadRelations)
|
||||||
{
|
{
|
||||||
var pendingStatuses = new[] { PreorderStatus.Pending, PreorderStatus.PartiallyFulfilled };
|
var pendingStatuses = new[] { PreOrderStatus.Pending, PreOrderStatus.PartiallyFulfilled };
|
||||||
return GetAll(loadRelations).Where(p => pendingStatuses.Contains(p.Status));
|
return GetAll(loadRelations).Where(p => pendingStatuses.Contains(p.Status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ using Nop.Data;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||||
|
|
||||||
public class PreorderItemDbTable : MgDbTableBase<PreorderItem>
|
public class PreOrderItemDbTable : MgDbTableBase<PreOrderItem>
|
||||||
{
|
{
|
||||||
public PreorderItemDbTable(
|
public PreOrderItemDbTable(
|
||||||
IEventPublisher eventPublisher,
|
IEventPublisher eventPublisher,
|
||||||
INopDataProvider dataProvider,
|
INopDataProvider dataProvider,
|
||||||
IShortTermCacheManager shortTermCacheManager,
|
IShortTermCacheManager shortTermCacheManager,
|
||||||
|
|
@ -22,21 +22,21 @@ public class PreorderItemDbTable : MgDbTableBase<PreorderItem>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQueryable<PreorderItem> GetAllByPreorderIdAsync(int preorderId)
|
public IQueryable<PreOrderItem> GetAllByPreOrderIdAsync(int preorderId)
|
||||||
=> GetAll().Where(i => i.PreorderId == preorderId);
|
=> GetAll().Where(i => i.PreOrderId == preorderId);
|
||||||
|
|
||||||
public IQueryable<PreorderItem> GetAllByProductIdAsync(int productId)
|
public IQueryable<PreOrderItem> GetAllByProductIdAsync(int productId)
|
||||||
=> GetAll().Where(i => i.ProductId == productId);
|
=> GetAll().Where(i => i.ProductId == productId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All pending/partially-fulfilled items for a product, ordered by their parent preorder's
|
/// All pending/partially-fulfilled items for a product, ordered by their parent preorder's
|
||||||
/// CreatedOnUtc for first-come-first-served allocation.
|
/// CreatedOnUtc for first-come-first-served allocation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IQueryable<PreorderItem> GetPendingByProductIdOrderedAsync(int productId)
|
public IQueryable<PreOrderItem> GetPendingByProductIdOrderedAsync(int productId)
|
||||||
{
|
{
|
||||||
var pendingStatuses = new[] { PreorderItemStatus.Pending, PreorderItemStatus.PartiallyFulfilled };
|
var pendingStatuses = new[] { PreOrderItemStatus.Pending, PreOrderItemStatus.PartiallyFulfilled };
|
||||||
return GetAll()
|
return GetAll()
|
||||||
.Where(i => i.ProductId == productId && pendingStatuses.Contains(i.Status))
|
.Where(i => i.ProductId == productId && pendingStatuses.Contains(i.Status))
|
||||||
.OrderBy(i => i.PreorderId);
|
.OrderBy(i => i.PreOrderId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,11 @@ public class FruitBankEventConsumer :
|
||||||
private readonly FruitBankDbContext _ctx;
|
private readonly FruitBankDbContext _ctx;
|
||||||
private readonly MeasurementService _measurementService;
|
private readonly MeasurementService _measurementService;
|
||||||
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||||
private readonly PreorderConversionService _preorderConversionService;
|
private readonly PreOrderConversionService _preorderConversionService;
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
|
|
||||||
public FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBankDbContext ctx, MeasurementService measurementService,
|
public FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBankDbContext ctx, MeasurementService measurementService,
|
||||||
FruitBankAttributeService fruitBankAttributeService, PreorderConversionService preorderConversionService,
|
FruitBankAttributeService fruitBankAttributeService, PreOrderConversionService preorderConversionService,
|
||||||
IServiceScopeFactory serviceScopeFactory, IEnumerable<IAcLogWriterBase> logWriters) : base(ctx, httpContextAcc, logWriters)
|
IServiceScopeFactory serviceScopeFactory, IEnumerable<IAcLogWriterBase> logWriters) : base(ctx, httpContextAcc, logWriters)
|
||||||
{
|
{
|
||||||
_ctx = ctx;
|
_ctx = ctx;
|
||||||
|
|
@ -212,9 +212,9 @@ public class FruitBankEventConsumer :
|
||||||
System.Transactions.TransactionScopeOption.Suppress,
|
System.Transactions.TransactionScopeOption.Suppress,
|
||||||
System.Transactions.TransactionScopeAsyncFlowOption.Enabled);
|
System.Transactions.TransactionScopeAsyncFlowOption.Enabled);
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
var conversion = scope.ServiceProvider.GetRequiredService<PreorderConversionService>();
|
var conversion = scope.ServiceProvider.GetRequiredService<PreOrderConversionService>();
|
||||||
try { await conversion.ConvertPreordersForProductsAsync(new List<int> { item.ProductId.Value }, item.ShippingDocumentId); }
|
try { await conversion.ConvertPreOrdersForProductsAsync(new List<int> { item.ProductId.Value }, item.ShippingDocumentId); }
|
||||||
catch (Exception ex) { Logger.Error($"[FruitBankEventConsumer] Preorder conversion failed for ProductId={item.ProductId}: {ex.Message}", ex); }
|
catch (Exception ex) { Logger.Error($"[FruitBankEventConsumer] PreOrder conversion failed for ProductId={item.ProductId}: {ex.Message}", ex); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -238,9 +238,9 @@ public class FruitBankEventConsumer :
|
||||||
System.Transactions.TransactionScopeOption.Suppress,
|
System.Transactions.TransactionScopeOption.Suppress,
|
||||||
System.Transactions.TransactionScopeAsyncFlowOption.Enabled);
|
System.Transactions.TransactionScopeAsyncFlowOption.Enabled);
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
var conversion = scope.ServiceProvider.GetRequiredService<PreorderConversionService>();
|
var conversion = scope.ServiceProvider.GetRequiredService<PreOrderConversionService>();
|
||||||
try { await conversion.ConvertPreordersForProductsAsync(new List<int> { shippingItem.ProductId.Value }, shippingItem.ShippingDocumentId); }
|
try { await conversion.ConvertPreOrdersForProductsAsync(new List<int> { shippingItem.ProductId.Value }, shippingItem.ShippingDocumentId); }
|
||||||
catch (Exception ex) { Logger.Error($"[FruitBankEventConsumer] Preorder conversion failed for ProductId={shippingItem.ProductId}: {ex.Message}", ex); }
|
catch (Exception ex) { Logger.Error($"[FruitBankEventConsumer] PreOrder conversion failed for ProductId={shippingItem.ProductId}: {ex.Message}", ex); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin
|
||||||
public static class FruitBankPluginConst
|
public static class FruitBankPluginConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Preorders whose DateOfReceipt is further than this many days in the future
|
/// PreOrders whose DateOfReceipt is further than this many days in the future
|
||||||
/// are NOT converted at the current conversion run.
|
/// are NOT converted at the current conversion run.
|
||||||
/// Based on the bi-weekly truck cycle (~3-4 days between arrivals):
|
/// Based on the bi-weekly truck cycle (~3-4 days between arrivals):
|
||||||
/// if delivery is more than 4 days away, the next truck will arrive before
|
/// if delivery is more than 4 days away, the next truck will arrive before
|
||||||
/// that delivery date, and its document processing will be the correct trigger.
|
/// that delivery date, and its document processing will be the correct trigger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int PreorderConversionWindowDays = 4;
|
public const int PreOrderConversionWindowDays = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,92 +228,92 @@ namespace Nop.Plugin.Misc.FruitBankPlugin
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.QuickOrder.InvalidProductOrQuantity", "Invalid product or quantity", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.QuickOrder.InvalidProductOrQuantity", "Invalid product or quantity", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.QuickOrder.InvalidProductOrQuantity", "\u00c9rv\u00e9nytelen term\u00e9k vagy mennyis\u00e9g", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.QuickOrder.InvalidProductOrQuantity", "\u00c9rv\u00e9nytelen term\u00e9k vagy mennyis\u00e9g", hu);
|
||||||
|
|
||||||
// ── Preorder page ───────────────────────────────────────────────────
|
// ── PreOrder page ───────────────────────────────────────────────────
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PageTitle", "Preorder", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PageTitle", "PreOrder", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PageTitle", "El\u0151rendel\u00e9s", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PageTitle", "El\u0151rendel\u00e9s", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.MenuLabel", "Preorder", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.MenuLabel", "PreOrder", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.MenuLabel", "El\u0151rendel\u00e9s", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.MenuLabel", "El\u0151rendel\u00e9s", hu);
|
||||||
// Delivery step
|
// Delivery step
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Title", "When do you want to receive your preorder?", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Title", "When do you want to receive your preorder?", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Title", "Mikor k\u00e9red a rendel\u00e9st?", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Title", "Mikor k\u00e9red a rendel\u00e9st?", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Subtitle", "Choose a delivery day and time (we\u2019ll confirm availability)", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Subtitle", "Choose a delivery day and time (we\u2019ll confirm availability)", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Subtitle", "V\u00e1lassz sz\u00e1ll\u00edt\u00e1si napot \u00e9s id\u0151pontot (az el\u00e9rhet\u0151s\u00e9get meger\u0151s\u00edtj\u00fck)", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Subtitle", "V\u00e1lassz sz\u00e1ll\u00edt\u00e1si napot \u00e9s id\u0151pontot (az el\u00e9rhet\u0151s\u00e9get meger\u0151s\u00edtj\u00fck)", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.DayLabel", "Delivery day", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.DayLabel", "Delivery day", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.DayLabel", "K\u00edv\u00e1nt sz\u00e1ll\u00edt\u00e1si nap", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.DayLabel", "K\u00edv\u00e1nt sz\u00e1ll\u00edt\u00e1si nap", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeLabel", "Delivery time", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeLabel", "Delivery time", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeLabel", "K\u00edv\u00e1nt id\u0151pont", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeLabel", "K\u00edv\u00e1nt id\u0151pont", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeHint", "Choose an exact time", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeHint", "Choose an exact time", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeHint", "V\u00e1lassz pontos id\u0151pontot", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeHint", "V\u00e1lassz pontos id\u0151pontot", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton", "Show available products", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ConfirmButton", "Show available products", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton", "El\u00e9rhet\u0151 term\u00e9kek mutat\u00e1sa", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ConfirmButton", "El\u00e9rhet\u0151 term\u00e9kek mutat\u00e1sa", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeLabel", "Delivery:", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeLabel", "Delivery:", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeLabel", "Sz\u00e1ll\u00edt\u00e1s:", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeLabel", "Sz\u00e1ll\u00edt\u00e1s:", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeButton", "Change", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeButton", "Change", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeButton", "M\u00f3dos\u00edt\u00e1s", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeButton", "M\u00f3dos\u00edt\u00e1s", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Today", "Today", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Today", "Today", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Today", "Ma", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Today", "Ma", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Tomorrow", "Tomorrow", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Tomorrow", "Tomorrow", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Tomorrow", "Holnap", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Tomorrow", "Holnap", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Saving", "Saving...", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Saving", "Saving...", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Saving", "Ment\u00e9s...", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Saving", "Ment\u00e9s...", hu);
|
||||||
// Products
|
// Products
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.InfoBanner", "Preorders are wishes \u2014 we will confirm availability when the shipment arrives and notify you of any changes.", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.InfoBanner", "PreOrders are wishes \u2014 we will confirm availability when the shipment arrives and notify you of any changes.", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.InfoBanner", "Az el\u0151rendel\u00e9s egy k\u00edv\u00e1ns\u00e1glista \u2014 az áruk meger\u0151s\u00edt\u00e9se a sz\u00e1ll\u00edtm\u00e1ny be\u00e9rkez\u00e9sekor t\u00f6rt\u00e9nik, \u00e9s az esetleges v\u00e1ltoz\u00e1sokr\u00f3l \u00e9rtes\u00edt\u00fcnk.", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.InfoBanner", "Az el\u0151rendel\u00e9s egy k\u00edv\u00e1ns\u00e1glista \u2014 az áruk meger\u0151s\u00edt\u00e9se a sz\u00e1ll\u00edtm\u00e1ny be\u00e9rkez\u00e9sekor t\u00f6rt\u00e9nik, \u00e9s az esetleges v\u00e1ltoz\u00e1sokr\u00f3l \u00e9rtes\u00edt\u00fcnk.", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.LoadingProducts", "Loading available products...", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.LoadingProducts", "Loading available products...", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.LoadingProducts", "El\u00e9rhet\u0151 term\u00e9kek bet\u00f6lt\u00e9se...", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.LoadingProducts", "El\u00e9rhet\u0151 term\u00e9kek bet\u00f6lt\u00e9se...", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoProductsAvailable", "No products are currently available for preorder. Please check back later.", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoProductsAvailable", "No products are currently available for preorder. Please check back later.", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoProductsAvailable", "Jelenleg nincs el\u0151rendelhet\u0151 term\u00e9k. K\u00e9rj\u00fck, l\u00e1togass vissza k\u00e9s\u0151bb.", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoProductsAvailable", "Jelenleg nincs el\u0151rendelhet\u0151 term\u00e9k. K\u00e9rj\u00fck, l\u00e1togass vissza k\u00e9s\u0151bb.", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.ProductsLabel", "Available for preorder \u2014 set quantities:", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.ProductsLabel", "Available for preorder \u2014 set quantities:", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.ProductsLabel", "El\u0151rendelhet\u0151 term\u00e9kek \u2014 add meg a mennyis\u00e9geket:", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.ProductsLabel", "El\u0151rendelhet\u0151 term\u00e9kek \u2014 add meg a mennyis\u00e9geket:", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.MeasurableBadge", "Requires weighing", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.MeasurableBadge", "Requires weighing", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.MeasurableBadge", "S\u00falym\u00e9r\u00e9st ig\u00e9nyel", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.MeasurableBadge", "S\u00falym\u00e9r\u00e9st ig\u00e9nyel", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PricePerPiece", "Ft/pcs", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PricePerPiece", "Ft/pcs", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PricePerPiece", "Ft/db", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PricePerPiece", "Ft/db", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PieceUnit", "pcs", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PieceUnit", "pcs", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PieceUnit", "db", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PieceUnit", "db", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.StockLabel", "Incoming stock:", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.StockLabel", "Incoming stock:", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.StockLabel", "V\u00e1rhat\u00f3 k\u00e9szlet:", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.StockLabel", "V\u00e1rhat\u00f3 k\u00e9szlet:", hu);
|
||||||
// Note + submit
|
// Note + submit
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoteLabel", "Additional note (optional)", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoteLabel", "Additional note (optional)", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoteLabel", "Megjegyz\u00e9s (nem k\u00f6telez\u0151)", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoteLabel", "Megjegyz\u00e9s (nem k\u00f6telez\u0151)", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NotePlaceholder", "Any special requests or notes for this preorder...", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NotePlaceholder", "Any special requests or notes for this preorder...", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NotePlaceholder", "Esetleges megjegyz\u00e9sek az el\u0151rendel\u00e9ssel kapcsolatban...", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NotePlaceholder", "Esetleges megjegyz\u00e9sek az el\u0151rendel\u00e9ssel kapcsolatban...", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SelectionNone", "No products selected yet", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SelectionNone", "No products selected yet", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SelectionNone", "M\u00e9g nincs kiv\u00e1lasztott term\u00e9k", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SelectionNone", "M\u00e9g nincs kiv\u00e1lasztott term\u00e9k", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SelectionItems", "product(s) selected", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SelectionItems", "product(s) selected", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SelectionItems", "term\u00e9k kiv\u00e1lasztva", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SelectionItems", "term\u00e9k kiv\u00e1lasztva", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton", "Place preorder", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SubmitButton", "Place preorder", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton", "El\u0151rendel\u00e9s lead\u00e1sa", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SubmitButton", "El\u0151rendel\u00e9s lead\u00e1sa", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.Submitting", "Placing preorder...", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.Submitting", "Placing preorder...", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.Submitting", "El\u0151rendel\u00e9s ment\u00e9se...", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.Submitting", "El\u0151rendel\u00e9s ment\u00e9se...", hu);
|
||||||
// Summary panel
|
// Summary panel
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SummaryTitle", "Your preorder", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryTitle", "Your preorder", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SummaryTitle", "El\u0151rendel\u00e9sed", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryTitle", "El\u0151rendel\u00e9sed", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SummaryEmpty", "Set quantities above to build your preorder.", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryEmpty", "Set quantities above to build your preorder.", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SummaryEmpty", "Add meg a mennyis\u00e9geket a term\u00e9kekn\u00e9l.", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryEmpty", "Add meg a mennyis\u00e9geket a term\u00e9kekn\u00e9l.", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SummaryNote", "Prices for weighed items will be finalised after measurement. Preorder quantities may change depending on actual shipment.", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryNote", "Prices for weighed items will be finalised after measurement. PreOrder quantities may change depending on actual shipment.", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SummaryNote", "A s\u00falym\u00e9r\u00e9st ig\u00e9nyl\u0151 t\u00e9teleikn\u00e9l az \u00e1r a m\u00e9r\u00e9s ut\u00e1n v\u00e9glegesedik. A mennyis\u00e9gek a t\u00e9nyleges sz\u00e1ll\u00edtm\u00e1nyt\u00f3l f\u00fcgg\u0151en v\u00e1ltozhatnak.", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryNote", "A s\u00falym\u00e9r\u00e9st ig\u00e9nyl\u0151 t\u00e9teleikn\u00e9l az \u00e1r a m\u00e9r\u00e9s ut\u00e1n v\u00e9glegesedik. A mennyis\u00e9gek a t\u00e9nyleges sz\u00e1ll\u00edtm\u00e1nyt\u00f3l f\u00fcgg\u0151en v\u00e1ltozhatnak.", hu);
|
||||||
// Success + errors
|
// Success + errors
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SuccessTitle", "Preorder placed!", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SuccessTitle", "PreOrder placed!", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SuccessTitle", "El\u0151rendel\u00e9s leadva!", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SuccessTitle", "El\u0151rendel\u00e9s leadva!", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SuccessMessage", "Your preorder #{0} has been received. We will notify you when the shipment arrives.", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SuccessMessage", "Your preorder #{0} has been received. We will notify you when the shipment arrives.", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.SuccessMessage", "#{0} sz\u00e1m\u00fa el\u0151rendel\u00e9sed be\u00e9rkezett. A sz\u00e1ll\u00edtm\u00e1ny meger\u0151s\u00edt\u00e9sekor \u00e9rtes\u00edt\u00fcnk.", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.SuccessMessage", "#{0} sz\u00e1m\u00fa el\u0151rendel\u00e9sed be\u00e9rkezett. A sz\u00e1ll\u00edtm\u00e1ny meger\u0151s\u00edt\u00e9sekor \u00e9rtes\u00edt\u00fcnk.", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.BackToHome", "Back to home", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.BackToHome", "Back to home", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.BackToHome", "Vissza a f\u0151oldalra", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.BackToHome", "Vissza a f\u0151oldalra", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.ErrorPrefix", "Error: ", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.ErrorPrefix", "Error: ", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.ErrorPrefix", "Hiba: ", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.ErrorPrefix", "Hiba: ", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NotLoggedIn", "Not logged in", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NotLoggedIn", "Not logged in", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NotLoggedIn", "Nincs bejelentkezve", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NotLoggedIn", "Nincs bejelentkezve", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoItemsSelected", "No items selected", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoItemsSelected", "No items selected", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoItemsSelected", "Nincs kiv\u00e1lasztott term\u00e9k", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoItemsSelected", "Nincs kiv\u00e1lasztott term\u00e9k", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoValidItems", "No valid items in preorder", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoValidItems", "No valid items in preorder", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoValidItems", "Nincs \u00e9rv\u00e9nyes term\u00e9k az el\u0151rendel\u00e9sben", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoValidItems", "Nincs \u00e9rv\u00e9nyes term\u00e9k az el\u0151rendel\u00e9sben", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoDeliveryDateTimeProvided", "No delivery date/time provided", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoDeliveryDateTimeProvided", "No delivery date/time provided", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.NoDeliveryDateTimeProvided", "Nincs sz\u00e1ll\u00edt\u00e1si id\u0151pont megadva", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.NoDeliveryDateTimeProvided", "Nincs sz\u00e1ll\u00edt\u00e1si id\u0151pont megadva", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.InvalidDeliveryDateTime", "Invalid delivery date/time format", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.InvalidDeliveryDateTime", "Invalid delivery date/time format", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.InvalidDeliveryDateTime", "\u00c9rv\u00e9nytelen sz\u00e1ll\u00edt\u00e1si d\u00e1tum/id\u0151 form\u00e1tum", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.InvalidDeliveryDateTime", "\u00c9rv\u00e9nytelen sz\u00e1ll\u00edt\u00e1si d\u00e1tum/id\u0151 form\u00e1tum", hu);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PlacedSuccessfully", "Preorder placed successfully", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PlacedSuccessfully", "PreOrder placed successfully", en);
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.Preorder.PlacedSuccessfully", "El\u0151rendel\u00e9s sikeresen leadva", hu);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.PreOrder.PlacedSuccessfully", "El\u0151rendel\u00e9s sikeresen leadva", hu);
|
||||||
|
|
||||||
// ── Customer Credit ────────────────────────────────────────────────────
|
// ── Customer Credit ────────────────────────────────────────────────────
|
||||||
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.CustomerCredit.PageTitle", "Customer Credit Management", en);
|
await _localizationService.AddOrUpdateLocaleResourceAsync("Plugins.Misc.FruitBankPlugin.CustomerCredit.PageTitle", "Customer Credit Management", en);
|
||||||
|
|
@ -464,7 +464,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin
|
||||||
}
|
}
|
||||||
else if (widgetZone == PublicWidgetZones.AccountNavigationAfter)
|
else if (widgetZone == PublicWidgetZones.AccountNavigationAfter)
|
||||||
{
|
{
|
||||||
return typeof(CustomerPreorderNavViewComponent);
|
return typeof(CustomerPreOrderNavViewComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,9 +94,9 @@ public class PluginNopStartup : INopStartup
|
||||||
services.AddScoped<StockTakingItemDbTable>();
|
services.AddScoped<StockTakingItemDbTable>();
|
||||||
services.AddScoped<StockTakingItemPalletDbTable>();
|
services.AddScoped<StockTakingItemPalletDbTable>();
|
||||||
services.AddScoped<CustomerCreditDbTable>();
|
services.AddScoped<CustomerCreditDbTable>();
|
||||||
services.AddScoped<PreorderDbTable>();
|
services.AddScoped<PreOrderDbTable>();
|
||||||
services.AddScoped<PreorderItemDbTable>();
|
services.AddScoped<PreOrderItemDbTable>();
|
||||||
services.AddScoped<PreorderDbContext>();
|
services.AddScoped<PreOrderDbContext>();
|
||||||
|
|
||||||
services.AddScoped<StockTakingDbContext>();
|
services.AddScoped<StockTakingDbContext>();
|
||||||
services.AddScoped<FruitBankDbContext>();
|
services.AddScoped<FruitBankDbContext>();
|
||||||
|
|
@ -151,7 +151,7 @@ public class PluginNopStartup : INopStartup
|
||||||
services.AddScoped<PdfToImageService>();
|
services.AddScoped<PdfToImageService>();
|
||||||
services.AddScoped<FruitBankNotificationService>();
|
services.AddScoped<FruitBankNotificationService>();
|
||||||
services.AddScoped<FruitBankOrderItemService>();
|
services.AddScoped<FruitBankOrderItemService>();
|
||||||
services.AddScoped<PreorderConversionService>();
|
services.AddScoped<PreOrderConversionService>();
|
||||||
services.AddSingleton<IFileStorageProvider>(sp =>
|
services.AddSingleton<IFileStorageProvider>(sp =>
|
||||||
new LocalFileStorageProvider() // Uses default wwwroot/uploads
|
new LocalFileStorageProvider() // Uses default wwwroot/uploads
|
||||||
// Or specify custom path:
|
// Or specify custom path:
|
||||||
|
|
|
||||||
|
|
@ -197,57 +197,57 @@ public class RouteProvider : IRouteProvider
|
||||||
pattern: "Admin/CustomerCredit/UpdateCreditLimit",
|
pattern: "Admin/CustomerCredit/UpdateCreditLimit",
|
||||||
defaults: new { controller = "CustomerCredit", action = "UpdateCreditLimit", area = AreaNames.ADMIN });
|
defaults: new { controller = "CustomerCredit", action = "UpdateCreditLimit", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
// ── Admin: Preorder list ───────────────────────────────────────────
|
// ── Admin: PreOrder list ───────────────────────────────────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorders.List",
|
name: "Plugin.FruitBank.PreOrders.List",
|
||||||
pattern: "Admin/Preorders",
|
pattern: "Admin/PreOrders",
|
||||||
defaults: new { controller = "PreorderAdmin", action = "List", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAdmin", action = "List", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorders.PreorderList",
|
name: "Plugin.FruitBank.PreOrders.PreOrderList",
|
||||||
pattern: "Admin/Preorders/PreorderList",
|
pattern: "Admin/PreOrders/PreOrderList",
|
||||||
defaults: new { controller = "PreorderAdmin", action = "PreorderList", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAdmin", action = "PreOrderList", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorders.Detail",
|
name: "Plugin.FruitBank.PreOrders.Detail",
|
||||||
pattern: "Admin/Preorders/Detail/{id:int}",
|
pattern: "Admin/PreOrders/Detail/{id:int}",
|
||||||
defaults: new { controller = "PreorderAdmin", action = "Detail", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAdmin", action = "Detail", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorders.Cancel",
|
name: "Plugin.FruitBank.PreOrders.Cancel",
|
||||||
pattern: "Admin/Preorders/Cancel/{id:int}",
|
pattern: "Admin/PreOrders/Cancel/{id:int}",
|
||||||
defaults: new { controller = "PreorderAdmin", action = "Cancel", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAdmin", action = "Cancel", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorders.CreatePreorder",
|
name: "Plugin.FruitBank.PreOrders.CreatePreOrder",
|
||||||
pattern: "Admin/Preorders/CreatePreorder",
|
pattern: "Admin/PreOrders/CreatePreOrder",
|
||||||
defaults: new { controller = "PreorderAdmin", action = "CreatePreorder", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAdmin", action = "CreatePreOrder", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorders.DemandList",
|
name: "Plugin.FruitBank.PreOrders.DemandList",
|
||||||
pattern: "Admin/Preorders/DemandList",
|
pattern: "Admin/PreOrders/DemandList",
|
||||||
defaults: new { controller = "PreorderAdmin", action = "DemandList", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAdmin", action = "DemandList", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
// ── Admin: Preorder availability ─────────────────────────────────
|
// ── Admin: PreOrder availability ─────────────────────────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.PreorderAvailability.Index",
|
name: "Plugin.FruitBank.PreOrderAvailability.Index",
|
||||||
pattern: "Admin/PreorderAvailability",
|
pattern: "Admin/PreOrderAvailability",
|
||||||
defaults: new { controller = "PreorderAvailability", action = "Index", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAvailability", action = "Index", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.PreorderAvailability.ProductList",
|
name: "Plugin.FruitBank.PreOrderAvailability.ProductList",
|
||||||
pattern: "Admin/PreorderAvailability/ProductList",
|
pattern: "Admin/PreOrderAvailability/ProductList",
|
||||||
defaults: new { controller = "PreorderAvailability", action = "ProductList", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAvailability", action = "ProductList", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.PreorderAvailability.AvailableTodayList",
|
name: "Plugin.FruitBank.PreOrderAvailability.AvailableTodayList",
|
||||||
pattern: "Admin/PreorderAvailability/AvailableTodayList",
|
pattern: "Admin/PreOrderAvailability/AvailableTodayList",
|
||||||
defaults: new { controller = "PreorderAvailability", action = "AvailableTodayList", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAvailability", action = "AvailableTodayList", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.PreorderAvailability.SaveWindow",
|
name: "Plugin.FruitBank.PreOrderAvailability.SaveWindow",
|
||||||
pattern: "Admin/PreorderAvailability/SaveWindow",
|
pattern: "Admin/PreOrderAvailability/SaveWindow",
|
||||||
defaults: new { controller = "PreorderAvailability", action = "SaveWindow", area = AreaNames.ADMIN });
|
defaults: new { controller = "PreOrderAvailability", action = "SaveWindow", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
// ── Public: Unified Order flow ─────────────────────────────────────────
|
// ── Public: Unified Order flow ─────────────────────────────────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
|
|
@ -271,9 +271,9 @@ public class RouteProvider : IRouteProvider
|
||||||
defaults: new { controller = "Order", action = "GetAllProducts" });
|
defaults: new { controller = "Order", action = "GetAllProducts" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Order.GetPreorderProducts",
|
name: "Plugin.FruitBank.Order.GetPreOrderProducts",
|
||||||
pattern: "rendeles/elozetes-termekek",
|
pattern: "rendeles/elozetes-termekek",
|
||||||
defaults: new { controller = "Order", action = "GetPreorderProducts" });
|
defaults: new { controller = "Order", action = "GetPreOrderProducts" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Order.SearchProducts",
|
name: "Plugin.FruitBank.Order.SearchProducts",
|
||||||
|
|
@ -296,9 +296,9 @@ public class RouteProvider : IRouteProvider
|
||||||
defaults: new { controller = "Order", action = "GetCartItems" });
|
defaults: new { controller = "Order", action = "GetCartItems" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Order.PlacePreorder",
|
name: "Plugin.FruitBank.Order.PlacePreOrder",
|
||||||
pattern: "rendeles/elozetes-leadás",
|
pattern: "rendeles/elozetes-leadás",
|
||||||
defaults: new { controller = "Order", action = "PlacePreorder" });
|
defaults: new { controller = "Order", action = "PlacePreOrder" });
|
||||||
|
|
||||||
// ── Public: Help page ───────────────────────────────────────────────────
|
// ── Public: Help page ───────────────────────────────────────────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
|
|
@ -308,35 +308,35 @@ public class RouteProvider : IRouteProvider
|
||||||
|
|
||||||
// ── Public: Customer preorder list ───────────────────────────────────────────
|
// ── Public: Customer preorder list ───────────────────────────────────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.CustomerPreorder.List",
|
name: "Plugin.FruitBank.CustomerPreOrder.List",
|
||||||
pattern: "fiokom/elorerendeles-aim",
|
pattern: "fiokom/elorerendeles-aim",
|
||||||
defaults: new { controller = "CustomerPreorder", action = "List" });
|
defaults: new { controller = "CustomerPreOrder", action = "List" });
|
||||||
|
|
||||||
// ── Public: Preorder (legacy, kept for backward compat) ───────────────
|
// ── Public: PreOrder (legacy, kept for backward compat) ───────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorder.Index",
|
name: "Plugin.FruitBank.PreOrder.Index",
|
||||||
pattern: "elozetes-rendeles",
|
pattern: "elozetes-rendeles",
|
||||||
defaults: new { controller = "Preorder", action = "Index" });
|
defaults: new { controller = "PreOrder", action = "Index" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorder.GetDeliveryDateTime",
|
name: "Plugin.FruitBank.PreOrder.GetDeliveryDateTime",
|
||||||
pattern: "elozetes-rendeles/szallitas-idopont",
|
pattern: "elozetes-rendeles/szallitas-idopont",
|
||||||
defaults: new { controller = "Preorder", action = "GetDeliveryDateTime" });
|
defaults: new { controller = "PreOrder", action = "GetDeliveryDateTime" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorder.SetDeliveryDateTime",
|
name: "Plugin.FruitBank.PreOrder.SetDeliveryDateTime",
|
||||||
pattern: "elozetes-rendeles/szallitas-idopont-beallitas",
|
pattern: "elozetes-rendeles/szallitas-idopont-beallitas",
|
||||||
defaults: new { controller = "Preorder", action = "SetDeliveryDateTime" });
|
defaults: new { controller = "PreOrder", action = "SetDeliveryDateTime" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorder.GetAvailableProducts",
|
name: "Plugin.FruitBank.PreOrder.GetAvailableProducts",
|
||||||
pattern: "elozetes-rendeles/termekek",
|
pattern: "elozetes-rendeles/termekek",
|
||||||
defaults: new { controller = "Preorder", action = "GetAvailableProducts" });
|
defaults: new { controller = "PreOrder", action = "GetAvailableProducts" });
|
||||||
|
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
name: "Plugin.FruitBank.Preorder.PlacePreorder",
|
name: "Plugin.FruitBank.PreOrder.PlacePreOrder",
|
||||||
pattern: "elozetes-rendeles/leadás",
|
pattern: "elozetes-rendeles/leadás",
|
||||||
defaults: new { controller = "Preorder", action = "PlacePreorder" });
|
defaults: new { controller = "PreOrder", action = "PlacePreOrder" });
|
||||||
|
|
||||||
// ── Public: Quick Order ──────────────────────────────────────────────
|
// ── Public: Quick Order ──────────────────────────────────────────────
|
||||||
endpointRouteBuilder.MapControllerRoute(
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
|
|
|
||||||
|
|
@ -2,142 +2,142 @@
|
||||||
<Language Name="English" IsDefault="false" IsRightToLeft="false">
|
<Language Name="English" IsDefault="false" IsRightToLeft="false">
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════
|
<!-- ═══════════════════════════════════════════════════════════
|
||||||
Preorder page — Plugins.Misc.FruitBankPlugin.Preorder.*
|
PreOrder page — Plugins.Misc.FruitBankPlugin.PreOrder.*
|
||||||
Import: Admin > Configuration > Languages > [English] > Import resources
|
Import: Admin > Configuration > Languages > [English] > Import resources
|
||||||
═══════════════════════════════════════════════════════════ -->
|
═══════════════════════════════════════════════════════════ -->
|
||||||
|
|
||||||
<!-- General -->
|
<!-- General -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PageTitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PageTitle">
|
||||||
<Value><![CDATA[Preorder]]></Value>
|
<Value><![CDATA[PreOrder]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.MenuLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.MenuLabel">
|
||||||
<Value><![CDATA[Preorder]]></Value>
|
<Value><![CDATA[PreOrder]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Delivery step -->
|
<!-- Delivery step -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Title">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Title">
|
||||||
<Value><![CDATA[When do you want to receive your preorder?]]></Value>
|
<Value><![CDATA[When do you want to receive your preorder?]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Subtitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Subtitle">
|
||||||
<Value><![CDATA[Choose a delivery day and time (we'll confirm availability)]]></Value>
|
<Value><![CDATA[Choose a delivery day and time (we'll confirm availability)]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.DayLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.DayLabel">
|
||||||
<Value><![CDATA[Delivery day]]></Value>
|
<Value><![CDATA[Delivery day]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeLabel">
|
||||||
<Value><![CDATA[Delivery time]]></Value>
|
<Value><![CDATA[Delivery time]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeHint">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeHint">
|
||||||
<Value><![CDATA[Choose an exact time]]></Value>
|
<Value><![CDATA[Choose an exact time]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ConfirmButton">
|
||||||
<Value><![CDATA[Show available products]]></Value>
|
<Value><![CDATA[Show available products]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeLabel">
|
||||||
<Value><![CDATA[Delivery:]]></Value>
|
<Value><![CDATA[Delivery:]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeButton">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeButton">
|
||||||
<Value><![CDATA[Change]]></Value>
|
<Value><![CDATA[Change]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Today">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Today">
|
||||||
<Value><![CDATA[Today]]></Value>
|
<Value><![CDATA[Today]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Tomorrow">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Tomorrow">
|
||||||
<Value><![CDATA[Tomorrow]]></Value>
|
<Value><![CDATA[Tomorrow]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Saving">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Saving">
|
||||||
<Value><![CDATA[Saving...]]></Value>
|
<Value><![CDATA[Saving...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Product list -->
|
<!-- Product list -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.InfoBanner">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.InfoBanner">
|
||||||
<Value><![CDATA[Preorders are wishes — we will confirm availability when the shipment arrives and notify you of any changes.]]></Value>
|
<Value><![CDATA[PreOrders are wishes — we will confirm availability when the shipment arrives and notify you of any changes.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.LoadingProducts">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.LoadingProducts">
|
||||||
<Value><![CDATA[Loading available products...]]></Value>
|
<Value><![CDATA[Loading available products...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoProductsAvailable">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoProductsAvailable">
|
||||||
<Value><![CDATA[No products are currently available for preorder. Please check back later.]]></Value>
|
<Value><![CDATA[No products are currently available for preorder. Please check back later.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.ProductsLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.ProductsLabel">
|
||||||
<Value><![CDATA[Available for preorder — set quantities:]]></Value>
|
<Value><![CDATA[Available for preorder — set quantities:]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.MeasurableBadge">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.MeasurableBadge">
|
||||||
<Value><![CDATA[Requires weighing]]></Value>
|
<Value><![CDATA[Requires weighing]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PricePerPiece">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PricePerPiece">
|
||||||
<Value><![CDATA[Ft/pcs]]></Value>
|
<Value><![CDATA[Ft/pcs]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PieceUnit">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PieceUnit">
|
||||||
<Value><![CDATA[pcs]]></Value>
|
<Value><![CDATA[pcs]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.StockLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.StockLabel">
|
||||||
<Value><![CDATA[Incoming stock:]]></Value>
|
<Value><![CDATA[Incoming stock:]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Note and submit -->
|
<!-- Note and submit -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoteLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoteLabel">
|
||||||
<Value><![CDATA[Additional note (optional)]]></Value>
|
<Value><![CDATA[Additional note (optional)]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NotePlaceholder">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NotePlaceholder">
|
||||||
<Value><![CDATA[Any special requests or notes for this preorder...]]></Value>
|
<Value><![CDATA[Any special requests or notes for this preorder...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SelectionNone">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SelectionNone">
|
||||||
<Value><![CDATA[No products selected yet]]></Value>
|
<Value><![CDATA[No products selected yet]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SelectionItems">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SelectionItems">
|
||||||
<Value><![CDATA[product(s) selected]]></Value>
|
<Value><![CDATA[product(s) selected]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SubmitButton">
|
||||||
<Value><![CDATA[Place preorder]]></Value>
|
<Value><![CDATA[Place preorder]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.Submitting">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.Submitting">
|
||||||
<Value><![CDATA[Placing preorder...]]></Value>
|
<Value><![CDATA[Placing preorder...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Summary panel -->
|
<!-- Summary panel -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SummaryTitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SummaryTitle">
|
||||||
<Value><![CDATA[Your preorder]]></Value>
|
<Value><![CDATA[Your preorder]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SummaryEmpty">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SummaryEmpty">
|
||||||
<Value><![CDATA[Set quantities above to build your preorder.]]></Value>
|
<Value><![CDATA[Set quantities above to build your preorder.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SummaryNote">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SummaryNote">
|
||||||
<Value><![CDATA[Prices for weighed items will be finalised after measurement. Preorder quantities may change depending on actual shipment.]]></Value>
|
<Value><![CDATA[Prices for weighed items will be finalised after measurement. PreOrder quantities may change depending on actual shipment.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Success -->
|
<!-- Success -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SuccessTitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SuccessTitle">
|
||||||
<Value><![CDATA[Preorder placed!]]></Value>
|
<Value><![CDATA[PreOrder placed!]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SuccessMessage">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SuccessMessage">
|
||||||
<Value><![CDATA[Your preorder #{0} has been received. We will notify you when the shipment arrives.]]></Value>
|
<Value><![CDATA[Your preorder #{0} has been received. We will notify you when the shipment arrives.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.BackToHome">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.BackToHome">
|
||||||
<Value><![CDATA[Back to home]]></Value>
|
<Value><![CDATA[Back to home]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Error messages -->
|
<!-- Error messages -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.ErrorPrefix">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.ErrorPrefix">
|
||||||
<Value><![CDATA[Error: ]]></Value>
|
<Value><![CDATA[Error: ]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NotLoggedIn">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NotLoggedIn">
|
||||||
<Value><![CDATA[Not logged in]]></Value>
|
<Value><![CDATA[Not logged in]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoItemsSelected">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoItemsSelected">
|
||||||
<Value><![CDATA[No items selected]]></Value>
|
<Value><![CDATA[No items selected]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoValidItems">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoValidItems">
|
||||||
<Value><![CDATA[No valid items in preorder]]></Value>
|
<Value><![CDATA[No valid items in preorder]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoDeliveryDateTimeProvided">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoDeliveryDateTimeProvided">
|
||||||
<Value><![CDATA[No delivery date/time provided]]></Value>
|
<Value><![CDATA[No delivery date/time provided]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.InvalidDeliveryDateTime">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.InvalidDeliveryDateTime">
|
||||||
<Value><![CDATA[Invalid delivery date/time format]]></Value>
|
<Value><![CDATA[Invalid delivery date/time format]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PlacedSuccessfully">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PlacedSuccessfully">
|
||||||
<Value><![CDATA[Preorder placed successfully]]></Value>
|
<Value><![CDATA[PreOrder placed successfully]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
</Language>
|
</Language>
|
||||||
|
|
|
||||||
|
|
@ -2,141 +2,141 @@
|
||||||
<Language Name="Hungarian" IsDefault="false" IsRightToLeft="false">
|
<Language Name="Hungarian" IsDefault="false" IsRightToLeft="false">
|
||||||
|
|
||||||
<!-- ═══════════════════════════════════════════════════════════
|
<!-- ═══════════════════════════════════════════════════════════
|
||||||
Előrendelés oldal — Plugins.Misc.FruitBankPlugin.Preorder.*
|
Előrendelés oldal — Plugins.Misc.FruitBankPlugin.PreOrder.*
|
||||||
Import: Admin > Konfiguráció > Nyelvek > [Magyar] > Erőforrások importálása
|
Import: Admin > Konfiguráció > Nyelvek > [Magyar] > Erőforrások importálása
|
||||||
═══════════════════════════════════════════════════════════ -->
|
═══════════════════════════════════════════════════════════ -->
|
||||||
|
|
||||||
<!-- Általános -->
|
<!-- Általános -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PageTitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PageTitle">
|
||||||
<Value><![CDATA[Előrendelés]]></Value>
|
<Value><![CDATA[Előrendelés]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.MenuLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.MenuLabel">
|
||||||
<Value><![CDATA[Előrendelés]]></Value>
|
<Value><![CDATA[Előrendelés]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Szállítási időpont lépés -->
|
<!-- Szállítási időpont lépés -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Title">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Title">
|
||||||
<Value><![CDATA[Mikor kéred a rendelést?]]></Value>
|
<Value><![CDATA[Mikor kéred a rendelést?]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Subtitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Subtitle">
|
||||||
<Value><![CDATA[Válassz szállítási napot és időpontot (az elérhetőséget megerősítjük)]]></Value>
|
<Value><![CDATA[Válassz szállítási napot és időpontot (az elérhetőséget megerősítjük)]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.DayLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.DayLabel">
|
||||||
<Value><![CDATA[Kívánt szállítási nap]]></Value>
|
<Value><![CDATA[Kívánt szállítási nap]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeLabel">
|
||||||
<Value><![CDATA[Kívánt időpont]]></Value>
|
<Value><![CDATA[Kívánt időpont]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeHint">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeHint">
|
||||||
<Value><![CDATA[Válassz pontos időpontot]]></Value>
|
<Value><![CDATA[Válassz pontos időpontot]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ConfirmButton">
|
||||||
<Value><![CDATA[Elérhető termékek mutatása]]></Value>
|
<Value><![CDATA[Elérhető termékek mutatása]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeLabel">
|
||||||
<Value><![CDATA[Szállítás:]]></Value>
|
<Value><![CDATA[Szállítás:]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeButton">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeButton">
|
||||||
<Value><![CDATA[Módosítás]]></Value>
|
<Value><![CDATA[Módosítás]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Today">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Today">
|
||||||
<Value><![CDATA[Ma]]></Value>
|
<Value><![CDATA[Ma]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Tomorrow">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Tomorrow">
|
||||||
<Value><![CDATA[Holnap]]></Value>
|
<Value><![CDATA[Holnap]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Saving">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Saving">
|
||||||
<Value><![CDATA[Mentés...]]></Value>
|
<Value><![CDATA[Mentés...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Terméklista -->
|
<!-- Terméklista -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.InfoBanner">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.InfoBanner">
|
||||||
<Value><![CDATA[Az előrendelés egy kívánságlista — az áruk megerősítése a szállítmány beérkezésekor történik, és az esetleges változásokról értesítünk.]]></Value>
|
<Value><![CDATA[Az előrendelés egy kívánságlista — az áruk megerősítése a szállítmány beérkezésekor történik, és az esetleges változásokról értesítünk.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.LoadingProducts">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.LoadingProducts">
|
||||||
<Value><![CDATA[Elérhető termékek betöltése...]]></Value>
|
<Value><![CDATA[Elérhető termékek betöltése...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoProductsAvailable">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoProductsAvailable">
|
||||||
<Value><![CDATA[Jelenleg nincs előrendelhető termék. Kérjük, látogass vissza később.]]></Value>
|
<Value><![CDATA[Jelenleg nincs előrendelhető termék. Kérjük, látogass vissza később.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.ProductsLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.ProductsLabel">
|
||||||
<Value><![CDATA[Előrendelhető termékek — add meg a mennyiségeket:]]></Value>
|
<Value><![CDATA[Előrendelhető termékek — add meg a mennyiségeket:]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.MeasurableBadge">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.MeasurableBadge">
|
||||||
<Value><![CDATA[Súlymérést igényel]]></Value>
|
<Value><![CDATA[Súlymérést igényel]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PricePerPiece">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PricePerPiece">
|
||||||
<Value><![CDATA[Ft/db]]></Value>
|
<Value><![CDATA[Ft/db]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PieceUnit">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PieceUnit">
|
||||||
<Value><![CDATA[db]]></Value>
|
<Value><![CDATA[db]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.StockLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.StockLabel">
|
||||||
<Value><![CDATA[Várható készlet:]]></Value>
|
<Value><![CDATA[Várható készlet:]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Megjegyzés és leadás -->
|
<!-- Megjegyzés és leadás -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoteLabel">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoteLabel">
|
||||||
<Value><![CDATA[Megjegyzés (nem kötelező)]]></Value>
|
<Value><![CDATA[Megjegyzés (nem kötelező)]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NotePlaceholder">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NotePlaceholder">
|
||||||
<Value><![CDATA[Esetleges megjegyzések az előrendeléssel kapcsolatban...]]></Value>
|
<Value><![CDATA[Esetleges megjegyzések az előrendeléssel kapcsolatban...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SelectionNone">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SelectionNone">
|
||||||
<Value><![CDATA[Még nincs kiválasztott termék]]></Value>
|
<Value><![CDATA[Még nincs kiválasztott termék]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SelectionItems">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SelectionItems">
|
||||||
<Value><![CDATA[termék kiválasztva]]></Value>
|
<Value><![CDATA[termék kiválasztva]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SubmitButton">
|
||||||
<Value><![CDATA[Előrendelés leadása]]></Value>
|
<Value><![CDATA[Előrendelés leadása]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.Submitting">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.Submitting">
|
||||||
<Value><![CDATA[Előrendelés mentése...]]></Value>
|
<Value><![CDATA[Előrendelés mentése...]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Összefoglaló panel -->
|
<!-- Összefoglaló panel -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SummaryTitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SummaryTitle">
|
||||||
<Value><![CDATA[Előrendelésed]]></Value>
|
<Value><![CDATA[Előrendelésed]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SummaryEmpty">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SummaryEmpty">
|
||||||
<Value><![CDATA[Add meg a mennyiségeket a termékeknél.]]></Value>
|
<Value><![CDATA[Add meg a mennyiségeket a termékeknél.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SummaryNote">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SummaryNote">
|
||||||
<Value><![CDATA[A súlymérést igénylő tételeknél az ár a mérés után véglegesedik. A mennyiségek a tényleges szállítmánytól függően változhatnak.]]></Value>
|
<Value><![CDATA[A súlymérést igénylő tételeknél az ár a mérés után véglegesedik. A mennyiségek a tényleges szállítmánytól függően változhatnak.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Sikeres leadás -->
|
<!-- Sikeres leadás -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SuccessTitle">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SuccessTitle">
|
||||||
<Value><![CDATA[Előrendelés leadva!]]></Value>
|
<Value><![CDATA[Előrendelés leadva!]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.SuccessMessage">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.SuccessMessage">
|
||||||
<Value><![CDATA[#{0} számú előrendelésed beérkezett. A szállítmány megerősítésekor értesítünk.]]></Value>
|
<Value><![CDATA[#{0} számú előrendelésed beérkezett. A szállítmány megerősítésekor értesítünk.]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.BackToHome">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.BackToHome">
|
||||||
<Value><![CDATA[Vissza a főoldalra]]></Value>
|
<Value><![CDATA[Vissza a főoldalra]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
<!-- Hibaüzenetek -->
|
<!-- Hibaüzenetek -->
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.ErrorPrefix">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.ErrorPrefix">
|
||||||
<Value><![CDATA[Hiba: ]]></Value>
|
<Value><![CDATA[Hiba: ]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NotLoggedIn">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NotLoggedIn">
|
||||||
<Value><![CDATA[Nincs bejelentkezve]]></Value>
|
<Value><![CDATA[Nincs bejelentkezve]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoItemsSelected">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoItemsSelected">
|
||||||
<Value><![CDATA[Nincs kiválasztott termék]]></Value>
|
<Value><![CDATA[Nincs kiválasztott termék]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoValidItems">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoValidItems">
|
||||||
<Value><![CDATA[Nincs érvényes termék az előrendelésben]]></Value>
|
<Value><![CDATA[Nincs érvényes termék az előrendelésben]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.NoDeliveryDateTimeProvided">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.NoDeliveryDateTimeProvided">
|
||||||
<Value><![CDATA[Nincs szállítási időpont megadva]]></Value>
|
<Value><![CDATA[Nincs szállítási időpont megadva]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.InvalidDeliveryDateTime">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.InvalidDeliveryDateTime">
|
||||||
<Value><![CDATA[Érvénytelen szállítási dátum/idő formátum]]></Value>
|
<Value><![CDATA[Érvénytelen szállítási dátum/idő formátum]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.Preorder.PlacedSuccessfully">
|
<LocaleResource Name="Plugins.Misc.FruitBankPlugin.PreOrder.PlacedSuccessfully">
|
||||||
<Value><![CDATA[Előrendelés sikeresen leadva]]></Value>
|
<Value><![CDATA[Előrendelés sikeresen leadva]]></Value>
|
||||||
</LocaleResource>
|
</LocaleResource>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ public partial class NameCompatibility : INameCompatibility
|
||||||
{ typeof(StockTakingItemPallet), FruitBankConstClient.StockTakingItemPalletDbTableName},
|
{ typeof(StockTakingItemPallet), FruitBankConstClient.StockTakingItemPalletDbTableName},
|
||||||
|
|
||||||
{ typeof(CustomerCredit), FruitBankConstClient.CustomerCreditDbTableName},
|
{ typeof(CustomerCredit), FruitBankConstClient.CustomerCreditDbTableName},
|
||||||
{ typeof(Preorder), FruitBankConstClient.PreOrderDbTableName},
|
{ typeof(PreOrder), FruitBankConstClient.PreOrderDbTableName},
|
||||||
{ typeof(PreorderItem), FruitBankConstClient.PreOrderItemDbTableName},
|
{ typeof(PreOrderItem), FruitBankConstClient.PreOrderItemDbTableName},
|
||||||
|
|
||||||
{ typeof(CargoPartner), FruitBankConstClient.CargoPartnerDbTableName},
|
{ typeof(CargoPartner), FruitBankConstClient.CargoPartnerDbTableName},
|
||||||
{ typeof(CargoTruck), FruitBankConstClient.CargoTruckDbTableName},
|
{ typeof(CargoTruck), FruitBankConstClient.CargoTruckDbTableName},
|
||||||
|
|
|
||||||
|
|
@ -224,13 +224,13 @@
|
||||||
<None Update="Areas\Admin\Views\Order\List.cshtml">
|
<None Update="Areas\Admin\Views\Order\List.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Areas\Admin\Views\PreorderAvailability\Index.cshtml">
|
<None Update="Areas\Admin\Views\PreOrderAvailability\Index.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Areas\Admin\Views\Preorder\Detail.cshtml">
|
<None Update="Areas\Admin\Views\PreOrder\Detail.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Areas\Admin\Views\Preorder\List.cshtml">
|
<None Update="Areas\Admin\Views\PreOrder\List.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Areas\Admin\Views\Product\List.cshtml">
|
<None Update="Areas\Admin\Views\Product\List.cshtml">
|
||||||
|
|
@ -677,10 +677,10 @@
|
||||||
<None Update="Views\CustomerCreditWidget.cshtml">
|
<None Update="Views\CustomerCreditWidget.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Views\CustomerPreorder\List.cshtml">
|
<None Update="Views\CustomerPreOrder\List.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Views\CustomerPreorder\NavItem.cshtml">
|
<None Update="Views\CustomerPreOrder\NavItem.cshtml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Views\Help\Index.cshtml">
|
<None Update="Views\Help\Index.cshtml">
|
||||||
|
|
@ -689,7 +689,7 @@
|
||||||
<None Update="Views\Order\Index.cshtml">
|
<None Update="Views\Order\Index.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Views\Preorder\Index.cshtml">
|
<None Update="Views\PreOrder\Index.cshtml">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="Views\ProductAIListWidget.cshtml">
|
<None Update="Views\ProductAIListWidget.cshtml">
|
||||||
|
|
|
||||||
|
|
@ -260,10 +260,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
var preorderAvailabilityMenuItem = new AdminMenuItem
|
var preorderAvailabilityMenuItem = new AdminMenuItem
|
||||||
{
|
{
|
||||||
Visible = true,
|
Visible = true,
|
||||||
SystemName = "PreorderAvailability",
|
SystemName = "PreOrderAvailability",
|
||||||
Title = "Előrendelés — elérhetőség",
|
Title = "Előrendelés — elérhetőség",
|
||||||
IconClass = "fas fa-calendar-check",
|
IconClass = "fas fa-calendar-check",
|
||||||
Url = _adminMenu.GetMenuItemUrl("PreorderAvailability", "Index")
|
Url = _adminMenu.GetMenuItemUrl("PreOrderAvailability", "Index")
|
||||||
};
|
};
|
||||||
|
|
||||||
//shippingConfigurationItem.ChildNodes.Insert(4, preorderAvailabilityMenuItem);
|
//shippingConfigurationItem.ChildNodes.Insert(4, preorderAvailabilityMenuItem);
|
||||||
|
|
@ -271,10 +271,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
var preorderListMenuItem = new AdminMenuItem
|
var preorderListMenuItem = new AdminMenuItem
|
||||||
{
|
{
|
||||||
Visible = true,
|
Visible = true,
|
||||||
SystemName = "Preorders.List",
|
SystemName = "PreOrders.List",
|
||||||
Title = "Előrendelések",
|
Title = "Előrendelések",
|
||||||
IconClass = "fas fa-calendar-plus",
|
IconClass = "fas fa-calendar-plus",
|
||||||
Url = _adminMenu.GetMenuItemUrl("PreorderAdmin", "List")
|
Url = _adminMenu.GetMenuItemUrl("PreOrderAdmin", "List")
|
||||||
};
|
};
|
||||||
|
|
||||||
var preordersRootMenuItem = new AdminMenuItem
|
var preordersRootMenuItem = new AdminMenuItem
|
||||||
|
|
@ -282,7 +282,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
Visible = true,
|
Visible = true,
|
||||||
SystemName = "FruitBank",
|
SystemName = "FruitBank",
|
||||||
Title = "Előrendelés",
|
Title = "Előrendelés",
|
||||||
//Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.Preorders"), // You can localize this with await _localizationService.GetResourceAsync("...")
|
//Title = await _localizationService.GetResourceAsync("Plugins.Misc.FruitBankPlugin.Menu.PreOrders"), // You can localize this with await _localizationService.GetResourceAsync("...")
|
||||||
IconClass = "fas fa-heart",
|
IconClass = "fas fa-heart",
|
||||||
//Url = _adminMenu.GetMenuItemUrl("Shipping", "List")
|
//Url = _adminMenu.GetMenuItemUrl("Shipping", "List")
|
||||||
//ChildNodes = [shippingsListMenuItem, createShippingMenuItem, editShippingMenuItem]
|
//ChildNodes = [shippingsListMenuItem, createShippingMenuItem, editShippingMenuItem]
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shared service for creating, adding and removing order items.
|
/// Shared service for creating, adding and removing order items.
|
||||||
/// Extracted from CustomOrderController so the same logic can be reused
|
/// Extracted from CustomOrderController so the same logic can be reused
|
||||||
/// by PreorderConversionService without duplication.
|
/// by PreOrderConversionService without duplication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FruitBankOrderItemService
|
public class FruitBankOrderItemService
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ using Nop.Services.Orders;
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Services;
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension methods for product replacement logic on PreorderConversionService.
|
/// Extension methods for product replacement logic on PreOrderConversionService.
|
||||||
///
|
///
|
||||||
/// SETUP REQUIRED: Add this as a partial class by:
|
/// SETUP REQUIRED: Add this as a partial class by:
|
||||||
/// 1. Add `partial` keyword to PreorderConversionService class declaration
|
/// 1. Add `partial` keyword to PreOrderConversionService class declaration
|
||||||
/// 2. This file uses the same injected fields — no extra DI needed
|
/// 2. This file uses the same injected fields — no extra DI needed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PreorderConversionService
|
public partial class PreOrderConversionService
|
||||||
{
|
{
|
||||||
// ── Product replacement ───────────────────────────────────────────────────
|
// ── Product replacement ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -127,32 +127,32 @@ public partial class PreorderConversionService
|
||||||
// ── Swap preorder items ───────────────────────────────────────────────
|
// ── Swap preorder items ───────────────────────────────────────────────
|
||||||
if (affectedOrderIds.Any())
|
if (affectedOrderIds.Any())
|
||||||
{
|
{
|
||||||
var preorders = (await _preorderDbContext.Preorders.GetAll(false).ToListAsync())
|
var preorders = (await _preorderDbContext.PreOrders.GetAll(false).ToListAsync())
|
||||||
.Where(p => p.OrderId.HasValue && affectedOrderIds.Contains(p.OrderId.Value))
|
.Where(p => p.OrderId.HasValue && affectedOrderIds.Contains(p.OrderId.Value))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
foreach (var preorder in preorders)
|
foreach (var preorder in preorders)
|
||||||
{
|
{
|
||||||
var piList = await _preorderDbContext.PreorderItems
|
var piList = await _preorderDbContext.PreOrderItems
|
||||||
.GetAllByPreorderIdAsync(preorder.Id)
|
.GetAllByPreOrderIdAsync(preorder.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
foreach (var pi in piList.Where(i => i.ProductId == oldProductId))
|
foreach (var pi in piList.Where(i => i.ProductId == oldProductId))
|
||||||
{
|
{
|
||||||
pi.ProductId = newProductId;
|
pi.ProductId = newProductId;
|
||||||
await _preorderDbContext.PreorderItems.UpdateAsync(pi);
|
await _preorderDbContext.PreOrderItems.UpdateAsync(pi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Trigger conversion for new product ────────────────────────────────
|
// ── Trigger conversion for new product ────────────────────────────────
|
||||||
// New product may now have pending preorders that can be fulfilled
|
// New product may now have pending preorders that can be fulfilled
|
||||||
await ConvertPreordersForProductsAsync(
|
await ConvertPreOrdersForProductsAsync(
|
||||||
new List<int> { newProductId },
|
new List<int> { newProductId },
|
||||||
oldItem.ShippingDocumentId);
|
oldItem.ShippingDocumentId);
|
||||||
|
|
||||||
// TODO: SignalR notification to admin hub
|
// TODO: SignalR notification to admin hub
|
||||||
// TODO: SendPreorderProductReplacedNotificationAsync per affected customer
|
// TODO: SendPreOrderProductReplacedNotificationAsync per affected customer
|
||||||
|
|
||||||
Console.WriteLine($"[ReplaceShippingItemProduct] Complete: " +
|
Console.WriteLine($"[ReplaceShippingItemProduct] Complete: " +
|
||||||
$"{affectedOrderIds.Count} orders swapped, budget remaining={replacementBudget}");
|
$"{affectedOrderIds.Count} orders swapped, budget remaining={replacementBudget}");
|
||||||
|
|
@ -161,7 +161,7 @@ public partial class PreorderConversionService
|
||||||
private async Task<List<OrderDto>> GetAffectedOpenOrdersAsync(int oldProductId)
|
private async Task<List<OrderDto>> GetAffectedOpenOrdersAsync(int oldProductId)
|
||||||
{
|
{
|
||||||
// Orders that are referenced by a preorder AND contain the old product
|
// Orders that are referenced by a preorder AND contain the old product
|
||||||
var preorderOrderIds = (await _preorderDbContext.Preorders.GetAll(false).ToListAsync())
|
var preorderOrderIds = (await _preorderDbContext.PreOrders.GetAll(false).ToListAsync())
|
||||||
.Where(p => p.OrderId.HasValue)
|
.Where(p => p.OrderId.HasValue)
|
||||||
.Select(p => p.OrderId!.Value)
|
.Select(p => p.OrderId!.Value)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||||
/// Called once per shipping document save, after all IncomingQuantity
|
/// Called once per shipping document save, after all IncomingQuantity
|
||||||
/// attributes have been written for that document's product set.
|
/// attributes have been written for that document's product set.
|
||||||
///
|
///
|
||||||
/// Allocation strategy: first-come-first-served by PreorderId (insertion order).
|
/// Allocation strategy: first-come-first-served by PreOrderId (insertion order).
|
||||||
///
|
///
|
||||||
/// Multi-document design:
|
/// Multi-document design:
|
||||||
/// - Preorder.OrderId tracks the linked real order once created.
|
/// - PreOrder.OrderId tracks the linked real order once created.
|
||||||
/// - First partial fulfillment → creates the order, saves OrderId on Preorder.
|
/// - First partial fulfillment → creates the order, saves OrderId on PreOrder.
|
||||||
/// - Subsequent documents → appends only newly-fulfilled items to that same order.
|
/// - Subsequent documents → appends only newly-fulfilled items to that same order.
|
||||||
/// - Dropped items are recorded in an order note but never become OrderItems.
|
/// - Dropped items are recorded in an order note but never become OrderItems.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PreorderConversionService
|
public partial class PreOrderConversionService
|
||||||
{
|
{
|
||||||
private readonly PreorderDbContext _preorderDbContext;
|
private readonly PreOrderDbContext _preorderDbContext;
|
||||||
private readonly FruitBankDbContext _dbContext;
|
private readonly FruitBankDbContext _dbContext;
|
||||||
private readonly ICustomerService _customerService;
|
private readonly ICustomerService _customerService;
|
||||||
private readonly IProductService _productService;
|
private readonly IProductService _productService;
|
||||||
|
|
@ -45,8 +45,8 @@ public partial class PreorderConversionService
|
||||||
private readonly FruitBankOrderItemService _orderItemService;
|
private readonly FruitBankOrderItemService _orderItemService;
|
||||||
private readonly IStoreContext _storeContext;
|
private readonly IStoreContext _storeContext;
|
||||||
|
|
||||||
public PreorderConversionService(
|
public PreOrderConversionService(
|
||||||
PreorderDbContext preorderDbContext,
|
PreOrderDbContext preorderDbContext,
|
||||||
FruitBankDbContext dbContext,
|
FruitBankDbContext dbContext,
|
||||||
ICustomerService customerService,
|
ICustomerService customerService,
|
||||||
IProductService productService,
|
IProductService productService,
|
||||||
|
|
@ -71,54 +71,54 @@ public partial class PreorderConversionService
|
||||||
|
|
||||||
// ── Entry point ───────────────────────────────────────────────────────────
|
// ── Entry point ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
public async Task ConvertPreordersForProductsAsync(IList<int> productIds, int shippingDocumentId)
|
public async Task ConvertPreOrdersForProductsAsync(IList<int> productIds, int shippingDocumentId)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[PreorderConversion] Starting for {productIds.Count} products, shippingDocumentId={shippingDocumentId}");
|
Console.WriteLine($"[PreOrderConversion] Starting for {productIds.Count} products, shippingDocumentId={shippingDocumentId}");
|
||||||
|
|
||||||
// Always sweep expired preorders first — any preorder whose DateOfReceipt
|
// Always sweep expired preorders first — any preorder whose DateOfReceipt
|
||||||
// is in the past is closed regardless of stock, before we allocate anything
|
// is in the past is closed regardless of stock, before we allocate anything
|
||||||
await SweepExpiredPreordersAsync();
|
await SweepExpiredPreOrdersAsync();
|
||||||
|
|
||||||
var pendingItems = await _preorderDbContext.GetPendingItemsForProductsAsync(productIds);
|
var pendingItems = await _preorderDbContext.GetPendingItemsForProductsAsync(productIds);
|
||||||
if (!pendingItems.Any())
|
if (!pendingItems.Any())
|
||||||
{
|
{
|
||||||
Console.WriteLine("[PreorderConversion] No pending preorder items — done.");
|
Console.WriteLine("[PreOrderConversion] No pending preorder items — done.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out preorders whose delivery date is more than PreorderConversionWindowDays
|
// Filter out preorders whose delivery date is more than PreOrderConversionWindowDays
|
||||||
// (4 days) away. With bi-weekly trucks, a delivery that far out will be served
|
// (4 days) away. With bi-weekly trucks, a delivery that far out will be served
|
||||||
// by the next truck's document — converting now would steal stock from
|
// by the next truck's document — converting now would steal stock from
|
||||||
// earlier deliveries that legitimately need it.
|
// earlier deliveries that legitimately need it.
|
||||||
var conversionCutoff = DateTime.UtcNow.Date.AddDays(FruitBankPluginConst.PreorderConversionWindowDays);
|
var conversionCutoff = DateTime.UtcNow.Date.AddDays(FruitBankPluginConst.PreOrderConversionWindowDays);
|
||||||
var pendingPreorderIds = pendingItems.Select(i => i.PreorderId).Distinct().ToList();
|
var pendingPreOrderIds = pendingItems.Select(i => i.PreOrderId).Distinct().ToList();
|
||||||
var parentPreorders = await _preorderDbContext.Preorders
|
var parentPreOrders = await _preorderDbContext.PreOrders
|
||||||
.GetAll(false)
|
.GetAll(false)
|
||||||
.Where(p => pendingPreorderIds.Contains(p.Id))
|
.Where(p => pendingPreOrderIds.Contains(p.Id))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var eligiblePreorderIds = parentPreorders
|
var eligiblePreOrderIds = parentPreOrders
|
||||||
.Where(p => p.DateOfReceipt.Date <= conversionCutoff)
|
.Where(p => p.DateOfReceipt.Date <= conversionCutoff)
|
||||||
.Select(p => p.Id)
|
.Select(p => p.Id)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
|
|
||||||
pendingItems = pendingItems.Where(i => eligiblePreorderIds.Contains(i.PreorderId)).ToList();
|
pendingItems = pendingItems.Where(i => eligiblePreOrderIds.Contains(i.PreOrderId)).ToList();
|
||||||
|
|
||||||
if (!pendingItems.Any())
|
if (!pendingItems.Any())
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[PreorderConversion] All pending preorders are beyond the " +
|
Console.WriteLine($"[PreOrderConversion] All pending preorders are beyond the " +
|
||||||
$"{FruitBankPluginConst.PreorderConversionWindowDays}-day window — skipped.");
|
$"{FruitBankPluginConst.PreOrderConversionWindowDays}-day window — skipped.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] {pendingItems.Count} items eligible " +
|
Console.WriteLine($"[PreOrderConversion] {pendingItems.Count} items eligible " +
|
||||||
$"(within {FruitBankPluginConst.PreorderConversionWindowDays}-day window).");
|
$"(within {FruitBankPluginConst.PreOrderConversionWindowDays}-day window).");
|
||||||
|
|
||||||
var incomingPool = await BuildIncomingQuantityPoolAsync(productIds);
|
var incomingPool = await BuildIncomingQuantityPoolAsync(productIds);
|
||||||
|
|
||||||
// Track which items were newly resolved in THIS run, grouped by preorder
|
// Track which items were newly resolved in THIS run, grouped by preorder
|
||||||
// Key: preorderId Value: list of items whose status changed in this run
|
// Key: preorderId Value: list of items whose status changed in this run
|
||||||
var newlyResolvedByPreorder = new Dictionary<int, List<PreorderItem>>();
|
var newlyResolvedByPreOrder = new Dictionary<int, List<PreOrderItem>>();
|
||||||
|
|
||||||
foreach (var item in pendingItems)
|
foreach (var item in pendingItems)
|
||||||
{
|
{
|
||||||
|
|
@ -138,49 +138,49 @@ public partial class PreorderConversionService
|
||||||
incomingPool[item.ProductId] -= fulfill;
|
incomingPool[item.ProductId] -= fulfill;
|
||||||
|
|
||||||
item.Status = item.FulfilledQuantity >= item.RequestedQuantity
|
item.Status = item.FulfilledQuantity >= item.RequestedQuantity
|
||||||
? PreorderItemStatus.Fulfilled
|
? PreOrderItemStatus.Fulfilled
|
||||||
: item.FulfilledQuantity > 0
|
: item.FulfilledQuantity > 0
|
||||||
? PreorderItemStatus.PartiallyFulfilled
|
? PreOrderItemStatus.PartiallyFulfilled
|
||||||
: PreorderItemStatus.Dropped;
|
: PreOrderItemStatus.Dropped;
|
||||||
|
|
||||||
await _preorderDbContext.PreorderItems.UpdateAsync(item);
|
await _preorderDbContext.PreOrderItems.UpdateAsync(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only track this item if something actually changed this run
|
// Only track this item if something actually changed this run
|
||||||
// (i.e. it gained fulfilled quantity or got dropped)
|
// (i.e. it gained fulfilled quantity or got dropped)
|
||||||
var gainedQuantity = item.FulfilledQuantity - prevFulfilled;
|
var gainedQuantity = item.FulfilledQuantity - prevFulfilled;
|
||||||
bool wasDropped = item.Status == PreorderItemStatus.Dropped && prevFulfilled == 0;
|
bool wasDropped = item.Status == PreOrderItemStatus.Dropped && prevFulfilled == 0;
|
||||||
|
|
||||||
if (gainedQuantity > 0 || wasDropped)
|
if (gainedQuantity > 0 || wasDropped)
|
||||||
{
|
{
|
||||||
if (!newlyResolvedByPreorder.ContainsKey(item.PreorderId))
|
if (!newlyResolvedByPreOrder.ContainsKey(item.PreOrderId))
|
||||||
newlyResolvedByPreorder[item.PreorderId] = new List<PreorderItem>();
|
newlyResolvedByPreOrder[item.PreOrderId] = new List<PreOrderItem>();
|
||||||
newlyResolvedByPreorder[item.PreorderId].Add(item);
|
newlyResolvedByPreOrder[item.PreOrderId].Add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] Item #{item.Id} (product {item.ProductId}): " +
|
Console.WriteLine($"[PreOrderConversion] Item #{item.Id} (product {item.ProductId}): " +
|
||||||
$"requested={item.RequestedQuantity}, fulfilled={item.FulfilledQuantity}, " +
|
$"requested={item.RequestedQuantity}, fulfilled={item.FulfilledQuantity}, " +
|
||||||
$"gained={item.FulfilledQuantity - prevFulfilled}, status={item.Status}");
|
$"gained={item.FulfilledQuantity - prevFulfilled}, status={item.Status}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process each affected preorder
|
// Process each affected preorder
|
||||||
foreach (var (preorderId, changedItems) in newlyResolvedByPreorder)
|
foreach (var (preorderId, changedItems) in newlyResolvedByPreOrder)
|
||||||
{
|
{
|
||||||
await _preorderDbContext.RefreshPreorderStatusAsync(preorderId);
|
await _preorderDbContext.RefreshPreOrderStatusAsync(preorderId);
|
||||||
|
|
||||||
var preorder = await _preorderDbContext.Preorders.GetByIdAsync(preorderId);
|
var preorder = await _preorderDbContext.PreOrders.GetByIdAsync(preorderId);
|
||||||
if (preorder == null) continue;
|
if (preorder == null) continue;
|
||||||
|
|
||||||
// Items newly gaining fulfilled quantity in this run
|
// Items newly gaining fulfilled quantity in this run
|
||||||
var newlyFulfilled = changedItems
|
var newlyFulfilled = changedItems
|
||||||
.Where(i => i.FulfilledQuantity - 0 > 0 &&
|
.Where(i => i.FulfilledQuantity - 0 > 0 &&
|
||||||
(i.Status == PreorderItemStatus.Fulfilled ||
|
(i.Status == PreOrderItemStatus.Fulfilled ||
|
||||||
i.Status == PreorderItemStatus.PartiallyFulfilled))
|
i.Status == PreOrderItemStatus.PartiallyFulfilled))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Items dropped in this run (no stock at all)
|
// Items dropped in this run (no stock at all)
|
||||||
var newlyDropped = changedItems
|
var newlyDropped = changedItems
|
||||||
.Where(i => i.Status == PreorderItemStatus.Dropped)
|
.Where(i => i.Status == PreOrderItemStatus.Dropped)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (preorder.OrderId == null)
|
if (preorder.OrderId == null)
|
||||||
|
|
@ -198,7 +198,7 @@ public partial class PreorderConversionService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] Done. {newlyResolvedByPreorder.Count} preorders affected.");
|
Console.WriteLine($"[PreOrderConversion] Done. {newlyResolvedByPreOrder.Count} preorders affected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Expiry sweep ───────────────────────────────────────────────────────────
|
// ── Expiry sweep ───────────────────────────────────────────────────────────
|
||||||
|
|
@ -210,71 +210,71 @@ public partial class PreorderConversionService
|
||||||
/// quantities already made it into a real order).
|
/// quantities already made it into a real order).
|
||||||
/// Called at the start of every conversion run.
|
/// Called at the start of every conversion run.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task SweepExpiredPreordersAsync()
|
private async Task SweepExpiredPreOrdersAsync()
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
var activePreorderStatuses = new[] { PreorderStatus.Pending, PreorderStatus.PartiallyFulfilled };
|
var activePreOrderStatuses = new[] { PreOrderStatus.Pending, PreOrderStatus.PartiallyFulfilled };
|
||||||
|
|
||||||
// Find preorders that are past their receipt date — fetch by date only,
|
// Find preorders that are past their receipt date — fetch by date only,
|
||||||
// then filter by status in memory (LinqToDB can't translate enum comparisons)
|
// then filter by status in memory (LinqToDB can't translate enum comparisons)
|
||||||
var expiredPreorders = (await _preorderDbContext.Preorders
|
var expiredPreOrders = (await _preorderDbContext.PreOrders
|
||||||
.GetAll(false)
|
.GetAll(false)
|
||||||
.Where(p => p.DateOfReceipt < now)
|
.Where(p => p.DateOfReceipt < now)
|
||||||
.ToListAsync())
|
.ToListAsync())
|
||||||
.Where(p => p.Status == PreorderStatus.Pending ||
|
.Where(p => p.Status == PreOrderStatus.Pending ||
|
||||||
p.Status == PreorderStatus.PartiallyFulfilled)
|
p.Status == PreOrderStatus.PartiallyFulfilled)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (!expiredPreorders.Any()) return;
|
if (!expiredPreOrders.Any()) return;
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] Sweeping {expiredPreorders.Count} expired preorders");
|
Console.WriteLine($"[PreOrderConversion] Sweeping {expiredPreOrders.Count} expired preorders");
|
||||||
|
|
||||||
foreach (var preorder in expiredPreorders)
|
foreach (var preorder in expiredPreOrders)
|
||||||
{
|
{
|
||||||
var items = await _preorderDbContext.PreorderItems
|
var items = await _preorderDbContext.PreOrderItems
|
||||||
.GetAllByPreorderIdAsync(preorder.Id)
|
.GetAllByPreOrderIdAsync(preorder.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
// Drop only the items that were never fulfilled — already-fulfilled
|
// Drop only the items that were never fulfilled — already-fulfilled
|
||||||
// items stay as-is since they are already on a real order
|
// items stay as-is since they are already on a real order
|
||||||
var stillPending = items.Where(i => i.Status == PreorderItemStatus.Pending).ToList();
|
var stillPending = items.Where(i => i.Status == PreOrderItemStatus.Pending).ToList();
|
||||||
foreach (var item in stillPending)
|
foreach (var item in stillPending)
|
||||||
{
|
{
|
||||||
item.Status = PreorderItemStatus.Dropped;
|
item.Status = PreOrderItemStatus.Dropped;
|
||||||
await _preorderDbContext.PreorderItems.UpdateAsync(item);
|
await _preorderDbContext.PreOrderItems.UpdateAsync(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recalculate header status
|
// Recalculate header status
|
||||||
await _preorderDbContext.RefreshPreorderStatusAsync(preorder.Id);
|
await _preorderDbContext.RefreshPreOrderStatusAsync(preorder.Id);
|
||||||
|
|
||||||
var hadAnyFulfillment = items.Any(i =>
|
var hadAnyFulfillment = items.Any(i =>
|
||||||
i.Status == PreorderItemStatus.Fulfilled ||
|
i.Status == PreOrderItemStatus.Fulfilled ||
|
||||||
i.Status == PreorderItemStatus.PartiallyFulfilled);
|
i.Status == PreOrderItemStatus.PartiallyFulfilled);
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] Expired preorder #{preorder.Id}: " +
|
Console.WriteLine($"[PreOrderConversion] Expired preorder #{preorder.Id}: " +
|
||||||
$"{stillPending.Count} items dropped, " +
|
$"{stillPending.Count} items dropped, " +
|
||||||
$"hadFulfillment={hadAnyFulfillment}, orderId={preorder.OrderId}");
|
$"hadFulfillment={hadAnyFulfillment}, orderId={preorder.OrderId}");
|
||||||
|
|
||||||
// TODO: Send expiry notification if nothing was ever fulfilled
|
// TODO: Send expiry notification if nothing was ever fulfilled
|
||||||
// (fully unfulfilled preorders — customer should be notified)
|
// (fully unfulfilled preorders — customer should be notified)
|
||||||
// if (!hadAnyFulfillment)
|
// if (!hadAnyFulfillment)
|
||||||
// await _fruitBankNotificationService.SendPreorderExpiredNotificationAsync(preorder);
|
// await _fruitBankNotificationService.SendPreOrderExpiredNotificationAsync(preorder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Create new order (first document that fulfills anything) ──────────────
|
// ── Create new order (first document that fulfills anything) ──────────────
|
||||||
|
|
||||||
private async Task CreateOrderAsync(
|
private async Task CreateOrderAsync(
|
||||||
Preorder preorder,
|
PreOrder preorder,
|
||||||
List<PreorderItem> fulfilledItems,
|
List<PreOrderItem> fulfilledItems,
|
||||||
List<PreorderItem> droppedItems,
|
List<PreOrderItem> droppedItems,
|
||||||
int shippingDocumentId)
|
int shippingDocumentId)
|
||||||
{
|
{
|
||||||
var customer = await _customerService.GetCustomerByIdAsync(preorder.CustomerId);
|
var customer = await _customerService.GetCustomerByIdAsync(preorder.CustomerId);
|
||||||
if (customer == null)
|
if (customer == null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[PreorderConversion] Customer {preorder.CustomerId} not found — skipping order creation for preorder #{preorder.Id}");
|
Console.WriteLine($"[PreOrderConversion] Customer {preorder.CustomerId} not found — skipping order creation for preorder #{preorder.Id}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,7 +289,7 @@ public partial class PreorderConversionService
|
||||||
|
|
||||||
if (billingAddressId == 0)
|
if (billingAddressId == 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[PreorderConversion] No billing address for customer {customer.Id} — skipping for preorder #{preorder.Id}");
|
Console.WriteLine($"[PreOrderConversion] No billing address for customer {customer.Id} — skipping for preorder #{preorder.Id}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,10 +351,10 @@ public partial class PreorderConversionService
|
||||||
order.CustomOrderNumber = order.Id.ToString();
|
order.CustomOrderNumber = order.Id.ToString();
|
||||||
await _dbContext.Orders.UpdateAsync(order);
|
await _dbContext.Orders.UpdateAsync(order);
|
||||||
|
|
||||||
// Save OrderId back on the Preorder so future documents can find it
|
// Save OrderId back on the PreOrder so future documents can find it
|
||||||
preorder.OrderId = order.Id;
|
preorder.OrderId = order.Id;
|
||||||
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
preorder.UpdatedOnUtc = DateTime.UtcNow;
|
||||||
await _preorderDbContext.Preorders.UpdateAsync(preorder);
|
await _preorderDbContext.PreOrders.UpdateAsync(preorder);
|
||||||
|
|
||||||
// DateOfReceipt generic attribute
|
// DateOfReceipt generic attribute
|
||||||
await _dbContext.GenericAttributes.InsertAsync(new Nop.Core.Domain.Common.GenericAttribute
|
await _dbContext.GenericAttributes.InsertAsync(new Nop.Core.Domain.Common.GenericAttribute
|
||||||
|
|
@ -370,26 +370,26 @@ public partial class PreorderConversionService
|
||||||
// Fire event so existing handlers (EventConsumer etc.) run
|
// Fire event so existing handlers (EventConsumer etc.) run
|
||||||
await _eventPublisher.PublishAsync(new OrderPlacedEvent(order));
|
await _eventPublisher.PublishAsync(new OrderPlacedEvent(order));
|
||||||
|
|
||||||
// TODO: Send "FruitBank.PreorderConverted.CustomerNotification" email
|
// TODO: Send "FruitBank.PreOrderConverted.CustomerNotification" email
|
||||||
// summarising fulfilled items, dropped items, order ID, DateOfReceipt
|
// summarising fulfilled items, dropped items, order ID, DateOfReceipt
|
||||||
// await _fruitBankNotificationService.SendPreorderConvertedNotificationAsync(order, preorder, fulfilledItems, droppedItems);
|
// await _fruitBankNotificationService.SendPreOrderConvertedNotificationAsync(order, preorder, fulfilledItems, droppedItems);
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] Created Order #{order.Id} from Preorder #{preorder.Id} — " +
|
Console.WriteLine($"[PreOrderConversion] Created Order #{order.Id} from PreOrder #{preorder.Id} — " +
|
||||||
$"{fulfilledItems.Count} fulfilled, {droppedItems.Count} dropped, total {orderTotal:N0} Ft");
|
$"{fulfilledItems.Count} fulfilled, {droppedItems.Count} dropped, total {orderTotal:N0} Ft");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Append to existing order (subsequent documents) ───────────────────────
|
// ── Append to existing order (subsequent documents) ───────────────────────
|
||||||
|
|
||||||
private async Task AppendItemsToOrderAsync(
|
private async Task AppendItemsToOrderAsync(
|
||||||
Preorder preorder,
|
PreOrder preorder,
|
||||||
List<PreorderItem> newlyFulfilled,
|
List<PreOrderItem> newlyFulfilled,
|
||||||
List<PreorderItem> newlyDropped,
|
List<PreOrderItem> newlyDropped,
|
||||||
int shippingDocumentId)
|
int shippingDocumentId)
|
||||||
{
|
{
|
||||||
var order = await _dbContext.Orders.GetByIdAsync(preorder.OrderId!.Value);
|
var order = await _dbContext.Orders.GetByIdAsync(preorder.OrderId!.Value);
|
||||||
if (order == null)
|
if (order == null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[PreorderConversion] Preorder #{preorder.Id} references Order #{preorder.OrderId} which no longer exists — creating fresh");
|
Console.WriteLine($"[PreOrderConversion] PreOrder #{preorder.Id} references Order #{preorder.OrderId} which no longer exists — creating fresh");
|
||||||
preorder.OrderId = null;
|
preorder.OrderId = null;
|
||||||
await CreateOrderAsync(preorder, newlyFulfilled, newlyDropped, shippingDocumentId);
|
await CreateOrderAsync(preorder, newlyFulfilled, newlyDropped, shippingDocumentId);
|
||||||
return;
|
return;
|
||||||
|
|
@ -397,7 +397,7 @@ public partial class PreorderConversionService
|
||||||
|
|
||||||
if (!newlyFulfilled.Any() && !newlyDropped.Any())
|
if (!newlyFulfilled.Any() && !newlyDropped.Any())
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[PreorderConversion] Preorder #{preorder.Id}: no new items to append to Order #{order.Id}");
|
Console.WriteLine($"[PreOrderConversion] PreOrder #{preorder.Id}: no new items to append to Order #{order.Id}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -422,16 +422,16 @@ public partial class PreorderConversionService
|
||||||
await InsertOrderNoteAsync(order.Id, preorder.Id, shippingDocumentId, newlyFulfilled, newlyDropped);
|
await InsertOrderNoteAsync(order.Id, preorder.Id, shippingDocumentId, newlyFulfilled, newlyDropped);
|
||||||
|
|
||||||
// TODO: Send update notification email (same template as initial, but framed as an update)
|
// TODO: Send update notification email (same template as initial, but framed as an update)
|
||||||
// await _fruitBankNotificationService.SendPreorderConvertedNotificationAsync(order, preorder, newlyFulfilled, newlyDropped);
|
// await _fruitBankNotificationService.SendPreOrderConvertedNotificationAsync(order, preorder, newlyFulfilled, newlyDropped);
|
||||||
|
|
||||||
Console.WriteLine($"[PreorderConversion] Appended {newlyFulfilled.Count} items to Order #{order.Id} " +
|
Console.WriteLine($"[PreOrderConversion] Appended {newlyFulfilled.Count} items to Order #{order.Id} " +
|
||||||
$"from Preorder #{preorder.Id} via document #{shippingDocumentId}. " +
|
$"from PreOrder #{preorder.Id} via document #{shippingDocumentId}. " +
|
||||||
$"New total: {newTotal:N0} Ft");
|
$"New total: {newTotal:N0} Ft");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Shared helpers ────────────────────────────────────────────────────────
|
// ── Shared helpers ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private async Task InsertOrderItemsAsync(Order order, List<PreorderItem> items)
|
private async Task InsertOrderItemsAsync(Order order, List<PreOrderItem> items)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
|
|
@ -475,13 +475,13 @@ public partial class PreorderConversionService
|
||||||
product,
|
product,
|
||||||
-item.FulfilledQuantity,
|
-item.FulfilledQuantity,
|
||||||
string.Empty,
|
string.Empty,
|
||||||
$"Előrendelés #{item.PreorderId} — rendelés #{order.Id} létrehozása");
|
$"Előrendelés #{item.PreOrderId} — rendelés #{order.Id} létrehozása");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task InsertOrderNoteAsync(
|
private async Task InsertOrderNoteAsync(
|
||||||
int orderId, int preorderId, int shippingDocumentId,
|
int orderId, int preorderId, int shippingDocumentId,
|
||||||
List<PreorderItem> fulfilled, List<PreorderItem> dropped)
|
List<PreOrderItem> fulfilled, List<PreOrderItem> dropped)
|
||||||
{
|
{
|
||||||
var fulfilledDesc = fulfilled.Any()
|
var fulfilledDesc = fulfilled.Any()
|
||||||
? $"Teljesített: {string.Join(", ", fulfilled.Select(i => $"#{i.ProductId} ({i.FulfilledQuantity} db)"))}"
|
? $"Teljesített: {string.Join(", ", fulfilled.Select(i => $"#{i.ProductId} ({i.FulfilledQuantity} db)"))}"
|
||||||
|
|
@ -521,10 +521,10 @@ public partial class PreorderConversionService
|
||||||
await _fruitBankAttributeService
|
await _fruitBankAttributeService
|
||||||
.InsertOrUpdateGenericAttributeAsync<Product, int>(
|
.InsertOrUpdateGenericAttributeAsync<Product, int>(
|
||||||
productId, nameof(IIncomingQuantity.IncomingQuantity), updated, storeId);
|
productId, nameof(IIncomingQuantity.IncomingQuantity), updated, storeId);
|
||||||
Console.WriteLine($"[PreorderConversion] SyncIncomingQty product #{productId}: {current}+({delta})={updated}");
|
Console.WriteLine($"[PreOrderConversion] SyncIncomingQty product #{productId}: {current}+({delta})={updated}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<decimal> CalculateTotalAsync(List<PreorderItem> items)
|
private async Task<decimal> CalculateTotalAsync(List<PreOrderItem> items)
|
||||||
{
|
{
|
||||||
var total = 0m;
|
var total = 0m;
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
|
|
@ -549,17 +549,17 @@ public partial class PreorderConversionService
|
||||||
p => p.Id,
|
p => p.Id,
|
||||||
p => p.AvailableQuantity);
|
p => p.AvailableQuantity);
|
||||||
|
|
||||||
var activeItemStatuses = new[] { PreorderItemStatus.Fulfilled, PreorderItemStatus.PartiallyFulfilled };
|
var activeItemStatuses = new[] { PreOrderItemStatus.Fulfilled, PreOrderItemStatus.PartiallyFulfilled };
|
||||||
|
|
||||||
// 2. Subtract quantities already committed to preorders in previous runs
|
// 2. Subtract quantities already committed to preorders in previous runs
|
||||||
// Fetch by productId only, filter by status in memory
|
// Fetch by productId only, filter by status in memory
|
||||||
var allCommittedItems = await _preorderDbContext.PreorderItems.Table
|
var allCommittedItems = await _preorderDbContext.PreOrderItems.Table
|
||||||
.Where(i => productIds.Contains(i.ProductId))
|
.Where(i => productIds.Contains(i.ProductId))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var alreadyAllocated = allCommittedItems
|
var alreadyAllocated = allCommittedItems
|
||||||
.Where(i => i.Status == PreorderItemStatus.Fulfilled ||
|
.Where(i => i.Status == PreOrderItemStatus.Fulfilled ||
|
||||||
i.Status == PreorderItemStatus.PartiallyFulfilled)
|
i.Status == PreOrderItemStatus.PartiallyFulfilled)
|
||||||
.GroupBy(i => i.ProductId)
|
.GroupBy(i => i.ProductId)
|
||||||
.Select(g => new { ProductId = g.Key, Allocated = g.Sum(i => i.FulfilledQuantity) })
|
.Select(g => new { ProductId = g.Key, Allocated = g.Sum(i => i.FulfilledQuantity) })
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
@using FruitBank.Common.Enums
|
@using FruitBank.Common.Enums
|
||||||
@using Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
@using Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
||||||
@model List<CustomerPreorderController.CustomerPreorderRow>
|
@model List<CustomerPreOrderController.CustomerPreOrderRow>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
Layout = "_ColumnsTwo";
|
Layout = "_ColumnsTwo";
|
||||||
|
|
@ -28,23 +28,23 @@
|
||||||
{
|
{
|
||||||
var statusClass = preorder.Status switch
|
var statusClass = preorder.Status switch
|
||||||
{
|
{
|
||||||
PreorderStatus.Confirmed => "po-status-confirmed",
|
PreOrderStatus.Confirmed => "po-status-confirmed",
|
||||||
PreorderStatus.PartiallyFulfilled => "po-status-partial",
|
PreOrderStatus.PartiallyFulfilled => "po-status-partial",
|
||||||
PreorderStatus.Cancelled => "po-status-cancelled",
|
PreOrderStatus.Cancelled => "po-status-cancelled",
|
||||||
_ => "po-status-pending"
|
_ => "po-status-pending"
|
||||||
};
|
};
|
||||||
var statusLabel = preorder.Status switch
|
var statusLabel = preorder.Status switch
|
||||||
{
|
{
|
||||||
PreorderStatus.Confirmed => "Megerősítve",
|
PreOrderStatus.Confirmed => "Megerősítve",
|
||||||
PreorderStatus.PartiallyFulfilled => "Részben teljesítve",
|
PreOrderStatus.PartiallyFulfilled => "Részben teljesítve",
|
||||||
PreorderStatus.Cancelled => "Törölve / Lejárt",
|
PreOrderStatus.Cancelled => "Törölve / Lejárt",
|
||||||
_ => "Függőben"
|
_ => "Függőben"
|
||||||
};
|
};
|
||||||
|
|
||||||
<div class="po-customer-card">
|
<div class="po-customer-card">
|
||||||
<div class="po-card-header">
|
<div class="po-card-header">
|
||||||
<div class="po-card-meta">
|
<div class="po-card-meta">
|
||||||
<span class="po-card-id">#@preorder.PreorderId előrendelés</span>
|
<span class="po-card-id">#@preorder.PreOrderId előrendelés</span>
|
||||||
<span class="po-card-date">
|
<span class="po-card-date">
|
||||||
<i class="fa fa-calendar"></i>
|
<i class="fa fa-calendar"></i>
|
||||||
Kért szállítás: <strong>@preorder.DateOfReceipt.ToLocalTime().ToString("yyyy. MM. dd. HH:mm")</strong>
|
Kért szállítás: <strong>@preorder.DateOfReceipt.ToLocalTime().ToString("yyyy. MM. dd. HH:mm")</strong>
|
||||||
|
|
@ -89,16 +89,16 @@
|
||||||
{
|
{
|
||||||
var itemStatusLabel = item.Status switch
|
var itemStatusLabel = item.Status switch
|
||||||
{
|
{
|
||||||
PreorderItemStatus.Fulfilled => "✓ Teljesítve",
|
PreOrderItemStatus.Fulfilled => "✓ Teljesítve",
|
||||||
PreorderItemStatus.PartiallyFulfilled => "◑ Részben",
|
PreOrderItemStatus.PartiallyFulfilled => "◑ Részben",
|
||||||
PreorderItemStatus.Dropped => "✕ Ejtve",
|
PreOrderItemStatus.Dropped => "✕ Ejtve",
|
||||||
_ => "⏳ Vár"
|
_ => "⏳ Vár"
|
||||||
};
|
};
|
||||||
var itemStatusClass = item.Status switch
|
var itemStatusClass = item.Status switch
|
||||||
{
|
{
|
||||||
PreorderItemStatus.Fulfilled => "item-fulfilled",
|
PreOrderItemStatus.Fulfilled => "item-fulfilled",
|
||||||
PreorderItemStatus.PartiallyFulfilled => "item-partial",
|
PreOrderItemStatus.PartiallyFulfilled => "item-partial",
|
||||||
PreorderItemStatus.Dropped => "item-dropped",
|
PreOrderItemStatus.Dropped => "item-dropped",
|
||||||
_ => "item-pending"
|
_ => "item-pending"
|
||||||
};
|
};
|
||||||
var unitPrice = item.IsMeasurable
|
var unitPrice = item.IsMeasurable
|
||||||
|
|
@ -147,7 +147,7 @@
|
||||||
|
|
||||||
.no-data p { margin-bottom: 16px; font-size: 15px; }
|
.no-data p { margin-bottom: 16px; font-size: 15px; }
|
||||||
|
|
||||||
/* ── Preorder card ────────────────────────────────────────────── */
|
/* ── PreOrder card ────────────────────────────────────────────── */
|
||||||
.po-customer-card {
|
.po-customer-card {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #dde8da;
|
border: 1px solid #dde8da;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<li class="customer-navigation-item @(Context.Request.Path.Value?.Contains("elorerendeles") == true ? "active" : "")">
|
<li class="customer-navigation-item @(Context.Request.Path.Value?.Contains("elorerendeles") == true ? "active" : "")">
|
||||||
<a href="@Url.Action("List", "CustomerPreorder")">
|
<a href="@Url.Action("List", "CustomerPreOrder")">
|
||||||
Előrendeléseim
|
Előrendeléseim
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -499,7 +499,7 @@
|
||||||
<i class="fa fa-chevron-down"></i>
|
<i class="fa fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="help-faq-a">
|
<div class="help-faq-a">
|
||||||
A <a href="@Url.Action("List", "CustomerPreorder")" style="color:#2d7a3a;font-weight:600;">Saját fiók → Előrendeléseim</a> oldalon látod az összes leadott előrendelést, azok állapotát és a létrejött rendelésekre mutató hivatkozást.
|
A <a href="@Url.Action("List", "CustomerPreOrder")" style="color:#2d7a3a;font-weight:600;">Saját fiók → Előrendeléseim</a> oldalon látod az összes leadott előrendelést, azok állapotát és a létrejött rendelésekre mutató hivatkozást.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@
|
||||||
</div><!-- /#sectionQuickOrder -->
|
</div><!-- /#sectionQuickOrder -->
|
||||||
|
|
||||||
<!-- ══ PREORDER SECTION ══════════════════════════════════════════════ -->
|
<!-- ══ PREORDER SECTION ══════════════════════════════════════════════ -->
|
||||||
<div id="sectionPreorder" style="display:none;">
|
<div id="sectionPreOrder" style="display:none;">
|
||||||
<div class="po-info-banner">
|
<div class="po-info-banner">
|
||||||
<i class="fa fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
Az előrendelés egy kívánságlista — az áruk megerősítése a szállítmány beérkezésekor történik, és az esetleges változásokról értesítünk.
|
Az előrendelés egy kívánságlista — az áruk megerősítése a szállítmány beérkezésekor történik, és az esetleges változásokról értesítünk.
|
||||||
|
|
@ -211,7 +211,7 @@
|
||||||
|
|
||||||
<div class="po-submit-row">
|
<div class="po-submit-row">
|
||||||
<div id="poSelectionSummary" class="po-selection-summary">Még nincs kiválasztott termék</div>
|
<div id="poSelectionSummary" class="po-selection-summary">Még nincs kiválasztott termék</div>
|
||||||
<button type="button" id="submitPreorderBtn" class="po-submit-btn" disabled>
|
<button type="button" id="submitPreOrderBtn" class="po-submit-btn" disabled>
|
||||||
<i class="fa fa-paper-plane"></i> Előrendelés leadása
|
<i class="fa fa-paper-plane"></i> Előrendelés leadása
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -237,11 +237,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /#sectionPreorder -->
|
</div><!-- /#sectionPreOrder -->
|
||||||
|
|
||||||
</div><!-- /#mainContent -->
|
</div><!-- /#mainContent -->
|
||||||
|
|
||||||
<!-- ── Preorder success state ────────────────────────────────────────── -->
|
<!-- ── PreOrder success state ────────────────────────────────────────── -->
|
||||||
<div id="successState" style="display:none;" class="po-success-state">
|
<div id="successState" style="display:none;" class="po-success-state">
|
||||||
<div class="po-success-icon"><i class="fa fa-check-circle"></i></div>
|
<div class="po-success-icon"><i class="fa fa-check-circle"></i></div>
|
||||||
<h2>Előrendelés leadva!</h2>
|
<h2>Előrendelés leadva!</h2>
|
||||||
|
|
@ -408,7 +408,7 @@
|
||||||
}
|
}
|
||||||
@@keyframes fadeIn { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
|
@@keyframes fadeIn { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
|
||||||
|
|
||||||
/* ── Preorder stock label variant ──────────────────────────── */
|
/* ── PreOrder stock label variant ──────────────────────────── */
|
||||||
.pc-stock.stock-preorder {
|
.pc-stock.stock-preorder {
|
||||||
color: #c87500;
|
color: #c87500;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
@ -500,7 +500,7 @@
|
||||||
var selectedDayLabel = null;
|
var selectedDayLabel = null;
|
||||||
var currentFlowType = null; // "quickorder" | "preorder"
|
var currentFlowType = null; // "quickorder" | "preorder"
|
||||||
|
|
||||||
// Preorder state
|
// PreOrder state
|
||||||
var poProducts = [];
|
var poProducts = [];
|
||||||
var poQuantities = {};
|
var poQuantities = {};
|
||||||
|
|
||||||
|
|
@ -547,7 +547,7 @@
|
||||||
|
|
||||||
$('#recordBtn').click(startRecording);
|
$('#recordBtn').click(startRecording);
|
||||||
$('#stopBtn').click(function () { stopRecording(false); });
|
$('#stopBtn').click(function () { stopRecording(false); });
|
||||||
$('#submitPreorderBtn').click(submitPreorder);
|
$('#submitPreOrderBtn').click(submitPreOrder);
|
||||||
|
|
||||||
// Restore saved datetime
|
// Restore saved datetime
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
@ -674,12 +674,12 @@
|
||||||
|
|
||||||
if (flowType === 'quickorder') {
|
if (flowType === 'quickorder') {
|
||||||
$('#sectionQuickOrder').show();
|
$('#sectionQuickOrder').show();
|
||||||
$('#sectionPreorder').hide();
|
$('#sectionPreOrder').hide();
|
||||||
loadAllProducts();
|
loadAllProducts();
|
||||||
} else {
|
} else {
|
||||||
$('#sectionPreorder').show();
|
$('#sectionPreOrder').show();
|
||||||
$('#sectionQuickOrder').hide();
|
$('#sectionQuickOrder').hide();
|
||||||
loadPreorderProducts();
|
loadPreOrderProducts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -942,21 +942,21 @@
|
||||||
// PREORDER FLOW
|
// PREORDER FLOW
|
||||||
// ══════════════════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
function loadPreorderProducts() {
|
function loadPreOrderProducts() {
|
||||||
$('#poLoadingState').show(); $('#poNoProducts, #poProductSection').hide();
|
$('#poLoadingState').show(); $('#poNoProducts, #poProductSection').hide();
|
||||||
|
|
||||||
$.ajax({ url: '@Url.Action("GetPreorderProducts", "Order")', type: 'GET',
|
$.ajax({ url: '@Url.Action("GetPreOrderProducts", "Order")', type: 'GET',
|
||||||
success: function (r) {
|
success: function (r) {
|
||||||
$('#poLoadingState').hide();
|
$('#poLoadingState').hide();
|
||||||
if (!r.success || !r.products || r.products.length === 0) { $('#poNoProducts').show(); return; }
|
if (!r.success || !r.products || r.products.length === 0) { $('#poNoProducts').show(); return; }
|
||||||
poProducts = r.products; poQuantities = {};
|
poProducts = r.products; poQuantities = {};
|
||||||
renderPreorderProducts(); $('#poProductSection').show();
|
renderPreOrderProducts(); $('#poProductSection').show();
|
||||||
},
|
},
|
||||||
error: function () { $('#poLoadingState').hide(); $('#poNoProducts').show(); }
|
error: function () { $('#poLoadingState').hide(); $('#poNoProducts').show(); }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPreorderProducts() {
|
function renderPreOrderProducts() {
|
||||||
var grid = $('#poProductGrid').empty();
|
var grid = $('#poProductGrid').empty();
|
||||||
$.each(poProducts, function (_, p) {
|
$.each(poProducts, function (_, p) {
|
||||||
poQuantities[p.id] = 0;
|
poQuantities[p.id] = 0;
|
||||||
|
|
@ -1004,7 +1004,7 @@
|
||||||
var selected = poProducts.filter(function (p) { return (poQuantities[p.id] || 0) > 0; });
|
var selected = poProducts.filter(function (p) { return (poQuantities[p.id] || 0) > 0; });
|
||||||
var count = selected.length;
|
var count = selected.length;
|
||||||
$('#poItemCountBadge').text(count);
|
$('#poItemCountBadge').text(count);
|
||||||
$('#submitPreorderBtn').prop('disabled', count === 0);
|
$('#submitPreOrderBtn').prop('disabled', count === 0);
|
||||||
$('#poSelectionSummary').text(count === 0 ? 'Még nincs kiválasztott termék' : count + ' termék kiválasztva');
|
$('#poSelectionSummary').text(count === 0 ? 'Még nincs kiválasztott termék' : count + ' termék kiválasztva');
|
||||||
|
|
||||||
if (count === 0) { $('#poSummaryEmpty').show(); $('#poSummaryList, #poSummaryNote').hide(); return; }
|
if (count === 0) { $('#poSummaryEmpty').show(); $('#poSummaryList, #poSummaryNote').hide(); return; }
|
||||||
|
|
@ -1022,14 +1022,14 @@
|
||||||
if (hasMeasurable) $('#poSummaryNote').show(); else $('#poSummaryNote').hide();
|
if (hasMeasurable) $('#poSummaryNote').show(); else $('#poSummaryNote').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitPreorder() {
|
function submitPreOrder() {
|
||||||
var selected = poProducts.filter(function (p) { return (poQuantities[p.id] || 0) > 0; });
|
var selected = poProducts.filter(function (p) { return (poQuantities[p.id] || 0) > 0; });
|
||||||
if (!selected.length) return;
|
if (!selected.length) return;
|
||||||
var btn = $('#submitPreorderBtn').prop('disabled', true)
|
var btn = $('#submitPreOrderBtn').prop('disabled', true)
|
||||||
.html('<i class="fa fa-spinner fa-spin"></i> Előrendelés mentése...');
|
.html('<i class="fa fa-spinner fa-spin"></i> Előrendelés mentése...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : '@Url.Action("PlacePreorder", "Order")',
|
url : '@Url.Action("PlacePreOrder", "Order")',
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
data : JSON.stringify({
|
data : JSON.stringify({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
@using System.Text.Encodings.Web
|
@using System.Text.Encodings.Web
|
||||||
@{
|
@{
|
||||||
Layout = "_Root";
|
Layout = "_Root";
|
||||||
ViewBag.Title = T("Plugins.Misc.FruitBankPlugin.Preorder.PageTitle").Text;
|
ViewBag.Title = T("Plugins.Misc.FruitBankPlugin.PreOrder.PageTitle").Text;
|
||||||
}
|
}
|
||||||
|
|
||||||
<link rel="stylesheet" href="~/Plugins/Misc.FruitBankPlugin/css/quick-order.css" />
|
<link rel="stylesheet" href="~/Plugins/Misc.FruitBankPlugin/css/quick-order.css" />
|
||||||
|
|
@ -14,25 +14,25 @@
|
||||||
<div class="ds-header">
|
<div class="ds-header">
|
||||||
<i class="fa fa-calendar"></i>
|
<i class="fa fa-calendar"></i>
|
||||||
<div>
|
<div>
|
||||||
<div class="ds-title">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Title")</div>
|
<div class="ds-title">@T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Title")</div>
|
||||||
<div class="ds-subtitle">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Subtitle")</div>
|
<div class="ds-subtitle">@T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Subtitle")</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ds-body">
|
<div class="ds-body">
|
||||||
<div class="ds-section-label">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.DayLabel")</div>
|
<div class="ds-section-label">@T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.DayLabel")</div>
|
||||||
<div class="ds-day-buttons" id="dayButtons"></div>
|
<div class="ds-day-buttons" id="dayButtons"></div>
|
||||||
|
|
||||||
<div class="ds-section-label" style="margin-top:20px;">
|
<div class="ds-section-label" style="margin-top:20px;">
|
||||||
@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeLabel")
|
@T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeLabel")
|
||||||
</div>
|
</div>
|
||||||
<div class="ds-time-wrapper">
|
<div class="ds-time-wrapper">
|
||||||
<input type="time" id="deliveryTimePicker" class="ds-time-input" value="08:00" min="05:00" max="22:00" />
|
<input type="time" id="deliveryTimePicker" class="ds-time-input" value="08:00" min="05:00" max="22:00" />
|
||||||
<span class="ds-time-hint">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeHint")</span>
|
<span class="ds-time-hint">@T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.TimeHint")</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ds-footer">
|
<div class="ds-footer">
|
||||||
<button type="button" class="ds-confirm-btn" id="deliveryConfirmBtn" disabled>
|
<button type="button" class="ds-confirm-btn" id="deliveryConfirmBtn" disabled>
|
||||||
<i class="fa fa-arrow-right"></i> @T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton")
|
<i class="fa fa-arrow-right"></i> @T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ConfirmButton")
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -40,10 +40,10 @@
|
||||||
<!-- ── Delivery chip ────────────────────────────────────────────────── -->
|
<!-- ── Delivery chip ────────────────────────────────────────────────── -->
|
||||||
<div id="deliveryChip" class="qo-delivery-chip" style="display:none;">
|
<div id="deliveryChip" class="qo-delivery-chip" style="display:none;">
|
||||||
<i class="fa fa-calendar-check-o"></i>
|
<i class="fa fa-calendar-check-o"></i>
|
||||||
<span class="dc-label">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeLabel")</span>
|
<span class="dc-label">@T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeLabel")</span>
|
||||||
<strong id="deliveryChipText"></strong>
|
<strong id="deliveryChipText"></strong>
|
||||||
<button type="button" class="dc-change-btn" id="deliveryChangeBtn">
|
<button type="button" class="dc-change-btn" id="deliveryChangeBtn">
|
||||||
<i class="fa fa-pencil"></i> @T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeButton")
|
<i class="fa fa-pencil"></i> @T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ChangeButton")
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
<!-- Info banner -->
|
<!-- Info banner -->
|
||||||
<div class="po-info-banner">
|
<div class="po-info-banner">
|
||||||
<i class="fa fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
@T("Plugins.Misc.FruitBankPlugin.Preorder.InfoBanner")
|
@T("Plugins.Misc.FruitBankPlugin.PreOrder.InfoBanner")
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="qo-layout">
|
<div class="qo-layout">
|
||||||
|
|
@ -64,20 +64,20 @@
|
||||||
<!-- Loading -->
|
<!-- Loading -->
|
||||||
<div id="productsLoadingState" class="products-empty-state">
|
<div id="productsLoadingState" class="products-empty-state">
|
||||||
<i class="fa fa-spinner fa-spin"></i>
|
<i class="fa fa-spinner fa-spin"></i>
|
||||||
<p>@T("Plugins.Misc.FruitBankPlugin.Preorder.LoadingProducts")</p>
|
<p>@T("Plugins.Misc.FruitBankPlugin.PreOrder.LoadingProducts")</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- No products -->
|
<!-- No products -->
|
||||||
<div id="noProductsCard" class="no-results-card" style="display:none;">
|
<div id="noProductsCard" class="no-results-card" style="display:none;">
|
||||||
<i class="fa fa-calendar-times-o"></i>
|
<i class="fa fa-calendar-times-o"></i>
|
||||||
<p>@T("Plugins.Misc.FruitBankPlugin.Preorder.NoProductsAvailable")</p>
|
<p>@T("Plugins.Misc.FruitBankPlugin.PreOrder.NoProductsAvailable")</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Product grid -->
|
<!-- Product grid -->
|
||||||
<div id="productSection" style="display:none;">
|
<div id="productSection" style="display:none;">
|
||||||
<div class="matches-label">
|
<div class="matches-label">
|
||||||
<i class="fa fa-cubes"></i>
|
<i class="fa fa-cubes"></i>
|
||||||
<span>@T("Plugins.Misc.FruitBankPlugin.Preorder.ProductsLabel")</span>
|
<span>@T("Plugins.Misc.FruitBankPlugin.PreOrder.ProductsLabel")</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="productGrid" class="product-grid"></div>
|
<div id="productGrid" class="product-grid"></div>
|
||||||
|
|
||||||
|
|
@ -85,19 +85,19 @@
|
||||||
<div class="po-note-section">
|
<div class="po-note-section">
|
||||||
<label class="po-note-label" for="customerNote">
|
<label class="po-note-label" for="customerNote">
|
||||||
<i class="fa fa-comment-o"></i>
|
<i class="fa fa-comment-o"></i>
|
||||||
@T("Plugins.Misc.FruitBankPlugin.Preorder.NoteLabel")
|
@T("Plugins.Misc.FruitBankPlugin.PreOrder.NoteLabel")
|
||||||
</label>
|
</label>
|
||||||
<textarea id="customerNote" class="po-note-input"
|
<textarea id="customerNote" class="po-note-input"
|
||||||
placeholder="@T("Plugins.Misc.FruitBankPlugin.Preorder.NotePlaceholder")"
|
placeholder="@T("Plugins.Misc.FruitBankPlugin.PreOrder.NotePlaceholder")"
|
||||||
rows="3" maxlength="1000"></textarea>
|
rows="3" maxlength="1000"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Submit -->
|
<!-- Submit -->
|
||||||
<div class="po-submit-row">
|
<div class="po-submit-row">
|
||||||
<div id="selectionSummary" class="po-selection-summary"></div>
|
<div id="selectionSummary" class="po-selection-summary"></div>
|
||||||
<button type="button" id="submitPreorderBtn" class="po-submit-btn" disabled>
|
<button type="button" id="submitPreOrderBtn" class="po-submit-btn" disabled>
|
||||||
<i class="fa fa-paper-plane"></i>
|
<i class="fa fa-paper-plane"></i>
|
||||||
@T("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton")
|
@T("Plugins.Misc.FruitBankPlugin.PreOrder.SubmitButton")
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -108,13 +108,13 @@
|
||||||
<div class="qo-cart-panel">
|
<div class="qo-cart-panel">
|
||||||
<div class="qo-section-title">
|
<div class="qo-section-title">
|
||||||
<i class="fa fa-list-ul"></i>
|
<i class="fa fa-list-ul"></i>
|
||||||
@T("Plugins.Misc.FruitBankPlugin.Preorder.SummaryTitle")
|
@T("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryTitle")
|
||||||
<span id="itemCountBadge" class="cart-count-badge">0</span>
|
<span id="itemCountBadge" class="cart-count-badge">0</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="summaryEmpty" class="cart-empty">
|
<div id="summaryEmpty" class="cart-empty">
|
||||||
<i class="fa fa-list-ul"></i>
|
<i class="fa fa-list-ul"></i>
|
||||||
<p>@T("Plugins.Misc.FruitBankPlugin.Preorder.SummaryEmpty")</p>
|
<p>@T("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryEmpty")</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="summaryList" class="cart-items-list" style="display:none;"></div>
|
<div id="summaryList" class="cart-items-list" style="display:none;"></div>
|
||||||
|
|
@ -122,7 +122,7 @@
|
||||||
<div id="summaryNote" class="cart-total-row" style="display:none;">
|
<div id="summaryNote" class="cart-total-row" style="display:none;">
|
||||||
<div class="cart-total-note">
|
<div class="cart-total-note">
|
||||||
<i class="fa fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
<small>@T("Plugins.Misc.FruitBankPlugin.Preorder.SummaryNote")</small>
|
<small>@T("Plugins.Misc.FruitBankPlugin.PreOrder.SummaryNote")</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -133,10 +133,10 @@
|
||||||
<!-- ── SUCCESS STATE ─────────────────────────────────────────────────── -->
|
<!-- ── SUCCESS STATE ─────────────────────────────────────────────────── -->
|
||||||
<div id="successState" style="display:none;" class="po-success-state">
|
<div id="successState" style="display:none;" class="po-success-state">
|
||||||
<div class="po-success-icon"><i class="fa fa-check-circle"></i></div>
|
<div class="po-success-icon"><i class="fa fa-check-circle"></i></div>
|
||||||
<h2>@T("Plugins.Misc.FruitBankPlugin.Preorder.SuccessTitle")</h2>
|
<h2>@T("Plugins.Misc.FruitBankPlugin.PreOrder.SuccessTitle")</h2>
|
||||||
<p id="successMessage"></p>
|
<p id="successMessage"></p>
|
||||||
<a href="@Url.RouteUrl("Homepage")" class="po-back-btn">
|
<a href="@Url.RouteUrl("Homepage")" class="po-back-btn">
|
||||||
<i class="fa fa-home"></i> @T("Plugins.Misc.FruitBankPlugin.Preorder.BackToHome")
|
<i class="fa fa-home"></i> @T("Plugins.Misc.FruitBankPlugin.PreOrder.BackToHome")
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -146,19 +146,19 @@
|
||||||
|
|
||||||
<script asp-location="Footer">
|
<script asp-location="Footer">
|
||||||
var poStr = {
|
var poStr = {
|
||||||
dsToday : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Today").Text))',
|
dsToday : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Today").Text))',
|
||||||
dsTomorrow : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Tomorrow").Text))',
|
dsTomorrow : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Tomorrow").Text))',
|
||||||
dsSaving : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Saving").Text))',
|
dsSaving : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.Saving").Text))',
|
||||||
dsConfirm : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton").Text))',
|
dsConfirm : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.DeliveryStep.ConfirmButton").Text))',
|
||||||
measurable : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.MeasurableBadge").Text))',
|
measurable : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.MeasurableBadge").Text))',
|
||||||
pricePerPc : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.PricePerPiece").Text))',
|
pricePerPc : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.PricePerPiece").Text))',
|
||||||
pieceUnit : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.PieceUnit").Text))',
|
pieceUnit : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.PieceUnit").Text))',
|
||||||
stockLabel : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.StockLabel").Text))',
|
stockLabel : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.StockLabel").Text))',
|
||||||
selNone : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SelectionNone").Text))',
|
selNone : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.SelectionNone").Text))',
|
||||||
selItems : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SelectionItems").Text))',
|
selItems : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.SelectionItems").Text))',
|
||||||
submitting : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.Submitting").Text))',
|
submitting : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.Submitting").Text))',
|
||||||
successMsg : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SuccessMessage").Text))',
|
successMsg : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.SuccessMessage").Text))',
|
||||||
errorPfx : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.ErrorPrefix").Text))',
|
errorPfx : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.ErrorPrefix").Text))',
|
||||||
huDayNames : ['vas\u00e1rnap','h\u00e9tf\u0151','kedd','szerda','cs\u00fct\u00f6rt\u00f6k','p\u00e9ntek','szombat']
|
huDayNames : ['vas\u00e1rnap','h\u00e9tf\u0151','kedd','szerda','cs\u00fct\u00f6rt\u00f6k','p\u00e9ntek','szombat']
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -196,11 +196,11 @@ var poStr = {
|
||||||
$('#deliveryStep').show();
|
$('#deliveryStep').show();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#submitPreorderBtn').click(submitPreorder);
|
$('#submitPreOrderBtn').click(submitPreOrder);
|
||||||
|
|
||||||
// Restore saved delivery datetime if revisiting
|
// Restore saved delivery datetime if revisiting
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '@Url.Action("GetDeliveryDateTime", "Preorder")',
|
url: '@Url.Action("GetDeliveryDateTime", "PreOrder")',
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
success: function (result) {
|
success: function (result) {
|
||||||
if (!result.success || !result.hasValue) return;
|
if (!result.success || !result.hasValue) return;
|
||||||
|
|
@ -251,7 +251,7 @@ var poStr = {
|
||||||
|
|
||||||
var deliveryDateTime = selectedDeliveryDate + 'T' + selectedDeliveryTime;
|
var deliveryDateTime = selectedDeliveryDate + 'T' + selectedDeliveryTime;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : '@Url.Action("SetDeliveryDateTime", "Preorder")',
|
url : '@Url.Action("SetDeliveryDateTime", "PreOrder")',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {
|
data: {
|
||||||
deliveryDateTime: deliveryDateTime,
|
deliveryDateTime: deliveryDateTime,
|
||||||
|
|
@ -288,7 +288,7 @@ var poStr = {
|
||||||
$('#productSection').hide();
|
$('#productSection').hide();
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : '@Url.Action("GetAvailableProducts", "Preorder")',
|
url : '@Url.Action("GetAvailableProducts", "PreOrder")',
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
success: function (result) {
|
success: function (result) {
|
||||||
$('#productsLoadingState').hide();
|
$('#productsLoadingState').hide();
|
||||||
|
|
@ -369,7 +369,7 @@ var poStr = {
|
||||||
var count = selectedItems.length;
|
var count = selectedItems.length;
|
||||||
|
|
||||||
$('#itemCountBadge').text(count);
|
$('#itemCountBadge').text(count);
|
||||||
$('#submitPreorderBtn').prop('disabled', count === 0);
|
$('#submitPreOrderBtn').prop('disabled', count === 0);
|
||||||
|
|
||||||
// Selection summary text
|
// Selection summary text
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
|
|
@ -413,11 +413,11 @@ var poStr = {
|
||||||
|
|
||||||
// ── Submit ────────────────────────────────────────────────────────────────
|
// ── Submit ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function submitPreorder() {
|
function submitPreOrder() {
|
||||||
var selectedItems = products.filter(function (p) { return (quantities[p.id] || 0) > 0; });
|
var selectedItems = products.filter(function (p) { return (quantities[p.id] || 0) > 0; });
|
||||||
if (!selectedItems.length) return;
|
if (!selectedItems.length) return;
|
||||||
|
|
||||||
var btn = $('#submitPreorderBtn');
|
var btn = $('#submitPreOrderBtn');
|
||||||
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> ' + poStr.submitting);
|
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> ' + poStr.submitting);
|
||||||
|
|
||||||
var payload = {
|
var payload = {
|
||||||
|
|
@ -429,7 +429,7 @@ var poStr = {
|
||||||
};
|
};
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : '@Url.Action("PlacePreorder", "Preorder")',
|
url : '@Url.Action("PlacePreOrder", "PreOrder")',
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
data : JSON.stringify(payload),
|
data : JSON.stringify(payload),
|
||||||
|
|
@ -442,13 +442,13 @@ var poStr = {
|
||||||
} else {
|
} else {
|
||||||
alert(poStr.errorPfx + (result.message || ''));
|
alert(poStr.errorPfx + (result.message || ''));
|
||||||
btn.prop('disabled', false)
|
btn.prop('disabled', false)
|
||||||
.html('<i class="fa fa-paper-plane"></i> @Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton").Text))');
|
.html('<i class="fa fa-paper-plane"></i> @Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrder.SubmitButton").Text))');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
alert(poStr.errorPfx);
|
alert(poStr.errorPfx);
|
||||||
btn.prop('disabled', false)
|
btn.prop('disabled', false)
|
||||||
.html('<i class="fa fa-paper-plane"></i> @Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton").Text))');
|
.html('<i class="fa fa-paper-plane"></i> @Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.PreOrderPreOrder.SubmitButton").Text))');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Preorder page — supplemental styles
|
* PreOrder page — supplemental styles
|
||||||
* Inherits all base styles from quick-order.css
|
* Inherits all base styles from quick-order.css
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue