309 lines
13 KiB
C#
309 lines
13 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Nop.Core;
|
|
using Nop.Core.Domain.Orders;
|
|
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
|
using Nop.Services.Common;
|
|
using Nop.Services.Orders;
|
|
using Nop.Web.Framework;
|
|
using Nop.Web.Framework.Controllers;
|
|
using Nop.Web.Framework.Mvc.Filters;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
{
|
|
[Area(AreaNames.ADMIN)]
|
|
[AuthorizeAdmin]
|
|
public class InnVoiceOrderSyncController : BasePluginController
|
|
{
|
|
private readonly IOrderService _orderService;
|
|
private readonly IStoreContext _storeContext;
|
|
private readonly InnVoiceOrderService _innVoiceOrderService;
|
|
private readonly IGenericAttributeService _genericAttributeService;
|
|
|
|
public InnVoiceOrderSyncController(
|
|
IOrderService _orderService,
|
|
IStoreContext storeContext,
|
|
InnVoiceOrderService innVoiceOrderService,
|
|
IGenericAttributeService genericAttributeService)
|
|
{
|
|
this._orderService = _orderService;
|
|
_storeContext = storeContext;
|
|
_innVoiceOrderService = innVoiceOrderService;
|
|
_genericAttributeService = genericAttributeService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the order sync page
|
|
/// </summary>
|
|
[HttpGet]
|
|
public IActionResult Index()
|
|
{
|
|
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/InnVoiceOrderSync/Index.cshtml");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sync orders between NopCommerce and InnVoice for a given date range
|
|
/// </summary>
|
|
[HttpPost]
|
|
public async Task<IActionResult> SyncOrders(DateTime startDate, DateTime endDate)
|
|
{
|
|
try
|
|
{
|
|
// Validate dates
|
|
if (startDate > endDate)
|
|
{
|
|
return Json(new { success = false, message = "Start date cannot be after end date" });
|
|
}
|
|
|
|
// Get store ID
|
|
var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;
|
|
|
|
// Step 1: Get all InnVoice orders for the date range using UpdateTime
|
|
var innVoiceOrders = await GetInnVoiceOrdersByDateRange(startDate, endDate);
|
|
Console.WriteLine($"DEBUG: Found {innVoiceOrders.Count} InnVoice orders");
|
|
Console.WriteLine($"DEBUG: InnVoice TableIds: {string.Join(", ", innVoiceOrders.Select(o => o.TableId))}");
|
|
|
|
// Step 2: Get all NopCommerce orders in the date range (filtered by DateOfReceipt)
|
|
var nopOrders = await GetNopOrdersByDateRange(startDate, endDate, storeId);
|
|
Console.WriteLine($"DEBUG: Found {nopOrders.Count} NopCommerce orders with DateOfReceipt in range");
|
|
|
|
// Step 3: Get TableIds from NopCommerce orders
|
|
var nopOrderTableIds = new Dictionary<int, int>(); // orderId -> tableId
|
|
var nopOrdersWithoutTableId = new List<int>(); // Track orders without TableId
|
|
|
|
foreach (var order in nopOrders)
|
|
{
|
|
var tableIdStr = await _genericAttributeService.GetAttributeAsync<string>(
|
|
order,
|
|
"InnVoiceOrderTableId",
|
|
storeId
|
|
);
|
|
|
|
if (!string.IsNullOrEmpty(tableIdStr) && int.TryParse(tableIdStr, out int tableId))
|
|
{
|
|
nopOrderTableIds[order.Id] = tableId;
|
|
Console.WriteLine($"DEBUG: Order {order.Id} has TableId {tableId}");
|
|
}
|
|
else
|
|
{
|
|
nopOrdersWithoutTableId.Add(order.Id);
|
|
Console.WriteLine($"DEBUG: Order {order.Id} has NO TableId (tableIdStr='{tableIdStr}')");
|
|
}
|
|
}
|
|
|
|
// Step 4: Compare and find discrepancies
|
|
var innVoiceTableIds = new HashSet<int>(innVoiceOrders.Select(o => o.TableId).Where(t => t > 0));
|
|
var nopTableIds = new HashSet<int>(nopOrderTableIds.Values);
|
|
|
|
// Orders in InnVoice but not in NopCommerce
|
|
var innVoiceOnly = innVoiceTableIds.Except(nopTableIds).ToList();
|
|
|
|
// Orders in NopCommerce but not in InnVoice
|
|
var nopOnly = nopTableIds.Except(innVoiceTableIds).ToList();
|
|
|
|
// Orders in both systems
|
|
var inBoth = innVoiceTableIds.Intersect(nopTableIds).ToList();
|
|
|
|
// Build detailed results
|
|
var innVoiceOnlyDetails = innVoiceOrders
|
|
.Where(o => innVoiceOnly.Contains(o.TableId))
|
|
.Select(o => new
|
|
{
|
|
tableId = o.TableId,
|
|
techId = o.TechId,
|
|
customerName = o.CustomerName,
|
|
totalGross = o.TotalGross,
|
|
orderDate = o.OrderDate,
|
|
externalOrderNumber = o.ExternalOrderNumber
|
|
})
|
|
.ToList();
|
|
|
|
var nopOnlyDetails = nopOrders
|
|
.Where(o => nopOrderTableIds.ContainsKey(o.Id) && nopOnly.Contains(nopOrderTableIds[o.Id]))
|
|
.Select(o => new
|
|
{
|
|
orderId = o.Id,
|
|
tableId = nopOrderTableIds.ContainsKey(o.Id) ? nopOrderTableIds[o.Id] : 0,
|
|
customOrderNumber = o.CustomOrderNumber,
|
|
orderTotal = o.OrderTotal,
|
|
createdOn = o.CreatedOnUtc
|
|
})
|
|
.ToList();
|
|
|
|
// Orders without TableId (never uploaded to InnVoice)
|
|
var nopOrdersNotUploadedDetails = new List<dynamic>();
|
|
|
|
foreach (var order in nopOrders.Where(o => nopOrdersWithoutTableId.Contains(o.Id)))
|
|
{
|
|
// Check if order has items
|
|
var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
|
|
var hasItems = orderItems.Any();
|
|
|
|
nopOrdersNotUploadedDetails.Add(new
|
|
{
|
|
orderId = order.Id,
|
|
customOrderNumber = order.CustomOrderNumber,
|
|
orderTotal = order.OrderTotal,
|
|
createdOn = order.CreatedOnUtc,
|
|
orderStatus = order.OrderStatus.ToString(),
|
|
orderStatusId = (int)order.OrderStatus,
|
|
hasItems = hasItems,
|
|
itemCount = orderItems.Count()
|
|
});
|
|
}
|
|
|
|
// Separate orders by whether they have items
|
|
var deletedOrders = nopOrdersNotUploadedDetails
|
|
.Where(o => !o.hasItems) // No items = deleted/empty order
|
|
.ToList();
|
|
|
|
var missingUploads = nopOrdersNotUploadedDetails
|
|
.Where(o => o.hasItems) // Has items = should have been uploaded
|
|
.ToList();
|
|
|
|
return Json(new
|
|
{
|
|
success = true,
|
|
data = new
|
|
{
|
|
summary = new
|
|
{
|
|
totalInnVoiceOrders = innVoiceOrders.Count,
|
|
totalNopOrders = nopOrders.Count,
|
|
totalNopOrdersWithTableId = nopOrderTableIds.Count,
|
|
totalNopOrdersWithoutTableId = nopOrdersWithoutTableId.Count,
|
|
totalDeletedOrders = deletedOrders.Count,
|
|
totalMissingUploads = missingUploads.Count,
|
|
inBothSystems = inBoth.Count,
|
|
onlyInInnVoice = innVoiceOnly.Count,
|
|
onlyInNopCommerce = nopOnly.Count
|
|
},
|
|
innVoiceOnly = innVoiceOnlyDetails,
|
|
nopCommerceOnly = nopOnlyDetails,
|
|
nopCommerceNotUploaded = nopOrdersNotUploadedDetails,
|
|
deletedOrders = deletedOrders,
|
|
missingUploads = missingUploads,
|
|
dateRange = new
|
|
{
|
|
startDate = startDate.ToString("yyyy-MM-dd"),
|
|
endDate = endDate.ToString("yyyy-MM-dd")
|
|
}
|
|
}
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return Json(new
|
|
{
|
|
success = false,
|
|
message = $"Error: {ex.Message}"
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Quick action: Sync last 30 days
|
|
/// </summary>
|
|
[HttpPost]
|
|
public async Task<IActionResult> SyncLast30Days()
|
|
{
|
|
var endDate = DateTime.Now;
|
|
var startDate = endDate.AddDays(-30);
|
|
|
|
return await SyncOrders(startDate, endDate);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get InnVoice orders by UpdateTime range
|
|
/// We query by UpdateTime but DON'T filter by MegrendelesKelte
|
|
/// This allows comparing ALL orders regardless of order date mismatches
|
|
/// </summary>
|
|
private async Task<List<InnVoiceOrder>> GetInnVoiceOrdersByDateRange(DateTime startDate, DateTime endDate)
|
|
{
|
|
var allOrders = new List<InnVoiceOrder>();
|
|
|
|
// Add 14-day buffer to catch orders that might have been modified/created around this time
|
|
//var queryStartDate = startDate.AddDays(-14);
|
|
//var queryEndDate = endDate.AddDays(7);
|
|
//var currentDate = queryStartDate;
|
|
var currentDate = startDate;
|
|
|
|
//Console.WriteLine($"DEBUG: Querying InnVoice from {queryStartDate:yyyy-MM-dd} to {queryEndDate:yyyy-MM-dd}");
|
|
Console.WriteLine($"DEBUG: Querying InnVoice from {startDate:yyyy-MM-dd} to {endDate:yyyy-MM-dd}");
|
|
|
|
// Query day by day to avoid rate limits
|
|
while (currentDate <= endDate)
|
|
{
|
|
try
|
|
{
|
|
var orders = await _innVoiceOrderService.GetOrdersByOrderDateAsync(currentDate);
|
|
|
|
if (orders != null && orders.Any())
|
|
{
|
|
Console.WriteLine($"DEBUG: Found {orders.Count} orders updated on {currentDate:yyyy-MM-dd}");
|
|
allOrders.AddRange(orders);
|
|
}
|
|
|
|
currentDate = currentDate.AddDays(1);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log and continue
|
|
Console.WriteLine($"Error fetching InnVoice orders for {currentDate:yyyy-MM-dd}: {ex.Message}");
|
|
currentDate = currentDate.AddDays(1);
|
|
}
|
|
}
|
|
|
|
// Remove duplicates based on TableId
|
|
var uniqueOrders = allOrders
|
|
.GroupBy(o => o.TableId)
|
|
.Select(g => g.First())
|
|
.ToList();
|
|
|
|
Console.WriteLine($"DEBUG: Total unique InnVoice orders: {uniqueOrders.Count}");
|
|
|
|
return uniqueOrders;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get NopCommerce orders by DateOfReceipt generic attribute date range
|
|
/// </summary>
|
|
private async Task<List<Order>> GetNopOrdersByDateRange(DateTime startDate, DateTime endDate, int storeId)
|
|
{
|
|
// Get ALL orders (we'll filter by DateOfReceipt manually)
|
|
var allOrders = await _orderService.SearchOrdersAsync();
|
|
|
|
var filteredOrders = new List<Order>();
|
|
|
|
foreach (var order in allOrders)
|
|
{
|
|
// Get the DateOfReceipt generic attribute
|
|
var dateOfReceiptStr = await _genericAttributeService.GetAttributeAsync<string>(
|
|
order,
|
|
"DateOfReceipt",
|
|
storeId
|
|
);
|
|
|
|
// Parse and check if within range
|
|
if (!string.IsNullOrEmpty(dateOfReceiptStr) && DateTime.TryParse(dateOfReceiptStr, out DateTime dateOfReceipt))
|
|
{
|
|
// Compare dates (ignoring time component)
|
|
var receiptDate = dateOfReceipt.Date;
|
|
var start = startDate.Date;
|
|
var end = endDate.Date;
|
|
|
|
if (receiptDate >= start && receiptDate <= end)
|
|
{
|
|
filteredOrders.Add(order);
|
|
}
|
|
}
|
|
}
|
|
|
|
return filteredOrders;
|
|
}
|
|
}
|
|
}
|