diff --git a/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs b/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs
index 4c843f9..0f8b583 100644
--- a/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs
+++ b/src/Oms.Application/Dtos/Channels/SalesProductPriceListOutput.cs
@@ -65,8 +65,18 @@ public class SalesProductPriceListOutput
///
public DateTime CreateTime { get; set; }
+ ///
+ /// 创建人。
+ ///
+ public string? CreateBy { get; set; }
+
///
/// 更新时间。
///
public DateTime? UpdateTime { get; set; }
+
+ ///
+ /// 修改人。
+ ///
+ public string? UpdateBy { get; set; }
}
diff --git a/src/Oms.Application/Dtos/Operations/InboundOrderLookupOutput.cs b/src/Oms.Application/Dtos/Operations/InboundOrderLookupOutput.cs
new file mode 100644
index 0000000..9dfb3ca
--- /dev/null
+++ b/src/Oms.Application/Dtos/Operations/InboundOrderLookupOutput.cs
@@ -0,0 +1,62 @@
+namespace Oms.Application.Dtos.Operations;
+
+///
+/// 入库扫描按单号查订单(只读展示销售产品、目的国)。
+///
+public class InboundOrderLookupOutput
+{
+ ///
+ /// 订单主键。
+ ///
+ public int OrderId { get; set; }
+
+ ///
+ /// 服务单号。
+ ///
+ public string ServiceNo { get; set; } = string.Empty;
+
+ ///
+ /// 客户单号。
+ ///
+ public string? ClientNo { get; set; }
+
+ ///
+ /// 跟踪号。
+ ///
+ public string? TrackNo { get; set; }
+
+ ///
+ /// 产品编码。
+ ///
+ public string? ProductCode { get; set; }
+
+ ///
+ /// 销售产品主键。
+ ///
+ public int? SalesProductId { get; set; }
+
+ ///
+ /// 销售产品名称。
+ ///
+ public string? SalesProductName { get; set; }
+
+ ///
+ /// 目的国家编码。
+ ///
+ public string? DestinationCountry { get; set; }
+
+ ///
+ /// 目的国家名称。
+ ///
+ public string? DestinationCountryName { get; set; }
+
+ ///
+ /// 订单状态码。
+ ///
+ public string OrderStatus { get; set; } = string.Empty;
+
+ ///
+ /// 订单状态名称。
+ ///
+ public string? OrderStatusName { get; set; }
+}
diff --git a/src/Oms.Application/Dtos/Orders/OrderFeeOutput.cs b/src/Oms.Application/Dtos/Orders/OrderFeeOutput.cs
new file mode 100644
index 0000000..bc6a90f
--- /dev/null
+++ b/src/Oms.Application/Dtos/Orders/OrderFeeOutput.cs
@@ -0,0 +1,52 @@
+namespace Oms.Application.Dtos.Orders;
+
+///
+/// 订单费用出参。
+///
+public class OrderFeeOutput
+{
+ ///
+ /// 主键。
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 类型:receivable / payable。
+ ///
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// 费用项目。
+ ///
+ public string CostItem { get; set; } = string.Empty;
+
+ ///
+ /// 金额。
+ ///
+ public decimal Amount { get; set; }
+
+ ///
+ /// 币种。
+ ///
+ public string Currency { get; set; } = string.Empty;
+
+ ///
+ /// 下游服务商。
+ ///
+ public string? DownstreamProvider { get; set; }
+
+ ///
+ /// 价格版本号。
+ ///
+ public string? PriceNo { get; set; }
+
+ ///
+ /// 价格生效时间。
+ ///
+ public DateTime? PriceEffectiveTime { get; set; }
+
+ ///
+ /// 计费重量阶段。
+ ///
+ public string? ChargingStage { get; set; }
+}
diff --git a/src/Oms.Application/Dtos/Orders/OrderOutput.cs b/src/Oms.Application/Dtos/Orders/OrderOutput.cs
index 9e21deb..b8d347a 100644
--- a/src/Oms.Application/Dtos/Orders/OrderOutput.cs
+++ b/src/Oms.Application/Dtos/Orders/OrderOutput.cs
@@ -149,4 +149,19 @@ public class OrderOutput
/// 重量明细集合。
///
public List WeightList { get; set; } = [];
+
+ ///
+ /// 费用明细集合。
+ ///
+ public List FeeList { get; set; } = [];
+
+ ///
+ /// 计费重(优先收货行,其次预报行)。
+ ///
+ public decimal? ChargeableWeight { get; set; }
+
+ ///
+ /// 应收合计(当前有效应收费用之和)。
+ ///
+ public decimal? ReceivableAmount { get; set; }
}
diff --git a/src/Oms.Application/Dtos/Orders/OrderUpdateInput.cs b/src/Oms.Application/Dtos/Orders/OrderUpdateInput.cs
new file mode 100644
index 0000000..d9bc676
--- /dev/null
+++ b/src/Oms.Application/Dtos/Orders/OrderUpdateInput.cs
@@ -0,0 +1,91 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Oms.Application.Dtos.Orders;
+
+///
+/// 订单详情编辑入参。
+///
+public class OrderUpdateInput
+{
+ ///
+ /// 订单主键。
+ ///
+ [Required]
+ public int Id { get; set; }
+
+ ///
+ /// 客户单号。
+ ///
+ [StringLength(64)]
+ public string? ClientNo { get; set; }
+
+ ///
+ /// 跟踪号。
+ ///
+ [StringLength(64)]
+ public string? TrackNo { get; set; }
+
+ ///
+ /// 渠道编码。
+ ///
+ [StringLength(64)]
+ public string? ChannelCode { get; set; }
+
+ ///
+ /// 货物属性。
+ ///
+ [StringLength(32)]
+ public string? CargoAttribute { get; set; }
+
+ ///
+ /// 仓库主键。
+ ///
+ public int? WarehouseId { get; set; }
+
+ ///
+ /// 是否 COD。
+ ///
+ public bool? IsCod { get; set; }
+
+ ///
+ /// COD 币种。
+ ///
+ [StringLength(8)]
+ public string? CodCurrency { get; set; }
+
+ ///
+ /// COD 金额。
+ ///
+ public decimal? CodSum { get; set; }
+
+ ///
+ /// 申报币种。
+ ///
+ [StringLength(8)]
+ public string? DeclaredCurrency { get; set; }
+
+ ///
+ /// 申报金额。
+ ///
+ public decimal? DeclaredSum { get; set; }
+
+ ///
+ /// 件数。
+ ///
+ public int? Quantity { get; set; }
+
+ ///
+ /// 寄件人。
+ ///
+ public OrderParcelSenderInput? Sender { get; set; }
+
+ ///
+ /// 收件人。
+ ///
+ public OrderParcelReceiverInput? Receiver { get; set; }
+
+ ///
+ /// 申报明细;传入时全量替换原申报行。
+ ///
+ public List? Items { get; set; }
+}
diff --git a/src/Oms.Application/Dtos/Orders/OrderWeightUpdateInput.cs b/src/Oms.Application/Dtos/Orders/OrderWeightUpdateInput.cs
new file mode 100644
index 0000000..738f0e2
--- /dev/null
+++ b/src/Oms.Application/Dtos/Orders/OrderWeightUpdateInput.cs
@@ -0,0 +1,54 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Oms.Application.Dtos.Orders;
+
+///
+/// 订单件重尺编辑入参。
+///
+public class OrderWeightUpdateInput
+{
+ ///
+ /// 订单主键。
+ ///
+ [Required]
+ public int Id { get; set; }
+
+ ///
+ /// 件重尺行。
+ ///
+ [Required]
+ public List Weights { get; set; } = [];
+}
+
+///
+/// 单条件重尺编辑项。
+///
+public class OrderWeightItemInput
+{
+ ///
+ /// 类型:forecast/receive/ship/downstream。
+ ///
+ [Required]
+ [StringLength(16)]
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// 长度(cm)。
+ ///
+ public decimal? Length { get; set; }
+
+ ///
+ /// 宽度(cm)。
+ ///
+ public decimal? Width { get; set; }
+
+ ///
+ /// 高度(cm)。
+ ///
+ public decimal? Height { get; set; }
+
+ ///
+ /// 实重(kg)。
+ ///
+ public decimal? ActualWeight { get; set; }
+}
diff --git a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs
index c2b4372..df8f7c4 100644
--- a/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs
+++ b/src/Oms.Application/Services/Channels/SalesProductService.Helpers.cs
@@ -88,7 +88,9 @@ public partial class SalesProductService
Factors = factorMap.TryGetValue(price.Id, out var factors) ? factors.Select(MapPriceFactorOutput).ToList() : [],
Details = detailMap.TryGetValue(price.Id, out var details) ? details.Select(MapPriceDetailOutput).ToList() : [],
CreateTime = price.CreateTime,
- UpdateTime = OmsDocHelper.NullIfDefault(price.UpdateTime)
+ CreateBy = price.CreateUserId <= 0 ? null : price.CreateUserId.ToString(),
+ UpdateTime = OmsDocHelper.NullIfDefault(price.UpdateTime),
+ UpdateBy = price.UpdateUserId <= 0 ? null : price.UpdateUserId.ToString()
}).ToList();
}
diff --git a/src/Oms.Application/Services/Operations/InboundRecordService.cs b/src/Oms.Application/Services/Operations/InboundRecordService.cs
index 524958f..b039cb3 100644
--- a/src/Oms.Application/Services/Operations/InboundRecordService.cs
+++ b/src/Oms.Application/Services/Operations/InboundRecordService.cs
@@ -3,9 +3,11 @@ using Microsoft.AspNetCore.Mvc;
using Oms.Application.Dtos.Operations;
using Oms.Application.Dtos.Shared;
using Oms.Application.Services.Orders;
+using Oms.Application.Shared;
using Oms.Domain.ChannelsAggregate.Repositories;
using Oms.Domain.OperationsAggregate.Entities;
using Oms.Domain.OperationsAggregate.Repositories;
+using Oms.Domain.OrdersAggregate.Repositories;
using Oms.Domain.SystemAggregate.Repositories;
namespace Oms.Application.Services.Operations;
@@ -17,10 +19,60 @@ namespace Oms.Application.Services.Operations;
public class InboundRecordService : ApplicationService
{
private IInboundRecordRepository InboundRecordRepository => LazyServiceProvider.GetRequiredService();
+ private IOrderRepository OrderRepository => LazyServiceProvider.GetRequiredService();
private ISalesProductRepository SalesProductRepository => LazyServiceProvider.GetRequiredService();
private IWarehouseAddressRepository WarehouseAddressRepository => LazyServiceProvider.GetRequiredService();
+ private ICountryRepository CountryRepository => LazyServiceProvider.GetRequiredService();
private OrderService OrderService => LazyServiceProvider.GetRequiredService();
+ ///
+ /// 按单号查询订单(入库扫描展示销售产品、目的国,只读)。
+ ///
+ [HttpGet("/operation/inbound/order-lookup")]
+ public async Task LookupOrderAsync([FromQuery] string waybillNo)
+ {
+ if (string.IsNullOrWhiteSpace(waybillNo))
+ {
+ throw new BusinessException("请扫描或输入单号");
+ }
+
+ var order = await OrderRepository.GetByWaybillNoAsync(waybillNo.Trim());
+ if (order == null)
+ {
+ throw new BusinessException("未找到对应订单,请检查单号");
+ }
+
+ var output = new InboundOrderLookupOutput
+ {
+ OrderId = order.Id,
+ ServiceNo = order.ServiceNo,
+ ClientNo = order.ClientNo,
+ TrackNo = order.TrackNo,
+ ProductCode = order.ProductCode,
+ DestinationCountry = order.ConsigneeCountry,
+ OrderStatus = order.OrderStatus,
+ OrderStatusName = OmsDocHelper.GetOrderStatusName(order.OrderStatus)
+ };
+
+ if (!string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim());
+ if (product != null)
+ {
+ output.SalesProductId = product.Id;
+ output.SalesProductName = product.ProductName;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(order.ConsigneeCountry))
+ {
+ var country = await CountryRepository.FirstOrDefaultAsync(x => x.CountryCode == order.ConsigneeCountry);
+ output.DestinationCountryName = country?.ChineseName ?? country?.EnglishName ?? order.ConsigneeCountry;
+ }
+
+ return output;
+ }
+
///
/// 新增入库记录。
///
@@ -35,29 +87,55 @@ public class InboundRecordService : ApplicationService
[HttpPost("/operation/inbound/create")]
public async Task CreateAsync(CreateInboundRecordInput input)
{
+ if (string.IsNullOrWhiteSpace(input.WaybillNo))
+ {
+ throw new BusinessException("运单号不能为空");
+ }
+
+ var order = await OrderRepository.GetByWaybillNoAsync(input.WaybillNo.Trim());
+ if (order == null)
+ {
+ throw new BusinessException("未找到对应订单,请检查单号");
+ }
+
+ if (!input.Weight.HasValue || !input.Length.HasValue || !input.Width.HasValue || !input.Height.HasValue
+ || input.Weight <= 0 || input.Length <= 0 || input.Width <= 0 || input.Height <= 0)
+ {
+ throw new BusinessException("收货实重与长宽高不能为空且须大于 0");
+ }
+
+ int? salesProductId = null;
+ string? destinationCountry = order.ConsigneeCountry;
+ if (!string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim());
+ salesProductId = product?.Id;
+ }
+
var now = DateTime.Now;
var operatorId = CurrentUser.GetId();
var entity = new InboundRecord
{
- WaybillNo = input.WaybillNo,
+ WaybillNo = input.WaybillNo.Trim(),
Weight = input.Weight,
Length = input.Length,
Width = input.Width,
Height = input.Height,
- SalesProductId = input.SalesProductId,
- DestinationCountry = input.DestinationCountry,
- WarehouseId = input.WarehouseId,
+ SalesProductId = salesProductId,
+ DestinationCountry = destinationCountry,
+ WarehouseId = input.WarehouseId ?? order.WarehouseId,
OperateTime = now,
OperatorId = operatorId
};
- var savedEntity = await InboundRecordRepository.InsertAsync(entity);
await OrderService.ApplyReceiveBillingByWaybillAsync(
- input.WaybillNo,
+ input.WaybillNo.Trim(),
input.Length,
input.Width,
input.Height,
input.Weight);
+
+ var savedEntity = await InboundRecordRepository.InsertAsync(entity);
return ObjectMapper.Map(savedEntity);
}
diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
index 3b53e29..79dd976 100644
--- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
@@ -27,6 +27,7 @@ public partial class OrderService
var recipients = await OrderRepository.GetRecipientsByOrderIdsAsync(orderIds);
var declarations = await OrderRepository.GetDeclarationsByOrderIdsAsync(orderIds);
var weights = await OrderRepository.GetWeightsByOrderIdsAsync(orderIds);
+ var fees = await OrderRepository.GetFeesByOrderIdsAsync(orderIds);
var warehouses = warehouseIds.Length == 0
? []
: await WarehouseAddressRepository.GetListAsync(x => warehouseIds.Contains(x.Id));
@@ -35,6 +36,7 @@ public partial class OrderService
var recipientMap = recipients.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.First());
var declarationMap = declarations.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.ToList());
var weightMap = weights.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.ToList());
+ var feeMap = fees.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.ToList());
var warehouseMap = warehouses.ToDictionary(x => x.Id);
return orders.Select(order =>
@@ -43,6 +45,12 @@ public partial class OrderService
recipientMap.TryGetValue(order.Id, out var recipient);
declarationMap.TryGetValue(order.Id, out var items);
weightMap.TryGetValue(order.Id, out var orderWeights);
+ feeMap.TryGetValue(order.Id, out var orderFees);
+ var feeOutputs = (orderFees ?? []).Select(MapFee).ToList();
+ var chargeableWeight = ResolveDisplayChargeableWeight(orderWeights);
+ var receivableAmount = feeOutputs
+ .Where(x => x.Type == OrderFeeTypeCodes.Receivable)
+ .Sum(x => x.Amount);
return new OrderOutput
{
Id = order.Id,
@@ -75,7 +83,10 @@ public partial class OrderService
Sender = MapSender(sender),
Receiver = MapReceiver(recipient),
Items = (items ?? []).Select(MapDeclaration).ToList(),
- WeightList = (orderWeights ?? []).Select(MapWeight).ToList()
+ WeightList = (orderWeights ?? []).Select(MapWeight).ToList(),
+ FeeList = feeOutputs,
+ ChargeableWeight = chargeableWeight,
+ ReceivableAmount = feeOutputs.Count == 0 ? null : receivableAmount
};
}).ToList();
}
@@ -543,6 +554,37 @@ public partial class OrderService
};
}
+ private static OrderFeeOutput MapFee(OrderFee x) =>
+ new()
+ {
+ Id = x.Id,
+ Type = x.Type,
+ CostItem = x.CostItem,
+ Amount = x.Amount,
+ Currency = x.Currency,
+ DownstreamProvider = x.DownstreamProvider,
+ PriceNo = x.PriceNo,
+ PriceEffectiveTime = x.PriceEffectiveTime,
+ ChargingStage = x.ChargingStage
+ };
+
+ private static decimal? ResolveDisplayChargeableWeight(IReadOnlyList? weights)
+ {
+ if (weights == null || weights.Count == 0)
+ {
+ return null;
+ }
+
+ var receive = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Receive);
+ if (receive?.ChargeableWeight is > 0)
+ {
+ return receive.ChargeableWeight;
+ }
+
+ var forecast = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Forecast);
+ return forecast?.ChargeableWeight;
+ }
+
private static OrderTaskFail BuildTaskFail(int taskId, int rowNumber, string reason, object rawData)
{
return new OrderTaskFail
diff --git a/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs
new file mode 100644
index 0000000..a8c92ac
--- /dev/null
+++ b/src/Oms.Application/Services/Orders/OrderService.OrderEdit.cs
@@ -0,0 +1,648 @@
+using Fake.Application;
+using Microsoft.AspNetCore.Mvc;
+using Oms.Application.Dtos.Channels;
+using Oms.Application.Dtos.Orders;
+using Oms.Application.Shared;
+using Oms.Domain.ChannelsAggregate.Entities;
+using Oms.Domain.ChannelsAggregate.Repositories;
+using Oms.Domain.CustomersAggregate.Enums;
+using Oms.Domain.OrdersAggregate.Consts;
+using Oms.Domain.OrdersAggregate.Entities;
+using Oms.Domain.Shared.Enums;
+
+namespace Oms.Application.Services.Orders;
+
+public partial class OrderService
+{
+ private IServiceChannelRepository ServiceChannelRepository =>
+ LazyServiceProvider.GetRequiredService();
+
+ ///
+ /// 编辑订单详情(基本信息、发件人、收件人)。
+ ///
+ [HttpPost("/order/update")]
+ public async Task UpdateOrderDetailAsync(OrderUpdateInput input)
+ {
+ var order = await GetOrderOrThrowAsync(input.Id);
+
+ if (input.ClientNo != null)
+ {
+ order.ClientNo = string.IsNullOrWhiteSpace(input.ClientNo) ? null : input.ClientNo.Trim();
+ }
+
+ if (input.TrackNo != null)
+ {
+ order.TrackNo = string.IsNullOrWhiteSpace(input.TrackNo) ? null : input.TrackNo.Trim();
+ }
+
+ if (input.ChannelCode != null)
+ {
+ order.ChannelCode = string.IsNullOrWhiteSpace(input.ChannelCode) ? null : input.ChannelCode.Trim();
+ }
+
+ if (input.CargoAttribute != null)
+ {
+ order.CargoAttribute = input.CargoAttribute.Trim();
+ }
+
+ if (input.WarehouseId.HasValue)
+ {
+ order.WarehouseId = input.WarehouseId.Value > 0 ? input.WarehouseId : null;
+ }
+
+ if (input.IsCod.HasValue)
+ {
+ order.IsCod = input.IsCod.Value ? (byte)1 : (byte)0;
+ }
+
+ if (input.CodCurrency != null)
+ {
+ order.CodCurrency = input.CodCurrency;
+ }
+
+ if (input.CodSum.HasValue)
+ {
+ order.CodSum = input.CodSum;
+ }
+
+ if (input.DeclaredCurrency != null)
+ {
+ order.DeclaredCurrency = input.DeclaredCurrency;
+ }
+
+ if (input.DeclaredSum.HasValue)
+ {
+ order.DeclaredSum = input.DeclaredSum;
+ }
+
+ if (input.Quantity.HasValue)
+ {
+ order.Quantity = input.Quantity;
+ }
+
+ order.UpdateTime = DateTime.Now;
+ await OrderRepository.UpdateAsync(order);
+
+ if (input.Sender != null && order.SenderId.HasValue)
+ {
+ var senders = await OrderRepository.GetSendersByOrderIdsAsync([order.Id]);
+ var sender = senders.FirstOrDefault();
+ if (sender != null)
+ {
+ ApplySenderInput(sender, input.Sender);
+ if (input.Sender.CountryCode != null)
+ {
+ var country = await GetCountryOrThrowAsync(input.Sender.CountryCode, "发件国家不存在");
+ sender.Country = country.Id;
+ }
+
+ await OrderRepository.UpdateSenderAsync(sender);
+ }
+ }
+
+ if (input.Receiver != null && order.RecipientId.HasValue)
+ {
+ var recipients = await OrderRepository.GetRecipientsByOrderIdsAsync([order.Id]);
+ var recipient = recipients.FirstOrDefault();
+ if (recipient != null)
+ {
+ await ApplyReceiverInputAsync(order, recipient, input.Receiver);
+ await OrderRepository.UpdateRecipientAsync(recipient);
+ }
+ }
+
+ if (input.Items != null)
+ {
+ await OrderRepository.ReplaceDeclarationsAsync(
+ order.Id,
+ input.Items.Select(x => MapDeclarationInput(order.Id, x)));
+ }
+
+ return (await MapOrderOutputsAsync([order])).First();
+ }
+
+ ///
+ /// 编辑订单件重尺并重算计费重。
+ ///
+ [HttpPost("/order/weight/update")]
+ public async Task UpdateOrderWeightsAsync(OrderWeightUpdateInput input)
+ {
+ if (input.Weights.Count == 0)
+ {
+ throw new BusinessException("件重尺不能为空");
+ }
+
+ var order = await GetOrderOrThrowAsync(input.Id);
+ if (string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ throw new BusinessException("订单未绑定销售产品,无法重算计费重");
+ }
+
+ var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim());
+ if (product == null)
+ {
+ throw new BusinessException("销售产品不存在");
+ }
+
+ var weights = (await OrderRepository.GetWeightsByOrderIdsAsync([order.Id]))
+ .OrderBy(x => x.Id)
+ .ToList();
+ if (weights.Count == 0)
+ {
+ throw new BusinessException("订单件重尺不存在");
+ }
+
+ var snapshotBinding = await ResolveSnapshotBindingFromOrderAsync(product, order);
+ var updatedTypes = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var item in input.Weights)
+ {
+ var type = item.Type?.Trim();
+ if (string.IsNullOrWhiteSpace(type))
+ {
+ continue;
+ }
+
+ var row = weights.FirstOrDefault(x => string.Equals(x.Type, type, StringComparison.OrdinalIgnoreCase));
+ if (row == null)
+ {
+ throw new BusinessException($"缺少件重尺类型 {type}");
+ }
+
+ if (item.Length.HasValue)
+ {
+ row.Length = item.Length;
+ }
+
+ if (item.Width.HasValue)
+ {
+ row.Width = item.Width;
+ }
+
+ if (item.Height.HasValue)
+ {
+ row.Height = item.Height;
+ }
+
+ if (item.ActualWeight.HasValue)
+ {
+ row.ActualWeight = item.ActualWeight;
+ }
+
+ if (row.Length.HasValue && row.Width.HasValue && row.Height.HasValue)
+ {
+ row.Cubic = ParcelBillingCalculator.CalculateCubic(row.Length.Value, row.Width.Value, row.Height.Value);
+ }
+
+ await ApplyChargeableWeightAsync(product, snapshotBinding.Rules, row);
+ updatedTypes.Add(type);
+ }
+
+ if (updatedTypes.Contains(OrderWeightTypeCodes.Forecast))
+ {
+ var forecast = weights.First(x => x.Type == OrderWeightTypeCodes.Forecast);
+ SyncWeightRows(weights, forecast);
+ }
+ else if (updatedTypes.Contains(OrderWeightTypeCodes.Receive))
+ {
+ var receive = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Receive);
+ if (receive != null)
+ {
+ SyncReceiveToShipAndDownstream(weights, receive);
+ }
+ }
+ else if (updatedTypes.Contains(OrderWeightTypeCodes.Ship))
+ {
+ var ship = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Ship);
+ if (ship != null)
+ {
+ SyncShipToDownstream(weights, ship);
+ }
+ }
+
+ await OrderRepository.ReplaceWeightsAsync(order.Id, weights);
+ order.UpdateTime = DateTime.Now;
+ await OrderRepository.UpdateAsync(order);
+
+ return (await MapOrderOutputsAsync([order])).First();
+ }
+
+ ///
+ /// 重算订单应收费用。
+ ///
+ [HttpPost("/order/fee/recalculate-receivable")]
+ public async Task RecalculateReceivableFeesAsync(OrderActionInput input)
+ {
+ var order = await GetOrderOrThrowAsync(input.Id);
+ await RecalculateReceivableCoreAsync(order);
+ return (await MapOrderOutputsAsync([order])).First();
+ }
+
+ ///
+ /// 重算订单应付费用。
+ ///
+ [HttpPost("/order/fee/recalculate-payable")]
+ public async Task RecalculatePayableFeesAsync(OrderActionInput input)
+ {
+ var order = await GetOrderOrThrowAsync(input.Id);
+ await RecalculatePayableCoreAsync(order);
+ return (await MapOrderOutputsAsync([order])).First();
+ }
+
+ private async Task RecalculateReceivableCoreAsync(Order order)
+ {
+ if (string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ throw new BusinessException("订单未绑定销售产品,无法重算应收");
+ }
+
+ var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim());
+ if (product == null)
+ {
+ throw new BusinessException("销售产品不存在");
+ }
+
+ var weights = await OrderRepository.GetWeightsByOrderIdsAsync([order.Id]);
+ var (weightType, chargingStage) = ResolveReceivableBillingStage(order);
+ var weightRow = weights.FirstOrDefault(x => x.Type == weightType)
+ ?? throw new BusinessException($"缺少件重尺类型 {weightType}");
+ if (!weightRow.ChargeableWeight.HasValue || weightRow.ChargeableWeight <= 0)
+ {
+ throw new BusinessException("计费重无效,请先完善件重尺");
+ }
+
+ var snapshotBinding = await ResolveSnapshotBindingFromOrderAsync(product, order);
+ var recipients = await OrderRepository.GetRecipientsByOrderIdsAsync([order.Id]);
+ var recipient = recipients.FirstOrDefault();
+ var zoneKeys = await ResolvePriceZoneKeysAsync(
+ product,
+ snapshotBinding.Rules,
+ snapshotBinding.Price,
+ order.ConsigneeCountry ?? string.Empty,
+ recipient?.Province,
+ recipient?.City,
+ recipient?.ZipCode);
+ if (zoneKeys == null || zoneKeys.Count == 0)
+ {
+ throw new BusinessException("价格分区未匹配(规则快照)");
+ }
+
+ var customer = order.CustomerId.HasValue
+ ? await CustomerRepository.FirstOrDefaultAsync(x => x.Id == order.CustomerId.Value)
+ : null;
+
+ await UpsertReceivableFeesAsync(
+ order,
+ snapshotBinding,
+ weightRow.ChargeableWeight ?? 0,
+ zoneKeys,
+ order.CargoAttribute ?? CargoAttributeCodes.General,
+ customer?.CustomerGroup,
+ chargingStage,
+ replaceExistingReceivable: true);
+
+ order.UpdateTime = DateTime.Now;
+ await OrderRepository.UpdateAsync(order);
+ }
+
+ private async Task RecalculatePayableCoreAsync(Order order)
+ {
+ if (string.IsNullOrWhiteSpace(order.ChannelCode))
+ {
+ throw new BusinessException("订单未绑定服务渠道,无法重算应付");
+ }
+
+ var channel = await ServiceChannelRepository.GetByChannelCodeAsync(order.ChannelCode.Trim());
+ if (channel == null)
+ {
+ throw new BusinessException("服务渠道不存在");
+ }
+
+ var weights = await OrderRepository.GetWeightsByOrderIdsAsync([order.Id]);
+ var downstream = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Downstream)
+ ?? throw new BusinessException("缺少 downstream 件重尺");
+ if (!downstream.ChargeableWeight.HasValue || downstream.ChargeableWeight <= 0)
+ {
+ throw new BusinessException("downstream 计费重无效,请先完善件重尺");
+ }
+
+ if (string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ throw new BusinessException("订单未绑定销售产品,无法匹配分区");
+ }
+
+ var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim());
+ if (product == null)
+ {
+ throw new BusinessException("销售产品不存在");
+ }
+
+ var snapshotBinding = await ResolveSnapshotBindingFromOrderAsync(product, order);
+ var recipients = await OrderRepository.GetRecipientsByOrderIdsAsync([order.Id]);
+ var recipient = recipients.FirstOrDefault();
+ var zoneKeys = await ResolvePriceZoneKeysAsync(
+ product,
+ snapshotBinding.Rules,
+ snapshotBinding.Price,
+ order.ConsigneeCountry ?? string.Empty,
+ recipient?.Province,
+ recipient?.City,
+ recipient?.ZipCode);
+ if (zoneKeys == null || zoneKeys.Count == 0)
+ {
+ throw new BusinessException("价格分区未匹配(规则快照)");
+ }
+
+ var channelPrices = await BuildChannelPriceOutputsAsync(channel.Id);
+ var (_, chargingStage) = ResolveReceivableBillingStage(order);
+ var customer = order.CustomerId.HasValue
+ ? await CustomerRepository.FirstOrDefaultAsync(x => x.Id == order.CustomerId.Value)
+ : null;
+
+ var fees = ParcelBillingCalculator.CalculatePayableFees(
+ channelPrices,
+ $"LIVE-{channel.Id}",
+ order.CreateTime == default ? DateTime.Now : order.CreateTime,
+ downstream.ChargeableWeight ?? 0,
+ zoneKeys,
+ order.CargoAttribute ?? CargoAttributeCodes.General,
+ customer?.CustomerGroup,
+ chargingStage);
+
+ if (fees.Count == 0 && ParcelBillingCalculator.HasEnabledPayablePriceItems(channelPrices))
+ {
+ throw new BusinessException("渠道买价未配置或未匹配到有效价格");
+ }
+
+ if (fees.Count > 0)
+ {
+ await OrderRepository.DeletePayableFeesAsync(order.Id);
+ await OrderRepository.UpsertFeesAsync(order.Id, fees);
+ }
+
+ order.UpdateTime = DateTime.Now;
+ await OrderRepository.UpdateAsync(order);
+ }
+
+ private async Task> BuildChannelPriceOutputsAsync(int serviceChannelId)
+ {
+ var prices = await ServiceChannelRepository.GetPricesAsync(serviceChannelId);
+ var priceIds = prices.Select(x => x.Id).ToArray();
+ var factorMap = (await ServiceChannelRepository.GetPriceFactorsAsync(priceIds))
+ .GroupBy(x => x.PriceId)
+ .ToDictionary(x => x.Key, x => x.OrderBy(item => item.SerialNumber).ToList());
+ var detailMap = (await ServiceChannelRepository.GetPriceDetailsAsync(priceIds))
+ .GroupBy(x => x.PriceId)
+ .ToDictionary(x => x.Key, x => x.OrderBy(item => item.Id).ToList());
+
+ return prices.Select(price => new ServiceChannelPriceListOutput
+ {
+ Id = price.Id,
+ ServiceChannelId = price.ServiceChannelId,
+ SerialNumber = price.SerialNumber,
+ Status = (byte)price.Status,
+ PriceName = price.PriceName,
+ CostItem = price.CostItem,
+ CostUnit = price.CostUnit,
+ Currency = price.Currency,
+ ChargingCondition = price.ChargingCondition,
+ Factors = factorMap.TryGetValue(price.Id, out var factors)
+ ? factors.Select(MapChannelPriceFactorOutput).ToList()
+ : [],
+ Details = detailMap.TryGetValue(price.Id, out var details)
+ ? details.Select(MapChannelPriceDetailOutput).ToList()
+ : []
+ }).ToList();
+ }
+
+ private static ChannelPriceFactorOutput MapChannelPriceFactorOutput(ServiceChannelPriceFactor entity) =>
+ new()
+ {
+ Id = entity.Id,
+ PriceId = entity.PriceId,
+ SerialNumber = entity.SerialNumber,
+ Factor = entity.Factor,
+ FactorValues = OmsDocHelper.DeserializeJson>(entity.FactorValues) ?? []
+ };
+
+ private static ChannelPriceDetailOutput MapChannelPriceDetailOutput(ServiceChannelPriceDetail entity) =>
+ new()
+ {
+ Id = entity.Id,
+ PriceId = entity.PriceId,
+ FactorValuesJson = OmsDocHelper.DeserializeJsonObject(entity.FactorValuesJson),
+ CalculationFormula = entity.CalculationFormula,
+ CalculatedValue = entity.CalculatedValue,
+ CustomerGroupValues = OmsDocHelper.DeserializeJsonObject(entity.CustomerGroupValues)
+ };
+
+ private static (string WeightType, string ChargingStage) ResolveReceivableBillingStage(Order order) =>
+ order.OrderStatus switch
+ {
+ OrderStatusCodes.Received or OrderStatusCodes.PendingPayment or OrderStatusCodes.PendingShip
+ => (OrderWeightTypeCodes.Receive, OrderFeeChargingStageCodes.Inbound),
+ OrderStatusCodes.PendingOnline or OrderStatusCodes.Shipping
+ or OrderStatusCodes.DeliverySuccess or OrderStatusCodes.DeliveryFailed
+ => (OrderWeightTypeCodes.Ship, OrderFeeChargingStageCodes.Outbound),
+ _ => (OrderWeightTypeCodes.Forecast, OrderFeeChargingStageCodes.Forecast)
+ };
+
+ private static OrderDeclaration MapDeclarationInput(int orderId, OrderParcelItemInput x)
+ {
+ return new OrderDeclaration
+ {
+ OrderId = orderId,
+ CnName = x.CnName,
+ EnName = x.EnName,
+ Sku = x.Sku,
+ Unit = x.Unit,
+ UnitPrice = x.UnitPrice,
+ UnitWeight = x.UnitWeight,
+ Quantity = x.Quantity,
+ Amount = x.Amount,
+ Weight = x.Weight,
+ HsCode = x.ExHsCode ?? x.ImHsCode,
+ ImHsCode = x.ImHsCode,
+ ExHsCode = x.ExHsCode ?? x.ImHsCode,
+ SkuImage = x.SkuImage,
+ Category = x.Category,
+ Brand = x.Brand,
+ BrandType = x.BrandType,
+ IsMagnetic = x.IsMagnetic ? (byte)1 : (byte)0,
+ Distribution = x.Distribution,
+ Material = x.Material,
+ EnMaterial = x.EnMaterial,
+ Purpose = x.Purpose,
+ Attributes = x.Attributes,
+ OriginCountry = x.OriginCountry,
+ SalesLink = x.SalesLink,
+ Model = x.Model,
+ Specifications = x.Specifications,
+ Comment = x.Comment
+ };
+ }
+
+ private static void ApplySenderInput(OrderSender sender, OrderParcelSenderInput input)
+ {
+ if (input.SenderName != null)
+ {
+ sender.SenderName = input.SenderName;
+ }
+
+ if (input.Company != null)
+ {
+ sender.Company = input.Company;
+ }
+
+ if (input.Phone != null)
+ {
+ sender.Phone = input.Phone;
+ }
+
+ if (input.TelePhone != null)
+ {
+ sender.TelePhone = input.TelePhone;
+ }
+
+ if (input.Email != null)
+ {
+ sender.Email = input.Email;
+ }
+
+ if (input.Province != null)
+ {
+ sender.Province = input.Province;
+ }
+
+ if (input.City != null)
+ {
+ sender.City = input.City;
+ }
+
+ if (input.ZipCode != null)
+ {
+ sender.ZipCode = input.ZipCode;
+ }
+
+ if (input.StreetAddress1 != null)
+ {
+ sender.StreetAddress1 = input.StreetAddress1;
+ }
+
+ if (input.StreetAddress2 != null)
+ {
+ sender.StreetAddress2 = input.StreetAddress2;
+ }
+
+ if (input.StreetAddress3 != null)
+ {
+ sender.StreetAddress3 = input.StreetAddress3;
+ }
+
+ if (input.HouseNumber != null)
+ {
+ sender.HouseNumber = input.HouseNumber;
+ }
+ }
+
+ private async Task ApplyReceiverInputAsync(Order order, OrderRecipient recipient, OrderParcelReceiverInput input)
+ {
+ if (input.RecipientsName != null)
+ {
+ recipient.RecipientsName = input.RecipientsName;
+ }
+
+ if (input.CompanyName != null)
+ {
+ recipient.CompanyName = input.CompanyName;
+ }
+
+ if (input.Phone != null)
+ {
+ recipient.Phone = input.Phone;
+ }
+
+ if (input.TelePhone != null)
+ {
+ recipient.TelePhone = input.TelePhone;
+ }
+
+ if (input.Email != null)
+ {
+ recipient.Email = input.Email;
+ }
+
+ if (input.Province != null)
+ {
+ recipient.Province = input.Province;
+ }
+
+ if (input.City != null)
+ {
+ recipient.City = input.City;
+ }
+
+ if (input.StreetNumber != null)
+ {
+ recipient.StreetNumber = input.StreetNumber;
+ }
+
+ if (input.StreetAddress1 != null)
+ {
+ recipient.StreetAddress1 = input.StreetAddress1;
+ }
+
+ if (input.StreetAddress2 != null)
+ {
+ recipient.StreetAddress2 = input.StreetAddress2;
+ }
+
+ if (input.StreetAddress3 != null)
+ {
+ recipient.StreetAddress3 = input.StreetAddress3;
+ }
+
+ if (input.ZipCode != null)
+ {
+ recipient.ZipCode = input.ZipCode;
+ }
+
+ if (input.TaxType.HasValue)
+ {
+ recipient.TaxType = input.TaxType;
+ }
+
+ if (input.TaxNo != null)
+ {
+ recipient.TaxNo = input.TaxNo;
+ }
+
+ if (input.CertificateType.HasValue)
+ {
+ recipient.CertificateType = input.CertificateType;
+ }
+
+ if (input.CertificateNo != null)
+ {
+ recipient.CertificateNo = input.CertificateNo;
+ }
+
+ if (input.Eori != null)
+ {
+ recipient.Eori = input.Eori;
+ }
+
+ if (input.Vat != null)
+ {
+ recipient.Vat = input.Vat;
+ }
+
+ if (input.CountryCode != null)
+ {
+ var country = await GetCountryOrThrowAsync(input.CountryCode, "收件国家不存在");
+ recipient.Country = country.Id;
+ order.ConsigneeCountry = country.CountryCode;
+ await OrderRepository.UpdateAsync(order);
+ }
+ }
+}
diff --git a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs
index 7577f5e..b52cdf3 100644
--- a/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.ParcelBilling.cs
@@ -366,7 +366,8 @@ public partial class OrderService
IReadOnlyList zoneKeys,
string cargoAttribute,
CustomerGroup? customerGroup,
- string chargingStage)
+ string chargingStage,
+ bool replaceExistingReceivable = false)
{
var fees = ParcelBillingCalculator.CalculateReceivableFees(
snapshotBinding.Price,
@@ -385,6 +386,11 @@ public partial class OrderService
if (fees.Count > 0)
{
+ if (replaceExistingReceivable)
+ {
+ await OrderRepository.DeleteReceivableFeesAsync(order.Id);
+ }
+
await OrderRepository.UpsertFeesAsync(order.Id, fees);
}
}
@@ -857,6 +863,23 @@ public partial class OrderService
}
}
+ private static void SyncShipToDownstream(List weights, OrderWeight ship)
+ {
+ var row = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Downstream);
+ if (row == null)
+ {
+ return;
+ }
+
+ row.Length = ship.Length;
+ row.Width = ship.Width;
+ row.Height = ship.Height;
+ row.ActualWeight = ship.ActualWeight;
+ row.VolumetricWeight = ship.VolumetricWeight;
+ row.ChargeableWeight = ship.ChargeableWeight;
+ row.Cubic = ship.Cubic;
+ }
+
private static Order BuildDraftOrderEntity(
OrderParcelCreateInput input,
SalesProduct product,
@@ -1028,7 +1051,7 @@ public partial class OrderService
}
///
- /// 入库收货后:覆盖 receive 件重尺、重算计费重,写入 inbound 阶段应收,并同步 ship/downstream 重量行。
+ /// 入库收货后:覆盖 receive 件重尺、重算计费重,覆盖写入 inbound 阶段应收,并同步 ship/downstream 重量行。
///
internal async Task ApplyReceiveBillingByWaybillAsync(
string waybillNo,
@@ -1039,30 +1062,35 @@ public partial class OrderService
{
if (string.IsNullOrWhiteSpace(waybillNo))
{
- return;
+ throw new BusinessException("运单号不能为空");
}
if (!length.HasValue || !width.HasValue || !height.HasValue || !actualWeight.HasValue
|| length <= 0 || width <= 0 || height <= 0 || actualWeight <= 0)
{
- return;
+ throw new BusinessException("收货实重与长宽高不能为空且须大于 0");
}
- var order = await OrderRepository.GetByServiceNoAsync(waybillNo.Trim());
- if (order == null || string.IsNullOrWhiteSpace(order.ProductCode))
+ var order = await OrderRepository.GetByWaybillNoAsync(waybillNo.Trim());
+ if (order == null)
{
- return;
+ throw new BusinessException("未找到对应订单,请检查单号");
+ }
+
+ if (string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ throw new BusinessException("订单未绑定销售产品,无法入库计费");
}
if (order.OrderStatus is not (OrderStatusCodes.Predeclared or OrderStatusCodes.Received))
{
- return;
+ throw new BusinessException($"订单状态为「{OmsDocHelper.GetOrderStatusName(order.OrderStatus)}」,不允许入库收货");
}
var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim());
if (product == null)
{
- return;
+ throw new BusinessException("订单关联的销售产品不存在");
}
var weights = (await OrderRepository.GetWeightsByOrderIdsAsync([order.Id]))
@@ -1071,7 +1099,7 @@ public partial class OrderService
var receive = weights.FirstOrDefault(x => x.Type == OrderWeightTypeCodes.Receive);
if (receive == null)
{
- return;
+ throw new BusinessException("订单缺少收货件重尺记录");
}
receive.Length = length;
@@ -1080,25 +1108,8 @@ public partial class OrderService
receive.ActualWeight = actualWeight;
receive.Cubic = ParcelBillingCalculator.CalculateCubic(length.Value, width.Value, height.Value);
- ProductSnapshotBinding snapshotBinding;
- try
- {
- snapshotBinding = await ResolveSnapshotBindingFromOrderAsync(product, order);
- }
- catch (BusinessException)
- {
- return;
- }
-
- try
- {
- await ApplyChargeableWeightAsync(product, snapshotBinding.Rules, receive);
- }
- catch (BusinessException)
- {
- return;
- }
-
+ var snapshotBinding = await ResolveSnapshotBindingFromOrderAsync(product, order);
+ await ApplyChargeableWeightAsync(product, snapshotBinding.Rules, receive);
SyncReceiveToShipAndDownstream(weights, receive);
await OrderRepository.ReplaceWeightsAsync(order.Id, weights);
@@ -1114,28 +1125,22 @@ public partial class OrderService
recipient?.ZipCode);
if (zoneKeys == null || zoneKeys.Count == 0)
{
- return;
+ throw new BusinessException("价格分区未匹配(规则快照)");
}
var customer = order.CustomerId.HasValue
? await CustomerRepository.FirstOrDefaultAsync(x => x.Id == order.CustomerId.Value)
: null;
- try
- {
- await UpsertReceivableFeesAsync(
- order,
- snapshotBinding,
- receive.ChargeableWeight ?? 0,
- zoneKeys,
- order.CargoAttribute ?? CargoAttributeCodes.General,
- customer?.CustomerGroup,
- OrderFeeChargingStageCodes.Inbound);
- }
- catch (BusinessException)
- {
- return;
- }
+ await UpsertReceivableFeesAsync(
+ order,
+ snapshotBinding,
+ receive.ChargeableWeight ?? 0,
+ zoneKeys,
+ order.CargoAttribute ?? CargoAttributeCodes.General,
+ customer?.CustomerGroup,
+ OrderFeeChargingStageCodes.Inbound,
+ replaceExistingReceivable: true);
order.OrderStatus = OrderStatusCodes.Received;
order.ReceiveTime = DateTime.Now;
diff --git a/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs b/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs
index 7252a22..6587900 100644
--- a/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs
+++ b/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs
@@ -11,6 +11,8 @@ public interface IOrderRepository : IRepository
Task GetByTrackNoAsync(string trackNo, CancellationToken cancellationToken = default);
+ Task GetByWaybillNoAsync(string waybillNo, CancellationToken cancellationToken = default);
+
Task GetByCustomerAndClientNoAsync(int customerId, string clientNo, CancellationToken cancellationToken = default);
Task GetByRequestIdAsync(string requestId, CancellationToken cancellationToken = default);
@@ -29,11 +31,21 @@ public interface IOrderRepository : IRepository
Task InsertDeclarationsAsync(IEnumerable declarations, CancellationToken cancellationToken = default);
+ Task ReplaceDeclarationsAsync(int orderId, IEnumerable declarations, CancellationToken cancellationToken = default);
+
Task InsertWeightsAsync(IEnumerable weights, CancellationToken cancellationToken = default);
Task ReplaceWeightsAsync(int orderId, IEnumerable weights, CancellationToken cancellationToken = default);
Task UpsertFeesAsync(int orderId, IEnumerable fees, CancellationToken cancellationToken = default);
+ Task DeleteReceivableFeesAsync(int orderId, CancellationToken cancellationToken = default);
+
+ Task DeletePayableFeesAsync(int orderId, CancellationToken cancellationToken = default);
+
+ Task UpdateSenderAsync(OrderSender sender, CancellationToken cancellationToken = default);
+
+ Task UpdateRecipientAsync(OrderRecipient recipient, CancellationToken cancellationToken = default);
+
Task> GetFeesByOrderIdsAsync(IEnumerable orderIds, CancellationToken cancellationToken = default);
}
diff --git a/src/Oms.SqlSugarCore/Orders/OrderRepository.cs b/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
index b607128..8bd25c1 100644
--- a/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
+++ b/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
@@ -1,4 +1,5 @@
using Fake.DependencyInjection;
+using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Entities;
using Oms.Domain.OrdersAggregate.Repositories;
using Oms.SqlSugarCore.Repositories;
@@ -22,6 +23,29 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
return FirstOrDefaultAsync(x => x.TrackNo == trackNo, cancellationToken);
}
+ public async Task GetByWaybillNoAsync(string waybillNo, CancellationToken cancellationToken = default)
+ {
+ var normalized = waybillNo.Trim();
+ if (string.IsNullOrWhiteSpace(normalized))
+ {
+ return null;
+ }
+
+ var order = await GetByServiceNoAsync(normalized, cancellationToken);
+ if (order != null)
+ {
+ return order;
+ }
+
+ order = await GetByTrackNoAsync(normalized, cancellationToken);
+ if (order != null)
+ {
+ return order;
+ }
+
+ return await FirstOrDefaultAsync(x => x.ClientNo == normalized, cancellationToken);
+ }
+
public Task GetByCustomerAndClientNoAsync(int customerId, string clientNo, CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(x => x.CustomerId == customerId && x.ClientNo == clientNo, cancellationToken);
@@ -112,6 +136,17 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
await db.Insertable(items).ExecuteCommandAsync(cancellationToken);
}
+ public async Task ReplaceDeclarationsAsync(int orderId, IEnumerable declarations, CancellationToken cancellationToken = default)
+ {
+ var items = declarations.ToArray();
+ var db = await GetDbContextAsync(cancellationToken);
+ await db.Deleteable().Where(x => x.OrderId == orderId).ExecuteCommandAsync(cancellationToken);
+ if (items.Length > 0)
+ {
+ await db.Insertable(items).ExecuteCommandAsync(cancellationToken);
+ }
+ }
+
public async Task InsertWeightsAsync(IEnumerable weights, CancellationToken cancellationToken = default)
{
var items = weights.ToArray();
@@ -171,6 +206,36 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
}
}
+ public async Task DeleteReceivableFeesAsync(int orderId, CancellationToken cancellationToken = default)
+ {
+ var db = await GetDbContextAsync(cancellationToken);
+ await db.Deleteable()
+ .Where(x => x.OrderId == orderId && x.Type == OrderFeeTypeCodes.Receivable)
+ .ExecuteCommandAsync(cancellationToken);
+ }
+
+ public async Task DeletePayableFeesAsync(int orderId, CancellationToken cancellationToken = default)
+ {
+ var db = await GetDbContextAsync(cancellationToken);
+ await db.Deleteable()
+ .Where(x => x.OrderId == orderId && x.Type == OrderFeeTypeCodes.Payable)
+ .ExecuteCommandAsync(cancellationToken);
+ }
+
+ public async Task UpdateSenderAsync(OrderSender sender, CancellationToken cancellationToken = default)
+ {
+ var db = await GetDbContextAsync(cancellationToken);
+ sender.UpdateTime = DateTime.Now;
+ await db.Updateable(sender).ExecuteCommandAsync(cancellationToken);
+ }
+
+ public async Task UpdateRecipientAsync(OrderRecipient recipient, CancellationToken cancellationToken = default)
+ {
+ var db = await GetDbContextAsync(cancellationToken);
+ recipient.UpdateTime = DateTime.Now;
+ await db.Updateable(recipient).ExecuteCommandAsync(cancellationToken);
+ }
+
public async Task> GetFeesByOrderIdsAsync(IEnumerable orderIds, CancellationToken cancellationToken = default)
{
var ids = orderIds.Distinct().ToArray();