Compare commits

..

No commits in common. "b9cb52927f9dda8760f57fd38e90f24b6ae0f522" and "30c8cd3e00f22581bb7263b9f2731d1bd423b165" have entirely different histories.

10 changed files with 221 additions and 350 deletions

View File

@ -163,8 +163,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
OrderStatusIds = orderStatuses, OrderStatusIds = orderStatuses,
PaymentStatusIds = paymentStatuses, PaymentStatusIds = paymentStatuses,
ShippingStatusIds = shippingStatuses, ShippingStatusIds = shippingStatuses,
AvailablePageSizes = "20,50,100,500",
}); });
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/List.cshtml", model); return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/List.cshtml", model);
@ -216,79 +214,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Edit.cshtml", model); return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Edit.cshtml", model);
} }
//public async Task<OrderListModelExtended> GetOrderListModelByFilter(OrderSearchModelExtended searchModel)
//{
// //return _customOrderService.
// var orderListModel = await _orderModelFactory.PrepareOrderListModelExtendedAsync(searchModel);
// _logger.Detail($"Total: {orderListModel.RecordsTotal}, Data Count: {orderListModel.Data.Count()}");
// foreach (var item in orderListModel.Data.Take(3))
// {
// _logger.Detail($"Order: {item.Id}, {item.CustomOrderNumber}");
// }
// return orderListModel;
//}
public async Task<OrderListModelExtended> GetOrderListModelByFilter(OrderSearchModelExtended searchModel) public async Task<OrderListModelExtended> GetOrderListModelByFilter(OrderSearchModelExtended searchModel)
{ {
//return _customOrderService.
var sortColumnIndex = Request.Form["order[0][column]"].FirstOrDefault();
var sortDirection = Request.Form["order[0][dir]"].FirstOrDefault();
if (!string.IsNullOrEmpty(sortColumnIndex))
{
// Get the column name from the column index
var columnName = Request.Form[$"columns[{sortColumnIndex}][data]"].FirstOrDefault();
searchModel.SortColumn = columnName;
searchModel.SortColumnDirection = sortDirection; // "asc" or "desc"
}
// Get the paginated data
var orderListModel = await _orderModelFactory.PrepareOrderListModelExtendedAsync(searchModel); var orderListModel = await _orderModelFactory.PrepareOrderListModelExtendedAsync(searchModel);
_logger.Detail($"Total: {orderListModel.RecordsTotal}, Data Count: {orderListModel.Data.Count()}"); _logger.Detail($"Total: {orderListModel.RecordsTotal}, Data Count: {orderListModel.Data.Count()}");
// Apply sorting if specified
if (!string.IsNullOrEmpty(searchModel.SortColumn) && orderListModel.Data.Any())
{
var sortedData = orderListModel.Data.AsQueryable();
sortedData = searchModel.SortColumn.ToLowerInvariant() switch
{
"id" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.Id)
: sortedData.OrderByDescending(o => o.Id),
"customercompany" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.CustomerCompany)
: sortedData.OrderByDescending(o => o.CustomerCompany),
"customordernumber" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.CustomOrderNumber)
: sortedData.OrderByDescending(o => o.CustomOrderNumber),
"ordertotal" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.OrderTotal)
: sortedData.OrderByDescending(o => o.OrderTotal),
"createdon" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.CreatedOn)
: sortedData.OrderByDescending(o => o.CreatedOn),
"orderstatusid" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.OrderStatusId)
: sortedData.OrderByDescending(o => o.OrderStatusId),
"paymentstatusid" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.PaymentStatusId)
: sortedData.OrderByDescending(o => o.PaymentStatusId),
"shippingstatusid" => searchModel.SortColumnDirection == "asc"
? sortedData.OrderBy(o => o.ShippingStatusId)
: sortedData.OrderByDescending(o => o.ShippingStatusId),
_ => sortedData
};
orderListModel.Data = sortedData.ToList();
_logger.Detail($"Sorted by {searchModel.SortColumn} {searchModel.SortColumnDirection}");
}
foreach (var item in orderListModel.Data.Take(3)) foreach (var item in orderListModel.Data.Take(3))
{ {
_logger.Detail($"Order: {item.Id}, {item.CustomOrderNumber}"); _logger.Detail($"Order: {item.Id}, {item.CustomOrderNumber}");
@ -297,7 +228,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return orderListModel; return orderListModel;
} }
public virtual IActionResult Test() public virtual IActionResult Test()
{ {
// Your custom logic here // Your custom logic here

View File

@ -219,32 +219,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return Json(model); return Json(model);
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> ProcessShippingDocument(int shippingDocumentId)
{
return Ok("Ok");
}
[HttpPost]
[RequestSizeLimit(10485760)] // 10MB [RequestSizeLimit(10485760)] // 10MB
[RequestFormLimits(MultipartBodyLengthLimit = 10485760)] [RequestFormLimits(MultipartBodyLengthLimit = 10485760)]
public async Task<IActionResult> UploadFile(List<IFormFile> files, int shippingDocumentId, int? partnerId) public async Task<IActionResult> UploadFile(List<IFormFile> files, int shippingDocumentId, int? partnerId)
{ {
//an empty shippingdocument created by the user
var shippingDocument = await _dbContext.ShippingDocuments.GetByIdAsync(shippingDocumentId); var shippingDocument = await _dbContext.ShippingDocuments.GetByIdAsync(shippingDocumentId);
ShippingDocumentAnalysisResult shippingDocumentAnalysisResult = new ShippingDocumentAnalysisResult();
shippingDocumentAnalysisResult.Partner = new Partner();
shippingDocumentAnalysisResult.ShippingDocument = shippingDocument;
shippingDocumentAnalysisResult.ShippingItems = new List<ShippingItem>();
shippingDocumentAnalysisResult.ShippingDocument.ShippingDocumentToFiles = new List<ShippingDocumentToFiles>();
//checks //checks
// - files exist // - files exist
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
@ -260,7 +243,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
_logger.Debug($"Associated with Partner ID: {partnerId.Value}"); _logger.Debug($"Associated with Partner ID: {partnerId.Value}");
//let's get the partner //let's get the partner
shippingDocumentAnalysisResult.Partner = await _dbContext.Partners.GetByIdAsync(partnerId.Value); var partner = await _dbContext.Partners.GetByIdAsync(partnerId.Value);
} }
var filesList = new List<Files>(); var filesList = new List<Files>();
@ -269,34 +252,34 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
//iteratation 1: iterate documents to determine their type by AI //iteratation 1: iterate documents to determine their type by AI
for (int i = 0; i < files.Count; i++) foreach (var file in files)
{ {
var fileName = files[i].FileName; var fileName = file.FileName;
var fileSize = files[i].Length; var fileSize = file.Length;
var dbFile = new Files(); var dbFile = new Files();
string pdfText = ""; string pdfText = "";
_logger.Detail($"Received file: {fileName} for Document ID: {shippingDocumentId}, content type: {files[i].ContentType}"); _logger.Detail($"Received file: {fileName} for Document ID: {shippingDocumentId}, content type: {file.ContentType}");
if (!files[i].ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) && !files[i].ContentType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase)) if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) && !file.ContentType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase))
return Json(new { success = false, errorMessage = "Only PDF or jpg files are allowed" }); return Json(new { success = false, errorMessage = "Only PDF or jpg files are allowed" });
// Validate file size (max 20MB) // Validate file size (max 20MB)
if (files[i].Length > 20 * 1024 * 1024) if (file.Length > 20 * 1024 * 1024)
return Json(new { success = false, errorMessage = "File size must be less than 10MB" }); return Json(new { success = false, errorMessage = "File size must be less than 10MB" });
// - get text extracted from pdf // - get text extracted from pdf
// Validate file type (PDF only) // Validate file type (PDF only)
//if (file.Length > 0 && file.ContentType == "application/pdf") //if (file.Length > 0 && file.ContentType == "application/pdf")
if (files[i].Length > 0) if (file.Length > 0)
{ {
if (files[i].ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)) if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
{ {
try try
{ {
// Open the PDF from the IFormFile's stream directly in memory // Open the PDF from the IFormFile's stream directly in memory
using (var stream = files[i].OpenReadStream()) using (var stream = file.OpenReadStream())
using (var pdf = UglyToad.PdfPig.PdfDocument.Open(stream)) using (var pdf = UglyToad.PdfPig.PdfDocument.Open(stream))
{ {
// Now you can analyze the PDF content // Now you can analyze the PDF content
@ -313,7 +296,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
try try
{ {
// ✅ Use the service we implemented earlier // ✅ Use the service we implemented earlier
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, files[i].FileName, "Please extract all readable text from this PDF."); pdfText = await _openAIApiService.AnalyzePdfAsync(stream, file.FileName, "Please extract all readable text from this PDF.");
} }
catch (Exception aiEx) catch (Exception aiEx)
{ {
@ -323,14 +306,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
} }
// For demonstration, let's just log the extracted text // For demonstration, let's just log the extracted text
_logger.Detail($"Extracted text from {files[i].FileName}: {pdfText}"); _logger.Detail($"Extracted text from {file.FileName}: {pdfText}");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
// Handle potential exceptions during PDF processing // Handle potential exceptions during PDF processing
Console.Error.WriteLine($"Error processing PDF file {files[i].FileName}: {ex.Message}"); Console.Error.WriteLine($"Error processing PDF file {file.FileName}: {ex.Message}");
return StatusCode(500, $"Error processing PDF file: {ex.Message}"); return StatusCode(500, $"Error processing PDF file: {ex.Message}");
} }
} }
@ -339,13 +322,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
try try
{ {
// Open the Image from the IFormFile's stream directly in memory // Open the Image from the IFormFile's stream directly in memory
using (var stream = files[i].OpenReadStream()) using (var stream = file.OpenReadStream())
{ {
try try
{ {
// ✅ Use the service we implemented earlier // ✅ Use the service we implemented earlier
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, files[i].FileName, "Please extract all readable text from this image."); pdfText = await _openAIApiService.AnalyzePdfAsync(stream, file.FileName, "Please extract all readable text from this image.");
} }
catch (Exception aiEx) catch (Exception aiEx)
{ {
@ -354,7 +337,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
} }
// For demonstration, let's just log the extracted text // For demonstration, let's just log the extracted text
_logger.Detail($"Extracted text from {files[i].FileName}: {pdfText}"); _logger.Detail($"Extracted text from {file.FileName}: {pdfText}");
} }
} }
@ -362,7 +345,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
catch (Exception ex) catch (Exception ex)
{ {
// Handle potential exceptions during PDF processing // Handle potential exceptions during PDF processing
Console.Error.WriteLine($"Error processing PDF file {files[i].FileName}: {ex.Message}"); Console.Error.WriteLine($"Error processing PDF file {file.FileName}: {ex.Message}");
return StatusCode(500, $"Error processing PDF file: {ex.Message}"); return StatusCode(500, $"Error processing PDF file: {ex.Message}");
} }
} }
@ -379,7 +362,31 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
var metaAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), analysisPrompt); var metaAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), analysisPrompt);
var extractedMetaData = ParseMetaDataAIResponse(metaAnalyzis); var extractedMetaData = ParseMetaDataAIResponse(metaAnalyzis);
bool transactionSuccess = await SaveFileInfoToDB(shippingDocumentId, shippingDocumentAnalysisResult, filesList, dbFile, pdfText, extractedMetaData);
if (extractedMetaData.DocumentNumber != null)
{
dbFile.RawText = pdfText;
dbFile.FileExtension = "pdf";
dbFile.FileName = extractedMetaData.DocumentNumber;
}
var transactionSuccess = await _dbContext.TransactionSafeAsync(async _ =>
{
await _dbContext.Files.InsertAsync(dbFile);
filesList.Add(dbFile);
var shippingDocumentToFiles = new ShippingDocumentToFiles
{
ShippingDocumentId = shippingDocumentId,
FilesId = dbFile.Id,
DocumentType = extractedMetaData.DocumentType != null ? (DocumentType)Enum.Parse(typeof(DocumentType), extractedMetaData.DocumentType) : DocumentType.Unknown
};
_logger.Detail(shippingDocumentToFiles.DocumentType.ToString());
await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles);
return true;
});
if (!transactionSuccess) _logger.Error($"(transactionSuccess == false)"); if (!transactionSuccess) _logger.Error($"(transactionSuccess == false)");
@ -392,12 +399,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
//find partner in DB... there is a serious chance that the partner Name and Taxid determines the partner //find partner in DB... there is a serious chance that the partner Name and Taxid determines the partner
var partners = await _dbContext.Partners.GetAll().ToListAsync(); var partners = await _dbContext.Partners.GetAll().ToListAsync();
foreach (var dbpartner in partners) foreach (var partner in partners)
{ {
if (pdfText.Contains(dbpartner.Name) || (dbpartner.TaxId != null && pdfText.Contains(dbpartner.TaxId))) if (pdfText.Contains(partner.Name) || (partner.TaxId != null && pdfText.Contains(partner.TaxId)))
{ {
partnerId = dbpartner.Id; partnerId = partner.Id;
_logger.Detail($"Found existing partner in DB: {dbpartner.Name} (ID: {dbpartner.Id})"); _logger.Detail($"Found existing partner in DB: {partner.Name} (ID: {partner.Id})");
break; break;
} }
} }
@ -406,13 +413,48 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
{ {
_logger.Detail("No existing partner found in DB, proceeding to extract partner info via AI."); _logger.Detail("No existing partner found in DB, proceeding to extract partner info via AI.");
// string partnerAnalysisPrompt = "You are an agent of Fruitbank, helping to analyze pdf content. Determine which partner of Fruitbank sent this document, extract their data and return them as JSON: name, taxId, certificationNumber, postalCode, country, state, county, city, street. " + string partnerAnalysisPrompt = "You are an agent of Fruitbank, helping to analyze pdf content. Determine which partner of Fruitbank sent this document, extract their data and return them as JSON: name, taxId, certificationNumber, postalCode, country, state, county, city, street. " +
//"If you can't find information of any of these, return null value for that field."; "If you can't find information of any of these, return null value for that field.";
// //here I can start preparing the file entity //here I can start preparing the file entity
// var partnerAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), partnerAnalysisPrompt); var partnerAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), partnerAnalysisPrompt);
var extractedPartnerData = ParsePartnerDataAIResponse(partnerAnalyzis);
var partnerPrompt = @"Extract the partner/company information from the following text and return ONLY a valid JSON object with these exact fields: if (extractedPartnerData.Name != null)
{
_logger.Detail("AI Analysis Partner Result:");
_logger.Detail(extractedPartnerData.Name);
}
if (extractedPartnerData.TaxId != null)
{
_logger.Detail(extractedPartnerData.TaxId);
}
if (extractedPartnerData.Country != null)
{
_logger.Detail(extractedPartnerData.Country);
}
if (extractedPartnerData.State != null)
{
_logger.Detail(extractedPartnerData.State);
}
}
}
//shortcut
try
{
var partnerPrompt = @"Extract the partner/company information from the following text and return ONLY a valid JSON object with these exact fields:
{ {
""Name"": ""company name"", ""Name"": ""company name"",
""TaxId"": ""tax identification number"", ""TaxId"": ""tax identification number"",
@ -430,54 +472,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
Text to analyze: Text to analyze:
" + pdfText; " + pdfText;
var partnerResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText( var partnerResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
pdfText, pdfText,
partnerPrompt partnerPrompt
); );
shippingDocumentAnalysisResult.Partner = JsonSerializer.Deserialize<Partner>(CleanJsonResponse(partnerResponse),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (shippingDocumentAnalysisResult.Partner.Name != null)
{
_logger.Detail("AI Analysis Partner Result:");
_logger.Detail(shippingDocumentAnalysisResult.Partner.Name);
}
if (shippingDocumentAnalysisResult.Partner.TaxId != null)
{
_logger.Detail(shippingDocumentAnalysisResult.Partner.TaxId);
}
if (shippingDocumentAnalysisResult.Partner.Country != null)
{
_logger.Detail(shippingDocumentAnalysisResult.Partner.Country);
}
if (shippingDocumentAnalysisResult.Partner.State != null)
{
_logger.Detail(shippingDocumentAnalysisResult.Partner.State);
}
if (shippingDocumentAnalysisResult.Partner != null)
{
shippingDocumentAnalysisResult.Partner = shippingDocumentAnalysisResult.Partner;
}
}
}
//shortcut
try
{
var partner = JsonSerializer.Deserialize<Partner>(CleanJsonResponse(partnerResponse),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
// Step 2: Extract Shipping Document Information // Step 2: Extract Shipping Document Information
var shippingDocPrompt = @"Extract the shipping document information from the following text and return ONLY a valid JSON object with these exact fields: var shippingDocPrompt = @"Extract the shipping document information from the following text and return ONLY a valid JSON object with these exact fields:
@ -503,20 +504,20 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
// Step 3: Extract Shipping Items // Step 3: Extract Shipping Items
var itemsPrompt = @"Extract all shipping items (fruits, vegetables, or products) from the following text and return ONLY a valid JSON array with objects having these exact fields: var itemsPrompt = @"Extract all shipping items (fruits, vegetables, or products) from the following text and return ONLY a valid JSON array with objects having these exact fields:
[ [
{ {
""Name"": ""product name"", ""Name"": ""product name"",
""PalletsOnDocument"": number_of_pallets, ""PalletsOnDocument"": number_of_pallets,
""QuantityOnDocument"": quantity_count, ""QuantityOnDocument"": quantity_count,
""NetWeightOnDocument"": net_weight_in_kg, ""NetWeightOnDocument"": net_weight_in_kg,
""GrossWeightOnDocument"": gross_weight_in_kg ""GrossWeightOnDocument"": gross_weight_in_kg
} }
] ]
If a numeric field is not found, use 0. Return ONLY the JSON array, no additional text or explanation. If a numeric field is not found, use 0. Return ONLY the JSON array, no additional text or explanation.
Text to analyze: Text to analyze:
" + pdfText; " + pdfText;
var itemsResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText( var itemsResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
pdfText, pdfText,
@ -529,7 +530,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
// Prepare result // Prepare result
var result = new ShippingDocumentAnalysisResult var result = new ShippingDocumentAnalysisResult
{ {
Partner = shippingDocumentAnalysisResult.Partner, Partner = partner,
ShippingDocument = new ShippingDocument ShippingDocument = new ShippingDocument
{ {
DocumentIdNumber = shippingDocData.DocumentIdNumber, DocumentIdNumber = shippingDocData.DocumentIdNumber,
@ -540,40 +541,56 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
ShippingItems = items ShippingItems = items
}; };
//return Ok(result); return Ok(result);
} }
catch (JsonException ex) catch (JsonException ex)
{ {
return BadRequest($"Failed to parse AI response: {ex.Message}"); return BadRequest($"Failed to parse AI response: {ex.Message}");
} }
catch (Exception ex)
{
return StatusCode(500, $"An error occurred: {ex.Message}");
}
// - save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf // - save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf
// where documentId is the number or id IN the document // where documentId is the number or id IN the document
try //try
{ //{
// Create upload directory if it doesn't exist // // Create upload directory if it doesn't exist
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId.ToString(), "documents"); // var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId.ToString(), "documents");
Directory.CreateDirectory(uploadsPath); // Directory.CreateDirectory(uploadsPath);
// Generate unique filename // // Generate unique filename
fileName = $"{Guid.NewGuid()}_{files[i].FileName}"; // fileName = $"{Guid.NewGuid()}_{file.FileName}";
var filePath = Path.Combine(uploadsPath, fileName); // var filePath = Path.Combine(uploadsPath, fileName);
// Save file // // Save file
using (var stream = new FileStream(filePath, FileMode.Create)) // using (var stream = new FileStream(filePath, FileMode.Create))
{ // {
await files[i].CopyToAsync(stream); // await file.CopyToAsync(stream);
} // }
//}
return Ok(shippingDocumentAnalysisResult); //catch (Exception ex)
} //{
catch (Exception ex) // _logger.Error($"Error saving file: {ex.Message}", ex);
{ // //return Json(new { success = false, errorMessage = ex.Message });
_logger.Error($"Error saving file: {ex.Message}", ex); // return BadRequest("No files were uploaded.");
//return Json(new { success = false, errorMessage = ex.Message }); //}
return BadRequest("No files were uploaded.");
}
// - create a list of documents to read information and to save the document's information to DB // - create a list of documents to read information and to save the document's information to DB
@ -663,35 +680,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return Ok($"Files for Shipping Document ID {shippingDocumentId} were uploaded successfully!"); return Ok($"Files for Shipping Document ID {shippingDocumentId} were uploaded successfully!");
} }
private async Task<bool> SaveFileInfoToDB(int shippingDocumentId, ShippingDocumentAnalysisResult shippingDocumentAnalysisResult, List<Files> filesList, Files dbFile, string pdfText, ExtractedDocumentMetaData extractedMetaData)
{
if (extractedMetaData.DocumentNumber != null)
{
dbFile.RawText = pdfText;
dbFile.FileExtension = "pdf";
dbFile.FileName = extractedMetaData.DocumentNumber;
}
var transactionSuccess = await _dbContext.TransactionSafeAsync(async _ =>
{
await _dbContext.Files.InsertAsync(dbFile);
filesList.Add(dbFile);
var shippingDocumentToFiles = new ShippingDocumentToFiles
{
ShippingDocumentId = shippingDocumentId,
FilesId = dbFile.Id,
DocumentType = extractedMetaData.DocumentType != null ? (DocumentType)Enum.Parse(typeof(DocumentType), extractedMetaData.DocumentType) : DocumentType.Unknown
};
_logger.Detail(shippingDocumentToFiles.DocumentType.ToString());
shippingDocumentAnalysisResult.ShippingDocument.ShippingDocumentToFiles.Add(shippingDocumentToFiles);
await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles);
return true;
});
return transactionSuccess;
}
//[HttpPost] //[HttpPost]
//public async Task<IActionResult> UploadFile(List<IFormFile> files, int shippingDocumentId) //public async Task<IActionResult> UploadFile(List<IFormFile> files, int shippingDocumentId)

View File

@ -15,8 +15,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order
[NopResourceDisplayName("Admin.Orders.List.BillingCompany")] [NopResourceDisplayName("Admin.Orders.List.BillingCompany")]
public string BillingCompany { get; set; } public string BillingCompany { get; set; }
public string SortColumn { get; set; } //public IList<SelectListItem> AvailableCompanies { get; set; }
public string SortColumnDirection { get; set; }
} }
} }

View File

@ -11,7 +11,8 @@
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6">
<form method="post" enctype="multipart/form-data" asp-controller="ManagementPage" asp-action="UploadFile"> <form method="post" enctype="multipart/form-data" asp-controller="ManagementPage" asp-action="UploadFile">
@(Html.DevExtreme().FileUploader() @(Html.DevExtreme().FileUploader()
.ID("shippingDocumentUploader-" + contextId) .ID("shippingDocumentUploader-" + contextId)
@ -30,12 +31,6 @@
.UseSubmitBehavior(true) .UseSubmitBehavior(true)
) )
</form> </form>
<button type="button" class="btn btn-primary mt-2" onclick="processShippingDocument_@(contextId)()">
<i class="fas fa-cog"></i> Process Shipping Document
</button>
<div class="content" id="selected-files"> <div class="content" id="selected-files">
<div> <div>
<h4>Selected Files</h4> <h4>Selected Files</h4>
@ -61,27 +56,6 @@
</div> </div>
</div> </div>
<script> <script>
// Process Shipping Document function
function processShippingDocument_@(contextId)() {
var shippingDocumentId = @contextId;
$.ajax({
url: '@Url.Action("ProcessShippingDocument", "ManagementPage")',
type: 'POST',
data: { shippingDocumentId: shippingDocumentId },
success: function(response) {
DevExpress.ui.notify("Shipping document processed successfully!", "success", 2000);
// Refresh the file manager
var fileManager = $("#@fileManagerId").dxFileManager("instance");
if (fileManager) {
fileManager.refresh();
}
},
error: function(xhr, status, error) {
DevExpress.ui.notify("Error processing shipping document: " + error, "error", 3000);
}
});
}
function fileUploader_valueChanged(e) { function fileUploader_valueChanged(e) {
var files = e.value; var files = e.value;

View File

@ -348,10 +348,8 @@
Name = "orders-grid", Name = "orders-grid",
UrlRead = new DataUrl("OrderList", "CustomOrder", null), UrlRead = new DataUrl("OrderList", "CustomOrder", null),
SearchButtonId = "search-orders", SearchButtonId = "search-orders",
Ordering = true, // Ordering = true,
ServerSide = true, // ServerSide = false,
Paging = true,
PagingType = "simple_numbers", // or Full
Length = Model.PageSize, Length = Model.PageSize,
LengthMenu = Model.AvailablePageSizes, LengthMenu = Model.AvailablePageSizes,
FooterCallback = !Model.IsLoggedInAsVendor ? "ordersfootercallback" : null, FooterCallback = !Model.IsLoggedInAsVendor ? "ordersfootercallback" : null,

View File

@ -279,7 +279,7 @@
}, },
new ColumnProperty(nameof(ProductModelExtended.IncomingQuantity)) new ColumnProperty(nameof(ProductModelExtended.IncomingQuantity))
{ {
Title = "Kamionon"//T("Admin.Catalog.Products.Fields.Tare").Text Title = "Bejövő"//T("Admin.Catalog.Products.Fields.Tare").Text
}, },
new ColumnProperty(nameof(ProductModelExtended.NetWeight)) new ColumnProperty(nameof(ProductModelExtended.NetWeight))
{ {

View File

@ -268,12 +268,9 @@ public class MgOrderModelFactory<TOrderListModelExt, TOrderModelExt> : OrderMode
} }
prefiltered.Data = null; prefiltered.Data = null;
var totalRecords = prefiltered.RecordsTotal;
var filteredRecords = prefiltered.RecordsFiltered;
//var orderListModelExtended = orderListModel.ToJson().JsonTo<TOrderListModelExt>(); //var orderListModelExtended = orderListModel.ToJson().JsonTo<TOrderListModelExt>();
if (searchModel.BillingCompany != null) if(searchModel.BillingCompany != null)
{ {
//extendedRows = extendedRows.Where(x => x.CustomerCompany != null && x.CustomerCompany.IndexOf(searchModel.BillingCompany, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList(); //extendedRows = extendedRows.Where(x => x.CustomerCompany != null && x.CustomerCompany.IndexOf(searchModel.BillingCompany, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList();
extendedRows = extendedRows.Where(x => x.CustomerId == Convert.ToInt32(searchModel.BillingCompany)).ToList(); extendedRows = extendedRows.Where(x => x.CustomerId == Convert.ToInt32(searchModel.BillingCompany)).ToList();
@ -282,9 +279,7 @@ public class MgOrderModelFactory<TOrderListModelExt, TOrderModelExt> : OrderMode
var orderListModelExtended = prefiltered.CloneTo<TOrderListModelExt>(); var orderListModelExtended = prefiltered.CloneTo<TOrderListModelExt>();
orderListModelExtended.Data = extendedRows; orderListModelExtended.Data = extendedRows;
orderListModelExtended.RecordsFiltered = extendedRows.Count;
orderListModelExtended.RecordsTotal = totalRecords;
orderListModelExtended.RecordsFiltered = filteredRecords;
return orderListModelExtended; return orderListModelExtended;
} }
} }

View File

@ -146,11 +146,6 @@ public class RouteProvider : IRouteProvider
name: "Plugin.FruitBank.Admin.Order.Edit", name: "Plugin.FruitBank.Admin.Order.Edit",
pattern: "Admin/Order/Edit/{id}", pattern: "Admin/Order/Edit/{id}",
defaults: new { controller = "CustomOrder", action = "Edit", area = AreaNames.ADMIN }); defaults: new { controller = "CustomOrder", action = "Edit", area = AreaNames.ADMIN });
endpointRouteBuilder.MapControllerRoute(
name: "Plugin.FruitBank.Admin.ManagementPage.ProcessShippingDocument",
pattern: "Admin/ManagamentPage/ProcessShippingDocument/{id}",
defaults: new { controller = "ManagementPage", action = "ProcessShippingdocument", area = AreaNames.ADMIN });
} }
/// <summary> /// <summary>

View File

@ -31,7 +31,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
public async Task<string> GetOpenAIPDFAnalysisFromText(string pdfText, string userQuestion) public async Task<string> GetOpenAIPDFAnalysisFromText(string pdfText, string userQuestion)
{ {
var systemMessage = $"You are a pdf analyzis assistant of FRUITBANK, the user is asking you questions about a PDF document, that you have access to. The content of the PDF document is the following: {pdfText}"; var systemMessage = $"You are a pdf analyzis assistant, the user is asking you questions about a PDF document, that you have access to. The content of the PDF document is the following: {pdfText}";
var response = await _openAIApiService.GetSimpleResponseAsync(systemMessage, userQuestion); var response = await _openAIApiService.GetSimpleResponseAsync(systemMessage, userQuestion);
if (response == null) return string.Empty; if (response == null) return string.Empty;

View File

@ -2,80 +2,41 @@
@model OrderAttributesModel @model OrderAttributesModel
<!-- InnVoice Management Section --> <!-- InnVoice Management Section -->
<!-- Order Subsection --> <div class="card card-default mb-3">
<div class="form-group row"> <div class="card-header">
<i class="fas fa-file-invoice"></i>
<div class="col-12 col-md-6"> InnVoice Management
<div class="card card-default mb-3">
<div class="card-header">
<i class="fas fa-file-invoice"></i>
Megrendelés beállítások
</div>
<div class="card-body">
<div class="form-group row">
<div class="col-md-3 text-right">
<strong>Mérés információ</strong>
</div>
<div class="col-md-9">
@(Model.IsMeasurable ? "Mérendő" : "Nem mérendő")
</div>
</div>
<div class="form-group row">
<div class="col-md-3">
<nop-label asp-for="DateOfReceipt" />
</div>
<div class="col-md-9">
<nop-editor asp-for="DateOfReceipt" asp-template="" />
</div>
</div>
<div class="form-group row">
<div class="col-md-12 text-right">
<button type="button" id="saveAttributesBtn" class="btn btn-primary">
<i class="fa fa-save"></i> Save Attributes
</button>
</div>
</div>
</div>
</div>
</div> </div>
<div class="card-body">
<div class="col-12 col-md-6"> <!-- Order Subsection -->
<div class="card card-default mb-3"> <div class="form-group row">
<div class="card-header"> <h3>Innvoice</h3>
<i class="fas fa-file-invoice"></i> <div class="col-12 col-md-3">
InnVoice Management <h5><i class="fas fa-shopping-cart"></i> Megrendelés beküldése Innvoice-ba</h5>
</div> <div id="orderStatus" class="alert alert-info" style="display: none;">
<div class="card-body"> <i class="fas fa-info-circle"></i> <span id="orderStatusMessage"></span>
<div class="col-6">
<h5><i class="fas fa-shopping-cart"></i> Megrendelés beküldése Innvoice-ba</h5>
<div id="orderStatus" class="alert alert-info" style="display: none;">
<i class="fas fa-info-circle"></i> <span id="orderStatusMessage"></span>
</div>
<div id="orderDetails" style="display: none;">
<p><strong>Order Table ID:</strong> <span id="orderTableId"></span></p>
<p><strong>Order Tech ID:</strong> <span id="orderTechId"></span></p>
<p>
<a id="orderPdfLink" href="#" target="_blank" class="btn btn-sm btn-info">
<i class="fas fa-file-pdf"></i> PDF megtekintése
</a>
</p>
</div>
</div> </div>
<div id="orderDetails" style="display: none;">
<div class="col-6 text-right float-end"> <p><strong>Order Table ID:</strong> <span id="orderTableId"></span></p>
<button type="button" id="createOrderBtn" class="btn btn-success"> <p><strong>Order Tech ID:</strong> <span id="orderTechId"></span></p>
<i class="fas fa-shopping-cart"></i> Létrehozás <p>
</button> <a id="orderPdfLink" href="#" target="_blank" class="btn btn-sm btn-info">
<button type="button" id="checkOrderBtn" class="btn btn-secondary" style="display: none;"> <i class="fas fa-file-pdf"></i> PDF megtekintése
<i class="fas fa-sync"></i> Invoice adat ellenőrzése </a>
</button> </p>
</div> </div>
</div> </div>
</div>
</div>
<div class="col-12 col-md-3 text-right float-end">
<button type="button" id="createOrderBtn" class="btn btn-success">
<i class="fas fa-shopping-cart"></i> Létrehozás
</button>
<button type="button" id="checkOrderBtn" class="btn btn-secondary" style="display: none;">
<i class="fas fa-sync"></i> Invoice adat ellenőrzése
</button>
</div>
@* <div class="col-12 col-md-3"> @* <div class="col-12 col-md-3">
<h5><i class="fas fa-file-invoice-dollar"></i> Invoice</h5> <h5><i class="fas fa-file-invoice-dollar"></i> Invoice</h5>
<div id="invoiceStatus" class="alert alert-info" style="display: none;"> <div id="invoiceStatus" class="alert alert-info" style="display: none;">
<i class="fas fa-info-circle"></i> <span id="invoiceStatusMessage"></span> <i class="fas fa-info-circle"></i> <span id="invoiceStatusMessage"></span>
@ -91,7 +52,7 @@
</div> </div>
</div> *@ </div> *@
@* <div class="col-12 col-md-3 text-right"> @* <div class="col-12 col-md-3 text-right">
<button type="button" id="createInvoiceBtn" class="btn btn-success"> <button type="button" id="createInvoiceBtn" class="btn btn-success">
<i class="fas fa-file-invoice-dollar"></i> Create Invoice <i class="fas fa-file-invoice-dollar"></i> Create Invoice
</button> </button>
@ -99,10 +60,41 @@
<i class="fas fa-sync"></i> Refresh Invoice <i class="fas fa-sync"></i> Refresh Invoice
</button> </button>
</div> *@ </div> *@
</div>
<!-- Attributes Section (NO FORM TAG - just a div) -->
<div id="orderAttributesSection">
<div class="form-group row">
<div class="col-12">
<div class="form-group row">
<div class="col-md-3 text-right">
<strong>Mérés információ</strong>
</div>
<div class="col-md-9">
@(Model.IsMeasurable ? "Mérendő" : "Nem mérendő")
</div>
</div>
<div class="form-group row">
<div class="col-md-3">
<nop-label asp-for="DateOfReceipt" />
</div>
<div class="col-md-9">
<nop-editor asp-for="DateOfReceipt" asp-template="" />
</div>
</div>
<div class="form-group row">
<div class="col-md-12 text-right">
<button type="button" id="saveAttributesBtn" class="btn btn-primary">
<i class="fa fa-save"></i> Save Attributes
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
var createOrderUrl = '/Admin/InnVoiceOrder/CreateOrder'; var createOrderUrl = '/Admin/InnVoiceOrder/CreateOrder';