diff --git a/src/Oms.Application/Dtos/Orders/Customs9610Dtos.cs b/src/Oms.Application/Dtos/Orders/Customs9610Dtos.cs index 5d7f66b..69d0acc 100644 --- a/src/Oms.Application/Dtos/Orders/Customs9610Dtos.cs +++ b/src/Oms.Application/Dtos/Orders/Customs9610Dtos.cs @@ -319,3 +319,68 @@ public class Customs9610DeclareBatchOutput public List Items { get; set; } = []; } + +/// +/// 从已有运单批量创建 9610 汇总申报单入参。 +/// +public class Customs9610CreateFromOrdersInput +{ + /// + /// 运单 Id 列表(t_order.Id)。 + /// + [Required] + [MinLength(1)] + public List OrderIds { get; set; } = []; + + /// + /// 出口资质备案 Id;为空时按各运单客户的默认备案。 + /// + public int? ExportQualificationId { get; set; } + + /// + /// 电商平台编码(e-commerce_platform 字典),最长 16。 + /// + [Required] + [StringLength(16)] + public string PlatformCode { get; set; } = string.Empty; +} + +/// +/// 单个运单创建 9610 申报单结果。 +/// +public class Customs9610CreateFromOrderResult +{ + /// + /// 运单 Id。 + /// + public int OrderId { get; set; } + + /// + /// 运单号。 + /// + public string? ServiceNo { get; set; } + + public bool Success { get; set; } + + /// + /// 失败原因;成功为空。 + /// + public string? Message { get; set; } + + /// + /// 生成的 9610 申报单 Id。 + /// + public int? Customs9610OrderId { get; set; } +} + +/// +/// 批量创建 9610 申报单结果。 +/// +public class Customs9610CreateFromOrdersOutput +{ + public int SuccessCount { get; set; } + + public int FailCount { get; set; } + + public List Items { get; set; } = []; +} diff --git a/src/Oms.Application/Services/Orders/Customs9610OrderService.cs b/src/Oms.Application/Services/Orders/Customs9610OrderService.cs index 2e45bea..9789c4b 100644 --- a/src/Oms.Application/Services/Orders/Customs9610OrderService.cs +++ b/src/Oms.Application/Services/Orders/Customs9610OrderService.cs @@ -41,6 +41,12 @@ public class Customs9610OrderService : ApplicationService private IOptions ItfxOptions => LazyServiceProvider.GetRequiredService>(); + private ICustomerExportQualificationRepository ExportQualificationRepository => + LazyServiceProvider.GetRequiredService(); + + private Customs9610OrderWriter Customs9610Writer => + LazyServiceProvider.GetRequiredService(); + [HttpPost("/order/customs-9610/list")] public async Task> ListAsync([FromBody] Customs9610ListInput input) { @@ -209,6 +215,113 @@ public class Customs9610OrderService : ApplicationService }; } + /// + /// 从已有运单批量创建 9610 汇总申报单:运单改为 9610Summary 并绑定出口资质备案,按运单申报明细生成待申报单。 + /// + /// 运单 Id 列表、出口资质备案 Id(空则用各客户默认备案)、电商平台编码。 + /// 逐单结果;已删除、已有 9610 单、无申报明细、无可用备案的运单记为失败,不影响其它运单。 + [HttpPost("/order/customs-9610/create-from-orders")] + public async Task CreateFromOrdersAsync( + [FromBody] Customs9610CreateFromOrdersInput input) + { + var platformCode = input.PlatformCode?.Trim() ?? string.Empty; + if (string.IsNullOrWhiteSpace(platformCode)) + { + throw new BusinessException("请选择电商平台"); + } + + if (platformCode.Length > 16) + { + throw new BusinessException("电商平台编码不能超过 16 个字符"); + } + + var orderIds = input.OrderIds.Where(x => x > 0).Distinct().ToList(); + if (orderIds.Count == 0) + { + throw new BusinessException("请选择运单"); + } + + var sessionCustomerId = await WarehouseCustomerHelper.ResolveListCustomerIdAsync( + CustomerRepository, + CurrentUser, + null); + var orders = (await OrderRepository.GetListAsync(x => orderIds.Contains(x.Id))) + .ToDictionary(x => x.Id); + var declarations = await OrderRepository.GetDeclarationsByOrderIdsAsync(orderIds); + var ordersWithItems = declarations.Select(x => x.OrderId).ToHashSet(); + + var results = new List(); + foreach (var orderId in orderIds) + { + var result = new Customs9610CreateFromOrderResult { OrderId = orderId }; + results.Add(result); + try + { + if (!orders.TryGetValue(orderId, out var order) + || (sessionCustomerId is > 0 && order.CustomerId != sessionCustomerId.Value)) + { + throw new BusinessException("运单不存在或无权访问"); + } + + result.ServiceNo = order.ServiceNo; + if (string.Equals(order.OrderStatus, OrderStatusCodes.Deleted, StringComparison.OrdinalIgnoreCase)) + { + throw new BusinessException("运单已删除"); + } + + if (order.CustomerId is not > 0) + { + throw new BusinessException("运单未关联客户"); + } + + if (await HeaderRepository.GetByOrderIdAsync(order.Id) != null) + { + throw new BusinessException("该运单已存在 9610 申报单"); + } + + if (!ordersWithItems.Contains(order.Id)) + { + throw new BusinessException("运单没有申报明细"); + } + + var qualification = input.ExportQualificationId is > 0 + ? await ExportQualificationRepository.GetOwnedAsync( + input.ExportQualificationId.Value, + order.CustomerId.Value) + : await ExportQualificationRepository.GetDefaultAsync(order.CustomerId.Value); + if (qualification == null) + { + throw new BusinessException(input.ExportQualificationId is > 0 + ? "所选出口资质备案不属于该运单客户" + : "运单客户未维护默认出口资质备案"); + } + + order.ExportCustomsMode = ExportCustomsModeCodes.Mode9610Summary; + order.ExportQualificationId = qualification.Id; + await Customs9610Writer.UpsertFromOrderAsync(order, platformCode); + order.UpdateTime = DateTime.Now; + await OrderRepository.UpdateAsync(order); + + var header = await HeaderRepository.GetByOrderIdAsync(order.Id); + result.Customs9610OrderId = header?.Id; + result.Success = header != null; + result.Message = header == null ? "9610 申报单生成失败" : null; + } + catch (Exception ex) + { + result.Success = false; + result.Message = string.IsNullOrWhiteSpace(ex.Message) ? "创建失败" : ex.Message; + } + } + + return new Customs9610CreateFromOrdersOutput + { + SuccessCount = results.Count(x => x.Success), + FailCount = results.Count(x => !x.Success), + Items = results, + }; + } + /// /// 批量获取清单数据(调 API 中心上游,标准码暂定),成功后回写本地字段。 ///