Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderSyncController.cs

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;
}
}
}