|
|
|
|
@ -13,6 +13,7 @@ using Nop.Web.Areas.Admin.Controllers;
|
|
|
|
|
using Nop.Web.Framework;
|
|
|
|
|
using Nop.Web.Framework.Mvc.Filters;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using UglyToad.PdfPig.DocumentLayoutAnalysis.TextExtractor;
|
|
|
|
|
|
|
|
|
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
@ -231,7 +232,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var filesList = new List<Files>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//iteratation 1: iterate documents to determine their type by AI
|
|
|
|
|
|
|
|
|
|
@ -258,7 +259,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
//if (file.Length > 0 && file.ContentType == "application/pdf")
|
|
|
|
|
if (file.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)){
|
|
|
|
|
if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Open the PDF from the IFormFile's stream directly in memory
|
|
|
|
|
@ -336,7 +338,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
_logger.Detail(pdfText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string analysisPrompt = "Extract the document identification number from this document, determine the type of the " +
|
|
|
|
|
"document IN ENGLISH from the available list, and return them as JSON: documentNumber, documentType. " +
|
|
|
|
|
$"Available filetypes: {nameof(DocumentType.Invoice)}, {nameof(DocumentType.ShippingDocument)} , {nameof(DocumentType.OrderConfirmation)}, {nameof(DocumentType.Unknown)}" +
|
|
|
|
|
@ -377,65 +378,206 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
// - IF WE DON'T HAVE PARTNERID ALREADY: read partner information
|
|
|
|
|
// (check if all 3 refers to the same partner)
|
|
|
|
|
// save partner information to partners table { Id, Name, TaxId, CertificationNumber, PostalCode, Country, State, County, City, Street }
|
|
|
|
|
if (partnerId != null)
|
|
|
|
|
if (partnerId == null)
|
|
|
|
|
{
|
|
|
|
|
string partnerAnalysisPrompt = "Extract the partner information from this document, 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.";
|
|
|
|
|
|
|
|
|
|
//here I can start preparing the file entity
|
|
|
|
|
var partnerAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), partnerAnalysisPrompt);
|
|
|
|
|
var extractedPartnerData = ParsePartnerDataAIResponse(partnerAnalyzis);
|
|
|
|
|
//find partner in DB... there is a serious chance that the partner Name and Taxid determines the partner
|
|
|
|
|
|
|
|
|
|
if (extractedPartnerData.Name != null)
|
|
|
|
|
var partners = await _dbContext.Partners.GetAll().ToListAsync();
|
|
|
|
|
foreach (var partner in partners)
|
|
|
|
|
{
|
|
|
|
|
_logger.Detail("AI Analysis Partner Result:");
|
|
|
|
|
_logger.Detail(extractedPartnerData.Name);
|
|
|
|
|
if (pdfText.Contains(partner.Name) || (partner.TaxId != null && pdfText.Contains(partner.TaxId)))
|
|
|
|
|
{
|
|
|
|
|
partnerId = partner.Id;
|
|
|
|
|
_logger.Detail($"Found existing partner in DB: {partner.Name} (ID: {partner.Id})");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (extractedPartnerData.TaxId != null)
|
|
|
|
|
if (partnerId == null)
|
|
|
|
|
{
|
|
|
|
|
_logger.Detail(extractedPartnerData.TaxId);
|
|
|
|
|
}
|
|
|
|
|
_logger.Detail("No existing partner found in DB, proceeding to extract partner info via AI.");
|
|
|
|
|
|
|
|
|
|
if (extractedPartnerData.Country != null)
|
|
|
|
|
{
|
|
|
|
|
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.";
|
|
|
|
|
|
|
|
|
|
_logger.Detail(extractedPartnerData.Country);
|
|
|
|
|
}
|
|
|
|
|
//here I can start preparing the file entity
|
|
|
|
|
var partnerAnalyzis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(pdfText.ToString(), partnerAnalysisPrompt);
|
|
|
|
|
var extractedPartnerData = ParsePartnerDataAIResponse(partnerAnalyzis);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (extractedPartnerData.State != null)
|
|
|
|
|
{
|
|
|
|
|
_logger.Detail(extractedPartnerData.State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// - save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf
|
|
|
|
|
// where documentId is the number or id IN the document
|
|
|
|
|
|
|
|
|
|
//shortcut
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// Create upload directory if it doesn't exist
|
|
|
|
|
var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId.ToString(), "documents");
|
|
|
|
|
Directory.CreateDirectory(uploadsPath);
|
|
|
|
|
|
|
|
|
|
// Generate unique filename
|
|
|
|
|
fileName = $"{Guid.NewGuid()}_{file.FileName}";
|
|
|
|
|
var filePath = Path.Combine(uploadsPath, fileName);
|
|
|
|
|
|
|
|
|
|
// Save file
|
|
|
|
|
using (var stream = new FileStream(filePath, FileMode.Create))
|
|
|
|
|
var partnerPrompt = @"Extract the partner/company information from the following text and return ONLY a valid JSON object with these exact fields:
|
|
|
|
|
{
|
|
|
|
|
await file.CopyToAsync(stream);
|
|
|
|
|
""Name"": ""company name"",
|
|
|
|
|
""TaxId"": ""tax identification number"",
|
|
|
|
|
""CertificationNumber"": ""certification number if exists, otherwise empty string"",
|
|
|
|
|
""PostalCode"": ""postal code"",
|
|
|
|
|
""Country"": ""country name"",
|
|
|
|
|
""State"": ""state if exists, otherwise empty string"",
|
|
|
|
|
""County"": ""county if exists, otherwise empty string"",
|
|
|
|
|
""City"": ""city name"",
|
|
|
|
|
""Street"": ""street address""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
If a field is not found in the text, use an empty string. Return ONLY the JSON object, no additional text or explanation.
|
|
|
|
|
|
|
|
|
|
Text to analyze:
|
|
|
|
|
" + pdfText;
|
|
|
|
|
|
|
|
|
|
var partnerResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
|
|
|
|
pdfText,
|
|
|
|
|
partnerPrompt
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var partner = JsonSerializer.Deserialize<Partner>(CleanJsonResponse(partnerResponse),
|
|
|
|
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
|
|
|
|
|
|
// 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:
|
|
|
|
|
{
|
|
|
|
|
""DocumentIdNumber"": ""document ID or reference number if exists, otherwise empty string"",
|
|
|
|
|
""ShippingDate"": ""shipping date in ISO format (yyyy-MM-dd)"",
|
|
|
|
|
""Country"": ""country of origin for pickup"",
|
|
|
|
|
""TotalPallets"": number_of_total_pallets
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
If a field is not found, use empty string for text fields, current date for ShippingDate, and 0 for TotalPallets. Return ONLY the JSON object, no additional text or explanation.
|
|
|
|
|
|
|
|
|
|
Text to analyze:
|
|
|
|
|
" + pdfText;
|
|
|
|
|
|
|
|
|
|
var shippingDocResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
|
|
|
|
pdfText,
|
|
|
|
|
shippingDocPrompt
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var shippingDocData = JsonSerializer.Deserialize<ShippingDocumentDto>(CleanJsonResponse(shippingDocResponse),
|
|
|
|
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
|
|
|
|
|
|
|
|
|
// 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:
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
""Name"": ""product name"",
|
|
|
|
|
""PalletsOnDocument"": number_of_pallets,
|
|
|
|
|
""QuantityOnDocument"": quantity_count,
|
|
|
|
|
""NetWeightOnDocument"": net_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.
|
|
|
|
|
|
|
|
|
|
Text to analyze:
|
|
|
|
|
" + pdfText;
|
|
|
|
|
|
|
|
|
|
var itemsResponse = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
|
|
|
|
pdfText,
|
|
|
|
|
itemsPrompt
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
var items = JsonSerializer.Deserialize<List<ShippingItem>>(CleanJsonResponse(itemsResponse),
|
|
|
|
|
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? new List<ShippingItem>();
|
|
|
|
|
|
|
|
|
|
// Prepare result
|
|
|
|
|
var result = new ShippingDocumentAnalysisResult
|
|
|
|
|
{
|
|
|
|
|
Partner = partner,
|
|
|
|
|
ShippingDocument = new ShippingDocument
|
|
|
|
|
{
|
|
|
|
|
DocumentIdNumber = shippingDocData.DocumentIdNumber,
|
|
|
|
|
ShippingDate = shippingDocData.ShippingDate,
|
|
|
|
|
Country = shippingDocData.Country,
|
|
|
|
|
TotalPallets = shippingDocData.TotalPallets
|
|
|
|
|
},
|
|
|
|
|
ShippingItems = items
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return Ok(result);
|
|
|
|
|
}
|
|
|
|
|
catch (JsonException ex)
|
|
|
|
|
{
|
|
|
|
|
return BadRequest($"Failed to parse AI response: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.Error($"Error saving file: {ex.Message}", ex);
|
|
|
|
|
//return Json(new { success = false, errorMessage = ex.Message });
|
|
|
|
|
return BadRequest("No files were uploaded.");
|
|
|
|
|
return StatusCode(500, $"An error occurred: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// - save the documents to file system - wwwroot/uploads/orders/order-{orderId}/fileId-documentId.pdf
|
|
|
|
|
// where documentId is the number or id IN the document
|
|
|
|
|
//try
|
|
|
|
|
//{
|
|
|
|
|
// // Create upload directory if it doesn't exist
|
|
|
|
|
// var uploadsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads", "orders", "order" + shippingDocumentId.ToString(), "documents");
|
|
|
|
|
// Directory.CreateDirectory(uploadsPath);
|
|
|
|
|
|
|
|
|
|
// // Generate unique filename
|
|
|
|
|
// fileName = $"{Guid.NewGuid()}_{file.FileName}";
|
|
|
|
|
// var filePath = Path.Combine(uploadsPath, fileName);
|
|
|
|
|
|
|
|
|
|
// // Save file
|
|
|
|
|
// using (var stream = new FileStream(filePath, FileMode.Create))
|
|
|
|
|
// {
|
|
|
|
|
// await file.CopyToAsync(stream);
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
//catch (Exception ex)
|
|
|
|
|
//{
|
|
|
|
|
// _logger.Error($"Error saving file: {ex.Message}", ex);
|
|
|
|
|
// //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
|
|
|
|
|
// - INSIDE ITERATION:
|
|
|
|
|
// - IF SHIPPINGDOCUMENT: read shipping information
|
|
|
|
|
@ -446,68 +588,70 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
// save document information into DB {financial db table not created yet}
|
|
|
|
|
// - save the documents to Files Table - { name, extension, type, rawtext }
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//try
|
|
|
|
|
//{
|
|
|
|
|
|
|
|
|
|
// Analyze PDF with AI to extract structured data
|
|
|
|
|
var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
|
|
|
|
pdfText.ToString(),
|
|
|
|
|
"You work for FruitBank. Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field."
|
|
|
|
|
);
|
|
|
|
|
// // Analyze PDF with AI to extract structured data
|
|
|
|
|
// var lastPrompt = "You work for FruitBank. Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field.";
|
|
|
|
|
|
|
|
|
|
// Parse AI response (assuming it returns JSON)
|
|
|
|
|
var extractedData = ParseShippingDocumentAIResponse(aiAnalysis);
|
|
|
|
|
// var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
|
|
|
|
// pdfText,
|
|
|
|
|
// lastPrompt
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
//TODO: Save document record to database
|
|
|
|
|
_logger.Detail("AI Analysis Result:");
|
|
|
|
|
_logger.Detail(extractedData.RecipientName);
|
|
|
|
|
_logger.Detail(extractedData.SenderName);
|
|
|
|
|
_logger.Detail(extractedData.InvoiceNumber);
|
|
|
|
|
_logger.Detail(extractedData.TotalAmount.ToString());
|
|
|
|
|
_logger.Detail(extractedData.ItemCount.ToString());
|
|
|
|
|
_logger.Detail(extractedData.Notes);
|
|
|
|
|
// // Parse AI response (assuming it returns JSON)
|
|
|
|
|
// var extractedData = ParseShippingDocumentAIResponse(aiAnalysis);
|
|
|
|
|
|
|
|
|
|
// //TODO: Save document record to database
|
|
|
|
|
// _logger.Detail("AI Analysis Result:");
|
|
|
|
|
// _logger.Detail(extractedData.RecipientName);
|
|
|
|
|
// _logger.Detail(extractedData.SenderName);
|
|
|
|
|
// _logger.Detail(extractedData.InvoiceNumber);
|
|
|
|
|
// _logger.Detail(extractedData.TotalAmount.ToString());
|
|
|
|
|
// _logger.Detail(extractedData.ItemCount.ToString());
|
|
|
|
|
// _logger.Detail(extractedData.Notes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var documentId = 1; // Replace with: savedDocument.Id
|
|
|
|
|
// var documentId = 1; // Replace with: savedDocument.Id
|
|
|
|
|
|
|
|
|
|
// Return structured document model
|
|
|
|
|
var documentModel = new ShippingDocumentModel
|
|
|
|
|
{
|
|
|
|
|
Id = documentId,
|
|
|
|
|
ShippingId = shippingDocumentId,
|
|
|
|
|
FileName = file.FileName,
|
|
|
|
|
FilePath = $"/uploads/shippingDocuments/{fileName}",
|
|
|
|
|
FileSize = (int)(file.Length / 1024),
|
|
|
|
|
DocumentDate = extractedData.DocumentDate,
|
|
|
|
|
RecipientName = extractedData.RecipientName,
|
|
|
|
|
SenderName = extractedData.SenderName,
|
|
|
|
|
InvoiceNumber = extractedData.InvoiceNumber,
|
|
|
|
|
TotalAmount = extractedData.TotalAmount,
|
|
|
|
|
ItemCount = extractedData.ItemCount,
|
|
|
|
|
Notes = extractedData.Notes,
|
|
|
|
|
RawAIAnalysis = aiAnalysis // Store the raw AI response for debugging
|
|
|
|
|
};
|
|
|
|
|
// // Return structured document model
|
|
|
|
|
// var documentModel = new ShippingDocumentModel
|
|
|
|
|
// {
|
|
|
|
|
// Id = documentId,
|
|
|
|
|
// ShippingId = shippingDocumentId,
|
|
|
|
|
// FileName = file.FileName,
|
|
|
|
|
// FilePath = $"/uploads/shippingDocuments/{fileName}",
|
|
|
|
|
// FileSize = (int)(file.Length / 1024),
|
|
|
|
|
// DocumentDate = extractedData.DocumentDate,
|
|
|
|
|
// RecipientName = extractedData.RecipientName,
|
|
|
|
|
// SenderName = extractedData.SenderName,
|
|
|
|
|
// InvoiceNumber = extractedData.InvoiceNumber,
|
|
|
|
|
// TotalAmount = extractedData.TotalAmount,
|
|
|
|
|
// ItemCount = extractedData.ItemCount,
|
|
|
|
|
// Notes = extractedData.Notes,
|
|
|
|
|
// RawAIAnalysis = aiAnalysis // Store the raw AI response for debugging
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// var savedDocument = await _documentService.InsertDocumentAsync(document);
|
|
|
|
|
// // var savedDocument = await _documentService.InsertDocumentAsync(document);
|
|
|
|
|
|
|
|
|
|
// Mock saved document ID
|
|
|
|
|
// // Mock saved document ID
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//return Json(new
|
|
|
|
|
//{
|
|
|
|
|
// success = true,
|
|
|
|
|
// document = documentModel
|
|
|
|
|
//});
|
|
|
|
|
// //return Json(new
|
|
|
|
|
// //{
|
|
|
|
|
// // success = true,
|
|
|
|
|
// // document = documentModel
|
|
|
|
|
// //});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.Error($"Error uploading file: {ex.Message}", ex);
|
|
|
|
|
//return Json(new { success = false, errorMessage = ex.Message });
|
|
|
|
|
return BadRequest("No files were uploaded.");
|
|
|
|
|
}
|
|
|
|
|
//}
|
|
|
|
|
//catch (Exception ex)
|
|
|
|
|
//{
|
|
|
|
|
// _logger.Error($"Error uploading file: {ex.Message}", ex);
|
|
|
|
|
// //return Json(new { success = false, errorMessage = ex.Message });
|
|
|
|
|
// return BadRequest("No files were uploaded.");
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -550,6 +694,47 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|
|
|
|
// return Ok(new { message = "Files uploaded successfully." });
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
private string CleanJsonResponse(string response)
|
|
|
|
|
{
|
|
|
|
|
// Remove markdown code blocks if present
|
|
|
|
|
response = response.Trim();
|
|
|
|
|
if (response.StartsWith("```json"))
|
|
|
|
|
{
|
|
|
|
|
response = response.Substring(7);
|
|
|
|
|
}
|
|
|
|
|
else if (response.StartsWith("```"))
|
|
|
|
|
{
|
|
|
|
|
response = response.Substring(3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.EndsWith("```"))
|
|
|
|
|
{
|
|
|
|
|
response = response.Substring(0, response.Length - 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.Trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ShippingDocumentAnalysisRequest
|
|
|
|
|
{
|
|
|
|
|
public string RawText { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ShippingDocumentAnalysisResult
|
|
|
|
|
{
|
|
|
|
|
public Partner Partner { get; set; }
|
|
|
|
|
public ShippingDocument ShippingDocument { get; set; }
|
|
|
|
|
public List<ShippingItem> ShippingItems { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ShippingDocumentDto
|
|
|
|
|
{
|
|
|
|
|
public string DocumentIdNumber { get; set; }
|
|
|
|
|
public DateTime ShippingDate { get; set; }
|
|
|
|
|
public string Country { get; set; }
|
|
|
|
|
public int TotalPallets { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ExtractedDocumentData ParseShippingDocumentAIResponse(string aiResponse)
|
|
|
|
|
{
|
|
|
|
|
|