innvoice
This commit is contained in:
parent
89aa10e07b
commit
f5b27f8c18
|
|
@ -5,6 +5,7 @@ using FruitBank.Common.Entities;
|
||||||
using FruitBank.Common.Interfaces;
|
using FruitBank.Common.Interfaces;
|
||||||
using FruitBank.Common.Server.Interfaces;
|
using FruitBank.Common.Server.Interfaces;
|
||||||
using FruitBank.Common.SignalRs;
|
using FruitBank.Common.SignalRs;
|
||||||
|
using Mango.Nop.Core.Extensions;
|
||||||
using Mango.Nop.Core.Loggers;
|
using Mango.Nop.Core.Loggers;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
@ -176,7 +177,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
var order = new Order
|
var order = new Order
|
||||||
{
|
{
|
||||||
OrderGuid = Guid.NewGuid(),
|
OrderGuid = Guid.NewGuid(),
|
||||||
CustomOrderNumber = "",
|
CustomOrderNumber = null,
|
||||||
CustomerId = customerId,
|
CustomerId = customerId,
|
||||||
CustomerLanguageId = customer.LanguageId ?? 1,
|
CustomerLanguageId = customer.LanguageId ?? 1,
|
||||||
CustomerTaxDisplayType = TaxDisplayType.IncludingTax,
|
CustomerTaxDisplayType = TaxDisplayType.IncludingTax,
|
||||||
|
|
@ -217,7 +218,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
DiscountAmountInclTax = 0,
|
DiscountAmountInclTax = 0,
|
||||||
DiscountAmountExclTax = 0
|
DiscountAmountExclTax = 0
|
||||||
};
|
};
|
||||||
|
//var valami = product.GenericAttributes.GetValueOrNull<bool>(nameof(IOrderDto.DateOfReceipt)); TEST - A.
|
||||||
await _orderService.InsertOrderItemAsync(orderItem);
|
await _orderService.InsertOrderItemAsync(orderItem);
|
||||||
}
|
}
|
||||||
else _logger.Error($"(productDtosById.TryGetValue(item.Id, out var product) == false); {item}");
|
else _logger.Error($"(productDtosById.TryGetValue(item.Id, out var product) == false); {item}");
|
||||||
|
|
@ -226,7 +227,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (transactionSuccess) return RedirectToAction("Edit", new { id = order.Id });
|
if (transactionSuccess) return RedirectToAction("Edit", "Order", new { id = order.Id });
|
||||||
|
|
||||||
_logger.Error($"(transactionSuccess == false)");
|
_logger.Error($"(transactionSuccess == false)");
|
||||||
return RedirectToAction("Error", new { id = order.Id });
|
return RedirectToAction("Error", new { id = order.Id });
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,397 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Nop.Core;
|
||||||
|
using Nop.Core.Domain.Orders;
|
||||||
|
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||||
|
using Nop.Services.Catalog;
|
||||||
|
using Nop.Services.Common;
|
||||||
|
using Nop.Services.Customers;
|
||||||
|
using Nop.Services.Directory;
|
||||||
|
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 InnVoiceOrderController : BasePluginController
|
||||||
|
{
|
||||||
|
private readonly IOrderService _orderService;
|
||||||
|
private readonly IWorkContext _workContext;
|
||||||
|
private readonly IStoreContext _storeContext;
|
||||||
|
private readonly ICustomerService _customerService;
|
||||||
|
private readonly ICountryService _countryService;
|
||||||
|
private readonly IProductService _productService;
|
||||||
|
private readonly InnVoiceOrderService _innVoiceOrderService;
|
||||||
|
private readonly IGenericAttributeService _genericAttributeService;
|
||||||
|
|
||||||
|
public InnVoiceOrderController(
|
||||||
|
IOrderService orderService,
|
||||||
|
IWorkContext workContext,
|
||||||
|
IStoreContext storeContext,
|
||||||
|
ICustomerService customerService,
|
||||||
|
ICountryService countryService,
|
||||||
|
IProductService productService,
|
||||||
|
InnVoiceOrderService innVoiceOrderService,
|
||||||
|
IGenericAttributeService genericAttributeService)
|
||||||
|
{
|
||||||
|
_orderService = orderService;
|
||||||
|
_workContext = workContext;
|
||||||
|
_storeContext = storeContext;
|
||||||
|
_customerService = customerService;
|
||||||
|
_countryService = countryService;
|
||||||
|
_productService = productService;
|
||||||
|
_innVoiceOrderService = innVoiceOrderService;
|
||||||
|
_genericAttributeService = genericAttributeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an order in InnVoice from a NopCommerce order
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
[IgnoreAntiforgeryToken]
|
||||||
|
public async Task<IActionResult> CreateOrder(int orderId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var order = await _orderService.GetOrderByIdAsync(orderId);
|
||||||
|
if (order == null)
|
||||||
|
return Json(new { success = false, message = "Order not found" });
|
||||||
|
|
||||||
|
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
|
||||||
|
|
||||||
|
// Get billing address
|
||||||
|
var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer);
|
||||||
|
if (billingAddress == null)
|
||||||
|
return Json(new { success = false, message = "Billing address not found" });
|
||||||
|
|
||||||
|
var billingCountry = await _countryService.GetCountryByAddressAsync(billingAddress);
|
||||||
|
var billingCountryCode = billingCountry?.TwoLetterIsoCode ?? "HU";
|
||||||
|
|
||||||
|
// Get shipping address
|
||||||
|
var shippingAddress = await _customerService.GetCustomerShippingAddressAsync(customer);
|
||||||
|
var shippingCountry = shippingAddress != null
|
||||||
|
? await _countryService.GetCountryByAddressAsync(shippingAddress)
|
||||||
|
: null;
|
||||||
|
var shippingCountryCode = shippingCountry?.TwoLetterIsoCode ?? billingCountryCode;
|
||||||
|
|
||||||
|
// Create order request
|
||||||
|
var orderRequest = new OrderCreateRequest
|
||||||
|
{
|
||||||
|
VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}",
|
||||||
|
VevoIrsz = billingAddress.ZipPostalCode ?? "",
|
||||||
|
VevoTelep = billingAddress.City ?? "",
|
||||||
|
VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(),
|
||||||
|
VevoOrszag = billingCountryCode,
|
||||||
|
VevoAdoszam = billingAddress.Company, // Or a custom field for tax number
|
||||||
|
MegrendelestombID = 1, // Configure this based on your setup
|
||||||
|
MegrendelesKelte = order.CreatedOnUtc.ToLocalTime(),
|
||||||
|
Hatarido = DateTime.Now.AddDays(7), // 7 days delivery time
|
||||||
|
Devizanem = order.CustomerCurrencyCode,
|
||||||
|
FizetesiMod = order.PaymentMethodSystemName,
|
||||||
|
Email = billingAddress.Email,
|
||||||
|
Telefon = billingAddress.PhoneNumber,
|
||||||
|
MegrendelesSzamStr = order.Id.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add shipping address if different
|
||||||
|
if (shippingAddress != null)
|
||||||
|
{
|
||||||
|
orderRequest.SzallNev = $"{shippingAddress.FirstName} {shippingAddress.LastName}";
|
||||||
|
orderRequest.SzallIrsz = shippingAddress.ZipPostalCode ?? "";
|
||||||
|
orderRequest.SzallTelep = shippingAddress.City ?? "";
|
||||||
|
orderRequest.SzallUtcaHsz = $"{shippingAddress.Address1} {shippingAddress.Address2}".Trim();
|
||||||
|
orderRequest.SzallOrszag = shippingCountryCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add order items
|
||||||
|
var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
|
||||||
|
foreach (var item in orderItems)
|
||||||
|
{
|
||||||
|
var product = await _productService.GetProductByIdAsync(item.ProductId);
|
||||||
|
|
||||||
|
orderRequest.AddItem(new InnVoiceOrderItem
|
||||||
|
{
|
||||||
|
TetelNev = product?.Name ?? "Product",
|
||||||
|
AfaSzoveg = "27%", // Configure VAT rate as needed
|
||||||
|
Brutto = true,
|
||||||
|
EgysegAr = item.UnitPriceInclTax,
|
||||||
|
Mennyiseg = item.Quantity,
|
||||||
|
MennyisegEgyseg = "db",
|
||||||
|
CikkSzam = product?.Sku
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create order via API
|
||||||
|
var response = await _innVoiceOrderService.CreateOrderAsync(orderRequest);
|
||||||
|
|
||||||
|
if (response.IsSuccess)
|
||||||
|
{
|
||||||
|
// Save the TechId, TableId, and PrintUrl to the order for future reference
|
||||||
|
await _genericAttributeService.SaveAttributeAsync(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderTechId",
|
||||||
|
response.TechId,
|
||||||
|
(await _storeContext.GetCurrentStoreAsync()).Id
|
||||||
|
);
|
||||||
|
|
||||||
|
await _genericAttributeService.SaveAttributeAsync(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderTableId",
|
||||||
|
response.TableId?.ToString(),
|
||||||
|
(await _storeContext.GetCurrentStoreAsync()).Id
|
||||||
|
);
|
||||||
|
|
||||||
|
await _genericAttributeService.SaveAttributeAsync(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderPrintLink",
|
||||||
|
response.PrintUrl?.ToString(),
|
||||||
|
(await _storeContext.GetCurrentStoreAsync()).Id
|
||||||
|
);
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
message = "Order created successfully in InnVoice",
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
tableId = response.TableId,
|
||||||
|
techId = response.TechId,
|
||||||
|
printUrl = response.PrintUrl
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"InnVoice API Error: {response.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"Error: {ex.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get InnVoice order status for a NopCommerce order
|
||||||
|
/// </summary>
|
||||||
|
/// <summary>
|
||||||
|
/// Get InnVoice order status for a NopCommerce order
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetOrderStatus(int orderId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var order = await _orderService.GetOrderByIdAsync(orderId);
|
||||||
|
if (order == null)
|
||||||
|
return Json(new { success = false, message = "Order not found" });
|
||||||
|
|
||||||
|
var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;
|
||||||
|
|
||||||
|
// Get saved InnVoice order TechId
|
||||||
|
var techId = await _genericAttributeService.GetAttributeAsync<string>(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderTechId",
|
||||||
|
storeId
|
||||||
|
);
|
||||||
|
|
||||||
|
var printLink = await _genericAttributeService.GetAttributeAsync<string>(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderPrintLink",
|
||||||
|
storeId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(techId))
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = "No InnVoice order found for this order"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the order details from InnVoice API using TechId
|
||||||
|
var innVoiceOrders = await _innVoiceOrderService.GetOrderByTechIdAsync(techId);
|
||||||
|
|
||||||
|
if (innVoiceOrders == null || innVoiceOrders.Count == 0)
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = "Order not found in InnVoice"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var innVoiceOrder = innVoiceOrders.FirstOrDefault();
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
tableId = innVoiceOrder.TableId,
|
||||||
|
techId = innVoiceOrder.TechId,
|
||||||
|
printUrl = printLink,
|
||||||
|
customerName = innVoiceOrder.CustomerName,
|
||||||
|
totalGross = innVoiceOrder.TotalGross,
|
||||||
|
currency = innVoiceOrder.Currency,
|
||||||
|
orderDate = innVoiceOrder.OrderDate
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"Error: {ex.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create multiple orders in InnVoice from multiple NopCommerce orders
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
[IgnoreAntiforgeryToken]
|
||||||
|
public async Task<IActionResult> CreateMultipleOrders([FromBody] int[] orderIds)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (orderIds == null || orderIds.Length == 0)
|
||||||
|
return Json(new { success = false, message = "No order IDs provided" });
|
||||||
|
|
||||||
|
var orderRequests = new List<OrderCreateRequest>();
|
||||||
|
|
||||||
|
foreach (var orderId in orderIds)
|
||||||
|
{
|
||||||
|
var order = await _orderService.GetOrderByIdAsync(orderId);
|
||||||
|
if (order == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
|
||||||
|
var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer);
|
||||||
|
|
||||||
|
if (billingAddress == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var billingCountry = await _countryService.GetCountryByAddressAsync(billingAddress);
|
||||||
|
var billingCountryCode = billingCountry?.TwoLetterIsoCode ?? "HU";
|
||||||
|
|
||||||
|
var orderRequest = new OrderCreateRequest
|
||||||
|
{
|
||||||
|
VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}",
|
||||||
|
VevoIrsz = billingAddress.ZipPostalCode ?? "",
|
||||||
|
VevoTelep = billingAddress.City ?? "",
|
||||||
|
VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(),
|
||||||
|
VevoOrszag = billingCountryCode,
|
||||||
|
MegrendelestombID = 1,
|
||||||
|
MegrendelesKelte = order.CreatedOnUtc.ToLocalTime(),
|
||||||
|
Hatarido = DateTime.Now.AddDays(7),
|
||||||
|
Devizanem = order.CustomerCurrencyCode,
|
||||||
|
FizetesiMod = order.PaymentMethodSystemName,
|
||||||
|
Email = billingAddress.Email,
|
||||||
|
Telefon = billingAddress.PhoneNumber,
|
||||||
|
MegrendelesSzamStr = order.Id.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add order items
|
||||||
|
var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
|
||||||
|
foreach (var item in orderItems)
|
||||||
|
{
|
||||||
|
var product = await _productService.GetProductByIdAsync(item.ProductId);
|
||||||
|
|
||||||
|
orderRequest.AddItem(new InnVoiceOrderItem
|
||||||
|
{
|
||||||
|
TetelNev = product?.Name ?? "Product",
|
||||||
|
AfaSzoveg = "27%",
|
||||||
|
Brutto = true,
|
||||||
|
EgysegAr = item.UnitPriceInclTax,
|
||||||
|
Mennyiseg = item.Quantity,
|
||||||
|
MennyisegEgyseg = "db",
|
||||||
|
CikkSzam = product?.Sku
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
orderRequests.Add(orderRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderRequests.Count == 0)
|
||||||
|
return Json(new { success = false, message = "No valid orders to create" });
|
||||||
|
|
||||||
|
// Create orders via API
|
||||||
|
var responses = await _innVoiceOrderService.CreateOrdersAsync(orderRequests);
|
||||||
|
|
||||||
|
var successCount = responses.Count(r => r.IsSuccess);
|
||||||
|
var failureCount = responses.Count - successCount;
|
||||||
|
|
||||||
|
// Save TechIds for successful orders
|
||||||
|
var storeId = (await _storeContext.GetCurrentStoreAsync()).Id;
|
||||||
|
for (int i = 0; i < responses.Count && i < orderIds.Length; i++)
|
||||||
|
{
|
||||||
|
if (responses[i].IsSuccess)
|
||||||
|
{
|
||||||
|
var order = await _orderService.GetOrderByIdAsync(orderIds[i]);
|
||||||
|
if (order != null)
|
||||||
|
{
|
||||||
|
await _genericAttributeService.SaveAttributeAsync(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderTechId",
|
||||||
|
responses[i].TechId,
|
||||||
|
storeId
|
||||||
|
);
|
||||||
|
|
||||||
|
await _genericAttributeService.SaveAttributeAsync(
|
||||||
|
order,
|
||||||
|
"InnVoiceOrderTableId",
|
||||||
|
responses[i].TableId?.ToString(),
|
||||||
|
storeId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = successCount > 0,
|
||||||
|
message = $"Created {successCount} orders successfully. {failureCount} failed.",
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
successCount,
|
||||||
|
failureCount,
|
||||||
|
responses = responses.Select(r => new
|
||||||
|
{
|
||||||
|
success = r.IsSuccess,
|
||||||
|
tableId = r.TableId,
|
||||||
|
techId = r.TechId,
|
||||||
|
message = r.Message,
|
||||||
|
printUrl = r.PrintUrl
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"Error: {ex.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,28 +1,219 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Nop.Web.Areas.Admin.Controllers;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Nop.Web.Framework.Mvc.Filters;
|
using Nop.Core;
|
||||||
|
using Nop.Core.Domain.Orders;
|
||||||
|
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||||
|
using Nop.Services.Catalog;
|
||||||
|
using Nop.Services.Common;
|
||||||
|
using Nop.Services.Customers;
|
||||||
|
using Nop.Services.Directory;
|
||||||
|
using Nop.Services.Orders;
|
||||||
using Nop.Web.Framework;
|
using Nop.Web.Framework;
|
||||||
using Nop.Services.Security;
|
using Nop.Web.Framework.Controllers;
|
||||||
|
using Nop.Web.Framework.Mvc.Filters;
|
||||||
|
|
||||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||||
{
|
{
|
||||||
[Area(AreaNames.ADMIN)]
|
[Area(AreaNames.ADMIN)]
|
||||||
[AuthorizeAdmin]
|
[AuthorizeAdmin]
|
||||||
public class InvoiceController : BaseAdminController
|
//[AutoValidateAntiforgeryToken]
|
||||||
|
public class InvoiceController : BasePluginController
|
||||||
{
|
{
|
||||||
private readonly IPermissionService _permissionService;
|
private readonly IOrderService _orderService;
|
||||||
|
private readonly IWorkContext _workContext;
|
||||||
|
private readonly IStoreContext _storeContext;
|
||||||
|
private readonly ICustomerService _customerService;
|
||||||
|
private readonly ICountryService _countryService;
|
||||||
|
private readonly IStateProvinceService _stateProvinceService;
|
||||||
|
// Add your InnVoice API service
|
||||||
|
private readonly InnVoiceApiService _innVoiceApiService;
|
||||||
|
private readonly IProductService _productService;
|
||||||
|
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||||
|
|
||||||
public InvoiceController(IPermissionService permissionService)
|
public InvoiceController(
|
||||||
|
IOrderService orderService,
|
||||||
|
IWorkContext workContext,
|
||||||
|
IStoreContext storeContext,
|
||||||
|
ICustomerService customerService,
|
||||||
|
ICountryService countryService,
|
||||||
|
IStateProvinceService stateProvinceService,
|
||||||
|
InnVoiceApiService innVoiceApiService,
|
||||||
|
IProductService productService,
|
||||||
|
FruitBankAttributeService fruitBankAttributeService)
|
||||||
{
|
{
|
||||||
_permissionService = permissionService;
|
_orderService = orderService;
|
||||||
|
_workContext = workContext;
|
||||||
|
_storeContext = storeContext;
|
||||||
|
_customerService = customerService;
|
||||||
|
_countryService = countryService;
|
||||||
|
_stateProvinceService = stateProvinceService;
|
||||||
|
_innVoiceApiService = innVoiceApiService;
|
||||||
|
_productService = productService;
|
||||||
|
_fruitBankAttributeService = fruitBankAttributeService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> List()
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> CreateInvoice(int orderId)
|
||||||
{
|
{
|
||||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
try
|
||||||
return AccessDeniedView();
|
{
|
||||||
|
var order = await _orderService.GetOrderByIdAsync(orderId);
|
||||||
|
if (order == null)
|
||||||
|
return Json(new { success = false, message = "Order not found" });
|
||||||
|
|
||||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Invoice/List.cshtml");
|
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
|
||||||
|
|
||||||
|
var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer);
|
||||||
|
if (billingAddress == null)
|
||||||
|
return Json(new { success = false, message = "Billing address not found" });
|
||||||
|
|
||||||
|
var country = await _countryService.GetCountryByAddressAsync(billingAddress);
|
||||||
|
var countryCode = country?.TwoLetterIsoCode ?? "HU";
|
||||||
|
|
||||||
|
// Create invoice request
|
||||||
|
var invoiceRequest = new InvoiceCreateRequest
|
||||||
|
{
|
||||||
|
VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}",
|
||||||
|
VevoIrsz = billingAddress.ZipPostalCode ?? "",
|
||||||
|
VevoTelep = billingAddress.City ?? "",
|
||||||
|
VevoOrszag = countryCode,
|
||||||
|
VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(),
|
||||||
|
SzamlatombID = 1, // Configure this based on your setup
|
||||||
|
SzamlaKelte = DateTime.Now,
|
||||||
|
TeljesitesKelte = DateTime.Now,
|
||||||
|
Hatarido = DateTime.Now.AddDays(15), // 15 days payment term
|
||||||
|
Devizanem = order.CustomerCurrencyCode,
|
||||||
|
FizetesiMod = order.PaymentMethodSystemName,
|
||||||
|
Felretett = false,
|
||||||
|
Proforma = true,
|
||||||
|
Email = billingAddress.Email,
|
||||||
|
MegrendelesSzamStr = order.CustomOrderNumber,
|
||||||
|
Telefon = billingAddress.PhoneNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add order items
|
||||||
|
var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
|
||||||
|
foreach (var item in orderItems)
|
||||||
|
{
|
||||||
|
var product = await _productService.GetProductByIdAsync(item.ProductId);
|
||||||
|
|
||||||
|
invoiceRequest.AddItem(new InvoiceItem
|
||||||
|
{
|
||||||
|
TetelNev = product?.Name ?? "Product",
|
||||||
|
AfaSzoveg = "27%", // Configure VAT rate as needed
|
||||||
|
Brutto = true,
|
||||||
|
EgysegAr = item.UnitPriceInclTax,
|
||||||
|
Mennyiseg = item.Quantity,
|
||||||
|
MennyisegEgyseg = "db",
|
||||||
|
CikkSzam = product?.Sku
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create invoice via API
|
||||||
|
var response = await _innVoiceApiService.CreateInvoiceAsync(invoiceRequest);
|
||||||
|
|
||||||
|
if (response.IsSuccess)
|
||||||
|
{
|
||||||
|
// TODO: Save invoice details to your database for future reference
|
||||||
|
// You might want to create a custom table to store:
|
||||||
|
// - OrderId
|
||||||
|
// - InnVoice TableId
|
||||||
|
// - Invoice Number
|
||||||
|
// - PDF URL
|
||||||
|
// - Created Date
|
||||||
|
|
||||||
|
await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Order, string>(orderId, nameof(InvoiceCreateResponse.TechId), response.TechId);
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
message = "Invoice created successfully",
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
tableId = response.TableId,
|
||||||
|
invoiceNumber = response.Sorszam,
|
||||||
|
sorszam = response.Sorszam,
|
||||||
|
printUrl = response.PrintUrl
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"InnVoice API Error: {response.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"Error: {ex.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetInvoiceStatus(int orderId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get the invoice from InnVoice using the saved TechId
|
||||||
|
var techId = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Order, string>(
|
||||||
|
orderId,
|
||||||
|
nameof(InvoiceCreateResponse.TechId),
|
||||||
|
_storeContext.GetCurrentStore().Id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(techId))
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = "No invoice TechId found for this order"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var invoices = await _innVoiceApiService.GetInvoiceByTechIdAsync(techId);
|
||||||
|
|
||||||
|
if (invoices != null && invoices.Count > 0)
|
||||||
|
{
|
||||||
|
var invoice = invoices.FirstOrDefault();
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
data = new
|
||||||
|
{
|
||||||
|
tableId = invoice.TableId,
|
||||||
|
invoiceNumber = invoice.InvoiceNumberFormatted, // or invoice.InvoiceNumber
|
||||||
|
sorszam = invoice.InvoiceNumberFormatted,
|
||||||
|
printUrl = invoice.PrintUrl,
|
||||||
|
customerName = invoice.CustomerName,
|
||||||
|
totalGross = invoice.TotalGross,
|
||||||
|
currency = invoice.Currency,
|
||||||
|
isProforma = invoice.IsProforma,
|
||||||
|
isDraft = invoice.IsDraft
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = "No invoice found for this order"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
message = $"Error: {ex.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,6 +81,8 @@ public class PluginNopStartup : INopStartup
|
||||||
services.AddScoped<IConsumer<OrderPlacedEvent>, EventConsumer>();
|
services.AddScoped<IConsumer<OrderPlacedEvent>, EventConsumer>();
|
||||||
services.AddScoped<IOrderMeasurementService, OrderMeasurementService>();
|
services.AddScoped<IOrderMeasurementService, OrderMeasurementService>();
|
||||||
services.AddScoped<PendingMeasurementCheckoutFilter>();
|
services.AddScoped<PendingMeasurementCheckoutFilter>();
|
||||||
|
services.AddScoped<InnVoiceApiService>();
|
||||||
|
services.AddScoped<InnVoiceOrderService>();
|
||||||
|
|
||||||
//services.AddScoped<OrderListModel, OrderListModelExtended>();
|
//services.AddScoped<OrderListModel, OrderListModelExtended>();
|
||||||
//services.AddScoped<OrderModel, OrderModelExtended>();
|
//services.AddScoped<OrderModel, OrderModelExtended>();
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,11 @@ public class RouteProvider : IRouteProvider
|
||||||
name: "Plugin.FruitBank.Admin.Orders.CustomerSearchAutoComplete",
|
name: "Plugin.FruitBank.Admin.Orders.CustomerSearchAutoComplete",
|
||||||
pattern: "Admin/CustomOrder/CustomerSearchAutoComplete",
|
pattern: "Admin/CustomOrder/CustomerSearchAutoComplete",
|
||||||
defaults: new { controller = "CustomOrder", action = "CustomerSearchAutoComplete", area = AreaNames.ADMIN });
|
defaults: new { controller = "CustomOrder", action = "CustomerSearchAutoComplete", area = AreaNames.ADMIN });
|
||||||
|
|
||||||
|
endpointRouteBuilder.MapControllerRoute(
|
||||||
|
name: "Plugin.FruitBank.Admin.Invoice.CreateInvoice",
|
||||||
|
pattern: "Admin/Invoice/CreateInvoice",
|
||||||
|
defaults: new { controller = "Invoice", action = "CreateInvoice", area = AreaNames.ADMIN });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,6 @@
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
|
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Services\InnvoiceApiService.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="logo.jpg" />
|
<None Remove="logo.jpg" />
|
||||||
|
|
@ -637,10 +634,6 @@
|
||||||
<Content Include="$(OutDir)\System.ServiceModel.Primitives.dll" CopyToOutputDirectory="PreserveNewest" />
|
<Content Include="$(OutDir)\System.ServiceModel.Primitives.dll" CopyToOutputDirectory="PreserveNewest" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="Services\InnvoiceApiService.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- This target execute after "Build" target -->
|
<!-- This target execute after "Build" target -->
|
||||||
<Target Name="NopTarget" AfterTargets="Build">
|
<Target Name="NopTarget" AfterTargets="Build">
|
||||||
<MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(OutDir)" Targets="NopClear" />
|
<MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(OutDir)" Targets="NopClear" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,417 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service for managing orders in InnVoice API
|
||||||
|
/// </summary>
|
||||||
|
public class InnVoiceOrderService
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly string _companyName;
|
||||||
|
private readonly string _username;
|
||||||
|
private readonly string _password;
|
||||||
|
private readonly string _baseUrl;
|
||||||
|
|
||||||
|
public InnVoiceOrderService(string companyName = "apiteszt", string username = "apiteszt", string password = "dsjfluio4324hjhjfdhkjskjh213kjgsd", string baseUrl = "https://api.innvoice.hu")
|
||||||
|
{
|
||||||
|
_companyName = companyName ?? throw new ArgumentNullException(nameof(companyName));
|
||||||
|
_username = username ?? throw new ArgumentNullException(nameof(username));
|
||||||
|
_password = password ?? throw new ArgumentNullException(nameof(password));
|
||||||
|
_baseUrl = baseUrl.TrimEnd('/');
|
||||||
|
|
||||||
|
_httpClient = new HttpClient();
|
||||||
|
SetupAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupAuthentication()
|
||||||
|
{
|
||||||
|
var authToken = Convert.ToBase64String(
|
||||||
|
System.Text.Encoding.ASCII.GetBytes($"{_username}:{_password}")
|
||||||
|
);
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization =
|
||||||
|
new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create one or more orders
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<OrderCreateResponse>> CreateOrdersAsync(List<OrderCreateRequest> orders)
|
||||||
|
{
|
||||||
|
var url = $"{_baseUrl}/{_companyName}/order";
|
||||||
|
|
||||||
|
var xml = BuildOrdersXml(orders);
|
||||||
|
var content = new FormUrlEncodedContent(new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("data", xml)
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.PostAsync(url, content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
return ParseOrderResponses(responseContent);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error creating orders: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a single order
|
||||||
|
/// </summary>
|
||||||
|
public async Task<OrderCreateResponse> CreateOrderAsync(OrderCreateRequest order)
|
||||||
|
{
|
||||||
|
var responses = await CreateOrdersAsync(new List<OrderCreateRequest> { order });
|
||||||
|
return responses.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildOrdersXml(List<OrderCreateRequest> orders)
|
||||||
|
{
|
||||||
|
var ordersElement = new XElement("orders");
|
||||||
|
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
var orderElement = new XElement("order");
|
||||||
|
|
||||||
|
orderElement.Add(new XElement("VevoNev", new XCData(order.VevoNev ?? "")));
|
||||||
|
orderElement.Add(new XElement("VevoIrsz", new XCData(order.VevoIrsz ?? "")));
|
||||||
|
orderElement.Add(new XElement("VevoTelep", new XCData(order.VevoTelep ?? "")));
|
||||||
|
orderElement.Add(new XElement("VevoUtcaHsz", new XCData(order.VevoUtcaHsz ?? "")));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(order.VevoOrszag))
|
||||||
|
orderElement.Add(new XElement("VevoOrszag", new XCData(order.VevoOrszag)));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(order.VevoAdoszam))
|
||||||
|
orderElement.Add(new XElement("VevoAdoszam", new XCData(order.VevoAdoszam)));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(order.SzallNev))
|
||||||
|
orderElement.Add(new XElement("SzallNev", new XCData(order.SzallNev)));
|
||||||
|
if (!string.IsNullOrEmpty(order.SzallIrsz))
|
||||||
|
orderElement.Add(new XElement("SzallIrsz", new XCData(order.SzallIrsz)));
|
||||||
|
if (!string.IsNullOrEmpty(order.SzallTelep))
|
||||||
|
orderElement.Add(new XElement("SzallTelep", new XCData(order.SzallTelep)));
|
||||||
|
if (!string.IsNullOrEmpty(order.SzallUtcaHsz))
|
||||||
|
orderElement.Add(new XElement("SzallUtcaHsz", new XCData(order.SzallUtcaHsz)));
|
||||||
|
if (!string.IsNullOrEmpty(order.SzallOrszag))
|
||||||
|
orderElement.Add(new XElement("SzallOrszag", new XCData(order.SzallOrszag)));
|
||||||
|
|
||||||
|
orderElement.Add(new XElement("MegrendelestombID", new XCData(order.MegrendelestombID.ToString())));
|
||||||
|
orderElement.Add(new XElement("MegrendelesKelte", new XCData(order.MegrendelesKelte.ToString("yyyy.MM.dd."))));
|
||||||
|
orderElement.Add(new XElement("Hatarido", new XCData(order.Hatarido.ToString("yyyy.MM.dd."))));
|
||||||
|
orderElement.Add(new XElement("Devizanem", new XCData(order.Devizanem ?? "")));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(order.FizetesiMod))
|
||||||
|
orderElement.Add(new XElement("FizetesiMod", new XCData(order.FizetesiMod)));
|
||||||
|
if (!string.IsNullOrEmpty(order.Megjegyzes))
|
||||||
|
orderElement.Add(new XElement("Megjegyzes", new XCData(order.Megjegyzes)));
|
||||||
|
if (!string.IsNullOrEmpty(order.Email))
|
||||||
|
orderElement.Add(new XElement("Email", new XCData(order.Email)));
|
||||||
|
if (!string.IsNullOrEmpty(order.Telefon))
|
||||||
|
orderElement.Add(new XElement("Telefon", new XCData(order.Telefon)));
|
||||||
|
if (!string.IsNullOrEmpty(order.MegrendelesSzamStr))
|
||||||
|
orderElement.Add(new XElement("MegrendelesSzamStr", new XCData(order.MegrendelesSzamStr)));
|
||||||
|
|
||||||
|
// Add items
|
||||||
|
foreach (var item in order.Items)
|
||||||
|
{
|
||||||
|
var tetelElement = new XElement("tetel");
|
||||||
|
tetelElement.Add(new XElement("TetelNev", item.TetelNev ?? ""));
|
||||||
|
tetelElement.Add(new XElement("AfaSzoveg", item.AfaSzoveg ?? ""));
|
||||||
|
tetelElement.Add(new XElement("Brutto", item.Brutto ? "1" : "0"));
|
||||||
|
tetelElement.Add(new XElement("EgysegAr", item.EgysegAr.ToString()));
|
||||||
|
tetelElement.Add(new XElement("Mennyiseg", item.Mennyiseg.ToString()));
|
||||||
|
tetelElement.Add(new XElement("MennyisegEgyseg", new XCData(item.MennyisegEgyseg ?? "")));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(item.CikkSzam))
|
||||||
|
tetelElement.Add(new XElement("CikkSzam", new XCData(item.CikkSzam)));
|
||||||
|
|
||||||
|
orderElement.Add(tetelElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
ordersElement.Add(orderElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new XDeclaration("1.0", "UTF-8", null).ToString() + "\n" + ordersElement.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OrderCreateResponse> ParseOrderResponses(string xml)
|
||||||
|
{
|
||||||
|
var responses = new List<OrderCreateResponse>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = XDocument.Parse(xml);
|
||||||
|
var orderElements = doc.Descendants("order");
|
||||||
|
|
||||||
|
foreach (var orderElement in orderElements)
|
||||||
|
{
|
||||||
|
var response = new OrderCreateResponse
|
||||||
|
{
|
||||||
|
ErrorCode = orderElement.Element("error")?.Value?.Trim(),
|
||||||
|
Message = orderElement.Element("message")?.Value?.Trim(),
|
||||||
|
TableId = int.TryParse(orderElement.Element("TABLE_ID")?.Value?.Trim(), out var tid) ? tid : (int?)null,
|
||||||
|
TechId = orderElement.Element("techid")?.Value?.Trim(),
|
||||||
|
PrintUrl = orderElement.Element("PrintUrl")?.Value?.Trim()
|
||||||
|
};
|
||||||
|
|
||||||
|
responses.Add(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error parsing order response XML: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get order by technical ID
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<InnVoiceOrder>> GetOrderByTechIdAsync(string techId)
|
||||||
|
{
|
||||||
|
var url = $"{_baseUrl}/{_companyName}/order/techid/{Uri.EscapeDataString(techId)}";
|
||||||
|
return await GetOrdersFromUrlAsync(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get order by table ID
|
||||||
|
/// </summary>
|
||||||
|
public async Task<InnVoiceOrder> GetOrderByIdAsync(int tableId)
|
||||||
|
{
|
||||||
|
var url = $"{_baseUrl}/{_companyName}/order/id/{tableId}";
|
||||||
|
var orders = await GetOrdersFromUrlAsync(url);
|
||||||
|
return orders.Count > 0 ? orders[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<InnVoiceOrder>> GetOrdersFromUrlAsync(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Parse XML response
|
||||||
|
return ParseOrdersFromXml(content);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<InnVoiceOrder> ParseOrdersFromXml(string xml)
|
||||||
|
{
|
||||||
|
var orders = new List<InnVoiceOrder>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = XDocument.Parse(xml);
|
||||||
|
var orderElements = doc.Descendants("order");
|
||||||
|
|
||||||
|
foreach (var orderElement in orderElements)
|
||||||
|
{
|
||||||
|
var order = new InnVoiceOrder
|
||||||
|
{
|
||||||
|
TableId = GetIntValue(orderElement, "TABLE_ID"),
|
||||||
|
VevoID = GetIntValue(orderElement, "VevoID"),
|
||||||
|
CustomerName = GetStringValue(orderElement, "VevoNev"),
|
||||||
|
CustomerZipCode = GetStringValue(orderElement, "VevoIrsz"),
|
||||||
|
CustomerCity = GetStringValue(orderElement, "VevoTelep"),
|
||||||
|
CustomerAddress = GetStringValue(orderElement, "VevoUtcaHsz"),
|
||||||
|
CustomerCountry = GetStringValue(orderElement, "VevoOrszag"),
|
||||||
|
CustomerTaxNumber = GetStringValue(orderElement, "VevoAdoszam"),
|
||||||
|
ShippingName = GetStringValue(orderElement, "SzallNev"),
|
||||||
|
ShippingZipCode = GetStringValue(orderElement, "SzallIrsz"),
|
||||||
|
ShippingCity = GetStringValue(orderElement, "SzallTelep"),
|
||||||
|
ShippingAddress = GetStringValue(orderElement, "SzallUtcaHsz"),
|
||||||
|
ShippingCountry = GetStringValue(orderElement, "SzallOrszag"),
|
||||||
|
OrderBookId = GetIntValue(orderElement, "MegrendelestombID"),
|
||||||
|
OrderDate = GetStringValue(orderElement, "MegrendelesKelte"),
|
||||||
|
DueDate = GetStringValue(orderElement, "Hatarido"),
|
||||||
|
Currency = GetStringValue(orderElement, "Devizanem"),
|
||||||
|
PaymentMethod = GetStringValue(orderElement, "FizetesiMod"),
|
||||||
|
Notes = GetStringValue(orderElement, "Megjegyzes"),
|
||||||
|
Email = GetStringValue(orderElement, "Email"),
|
||||||
|
Phone = GetStringValue(orderElement, "Telefon"),
|
||||||
|
ExternalOrderNumber = GetStringValue(orderElement, "MegrendelesSzamStr"),
|
||||||
|
TotalNet = GetDecimalValue(orderElement, "NettoErtek"),
|
||||||
|
TotalVAT = GetDecimalValue(orderElement, "AFAErtek"),
|
||||||
|
TotalGross = GetDecimalValue(orderElement, "BruttoErtek"),
|
||||||
|
TechId = GetStringValue(orderElement, "techid"),
|
||||||
|
PrintUrl = GetStringValue(orderElement, "PrintLink"),
|
||||||
|
UpdateTime = GetStringValue(orderElement, "_UpdateTime"),
|
||||||
|
QueryTime = GetStringValue(orderElement, "QueryTime")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse line items
|
||||||
|
var itemElements = orderElement.Descendants("tetel");
|
||||||
|
foreach (var itemElement in itemElements)
|
||||||
|
{
|
||||||
|
order.Items.Add(new InnVoiceOrderLineItem
|
||||||
|
{
|
||||||
|
TableId = GetIntValue(itemElement, "TABLE_ID"),
|
||||||
|
ItemName = GetStringValue(itemElement, "TetelNev"),
|
||||||
|
ArticleNumber = GetStringValue(itemElement, "CikkSzam"),
|
||||||
|
VATText = GetStringValue(itemElement, "AfaSzoveg"),
|
||||||
|
VATRate = GetDecimalValue(itemElement, "AfaKulcs"),
|
||||||
|
IsGross = GetIntValue(itemElement, "Brutto") == 1,
|
||||||
|
UnitPrice = GetDecimalValue(itemElement, "EgysegAr"),
|
||||||
|
Quantity = GetDecimalValue(itemElement, "Mennyiseg"),
|
||||||
|
Unit = GetStringValue(itemElement, "MennyisegEgyseg")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
orders.Add(order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error parsing XML: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return orders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetStringValue(XElement element, string name)
|
||||||
|
{
|
||||||
|
return element.Element(name)?.Value?.Trim() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetIntValue(XElement element, string name)
|
||||||
|
{
|
||||||
|
var value = GetStringValue(element, name);
|
||||||
|
return int.TryParse(value, out var result) ? result : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private decimal GetDecimalValue(XElement element, string name)
|
||||||
|
{
|
||||||
|
var value = GetStringValue(element, name);
|
||||||
|
return decimal.TryParse(value, out var result) ? result : 0m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order Models
|
||||||
|
public class OrderCreateRequest
|
||||||
|
{
|
||||||
|
public string VevoNev { get; set; }
|
||||||
|
public string VevoIrsz { get; set; }
|
||||||
|
public string VevoTelep { get; set; }
|
||||||
|
public string VevoUtcaHsz { get; set; }
|
||||||
|
public string VevoOrszag { get; set; }
|
||||||
|
public string VevoAdoszam { get; set; }
|
||||||
|
public string SzallNev { get; set; }
|
||||||
|
public string SzallIrsz { get; set; }
|
||||||
|
public string SzallTelep { get; set; }
|
||||||
|
public string SzallUtcaHsz { get; set; }
|
||||||
|
public string SzallOrszag { get; set; }
|
||||||
|
public int MegrendelestombID { get; set; }
|
||||||
|
public DateTime MegrendelesKelte { get; set; }
|
||||||
|
public DateTime Hatarido { get; set; }
|
||||||
|
public string Devizanem { get; set; }
|
||||||
|
public string FizetesiMod { get; set; }
|
||||||
|
public string Megjegyzes { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Telefon { get; set; }
|
||||||
|
public string MegrendelesSzamStr { get; set; }
|
||||||
|
|
||||||
|
public List<InnVoiceOrderItem> Items { get; set; } = new List<InnVoiceOrderItem>();
|
||||||
|
|
||||||
|
public void AddItem(InnVoiceOrderItem item)
|
||||||
|
{
|
||||||
|
Items.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InnVoiceOrderItem
|
||||||
|
{
|
||||||
|
public string TetelNev { get; set; }
|
||||||
|
public string AfaSzoveg { get; set; }
|
||||||
|
public bool Brutto { get; set; }
|
||||||
|
public decimal EgysegAr { get; set; }
|
||||||
|
public decimal Mennyiseg { get; set; }
|
||||||
|
public string MennyisegEgyseg { get; set; }
|
||||||
|
public string CikkSzam { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrderCreateResponse
|
||||||
|
{
|
||||||
|
public string ErrorCode { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public int? TableId { get; set; }
|
||||||
|
public string TechId { get; set; }
|
||||||
|
public string PrintUrl { get; set; }
|
||||||
|
|
||||||
|
public bool IsSuccess => ErrorCode == "200";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InnVoiceApiException : Exception
|
||||||
|
{
|
||||||
|
public InnVoiceApiException(string message) : base(message) { }
|
||||||
|
public InnVoiceApiException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order GET response models
|
||||||
|
public class InnVoiceOrder
|
||||||
|
{
|
||||||
|
public int TableId { get; set; }
|
||||||
|
public int VevoID { get; set; }
|
||||||
|
public string CustomerName { get; set; }
|
||||||
|
public string CustomerZipCode { get; set; }
|
||||||
|
public string CustomerCity { get; set; }
|
||||||
|
public string CustomerAddress { get; set; }
|
||||||
|
public string CustomerCountry { get; set; }
|
||||||
|
public string CustomerTaxNumber { get; set; }
|
||||||
|
public string ShippingName { get; set; }
|
||||||
|
public string ShippingZipCode { get; set; }
|
||||||
|
public string ShippingCity { get; set; }
|
||||||
|
public string ShippingAddress { get; set; }
|
||||||
|
public string ShippingCountry { get; set; }
|
||||||
|
public int OrderBookId { get; set; }
|
||||||
|
public string OrderDate { get; set; }
|
||||||
|
public string DueDate { get; set; }
|
||||||
|
public string Currency { get; set; }
|
||||||
|
public string PaymentMethod { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string ExternalOrderNumber { get; set; }
|
||||||
|
public decimal TotalNet { get; set; }
|
||||||
|
public decimal TotalVAT { get; set; }
|
||||||
|
public decimal TotalGross { get; set; }
|
||||||
|
public string TechId { get; set; }
|
||||||
|
public string PrintUrl { get; set; }
|
||||||
|
public string UpdateTime { get; set; }
|
||||||
|
public string QueryTime { get; set; }
|
||||||
|
|
||||||
|
public List<InnVoiceOrderLineItem> Items { get; set; } = new List<InnVoiceOrderLineItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InnVoiceOrderLineItem
|
||||||
|
{
|
||||||
|
public int TableId { get; set; }
|
||||||
|
public string ItemName { get; set; }
|
||||||
|
public string ArticleNumber { get; set; }
|
||||||
|
public string VATText { get; set; }
|
||||||
|
public decimal VATRate { get; set; }
|
||||||
|
public bool IsGross { get; set; }
|
||||||
|
public decimal UnitPrice { get; set; }
|
||||||
|
public decimal Quantity { get; set; }
|
||||||
|
public string Unit { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -22,7 +23,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
private readonly string _password;
|
private readonly string _password;
|
||||||
private readonly string _baseUrl;
|
private readonly string _baseUrl;
|
||||||
|
|
||||||
public InnVoiceApiService(string companyName, string username, string password, string baseUrl = "https://api.innvoice.hu")
|
public InnVoiceApiService(string companyName = "apiteszt", string username = "apiteszt", string password = "dsjfluio4324hjhjfdhkjskjh213kjgsd", string baseUrl = "https://api.innvoice.hu")
|
||||||
{
|
{
|
||||||
_companyName = companyName ?? throw new ArgumentNullException(nameof(companyName));
|
_companyName = companyName ?? throw new ArgumentNullException(nameof(companyName));
|
||||||
_username = username ?? throw new ArgumentNullException(nameof(username));
|
_username = username ?? throw new ArgumentNullException(nameof(username));
|
||||||
|
|
@ -71,6 +72,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
return await GetInvoicesFromUrlAsync(url);
|
return await GetInvoicesFromUrlAsync(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get invoice by invoice number
|
||||||
|
/// </summary>
|
||||||
|
public async Task<List<Invoice>> GetInvoiceByTechIdAsync(string techId)
|
||||||
|
{
|
||||||
|
//api.innvoice.hu/%regnev%/invoice/techid/%TECHID%
|
||||||
|
var url = $"{_baseUrl}/{_companyName}/invoice/techid/{Uri.EscapeDataString(techId)}";
|
||||||
|
return await GetInvoicesFromUrlAsync(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get invoices by creation date
|
/// Get invoices by creation date
|
||||||
/// Format: YYYYMMDD
|
/// Format: YYYYMMDD
|
||||||
|
|
@ -146,81 +158,265 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
var invoices = JsonSerializer.Deserialize<List<Invoice>>(content, new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true
|
|
||||||
});
|
|
||||||
|
|
||||||
return invoices ?? new List<Invoice>();
|
// Parse XML response
|
||||||
|
return ParseInvoicesFromXml(content);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex);
|
throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex);
|
throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private List<Invoice> ParseInvoicesFromXml(string xml)
|
||||||
|
{
|
||||||
|
var invoices = new List<Invoice>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = XDocument.Parse(xml);
|
||||||
|
var invoiceElements = doc.Descendants("invoice");
|
||||||
|
|
||||||
|
foreach (var invoiceElement in invoiceElements)
|
||||||
|
{
|
||||||
|
var invoice = new Invoice
|
||||||
|
{
|
||||||
|
TableId = GetIntValue(invoiceElement, "TABLE_ID"),
|
||||||
|
VevoID = GetIntValue(invoiceElement, "VevoID"),
|
||||||
|
CustomerName = GetStringValue(invoiceElement, "VevoNev"),
|
||||||
|
CustomerZipCode = GetStringValue(invoiceElement, "VevoIrsz"),
|
||||||
|
CustomerCity = GetStringValue(invoiceElement, "VevoTelep"),
|
||||||
|
CustomerAddress = GetStringValue(invoiceElement, "VevoUtcaHsz"),
|
||||||
|
CustomerCountry = GetStringValue(invoiceElement, "VevoOrszag"),
|
||||||
|
CustomerTaxNumber = GetStringValue(invoiceElement, "VevoAdoszam"),
|
||||||
|
CustomerGroupTaxNumber = GetStringValue(invoiceElement, "VevoCsAdoszam"),
|
||||||
|
PaymentMethod = GetStringValue(invoiceElement, "FizetesiMod"),
|
||||||
|
FulfillmentDate = GetStringValue(invoiceElement, "TeljesitesKelte"),
|
||||||
|
InvoiceDate = GetStringValue(invoiceElement, "SzamlaKelte"),
|
||||||
|
DueDate = GetStringValue(invoiceElement, "Hatarido"),
|
||||||
|
Notes = GetStringValue(invoiceElement, "Megjegyzes"),
|
||||||
|
Currency = GetStringValue(invoiceElement, "Devizanem"),
|
||||||
|
IsDraft = GetIntValue(invoiceElement, "Felretett") == 1,
|
||||||
|
IsCancelled = GetIntValue(invoiceElement, "Storno") == 1,
|
||||||
|
IsAdvance = GetIntValue(invoiceElement, "Eloleg") == 1,
|
||||||
|
IsProforma = GetIntValue(invoiceElement, "Proforma") == 1,
|
||||||
|
TotalNet = GetDecimalValue(invoiceElement, "NettoErtek"),
|
||||||
|
TotalVAT = GetDecimalValue(invoiceElement, "AFAErtek"),
|
||||||
|
TotalGross = GetDecimalValue(invoiceElement, "BruttoErtek"),
|
||||||
|
OutstandingAmount = GetDecimalValue(invoiceElement, "Hatralek"),
|
||||||
|
OverdueAmount = GetDecimalValue(invoiceElement, "LejartHatralek"),
|
||||||
|
PaidAmount = GetDecimalValue(invoiceElement, "Befizetes"),
|
||||||
|
InvoiceNumber = GetStringValue(invoiceElement, "Sorszam"),
|
||||||
|
InvoiceNumberFormatted = GetStringValue(invoiceElement, "SorszamFormatted"),
|
||||||
|
InvoiceId = GetIntValue(invoiceElement, "SzamlaID"),
|
||||||
|
UniqueId = GetStringValue(invoiceElement, "_UniqueID"),
|
||||||
|
ExchangeRate = GetDecimalValue(invoiceElement, "Arfolyam"),
|
||||||
|
InvoiceBookId = GetIntValue(invoiceElement, "SzamlatombID"),
|
||||||
|
InvoiceBookName = GetStringValue(invoiceElement, "SzamlatombNev"),
|
||||||
|
IsElectronic = GetIntValue(invoiceElement, "Eszamla") == 1,
|
||||||
|
Phone = GetStringValue(invoiceElement, "Telefon"),
|
||||||
|
Email = GetStringValue(invoiceElement, "Email"),
|
||||||
|
UpdateTime = GetStringValue(invoiceElement, "_UpdateTime"),
|
||||||
|
QueryTime = GetStringValue(invoiceElement, "QueryTime"),
|
||||||
|
OrderNumber = GetStringValue(invoiceElement, "MegrendelesSzamStr"),
|
||||||
|
OrderDate = GetStringValue(invoiceElement, "MegrendelesIdopontStr"),
|
||||||
|
ShippingName = GetStringValue(invoiceElement, "SzallNev"),
|
||||||
|
ShippingZipCode = GetStringValue(invoiceElement, "SzallIrsz"),
|
||||||
|
ShippingCity = GetStringValue(invoiceElement, "SzallTelep"),
|
||||||
|
ShippingAddress = GetStringValue(invoiceElement, "SzallUtcaHsz"),
|
||||||
|
ShippingCountry = GetStringValue(invoiceElement, "SzallOrszag"),
|
||||||
|
PrintUrl = GetStringValue(invoiceElement, "PrintLink"),
|
||||||
|
InvoiceLink = GetStringValue(invoiceElement, "SzamlaLink"),
|
||||||
|
HealthFundName = GetStringValue(invoiceElement, "VevoEPNev"),
|
||||||
|
HealthFundCode = GetStringValue(invoiceElement, "VevoEPKod")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse line items
|
||||||
|
var itemElements = invoiceElement.Descendants("tetel");
|
||||||
|
foreach (var itemElement in itemElements)
|
||||||
|
{
|
||||||
|
invoice.Items.Add(new InvoiceLineItem
|
||||||
|
{
|
||||||
|
TableId = GetIntValue(itemElement, "TABLE_ID"),
|
||||||
|
ItemName = GetStringValue(itemElement, "TetelNev"),
|
||||||
|
ArticleNumber = GetStringValue(itemElement, "CikkSzam"),
|
||||||
|
VTSZSZJ = GetStringValue(itemElement, "VTSZSZJ"),
|
||||||
|
VATText = GetStringValue(itemElement, "AfaSzoveg"),
|
||||||
|
VATRate = GetDecimalValue(itemElement, "AfaKulcs"),
|
||||||
|
IsGross = GetIntValue(itemElement, "Brutto") == 1,
|
||||||
|
UnitPrice = GetDecimalValue(itemElement, "EgysegAr"),
|
||||||
|
Quantity = GetDecimalValue(itemElement, "Mennyiseg"),
|
||||||
|
Unit = GetStringValue(itemElement, "MennyisegEgyseg"),
|
||||||
|
ProductId = GetIntValue(itemElement, "TermekID"),
|
||||||
|
DiscountAmount = GetDecimalValue(itemElement, "KedvezmenyOsszeg")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
invoices.Add(invoice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error parsing XML: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetStringValue(XElement element, string name)
|
||||||
|
{
|
||||||
|
return element.Element(name)?.Value?.Trim() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetIntValue(XElement element, string name)
|
||||||
|
{
|
||||||
|
var value = GetStringValue(element, name);
|
||||||
|
return int.TryParse(value, out var result) ? result : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private decimal GetDecimalValue(XElement element, string name)
|
||||||
|
{
|
||||||
|
var value = GetStringValue(element, name);
|
||||||
|
return decimal.TryParse(value, out var result) ? result : 0m;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new invoice
|
||||||
|
/// </summary>
|
||||||
|
public async Task<InvoiceCreateResponse> CreateInvoiceAsync(InvoiceCreateRequest request)
|
||||||
|
{
|
||||||
|
var url = $"{_baseUrl}/{_companyName}/invoice";
|
||||||
|
|
||||||
|
var xml = request.ToXml();
|
||||||
|
var content = new FormUrlEncodedContent(new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("data", xml)
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.PostAsync(url, content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
return InvoiceCreateResponse.FromXml(responseContent);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error creating invoice: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update an existing invoice
|
||||||
|
/// </summary>
|
||||||
|
public async Task<InvoiceCreateResponse> UpdateInvoiceAsync(int tableId, InvoiceCreateRequest request)
|
||||||
|
{
|
||||||
|
// Set the VevoID if updating customer information
|
||||||
|
var url = $"{_baseUrl}/{_companyName}/invoice";
|
||||||
|
|
||||||
|
var xml = request.ToXml();
|
||||||
|
var content = new FormUrlEncodedContent(new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("data", xml),
|
||||||
|
new KeyValuePair<string, string>("id", tableId.ToString())
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _httpClient.PostAsync(url, content);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
return InvoiceCreateResponse.FromXml(responseContent);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
throw new InnVoiceApiException($"Error updating invoice: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Models
|
// Models
|
||||||
public class Invoice
|
public class Invoice
|
||||||
{
|
{
|
||||||
[JsonPropertyName("TABLE_ID")]
|
|
||||||
public int TableId { get; set; }
|
public int TableId { get; set; }
|
||||||
|
public int VevoID { get; set; }
|
||||||
[JsonPropertyName("InvoiceNumber")]
|
|
||||||
public string InvoiceNumber { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Created")]
|
|
||||||
public string Created { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Fulfillment")]
|
|
||||||
public string Fulfillment { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("DueDate")]
|
|
||||||
public string DueDate { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("PaymentDate")]
|
|
||||||
public string PaymentDate { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("CustomerName")]
|
|
||||||
public string CustomerName { get; set; }
|
public string CustomerName { get; set; }
|
||||||
|
public string CustomerZipCode { get; set; }
|
||||||
[JsonPropertyName("CustomerTaxNumber")]
|
public string CustomerCity { get; set; }
|
||||||
public string CustomerTaxNumber { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("CustomerAddress")]
|
|
||||||
public string CustomerAddress { get; set; }
|
public string CustomerAddress { get; set; }
|
||||||
|
public string CustomerCountry { get; set; }
|
||||||
[JsonPropertyName("TotalNet")]
|
public string CustomerTaxNumber { get; set; }
|
||||||
public decimal TotalNet { get; set; }
|
public string CustomerGroupTaxNumber { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("TotalGross")]
|
|
||||||
public decimal TotalGross { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Currency")]
|
|
||||||
public string Currency { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("Status")]
|
|
||||||
public string Status { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("InvoiceType")]
|
|
||||||
public string InvoiceType { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("PaymentMethod")]
|
|
||||||
public string PaymentMethod { get; set; }
|
public string PaymentMethod { get; set; }
|
||||||
|
public string FulfillmentDate { get; set; }
|
||||||
|
public string InvoiceDate { get; set; }
|
||||||
|
public string DueDate { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
public string Currency { get; set; }
|
||||||
|
public bool IsDraft { get; set; }
|
||||||
|
public bool IsCancelled { get; set; }
|
||||||
|
public bool IsAdvance { get; set; }
|
||||||
|
public bool IsProforma { get; set; }
|
||||||
|
public decimal TotalNet { get; set; }
|
||||||
|
public decimal TotalVAT { get; set; }
|
||||||
|
public decimal TotalGross { get; set; }
|
||||||
|
public decimal OutstandingAmount { get; set; }
|
||||||
|
public decimal OverdueAmount { get; set; }
|
||||||
|
public decimal PaidAmount { get; set; }
|
||||||
|
public string InvoiceNumber { get; set; }
|
||||||
|
public string InvoiceNumberFormatted { get; set; }
|
||||||
|
public int InvoiceId { get; set; }
|
||||||
|
public string UniqueId { get; set; }
|
||||||
|
public decimal ExchangeRate { get; set; }
|
||||||
|
public int InvoiceBookId { get; set; }
|
||||||
|
public string InvoiceBookName { get; set; }
|
||||||
|
public bool IsElectronic { get; set; }
|
||||||
|
public string Phone { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string UpdateTime { get; set; }
|
||||||
|
public string QueryTime { get; set; }
|
||||||
|
public string OrderNumber { get; set; }
|
||||||
|
public string OrderDate { get; set; }
|
||||||
|
public string ShippingName { get; set; }
|
||||||
|
public string ShippingZipCode { get; set; }
|
||||||
|
public string ShippingCity { get; set; }
|
||||||
|
public string ShippingAddress { get; set; }
|
||||||
|
public string ShippingCountry { get; set; }
|
||||||
|
public string PrintUrl { get; set; }
|
||||||
|
public string InvoiceLink { get; set; }
|
||||||
|
public string HealthFundName { get; set; }
|
||||||
|
public string HealthFundCode { get; set; }
|
||||||
|
|
||||||
// Add more properties as needed based on actual API response
|
public List<InvoiceLineItem> Items { get; set; } = new List<InvoiceLineItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InnVoiceApiException : Exception
|
public class InvoiceLineItem
|
||||||
{
|
{
|
||||||
public InnVoiceApiException(string message) : base(message) { }
|
public int TableId { get; set; }
|
||||||
public InnVoiceApiException(string message, Exception innerException) : base(message, innerException) { }
|
public string ItemName { get; set; }
|
||||||
|
public string ArticleNumber { get; set; }
|
||||||
|
public string VTSZSZJ { get; set; }
|
||||||
|
public string VATText { get; set; }
|
||||||
|
public decimal VATRate { get; set; }
|
||||||
|
public bool IsGross { get; set; }
|
||||||
|
public decimal UnitPrice { get; set; }
|
||||||
|
public decimal Quantity { get; set; }
|
||||||
|
public string Unit { get; set; }
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
public decimal DiscountAmount { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Invoice Creation Models
|
// Invoice Creation Models
|
||||||
public class InvoiceCreateRequest
|
public class InvoiceCreateRequest
|
||||||
{
|
{
|
||||||
|
|
@ -438,59 +634,4 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// Create a new invoice
|
|
||||||
/// </summary>
|
|
||||||
public async Task<InvoiceCreateResponse> CreateInvoiceAsync(InvoiceCreateRequest request)
|
|
||||||
{
|
|
||||||
var url = $"{_baseUrl}/{_companyName}/invoice";
|
|
||||||
|
|
||||||
var xml = request.ToXml();
|
|
||||||
var content = new FormUrlEncodedContent(new[]
|
|
||||||
{
|
|
||||||
new KeyValuePair<string, string>("data", xml)
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var response = await _httpClient.PostAsync(url, content);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
|
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
|
||||||
return InvoiceCreateResponse.FromXml(responseContent);
|
|
||||||
}
|
|
||||||
catch (HttpRequestException ex)
|
|
||||||
{
|
|
||||||
throw new InnVoiceApiException($"Error creating invoice: {ex.Message}", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update an existing invoice
|
|
||||||
/// </summary>
|
|
||||||
public async Task<InvoiceCreateResponse> UpdateInvoiceAsync(int tableId, InvoiceCreateRequest request)
|
|
||||||
{
|
|
||||||
// Set the VevoID if updating customer information
|
|
||||||
var url = $"{_baseUrl}/{_companyName}/invoice";
|
|
||||||
|
|
||||||
var xml = request.ToXml();
|
|
||||||
var content = new FormUrlEncodedContent(new[]
|
|
||||||
{
|
|
||||||
new KeyValuePair<string, string>("data", xml),
|
|
||||||
new KeyValuePair<string, string>("id", tableId.ToString())
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var response = await _httpClient.PostAsync(url, content);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
|
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
|
||||||
return InvoiceCreateResponse.FromXml(responseContent);
|
|
||||||
}
|
|
||||||
catch (HttpRequestException ex)
|
|
||||||
{
|
|
||||||
throw new InnVoiceApiException($"Error updating invoice: {ex.Message}", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,44 @@
|
||||||
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
||||||
@model OrderAttributesModel
|
@model OrderAttributesModel
|
||||||
|
|
||||||
<!-- InnVoice Invoice Section -->
|
<!-- InnVoice Management Section -->
|
||||||
<div class="card card-default mb-3">
|
<div class="card card-default mb-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<i class="fas fa-file-invoice"></i>
|
<i class="fas fa-file-invoice"></i>
|
||||||
InnVoice Invoice Management
|
InnVoice Management
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<!-- Order Subsection -->
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-md-12">
|
<div class="col-12 col-md-3">
|
||||||
|
<h5><i class="fas fa-shopping-cart"></i> Order</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> View Order PDF
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 text-right">
|
||||||
|
<button type="button" id="createOrderBtn" class="btn btn-success">
|
||||||
|
<i class="fas fa-shopping-cart"></i> Create Order in InnVoice
|
||||||
|
</button>
|
||||||
|
<button type="button" id="checkOrderBtn" class="btn btn-secondary" style="display: none;">
|
||||||
|
<i class="fas fa-sync"></i> Refresh Order
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -18,32 +47,23 @@
|
||||||
<p><strong>Table ID:</strong> <span id="invoiceTableId"></span></p>
|
<p><strong>Table ID:</strong> <span id="invoiceTableId"></span></p>
|
||||||
<p>
|
<p>
|
||||||
<a id="invoicePdfLink" href="#" target="_blank" class="btn btn-sm btn-info">
|
<a id="invoicePdfLink" href="#" target="_blank" class="btn btn-sm btn-info">
|
||||||
<i class="fas fa-file-pdf"></i> View PDF
|
<i class="fas fa-file-pdf"></i> View Invoice PDF
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
<div class="col-12 col-md-3 text-right">
|
||||||
<div class="col-md-12 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 & Upload Invoice
|
<i class="fas fa-file-invoice-dollar"></i> Create Invoice
|
||||||
</button>
|
</button>
|
||||||
<button type="button" id="checkInvoiceBtn" class="btn btn-secondary" style="display: none;">
|
<button type="button" id="checkInvoiceBtn" class="btn btn-secondary" style="display: none;">
|
||||||
<i class="fas fa-sync"></i> Check Invoice Status
|
<i class="fas fa-sync"></i> Refresh Invoice
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group row">
|
||||||
</div>
|
<div class="col-12">
|
||||||
|
|
||||||
<!-- Custom Order Attributes Section -->
|
|
||||||
<div class="card card-default">
|
|
||||||
<div class="card-header">
|
|
||||||
<i class="fas fa-tags"></i>
|
|
||||||
Custom Order Attributes
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<nop-label asp-for="IsMeasurable" />
|
<nop-label asp-for="IsMeasurable" />
|
||||||
|
|
@ -70,10 +90,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
var createOrderUrl = '/Admin/InnVoiceOrder/CreateOrder';
|
||||||
|
var getOrderStatusUrl = '/Admin/InnVoiceOrder/GetOrderStatus';
|
||||||
|
var createInvoiceUrl = '/Admin/Invoice/CreateInvoice';
|
||||||
|
var getInvoiceStatusUrl = '/Admin/Invoice/GetInvoiceStatus';
|
||||||
|
|
||||||
|
var orderExists = false;
|
||||||
|
var invoiceExists = false;
|
||||||
|
|
||||||
// Save Order Attributes
|
// Save Order Attributes
|
||||||
$("#saveAttributesBtn").click(function () {
|
$("#saveAttributesBtn").click(function () {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
@ -94,8 +127,148 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========== ORDER MANAGEMENT ==========
|
||||||
|
|
||||||
|
// Create Order
|
||||||
|
$("#createOrderBtn").click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var btn = $(this);
|
||||||
|
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Creating Order...');
|
||||||
|
|
||||||
|
showOrderStatus("Creating order in InnVoice, please wait...", "info");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: createOrderUrl,
|
||||||
|
data: { orderId: @Model.OrderId },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
console.log("Order Response:", response);
|
||||||
|
btn.prop("disabled", false).html('<i class="fas fa-shopping-cart"></i> Create Order in InnVoice');
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
showOrderStatus("Order created successfully in InnVoice!", "success");
|
||||||
|
displayOrderDetails(response.data);
|
||||||
|
orderExists = true;
|
||||||
|
updateOrderButtons();
|
||||||
|
} else {
|
||||||
|
showOrderStatus("Error: " + (response.message || "Unknown error"), "danger");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error("Order AJAX Error:", xhr, status, error);
|
||||||
|
btn.prop("disabled", false).html('<i class="fas fa-shopping-cart"></i> Create Order in InnVoice');
|
||||||
|
|
||||||
|
var errorMessage = "Error creating order";
|
||||||
|
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
|
errorMessage = xhr.responseJSON.message;
|
||||||
|
} else if (xhr.responseText) {
|
||||||
|
try {
|
||||||
|
var errorObj = JSON.parse(xhr.responseText);
|
||||||
|
errorMessage = errorObj.message || errorMessage;
|
||||||
|
} catch (e) {
|
||||||
|
errorMessage = "Error: " + xhr.status + " - " + xhr.statusText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showOrderStatus(errorMessage, "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check Order Status
|
||||||
|
$("#checkOrderBtn").click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
var btn = $(this);
|
||||||
|
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Checking...');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: getOrderStatusUrl,
|
||||||
|
data: { orderId: @Model.OrderId },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Refresh Order');
|
||||||
|
|
||||||
|
if (response.success && response.data) {
|
||||||
|
displayOrderDetails(response.data);
|
||||||
|
showOrderStatus("Order details refreshed", "success");
|
||||||
|
} else {
|
||||||
|
showOrderStatus("No order found in InnVoice for this order", "warning");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Refresh Order');
|
||||||
|
showOrderStatus("Error checking order status", "danger");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
function showOrderStatus(message, type) {
|
||||||
|
var statusDiv = $("#orderStatus");
|
||||||
|
statusDiv.removeClass("alert-info alert-success alert-warning alert-danger")
|
||||||
|
.addClass("alert-" + type);
|
||||||
|
$("#orderStatusMessage").text(message);
|
||||||
|
statusDiv.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayOrderDetails(data) {
|
||||||
|
$("#orderTableId").text(data.tableId || "N/A");
|
||||||
|
$("#orderTechId").text(data.techId || "N/A");
|
||||||
|
|
||||||
|
if (data.printUrl) {
|
||||||
|
$("#orderPdfLink").attr("href", data.printUrl).show();
|
||||||
|
} else {
|
||||||
|
$("#orderPdfLink").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#orderDetails").show();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOrderButtons() {
|
||||||
|
if (orderExists) {
|
||||||
|
$("#createOrderBtn").hide();
|
||||||
|
$("#checkOrderBtn").show();
|
||||||
|
} else {
|
||||||
|
$("#createOrderBtn").show();
|
||||||
|
$("#checkOrderBtn").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if order exists on page load
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: getOrderStatusUrl,
|
||||||
|
data: { orderId: @Model.OrderId },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
if (response.success && response.data) {
|
||||||
|
displayOrderDetails(response.data);
|
||||||
|
orderExists = true;
|
||||||
|
updateOrderButtons();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
// Silently fail on page load
|
||||||
|
orderExists = false;
|
||||||
|
updateOrderButtons();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========== INVOICE MANAGEMENT ==========
|
||||||
|
|
||||||
// Create Invoice
|
// Create Invoice
|
||||||
$("#createInvoiceBtn").click(function () {
|
$("#createInvoiceBtn").click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
var btn = $(this);
|
var btn = $(this);
|
||||||
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Creating Invoice...');
|
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Creating Invoice...');
|
||||||
|
|
||||||
|
|
@ -103,60 +276,74 @@
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
url: "@Url.Action("CreateInvoice", "InnVoice")",
|
url: createInvoiceUrl,
|
||||||
data: {
|
data: { orderId: @Model.OrderId },
|
||||||
orderId: "@Model.OrderId",
|
dataType: 'json',
|
||||||
__RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
|
|
||||||
},
|
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
btn.prop("disabled", false).html('<i class="fas fa-file-invoice-dollar"></i> Create & Upload Invoice');
|
console.log("Invoice Response:", response);
|
||||||
|
btn.prop("disabled", false).html('<i class="fas fa-file-invoice-dollar"></i> Create Invoice');
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
showInvoiceStatus("Invoice created successfully!", "success");
|
showInvoiceStatus("Invoice created successfully!", "success");
|
||||||
displayInvoiceDetails(response.data);
|
displayInvoiceDetails(response.data);
|
||||||
$("#checkInvoiceBtn").show();
|
invoiceExists = true;
|
||||||
|
updateInvoiceButtons();
|
||||||
} else {
|
} else {
|
||||||
showInvoiceStatus("Error: " + response.message, "danger");
|
showInvoiceStatus("Error: " + (response.message || "Unknown error"), "danger");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr, status, error) {
|
||||||
btn.prop("disabled", false).html('<i class="fas fa-file-invoice-dollar"></i> Create & Upload Invoice');
|
console.error("Invoice AJAX Error:", xhr, status, error);
|
||||||
|
btn.prop("disabled", false).html('<i class="fas fa-file-invoice-dollar"></i> Create Invoice');
|
||||||
|
|
||||||
var errorMessage = "Error creating invoice";
|
var errorMessage = "Error creating invoice";
|
||||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
errorMessage = xhr.responseJSON.message;
|
errorMessage = xhr.responseJSON.message;
|
||||||
|
} else if (xhr.responseText) {
|
||||||
|
try {
|
||||||
|
var errorObj = JSON.parse(xhr.responseText);
|
||||||
|
errorMessage = errorObj.message || errorMessage;
|
||||||
|
} catch (e) {
|
||||||
|
errorMessage = "Error: " + xhr.status + " - " + xhr.statusText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
showInvoiceStatus(errorMessage, "danger");
|
showInvoiceStatus(errorMessage, "danger");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check Invoice Status
|
// Check Invoice Status
|
||||||
$("#checkInvoiceBtn").click(function () {
|
$("#checkInvoiceBtn").click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
var btn = $(this);
|
var btn = $(this);
|
||||||
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Checking...');
|
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Checking...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "@Url.Action("GetInvoiceStatus", "InnVoice")",
|
url: getInvoiceStatusUrl,
|
||||||
data: {
|
data: { orderId: @Model.OrderId },
|
||||||
orderId: "@Model.OrderId"
|
dataType: 'json',
|
||||||
},
|
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Check Invoice Status');
|
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Refresh Invoice');
|
||||||
|
|
||||||
if (response.success && response.data) {
|
if (response.success && response.data) {
|
||||||
displayInvoiceDetails(response.data);
|
displayInvoiceDetails(response.data);
|
||||||
showInvoiceStatus("Invoice details loaded", "success");
|
showInvoiceStatus("Invoice details refreshed", "success");
|
||||||
} else {
|
} else {
|
||||||
showInvoiceStatus("No invoice found for this order", "warning");
|
showInvoiceStatus("No invoice found for this order", "warning");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Check Invoice Status');
|
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Refresh Invoice');
|
||||||
showInvoiceStatus("Error checking invoice status", "danger");
|
showInvoiceStatus("Error checking invoice status", "danger");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
function showInvoiceStatus(message, type) {
|
function showInvoiceStatus(message, type) {
|
||||||
|
|
@ -180,18 +367,33 @@
|
||||||
$("#invoiceDetails").show();
|
$("#invoiceDetails").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateInvoiceButtons() {
|
||||||
|
if (invoiceExists) {
|
||||||
|
$("#createInvoiceBtn").hide();
|
||||||
|
$("#checkInvoiceBtn").show();
|
||||||
|
} else {
|
||||||
|
$("#createInvoiceBtn").show();
|
||||||
|
$("#checkInvoiceBtn").hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if invoice exists on page load
|
// Check if invoice exists on page load
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
url: "@Url.Action("GetInvoiceStatus", "InnVoice")",
|
url: getInvoiceStatusUrl,
|
||||||
data: {
|
data: { orderId: @Model.OrderId },
|
||||||
orderId: "@Model.OrderId"
|
dataType: 'json',
|
||||||
},
|
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
if (response.success && response.data) {
|
if (response.success && response.data) {
|
||||||
displayInvoiceDetails(response.data);
|
displayInvoiceDetails(response.data);
|
||||||
$("#checkInvoiceBtn").show();
|
invoiceExists = true;
|
||||||
|
updateInvoiceButtons();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
// Silently fail on page load
|
||||||
|
invoiceExists = false;
|
||||||
|
updateInvoiceButtons();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue