فهرست منبع

修改内容提交

Lyyyi 1 هفته پیش
والد
کامیت
a53d272ab0

+ 3 - 1
OASystem/EntitySync/Program.cs

@@ -186,6 +186,8 @@ db.CodeFirst.SetStringDefaultLength(50).BackupTable().InitTables(new Type[]
     //typeof(Grp_ConfProcessLog),// 团组会务流程总览操作日志  
     //typeof(Res_InvitationAI),// 商邀公务AI  
     //typeof(Res_InvitationAI_NoGroup),// 商邀公务AI-无团组版  
-    typeof(Res_OpLocalAI),// OP地接AI  
+    //typeof(Res_OpLocalAI),// OP地接AI  
+    //typeof(Grp_AirAdditionServices), //机票订单附加服务费用记录
+    
 });
 Console.WriteLine("数据库结构同步完成!");

+ 27 - 0
OASystem/OASystem.Api/Controllers/FinancialController.cs

@@ -3,6 +3,7 @@ using Aspose.Words;
 using EyeSoft.Extensions;
 using EyeSoft.IO;
 using FluentValidation;
+//using Microsoft.Graph.Models;
 using NPOI.SS.UserModel;
 using NPOI.XSSF.UserModel;
 using OASystem.API.OAMethodLib;
@@ -2980,6 +2981,7 @@ namespace OASystem.API.Controllers
 
             var designer = new WorkbookDesigner
             {
+
                 Workbook = new Workbook(AppSettingsHelper.Get("ExcelBasePath") + "Template/‌GroupAccFee‌Temp.xlsx")
             };
             designer.SetDataSource("DataView", proceedInfos);
@@ -4243,6 +4245,7 @@ namespace OASystem.API.Controllers
             var otherMoneyDatas = _sqlSugar.Queryable<Grp_DecreasePayments>().Where(it => it.IsDel == 0 && groupIds.Contains(it.DiId)).ToList();
             var refundPaymentDatas = _sqlSugar.Queryable<Fin_PaymentRefundAndOtherMoney>().Where(it => it.IsDel == 0 && groupIds.Contains(it.DiId)).ToList();
             var ExtraCostDatas = _sqlSugar.Queryable<Fin_GroupExtraCost>().Where(it => it.IsDel == 0 && groupIds.Contains(it.DiId)).ToList();
+            
             #endregion
             //Expense company
             foreach (var groupInfo in _groupDatas)
@@ -4389,6 +4392,30 @@ namespace OASystem.API.Controllers
                             string airPayType = setDatas.Find(it => it.Id == payInfo.PayDId)?.Name ?? "-";
                             priName = $"{flightsCode}【{airPayType}】";
                             break;
+                        case 1561://值机
+                        case 1562://选座
+                        case 1563://购买行李
+                        case 1564://行李直挂
+                        case 1565://火车票出票选座
+                        case 1566://行程单打印费
+                            Grp_AirAdditionServices additionalService = _sqlSugar.Queryable<Grp_AirAdditionServices>().Where(s => s.Id == payInfo.CId).ToList().FirstOrDefault();//附加服务费
+                            Grp_AirTicketReservations jpRes_addition = airDatas.Where(s => s.Id == additionalService.ResId).FirstOrDefault();
+                            if (jpRes_addition != null)
+                            {
+
+                                var clientInfo = _sqlSugar.Queryable<Crm_DeleClient>()
+                                       .Where(x => additionalService.ClientId == x.Id)
+                                       .First();
+                                string tempCrmName = "";
+                                if (clientInfo != null)
+                                {
+                                    tempCrmName = $"{AesEncryptionHelper.Decrypt(clientInfo.FirstName)} {AesEncryptionHelper.Decrypt(clientInfo.LastName)}";
+                                }
+                                string serviceTypeName = setDatas.FirstOrDefault(x => x.Id == additionalService.SysTypeId)?.Name ?? "";
+
+                                priName = $"[{tempCrmName}][{jpRes_addition.FlightsCode}]";
+                            }
+                            break;
                         case 98:  //  98 其他款项
 
                             var info = otherMoneyDatas.Find(it => payInfo.DIId == it.DiId && payInfo.CId == it.Id);

+ 148 - 28
OASystem/OASystem.Api/Controllers/GroupsController.cs

@@ -10909,6 +10909,7 @@ FROM
 
                 var clientNameList = GetSimplClientList(_dto.DiId);
 
+
                 #region 费用清单
                 // 传入审核状态
                 int auditStatus = _dto.AuditStatus;
@@ -10920,17 +10921,27 @@ FROM
                 var approvedAuditIds = new List<int>() { 1, 3 };
 
                 var exp = Expressionable.Create<Grp_CreditCardPayment>();
-                exp.AndIF(_dto.Label != -1, it => it.CTable == _dto.Label);
+                if (_dto.Label == 85)
+                {
+                    var temp_list = _sqlSugar.Queryable<Sys_SetData>().Where(s => s.STid == 139).Select(s => s.Id).ToList();
+                    temp_list.add(85);
+                    int[] temp_arr = temp_list.ToArray();
+
+                    exp = exp.And(it => temp_arr.Contains(it.CTable));
+                }
+                else if (_dto.Label != -1)
+                {
+                    exp = exp.And(it => it.CTable == _dto.Label);
+                }
 
                 // 费用类型为机票时,排除 金额、币种为0 的数据
                 exp.AndIF(_dto.Label == 85, it => it.PayMoney != 0.00M && it.PaymentCurrency != 0);
 
-
                 if (auditStatus == approvedStatus) exp.And(it => approvedAuditIds.Contains(it.IsAuditGM));
                 else if (auditStatus != allStatus) exp.And(it => it.IsAuditGM == auditStatus);
 
                 var entityList = _groupRepository.Query<Grp_CreditCardPayment>(s => s.DIId == _dto.DiId && s.IsDel == 0 && s.CreateUserId > 0).Where(exp.ToExpression()).ToList();
-
+     
                 var detailList = new List<Grp_CreditCardPaymentDetailView>();
 
                 var ccpCurrencyPrices = new List<CreditCardPaymentCurrencyPriceItem>();
@@ -10991,6 +11002,11 @@ FROM
 
                 var initDatas = await _groupRepository.Query<Sys_SetData>(s => s.IsDel == 0).ToListAsync();
 
+                /*
+                 * SetData.STid = 139 机票附加服务费用类型
+                 */
+                var setData_Addition_Items = initDatas.Where(s => s.STid == 139).ToList();
+
                 /*
                  * 币种信息
                  */
@@ -11645,38 +11661,41 @@ FROM
                                         .Select(x => new { x.Id, x.FirstName, x.LastName })
                                         .ToListAsync();
 
-                                    var additionalServices = new StringBuilder();
-                                    foreach (var item in jpRes.CustTicketInfos)
-                                    {
-                                        if (item.AdditionalServices.Any())
-                                        {
-                                            string name = string.Empty;
-
-                                            var clientInfo = clientInfos.FirstOrDefault(x => x.Id == item.ClientId);
-                                            if (item.ClientId == -1) name = "行程单";
-                                            else if (clientInfo != null)
-                                            {
-                                                name = $"{AesEncryptionHelper.Decrypt(clientInfo.FirstName)}{AesEncryptionHelper.Decrypt(clientInfo.LastName)}";
-                                            }
-
-                                            var additionalServiceLabels = item.AdditionalServices.Select(x =>
-                                            {
-                                                var typeName = initDatas.FirstOrDefault(x1 => x1.Id == x.ServiceTypeId)?.Name ?? "";
-                                                return $"{typeName}:{x.Amount:#0.00}  ";
-                                            }).ToList();
-
-                                            var label = $"<br/>{name}:{string.Join("、", additionalServiceLabels)}";
-                                            additionalServices.Append(label);
-                                        }
-                                    }
+                                    #region 拆分附加服务费用
+
+                                    //var additionalServices = new StringBuilder();
+                                    //foreach (var item in jpRes.CustTicketInfos)
+                                    //{
+                                    //    if (item.AdditionalServices.Any())
+                                    //    {
+                                    //        string name = string.Empty;
+
+                                    //        var clientInfo = clientInfos.FirstOrDefault(x => x.Id == item.ClientId);
+                                    //        if (item.ClientId == -1) name = "行程单";
+                                    //        else if (clientInfo != null)
+                                    //        {
+                                    //            name = $"{AesEncryptionHelper.Decrypt(clientInfo.FirstName)}{AesEncryptionHelper.Decrypt(clientInfo.LastName)}";
+                                    //        }
+
+                                    //        var additionalServiceLabels = item.AdditionalServices.Select(x =>
+                                    //        {
+                                    //            var typeName = initDatas.FirstOrDefault(x1 => x1.Id == x.ServiceTypeId)?.Name ?? "";
+                                    //            return $"{typeName}:{x.Amount:#0.00}  ";
+                                    //        }).ToList();
+
+                                    //        var label = $"<br/>{name}:{string.Join("、", additionalServiceLabels)}";
+                                    //        additionalServices.Append(label);
+                                    //    }
+                                    //}
+                                    #endregion
 
                                     _detail.PriceMsgContent = $@"
 航班号:{string.Join("、", flightNumbers)}<br/>
 城市A-B:{string.Join("/", flightCitys)}<br/>
 人数:{numbers.Count}<br/>
-附加服务:{additionalServices}<br/>
 航班描述:{(jpRes?.FlightsDescription ?? "").Replace("\r\n", "<br/>")}<br/>
 价格描述:{jpRes?.PriceDescription ?? ""}";
+                                    //20260826拆分:附加服务:{additionalServices}<br/>
 
                                     string recordType = jpRes.RecordType switch {
                                         1 => "(退票)",
@@ -11688,6 +11707,37 @@ FROM
                                     _detail.PriceNameContent = $"{jpRes.FlightsCode} - {cabinType} {recordType}";
                                 }
                             }
+                            break;
+                        case 1561://值机
+                        case 1562://选座
+                        case 1563://购买行李
+                        case 1564://行李直挂
+                        case 1565://火车票出票选座
+                        case 1566://行程单打印费
+                            Grp_AirAdditionServices additionalService = _sqlSugar.Queryable<Grp_AirAdditionServices>().Where(s => s.Id == entity.CId).ToList().FirstOrDefault();//附加服务费
+                            Grp_AirTicketReservations jpRes_addition = _AirTicketReservations.Where(s => s.Id == additionalService.ResId).FirstOrDefault();
+
+                            if (jpRes_addition != null) {
+
+                                var clientInfo = await _sqlSugar.Queryable<Crm_DeleClient>()
+                                       .Where(x => additionalService.ClientId == x.Id)
+                                       .FirstAsync();
+                                string name = "";
+                                if (clientInfo != null) {
+                                    name = $"{AesEncryptionHelper.Decrypt(clientInfo.FirstName)} {AesEncryptionHelper.Decrypt(clientInfo.LastName)}";
+                                }
+                                string serviceTypeName = setData_Addition_Items.FirstOrDefault(x => x.Id == additionalService.SysTypeId)?.Name ?? "";
+
+                                _detail.PriceMsgContent = $@"
+出行人:{name} <br/>
+附加服务:{serviceTypeName} <br />
+费用说明:{additionalService?.PriceDescription ?? ""}<br/>
+城市A-B:{jpRes_addition.FlightsCity} <br/>
+航班描述:{(jpRes_addition?.FlightsDescription ?? "").Replace("\r\n", "<br/>")} 
+";
+                                _detail.PriceNameContent = $"[{name}][{jpRes_addition.FlightsCode}]附加服务:{serviceTypeName}";
+                            }
+
                             break;
                         case 98://其他款项
                             Grp_DecreasePayments gdpRes = _DecreasePayments.Where(s => s.Id == entity.CId).FirstOrDefault();
@@ -16150,6 +16200,76 @@ Order By CreateTime",dto.GroupId);
             return Ok(jw);
         }
 
+        /// <summary>
+        /// 下载指定时间范围内团组的指定模块的所有文件
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost]
+        public IActionResult ExportDateGroupByModuleEx(ExportDateGroupByModuleExDto dto)
+        {
+            var jw = JsonView(false);
+            DateTime st = DateTime.Parse(dto.StartDate);
+            DateTime et = DateTime.Parse(dto.EndDate);
+            var groupList = _sqlSugar.Queryable<Grp_DelegationInfo>().Where(x => x.IsDel == 0 && x.VisitStartDate >= st && x.VisitEndDate <= et).ToList();
+            if (groupList.Count < 1)
+            {
+                jw.Msg = "时间范围内不存在出访的团组!";
+                return Ok(jw);
+            }
+
+            //var dbQuery = _sqlSugar.Queryable<Grp_GroupModelFile>().Where(x => x.IsDel == 0 && x.Diid == dto.Diid && x.Ctable == dto.Ctable).ToList();
+            //var key = _sqlSugar.Queryable<Sys_SetData>().First(x => x.Id == dto.Ctable
+            //&& x.IsDel == 0);
+
+            //if (key == null)
+            //{
+            //    jw.Msg = "Ctable指向错误!";
+            //    return Ok(jw);
+            //}
+
+            //key.Name = Regex.Replace(key.Name, @"[^\w\.@-]", "",
+            //                  RegexOptions.None, TimeSpan.FromSeconds(1.5));
+            //groupInfo.TeamName = Regex.Replace(groupInfo.TeamName, @"[^\w\.@-]", "",
+            //      RegexOptions.None, TimeSpan.FromSeconds(1.5));
+
+            //var io = new IOOperatorHelper();
+            //var Zips = new Dictionary<string, Stream>();
+
+            //foreach (var item in dbQuery)
+            //{
+            //    if (System.IO.File.Exists(item.FilePath))
+            //    {
+            //        using (var fileStream = new FileStream(item.FilePath, FileMode.Open, FileAccess.Read))
+            //        {
+            //            byte[] bytes = new byte[fileStream.Length];
+            //            fileStream.Read(bytes, 0, bytes.Length);
+            //            fileStream.Close();
+            //            var stream = new MemoryStream(bytes);
+            //            while (Zips.ContainsKey(item.FileName))
+            //            {
+            //                item.FileName = new Random().Next(100) + "_" + item.FileName;
+            //            }
+            //            Zips.Add(item.FileName, stream);
+            //        }
+            //    }
+            //}
+
+            //if (Zips.Count > 0)
+            //{
+            //    var byts = io.ConvertZipStream(Zips);
+            //    var path = "\\GroupModelFiles\\" + groupInfo.TeamName;
+            //    io.ByteToFile(byts, AppSettingsHelper.Get("GrpFileBasePath") + $"{path}\\{groupInfo.TeamName}_{key.Name}_.Zip");
+            //    jw = JsonView(true, "success", new { url = AppSettingsHelper.Get("GrpFileBaseUrl") + $"{AppSettingsHelper.Get("GrpFileFtpPath")}{path}\\{groupInfo.TeamName}_{key.Name}_.Zip" });
+            //}
+            //else
+            //{
+            //    jw.Msg = "暂无生成文件!";
+            //}
+
+            return Ok(jw);
+        }
+
         /// <summary>
         /// 删除该团组下的指定文件
         /// </summary>

+ 1 - 1
OASystem/OASystem.Api/OASystem.API.csproj

@@ -57,7 +57,7 @@
     <PackageReference Include="CurlThin.Native" Version="0.0.8" />
     <PackageReference Include="EPPlus" Version="7.4.1" />
     <PackageReference Include="Flurl.Http" Version="3.2.4" />
-    <PackageReference Include="iTextSharp" Version="5.5.13.5" />
+    <PackageReference Include="iTextSharp" Version="5.5.13.6" />
     <PackageReference Include="JsonDiffPatch.Net" Version="2.3.0" />
     <PackageReference Include="MailKit" Version="4.15.1" />
     <PackageReference Include="Markdig" Version="0.33.0" />

+ 2 - 0
OASystem/OASystem.Domain/AutoMappers/_baseMappingProfile.cs

@@ -115,6 +115,7 @@ namespace OASystem.Domain.AutoMappers
             CreateMap<AirTicketFeeOpInfo, Grp_AirTicketReservations>();
             CreateMap<AirTicketFeeOpInfo, Grp_CreditCardPayment>();
             CreateMap<GroupAirTicketList, GroupAirTicketInfo>();
+            CreateMap<AdditionalService, Grp_AirAdditionServices>();
 
             #endregion
 
@@ -190,6 +191,7 @@ namespace OASystem.Domain.AutoMappers
             #region 签证费用录入
             CreateMap<OpVisaPriceDto, Grp_VisaInfo>();
             CreateMap<OpVisaPriceDto, Grp_CreditCardPayment>();
+            CreateMap<AirTicketCardInfo, Grp_CreditCardPayment>();
             #endregion
 
             #region op费用

+ 9 - 0
OASystem/OASystem.Domain/Dtos/FileDto/FileDto.cs

@@ -91,6 +91,15 @@ namespace OASystem.Domain.Dtos.FileDto
         public int Diid { get; set; }
     }
 
+    public class ExportDateGroupByModuleExDto
+    {
+        public int Ctable { get; set; }
+
+        public string StartDate { get; set; }
+
+        public string EndDate { get; set; }
+    }
+
     public class DeleteGroupFileDto
     {
         public int Id { get; set; }

+ 48 - 0
OASystem/OASystem.Domain/Entities/Groups/Grp_AirAdditionServices.cs

@@ -0,0 +1,48 @@
+using System.Text.RegularExpressions;
+
+namespace OASystem.Domain.Entities.Groups;
+
+    /// <summary>
+    /// 机票附加服务费用
+    /// </summary>
+    [SugarTable("Grp_AirAdditionServices")]
+    public class Grp_AirAdditionServices: EntityBase
+    {
+        /// <summary>
+        ///  Res表Id
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public int ResId { get; set; }
+
+        /// <summary>
+        ///  Sys_SetDataId(服务类型)
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public int SysTypeId { get; set; }
+
+        /// <summary>
+        ///  出行人Id
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "int")]
+        public int ClientId { get; set; }
+
+        /// <summary>
+        ///  票号(TicketCode)
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(50)")]
+        public string TicketCode { get; set; }
+
+        /// <summary>
+        /// 附加服务费用
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "decimal(10,2)")]
+        public decimal Amount { get; set; }
+
+        /// <summary>
+        /// 附加服务费用说明
+        /// </summary>
+        [SugarColumn(IsNullable = true, ColumnDataType = "varchar(500)")]
+        public string PriceDescription { get; set; }
+
+    }
+

+ 109 - 0
OASystem/OASystem.Domain/Entities/Groups/Grp_AirTicketReservations.cs

@@ -359,6 +359,115 @@ public class AdditionalService
     /// </summary>
     public decimal Amount { get; set; }
 
+    /// <summary>
+    /// 报价说明
+    /// </summary>
+    public string PriceDescription { get; set; }
+
+    #region Credit Info
+    /// <summary>
+    /// 支付方式
+    /// </summary>
+    public int PayDId { get; set; }
+    /// <summary>
+    /// 消费方式
+    /// </summary>
+    public string ConsumptionPatterns { get; set; }
+    /// <summary>
+    /// 消费日期
+    /// </summary>
+    public string ConsumptionDate { get; set; }
+    /// <summary>
+    /// 付款金额
+    /// </summary>
+    public decimal PayMoney { get; set; }
+    /// <summary>
+    /// 付款币种 数据类型Id
+    /// </summary>
+    public int PaymentCurrency { get; set; }
+    /// <summary>
+    /// 卡类型
+    /// </summary>
+    public int CTDId { get; set; }
+    /// <summary>
+    /// 银行卡号
+    /// </summary>
+    public string BankNo { get; set; }
+    /// <summary>
+    /// 持卡人姓名
+    /// </summary>
+    public string CardholderName { get; set; }
+    /// <summary>
+    /// 当天汇率
+    /// </summary>
+    public decimal DayRate { get; set; }
+    /// <summary>
+    /// 公司银行卡号
+    /// </summary>
+    public string CompanyBankNo { get; set; }
+    /// <summary>
+    /// 对方开户行
+    /// </summary>
+    public string OtherBankName { get; set; }
+    /// <summary>
+    /// 对方银行账号
+    /// </summary>
+    public string OtherSideNo { get; set; }
+    /// <summary>
+    /// 对方姓名
+    /// </summary>
+    public string OtherSideName { get; set; }
+    /// 收款方
+    /// </summary>
+    public string Payee { get; set; }
+
+    /// <summary>
+    /// 费用标识
+    /// 0 公转 1 私转
+    /// </summary>
+    public int OrbitalPrivateTransfer { get; set; }
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
+
+    /// <summary>
+    /// 总经理是否审核  
+    /// 0 未审核 
+    /// 1 已通过 
+    /// 2 未通过 
+    /// 3 自动审核
+    /// </summary>
+    public int IsAuditGM { get; set; } = 0;
+
+    /// <summary>
+    ///  审核状态描述
+    /// </summary>
+    public string IsAuditGMStr => IsAuditGM switch
+    {
+        0 => "未审核",
+        1 => "已通过",
+        2 => "未通过",
+        3 => "自动审核",
+        _ => "未知状态"
+    };
+
+    /// <summary>
+    /// 总经理审核人
+    /// </summary>
+    public int AuditGMOperate { get; set; }
+
+    /// <summary>
+    /// 总经理审核时间
+    /// </summary>
+    public string AuditGMDate { get; set; }
+
+    /// <summary>
+    /// 审核描述
+    /// </summary>
+    public string AuditStr { get; set; }
+    #endregion
+
 }
 
 /// <summary>

+ 120 - 1
OASystem/OASystem.Domain/ViewModels/Groups/AirTicketReservationsView.cs

@@ -445,7 +445,7 @@ public class AirTicketFeeInfo
     /// <summary>
     /// 支付方式
     /// </summary>
-    public int PayDId { get; set; } 
+    public int PayDId { get; set; }
     /// <summary>
     /// 消费方式
     /// </summary>
@@ -545,6 +545,125 @@ public class AirTicketFeeInfo
     public string AuditStr { get; set; }
     #endregion
 
+    /// <summary>
+    /// 按机票编号分组的C表信息
+    /// </summary>
+    public List<AirTicketCardInfo> TicketCardInfos { get; set; } = new List<AirTicketCardInfo>();
+}
+
+
+public class AirTicketCardInfo 
+{
+    public string TicketNumber { get; set; }
+
+    /// <summary>
+    /// 报价说明
+    /// </summary>
+    public string PriceDescription { get; set; }
+
+    #region Credit Info
+    /// <summary>
+    /// 支付方式
+    /// </summary>
+    public int PayDId { get; set; }
+    /// <summary>
+    /// 消费方式
+    /// </summary>
+    public string ConsumptionPatterns { get; set; }
+    /// <summary>
+    /// 消费日期
+    /// </summary>
+    public string ConsumptionDate { get; set; }
+    /// <summary>
+    /// 付款金额
+    /// </summary>
+    public decimal PayMoney { get; set; }
+    /// <summary>
+    /// 付款币种 数据类型Id
+    /// </summary>
+    public int PaymentCurrency { get; set; }
+    /// <summary>
+    /// 卡类型
+    /// </summary>
+    public int CTDId { get; set; }
+    /// <summary>
+    /// 银行卡号
+    /// </summary>
+    public string BankNo { get; set; }
+    /// <summary>
+    /// 持卡人姓名
+    /// </summary>
+    public string CardholderName { get; set; }
+    /// <summary>
+    /// 当天汇率
+    /// </summary>
+    public decimal DayRate { get; set; }
+    /// <summary>
+    /// 公司银行卡号
+    /// </summary>
+    public string CompanyBankNo { get; set; }
+    /// <summary>
+    /// 对方开户行
+    /// </summary>
+    public string OtherBankName { get; set; }
+    /// <summary>
+    /// 对方银行账号
+    /// </summary>
+    public string OtherSideNo { get; set; }
+    /// <summary>
+    /// 对方姓名
+    /// </summary>
+    public string OtherSideName { get; set; }
+    /// 收款方
+    /// </summary>
+    public string Payee { get; set; }
+
+    /// <summary>
+    /// 费用标识
+    /// 0 公转 1 私转
+    /// </summary>
+    public int OrbitalPrivateTransfer { get; set; }
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string Remark { get; set; }
+
+    /// <summary>
+    /// 总经理是否审核  
+    /// 0 未审核 
+    /// 1 已通过 
+    /// 2 未通过 
+    /// 3 自动审核
+    /// </summary>
+    public int IsAuditGM { get; set; } = 0;
+
+    /// <summary>
+    ///  审核状态描述
+    /// </summary>
+    public string IsAuditGMStr => IsAuditGM switch
+    {
+        0 => "未审核",
+        1 => "已通过",
+        2 => "未通过",
+        3 => "自动审核",
+        _ => "未知状态"
+    };
+
+    /// <summary>
+    /// 总经理审核人
+    /// </summary>
+    public int AuditGMOperate { get; set; }
+
+    /// <summary>
+    /// 总经理审核时间
+    /// </summary>
+    public string AuditGMDate { get; set; }
+
+    /// <summary>
+    /// 审核描述
+    /// </summary>
+    public string AuditStr { get; set; }
+    #endregion
 }
 
 public class AppPushBodyView

+ 523 - 92
OASystem/OASystem.Infrastructure/Repositories/Groups/AirTicketResRepository.cs

@@ -14,6 +14,7 @@ using OASystem.Domain.ViewModels.Groups;
 using OASystem.Domain.ViewModels.JuHeExchangeRate;
 using StackExchange.Redis;
 using System.Security.Cryptography;
+using System.Security.Principal;
 
 namespace OASystem.Infrastructure.Repositories.Groups;
 
@@ -28,7 +29,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
         _groupInfoRep = groupInfoRep;
         _groupRateRep = groupRateRep;
     }
-    
+
     #region 旧版
     public async Task<Result> AirTicketResById(AirTicketResByIdDto dto)
     {
@@ -1193,7 +1194,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
             // 退票退票标识
             if (item.No == 1)
             {
-                item.CabinName += "(退票)"; 
+                item.CabinName += "(退票)";
             }
 
             // 7.1 处理航班描述
@@ -1447,7 +1448,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                 var sortedGroup = filteredSource
                     .OrderByDescending(x => x.Air.CustTicketInfos != null && x.Air.CustTicketInfos.Any())
                     .ThenBy(x => x.Air.RecordType)
-                    .ToList(); 
+                    .ToList();
 
                 // 锚定当前航段权重最高的记录作为代表单据
                 var primaryItem = sortedGroup.First();
@@ -1470,8 +1471,8 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                 // 汇总金额
                 decimal totalPayMoney = allCustTicketInfos?.Sum(c =>
                 {
-                    // 机票票面价 + 附加服务费
-                    decimal ticketAndServices = (c?.ActualPrice ?? 0M) + (c?.AdditionalServices?.Sum(d => d?.Amount ?? 0M) ?? 0M);
+                    // 机票票面价 
+                    decimal ticketAndServices = (c?.ActualPrice ?? 0M);
                     // 退票相关费用(退票金额通常为负值,退款给客户)
                     decimal refundFees = (c?.RefundRecord?.RefundAmount ?? 0M) + (c?.RefundRecord?.NonRefundableTax ?? 0M);
 
@@ -1500,6 +1501,37 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                     auditsStr.Append($"{cabinTypeName}({typeDesc}):{auditStatus};");
                 }
 
+                List<AirTicketCardInfo> airTicketCardInfos = new List<AirTicketCardInfo>();
+
+                foreach (var item in rawList)
+                {
+                    AirTicketCardInfo temp = new AirTicketCardInfo();
+                    temp.PriceDescription = item.Air.PriceDescription;
+                    temp.TicketNumber = item.Air.TicketNumber;
+                    temp.PayDId = item.Card.PayDId;
+                    temp.ConsumptionPatterns = item.Card.ConsumptionPatterns;
+                    temp.ConsumptionDate = item.Card.ConsumptionDate;
+                    temp.PayMoney = item.Card.PayMoney;
+                    temp.PaymentCurrency = item.Card.PaymentCurrency;
+                    temp.CTDId = item.Card.CTDId;
+                    temp.BankNo = item.Card.BankNo;
+                    temp.CardholderName = item.Card.CardholderName;
+                    temp.DayRate = item.Card.DayRate;
+                    temp.CompanyBankNo = item.Card.CompanyBankNo;
+                    temp.OtherBankName = item.Card.OtherBankName;
+                    temp.OtherSideNo = item.Card.OtherSideNo;
+                    temp.OtherSideName = item.Card.OtherSideName;
+                    temp.Payee = item.Card.Payee;
+                    temp.OrbitalPrivateTransfer = item.Card.OrbitalPrivateTransfer;
+                    temp.Remark = item.Card.Remark;
+                    temp.IsAuditGM = item.Card.IsAuditGM;
+                    temp.AuditGMOperate = item.Card.AuditGMOperate;
+                    temp.AuditGMDate = item.Card.AuditGMDate;
+
+                    //temp.AuditStr = auditsStr.ToString().TrimEnd(';');
+                    airTicketCardInfos.Add(temp);
+                }
+
                 return new AirTicketFeeInfo
                 {
                     Id = primaryItem.Air.Id,
@@ -1509,28 +1541,28 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                     ClientNameIds = clientNameIds,
                     CustTicketInfos = allCustTicketInfos,
                     PriceDescription = primaryItem.Air.PriceDescription,
-
-                    // 财务信息绑定
-                    PayDId = primaryItem.Card.PayDId,
-                    ConsumptionPatterns = primaryItem.Card.ConsumptionPatterns,
-                    ConsumptionDate = primaryItem.Card.ConsumptionDate,
-                    PayMoney = totalPayMoney,
-                    PaymentCurrency = primaryItem.Card.PaymentCurrency,
-                    CTDId = primaryItem.Card.CTDId,
-                    BankNo = primaryItem.Card.BankNo,
-                    CardholderName = primaryItem.Card.CardholderName,
-                    DayRate = primaryItem.Card.DayRate,
-                    CompanyBankNo = primaryItem.Card.CompanyBankNo,
-                    OtherBankName = primaryItem.Card.OtherBankName,
-                    OtherSideNo = primaryItem.Card.OtherSideNo,
-                    OtherSideName = primaryItem.Card.OtherSideName,
-                    Payee = primaryItem.Card.Payee,
-                    OrbitalPrivateTransfer = primaryItem.Card.OrbitalPrivateTransfer,
-                    Remark = primaryItem.Card.Remark,
-                    IsAuditGM = primaryItem.Card.IsAuditGM,
-                    AuditGMOperate = primaryItem.Card.AuditGMOperate,
-                    AuditGMDate = primaryItem.Card.AuditGMDate,
-                    AuditStr = auditsStr.ToString().TrimEnd(';')
+                    TicketCardInfos = airTicketCardInfos,
+                    //// 财务信息绑定
+                    //PayDId = primaryItem.Card.PayDId,
+                    //ConsumptionPatterns = primaryItem.Card.ConsumptionPatterns,
+                    //ConsumptionDate = primaryItem.Card.ConsumptionDate,
+                    //PayMoney = totalPayMoney,
+                    //PaymentCurrency = primaryItem.Card.PaymentCurrency,
+                    //CTDId = primaryItem.Card.CTDId,
+                    //BankNo = primaryItem.Card.BankNo,
+                    //CardholderName = primaryItem.Card.CardholderName,
+                    //DayRate = primaryItem.Card.DayRate,
+                    //CompanyBankNo = primaryItem.Card.CompanyBankNo,
+                    //OtherBankName = primaryItem.Card.OtherBankName,
+                    //OtherSideNo = primaryItem.Card.OtherSideNo,
+                    //OtherSideName = primaryItem.Card.OtherSideName,
+                    //Payee = primaryItem.Card.Payee,
+                    //OrbitalPrivateTransfer = primaryItem.Card.OrbitalPrivateTransfer,
+                    //Remark = primaryItem.Card.Remark,
+                    //IsAuditGM = primaryItem.Card.IsAuditGM,
+                    //AuditGMOperate = primaryItem.Card.AuditGMOperate,
+                    //AuditGMDate = primaryItem.Card.AuditGMDate,
+                    //AuditStr = auditsStr.ToString().TrimEnd(';')
                 };
             })
             .ToList();
@@ -1612,7 +1644,11 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
         var skipMessages = new List<string>();
 
         // 1. 数据清洗:按照【航段+舱位+票据类型】分类并完成退票剥离
-        var saveList = await OrganizeAirTicketDataAsync(dto.AirTicketFeeInfos, groupId);
+        //var saveList = await OrganizeAirTicketDataAsync(dto.AirTicketFeeInfos, groupId);
+        List<AirTicketFeeOpInfo> saveList = await OrganizeAirTicketDataAsyncByTicketNum(dto.AirTicketFeeInfos, groupId);
+
+        //return JsonView<AppPushBodyView>.Success("没有需要保存的数据", new AppPushBodyView());
+
         if (saveList == null || !saveList.Any())
         {
             return JsonView<AppPushBodyView>.Success("没有需要保存的数据", new AppPushBodyView());
@@ -1656,7 +1692,8 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
 
                     bool isShortContained = allFrontendFlightText.Contains(cleanDbFlight);
 
-                    bool isLongDevoured = saveList.Any(f => {
+                    bool isLongDevoured = saveList.Any(f =>
+                    {
                         var cleanFrontendItem = f.FlightsDescription.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper();
                         return !string.IsNullOrEmpty(cleanFrontendItem) && cleanDbFlight.Contains(cleanFrontendItem) && cleanDbFlight.Length > cleanFrontendItem.Length;
                     });
@@ -1670,12 +1707,12 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
             // 建立数据库记录的精确索引,每个组合只保留一条记录
             var dbAirIndex = dbAirList
                 .Where(x => !devouredDbAirIds.Contains(x.Id))
-                .GroupBy(x => new
-                {
-                    CleanFlightsDesc = x.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "",
-                    x.CType,
-                    x.RecordType
-                })
+                .GroupBy(x => 
+                (
+                    CleanFlightsDesc : x.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "",
+                    TicketNumber : x.TicketNumber,
+                    RecordType : x.RecordType
+                ))
                 .ToDictionary(
                     g => g.Key,
                     g => g.OrderBy(x => x.Id).FirstOrDefault()
@@ -1684,39 +1721,90 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
             // 建立新生成正常单的临时追踪字典
             var newCreatedNormalAirMap = new Dictionary<string, int>();
 
+            #region 20260820 Disable
             // 对 saveList 也进行去重,确保每个组合只有一条
-            var distinctSaveList = saveList
+            //var distinctSaveList = saveList
+            //    .GroupBy(x => new
+            //    {
+            //        CleanFlightsDesc = x.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "",
+            //        x.CType, //由CType改为TicketNumber
+            //        x.RecordType
+            //    })
+            //    .Select(g => g.First())
+            //    .ToList();
+
+            #endregion
+
+            #region 20260820 Enable
+
+
+            // 先手动展开
+            var expandedList = new List<(string FlightsDescription, string CleanFlightsDesc, string TicketNumber, int RecordType, AirTicketFeeOpInfo OriginalItem)>();
+
+            foreach (var item in saveList)
+            {
+                if (item.CustTicketInfos != null && item.CustTicketInfos.Any())
+                {
+                    foreach (var ticket in item.CustTicketInfos)
+                    {
+                        expandedList.Add((
+                            FlightsDescription: item.FlightsDescription,
+                            CleanFlightsDesc: item.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "",
+                            TicketNumber: ticket.TicketNumber,
+                            RecordType: item.RecordType,
+                            OriginalItem: item
+                        ));
+                    }
+                }
+            }
+
+            // 再去重
+            var distinctSaveList = expandedList
                 .GroupBy(x => new
                 {
-                    CleanFlightsDesc = x.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "",
-                    x.CType,
+                    x.CleanFlightsDesc,
+                    x.TicketNumber,
                     x.RecordType
                 })
-                .Select(g => g.First())
+                .Select(g => new
+                {
+                    OriginalItem = g.First().OriginalItem,
+                    FlightsDescription = g.First().FlightsDescription,
+                    CleanFlightsDesc = g.Key.CleanFlightsDesc,
+                    TicketNumber = g.Key.TicketNumber,
+                    RecordType = g.Key.RecordType
+                })
                 .ToList();
 
+            #endregion
+
+
             // 3. 进入循环:精准等值比对
             foreach (var item in distinctSaveList)
             {
-                var cleanNewFlightDesc = item.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "";
-                string normalLookupKey = $"{cleanNewFlightDesc}_{item.CType}";
+                string cleanNewFlightDesc = item.FlightsDescription?.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() ?? "";
+                string ticketNumber = item.TicketNumber;
+                int recordType = item.RecordType;
+                string normalLookupKey = $"{cleanNewFlightDesc}_{ticketNumber}";
 
-                var lookupKey = new
-                {
-                    CleanFlightsDesc = cleanNewFlightDesc,
-                    item.CType,
-                    item.RecordType
-                };
+                var lookupKey = 
+                (
+                    CleanFlightsDesc : cleanNewFlightDesc,
+                    TicketNumber : ticketNumber,
+                    RecordType : recordType
+                );
 
                 dbAirIndex.TryGetValue(lookupKey, out var dbAir);
 
-                decimal currentSegmentPrice = item.CustTicketInfos?.Sum(x =>
-                            (x?.ActualPrice ?? 0M) + (x.AdditionalServices?.Where(y => y != null).Sum(y => y?.Amount ?? 0M) ?? 0M)
-                        ) ?? 0M;
+                //decimal currentSegmentPrice = item.OriginalItem?.CustTicketInfos?.Sum(x =>
+                //            (x?.ActualPrice ?? 0M) + (x.AdditionalServices?.Where(y => y != null).Sum(y => y?.Amount ?? 0M) ?? 0M)
+                //        ) ?? 0M;
+
+                decimal currentSegmentPrice = item.OriginalItem?.CustTicketInfos?.Sum(x =>(x?.ActualPrice ?? 0M)) ?? 0M;
 
                 if (item.RecordType == 1)
                 {
-                    currentSegmentPrice = item.CustTicketInfos?.Sum(x =>
+                    currentSegmentPrice = item.OriginalItem?.CustTicketInfos?.Sum(x =>
                             (x?.RefundRecord?.RefundAmount ?? 0M) + (x?.RefundRecord?.NonRefundableTax ?? 0M)
                         ) ?? 0M;
                 }
@@ -1730,14 +1818,17 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                 // -------------------- 分支 A:全新增 --------------------
                 if (dbAir == null)
                 {
-                    var airEntity = _mapper.Map<Grp_AirTicketReservations>(item);
+                    var airEntity = _mapper.Map<Grp_AirTicketReservations>(item.OriginalItem);
+                    var tempCardSource = item.OriginalItem.TicketCardInfos.Where(x => x.TicketNumber == item.TicketNumber).FirstOrDefault();
+
                     airEntity.DIId = groupId;
                     airEntity.CreateUserId = currUserId;
                     airEntity.CreateTime = DateTime.Now;
                     airEntity.IsDel = 0;
                     airEntity.Price = currentSegmentPrice;
-                    airEntity.ClientNum = item.CustTicketInfos?.Select(x => x.ClientId).Distinct().Count() ?? 0;
-                    airEntity.ClientName = string.Join(",", item.CustTicketInfos?.Select(x => x.ClientId).Distinct() ?? new List<int>());
+                    airEntity.Currency = item.OriginalItem?.PaymentCurrency ?? 0;
+                    airEntity.ClientNum = item.OriginalItem?.CustTicketInfos?.Select(x => x.ClientId).Distinct().Count() ?? 0;
+                    airEntity.ClientName = string.Join(",", item.OriginalItem?.CustTicketInfos?.Select(x => x.ClientId).Distinct() ?? new List<int>());
 
                     airEntity.RecordType = item.RecordType;
 
@@ -1746,7 +1837,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                         // 优先从已存在但不被删除的记录中查找原购票正单
                         var originalNormalDbRow = dbAirList.FirstOrDefault(x =>
                             x.RecordType == 0 &&
-                            x.CType == item.CType &&
+                            x.TicketNumber == item.TicketNumber &&
                             !devouredDbAirIds.Contains(x.Id) &&
                             x.FlightsDescription.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim().ToUpper() == cleanNewFlightDesc
                         );
@@ -1765,13 +1856,16 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                         airEntity.OriginalReservationId = 0;
                     }
 
-                    airEntity.FlightsCode = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsCode));
-                    airEntity.FlightsCity = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsCity));
-                    airEntity.FlightsDate = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsDate));
-                    airEntity.FlightsTime = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsTime));
-                    airEntity.ArrivedTime = string.Join("/", item.AirTicketBasicInfos.Select(x => x.ArrivedTime));
+                    airEntity.FlightsCode = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsCode));
+                    airEntity.FlightsCity = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsCity));
+                    airEntity.FlightsDate = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsDate));
+                    airEntity.FlightsTime = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsTime));
+                    airEntity.ArrivedTime = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.ArrivedTime));
+                    airEntity.TicketNumber = item.TicketNumber;
+                    airEntity.TicketCode = item.OriginalItem?.CustTicketInfos.FirstOrDefault(x => x.TicketNumber == item.TicketNumber)?.TicketCode ?? string.Empty;
+                    airEntity.PriceDescription = tempCardSource.PriceDescription ?? string.Empty;
 
-                    var airId = await _sqlSugar.Insertable(airEntity).ExecuteReturnIdentityAsync();
+                    var airId = await _sqlSugar.Insertable(airEntity).ExecuteReturnIdentityAsync(); //Grp_AirTicketReservations.Id
 
                     if (airEntity.RecordType == 0)
                     {
@@ -1780,8 +1874,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                             newCreatedNormalAirMap.Add(normalLookupKey, airId);
                         }
                     }
-
-                    var cardEntity = _mapper.Map<Grp_CreditCardPayment>(item);
+                    var cardEntity = _mapper.Map<Grp_CreditCardPayment>(tempCardSource);
                     cardEntity.DIId = groupId;
                     cardEntity.CId = airId;
                     cardEntity.CTable = 85;
@@ -1792,7 +1885,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                     cardEntity.PayMoney = currentSegmentPrice;
                     cardEntity.PayThenMoney = cardEntity.PayMoney;
 
-                    decimal effectiveRate = cardEntity.DayRate > 0M ? cardEntity.DayRate : (item.DayRate > 0M ? item.DayRate : 1M);
+                    decimal effectiveRate = cardEntity.DayRate > 0M ? cardEntity.DayRate : (item.OriginalItem?.DayRate > 0M ? item.OriginalItem.DayRate : 1M);
                     cardEntity.RMBPrice = cardEntity.PayMoney * effectiveRate;
 
                     if (item.RecordType == 1)
@@ -1808,17 +1901,162 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                     {
                         appPushBodyInfos.Add(new AppPushBodyInfo { OperationType = 1, AirTicketId = airId, CardId = cardId, CardCNYAmount = cardEntity.RMBPrice });
                     }
+
+                    //添加附加服务费用表数据,以及C表对应费用审核数据
+                    List<CustTicketInfo> custList = item.OriginalItem?.CustTicketInfos.ToList();
+
+                    foreach (var custItem in custList)
+                    {
+                        List<AdditionalService> addList = custItem.AdditionalServices;
+                        foreach (var addItem in addList)
+                        {
+                            var addEntity = _mapper.Map<Grp_AirAdditionServices>(addItem);
+                            addEntity.ResId = airId;
+                            addEntity.SysTypeId = addItem.ServiceTypeId;
+                            addEntity.ClientId = custItem.ClientId;
+                            addEntity.TicketCode = custItem.TicketCode;
+                            addEntity.Amount = addItem.Amount;
+                            addEntity.CreateUserId = currUserId;
+                            addEntity.CreateTime = DateTime.Now;
+                            addEntity.PriceDescription = addItem.PriceDescription;
+
+                            var additionId = await _sqlSugar.Insertable(addEntity).ExecuteReturnIdentityAsync(); //Grp_AdditionServices.Id
+
+                            var cardEntity_addi = _mapper.Map<Grp_CreditCardPayment>(item.OriginalItem);
+                            cardEntity_addi.DIId = groupId;
+                            cardEntity_addi.CId = additionId;
+                            cardEntity_addi.CTable = addItem.ServiceTypeId;
+                            cardEntity_addi.IsDel = 0;
+                            cardEntity_addi.CreateUserId = currUserId;
+                            cardEntity_addi.CreateTime = DateTime.Now;
+                            cardEntity_addi.UpdateDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+                            cardEntity_addi.PayMoney = addItem.Amount;
+                            cardEntity_addi.PayThenMoney = addItem.Amount;
+                            cardEntity_addi.IsAuditGM = 0;
+
+                            var cardId_addi = await _sqlSugar.Insertable(cardEntity_addi).ExecuteReturnIdentityAsync();
+
+                        }
+                    }
                 }
                 // -------------------- 分支 B:编辑覆盖 --------------------
                 else
                 {
+                    //添加附加服务费用表数据,以及C表对应费用审核数据
+                    //需要预检查数据库中是否存在附加服务数据,如果存在则更新,不存在则新增
+                    List<CustTicketInfo> custList = item.OriginalItem?.CustTicketInfos.ToList();
+
+                    foreach (var custItem in custList)
+                    {
+                        List<AdditionalService> addList = custItem.AdditionalServices;
+                        //检查数据库中对应的附加服务数据集合
+                        List<Grp_AirAdditionServices> dbAddList = await _sqlSugar.Queryable<Grp_AirAdditionServices>()
+                            .Where(x => x.ResId == dbAir.Id && x.ClientId == custItem.ClientId && x.TicketCode == custItem.TicketCode && x.IsDel == 0)
+                            .ToListAsync();
+
+                        //添加、修改附加服务数据,并统计执行操作的附加服务数据集合与dbAddList比较,检查是否有多余的附加服务数据需要删除
+                        foreach (var addItem in addList)
+                        {
+                            // 先检查数据库中是否存在该附加服务数据
+                            var existingAddService = await _sqlSugar.Queryable<Grp_AirAdditionServices>()
+                                .Where(x => x.ResId == dbAir.Id && x.SysTypeId == addItem.ServiceTypeId && x.ClientId == custItem.ClientId && x.TicketCode == custItem.TicketCode && x.IsDel == 0)
+                                .FirstAsync();
+                            //如果存在,则检查数据库中对应审核状态是否为已审核,如果是已审核,则不允许修改
+                            if (existingAddService != null)
+                            {
+                                var checkCard = await _sqlSugar.Queryable<Grp_CreditCardPayment>()
+                                    .Where(x => x.CId == existingAddService.Id && x.CTable == addItem.ServiceTypeId && x.IsDel == 0)
+                                    .FirstAsync();
+                                if (checkCard != null && (checkCard.IsAuditGM == 1 || checkCard.IsAuditGM == 3))
+                                {
+                                    skipMessages.Add($"航段【{item.FlightsDescription}】附加服务【{addItem.ServiceTypeId}】({custItem.ClientId}-{custItem.TicketCode})已审核,无法修改");
+                                    continue;
+                                }
+                                else
+                                {
+                                    //如果未审核,则更新附加服务数据
+                                    existingAddService.Amount = addItem.Amount;
+                                    existingAddService.PriceDescription = addItem.PriceDescription;
+                                    await _sqlSugar.Updateable(existingAddService).ExecuteCommandAsync();
+
+                                    //更新对应的信用卡付款表数据
+                                    checkCard.UpdateDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+                                    checkCard.PayMoney = addItem.Amount;
+                                    checkCard.PayThenMoney = addItem.Amount;
+                                    await _sqlSugar.Updateable(checkCard).ExecuteCommandAsync();
+                                }
+                            }
+                            else
+                            {
+                                //添加附加服务数据
+                                var addEntity = _mapper.Map<Grp_AirAdditionServices>(addItem);
+                                addEntity.ResId = dbAir.Id;
+                                addEntity.SysTypeId = addItem.ServiceTypeId;
+                                addEntity.ClientId = custItem.ClientId;
+                                addEntity.TicketCode = custItem.TicketCode;
+                                addEntity.Amount = addItem.Amount;
+                                addEntity.CreateUserId = currUserId;
+                                addEntity.CreateTime = DateTime.Now;
+                                addEntity.PriceDescription = addItem.PriceDescription;
+
+                                var additionId = await _sqlSugar.Insertable(addEntity).ExecuteReturnIdentityAsync(); //Grp_AdditionServices.Id
+
+                                var cardEntity_addi = _mapper.Map<Grp_CreditCardPayment>(item.OriginalItem);
+                                cardEntity_addi.DIId = groupId;
+                                cardEntity_addi.CId = additionId;
+                                cardEntity_addi.CTable = addItem.ServiceTypeId;
+                                cardEntity_addi.IsDel = 0;
+                                cardEntity_addi.CreateUserId = currUserId;
+                                cardEntity_addi.CreateTime = DateTime.Now;
+                                cardEntity_addi.UpdateDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+                                cardEntity_addi.PayMoney = addItem.Amount;
+                                cardEntity_addi.PayThenMoney = addItem.Amount;
+                                cardEntity_addi.IsAuditGM = 0;
+
+                                var cardId_addi = await _sqlSugar.Insertable(cardEntity_addi).ExecuteReturnIdentityAsync();
+                            }
+                        }
+
+                        //检查是否有多余的附加服务数据需要删除
+                        foreach (var dbAddItem in dbAddList)
+                        {
+                            var existsInFrontend = addList.Any(x => x.ServiceTypeId == dbAddItem.SysTypeId);
+                            if (!existsInFrontend)
+                            {
+                                //检查数据库中对应审核状态是否为已审核,如果是已审核,则不允许删除
+                                var checkCard = await _sqlSugar.Queryable<Grp_CreditCardPayment>()
+                                    .Where(x => x.CId == dbAddItem.Id && x.CTable == dbAddItem.SysTypeId && x.IsDel == 0)
+                                    .FirstAsync();
+                                if (checkCard != null && (checkCard.IsAuditGM == 1 || checkCard.IsAuditGM == 3))
+                                {
+                                    skipMessages.Add($"航段【{item.FlightsDescription}】附加服务【{dbAddItem.SysTypeId}】({dbAddItem.ClientId}-{dbAddItem.TicketCode})已审核,无法删除");
+                                    continue;
+                                }
+                                else
+                                {
+                                    //如果未审核,则删除附加服务数据
+                                    await _sqlSugar.Updateable<Grp_AirAdditionServices>()
+                                        .SetColumns(x => new Grp_AirAdditionServices { IsDel = 1, DeleteUserId = currUserId, DeleteTime = DateTime.Now.ToString() })
+                                        .Where(x => x.Id == dbAddItem.Id)
+                                        .ExecuteCommandAsync();
+                                    //删除对应的信用卡付款表数据
+                                    await _sqlSugar.Updateable<Grp_CreditCardPayment>()
+                                        .SetColumns(x => new Grp_CreditCardPayment { IsDel = 1, DeleteUserId = currUserId, DeleteTime = DateTime.Now.ToString() })
+                                        .Where(x => x.CId == dbAddItem.Id && x.CTable == dbAddItem.SysTypeId)
+                                        .ExecuteCommandAsync();
+                                }
+                            }
+                        }
+                    }
+
+
                     var dbCard = dbCardList.FirstOrDefault(x => x.CId == dbAir.Id);
 
                     if (!CanModify(dbCard))
                     {
                         var reason = dbCard?.IsPay == 1 ? "已付款" : dbCard?.IsAuditGM == 1 ? "已审核" : dbCard?.IsAuditGM == 3 ? "自动审核" : "禁止修改";
                         string cabinTypeName = "未设舱位";
-                        if (item.CType > 0) cabinTypeName = cabinTypeList.FirstOrDefault(x => x.Id == item.CType)?.Name ?? $"舱位({item.CType})";
+                        if (item.OriginalItem?.CType > 0) cabinTypeName = cabinTypeList.FirstOrDefault(x => x.Id == item.OriginalItem.CType)?.Name ?? $"舱位({item.OriginalItem.CType})";
 
                         var shortFlight = item.FlightsDescription.Replace("\n", "").Replace("\r", "").Trim();
                         shortFlight = shortFlight.Length > 22 ? shortFlight.Substring(0, 22) + "..." : shortFlight;
@@ -1827,21 +2065,23 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                         continue;
                     }
 
-                    var frontendClients = item.CustTicketInfos ?? new List<CustTicketInfo>();
+                    var frontendClients = item.OriginalItem?.CustTicketInfos ?? new List<CustTicketInfo>();
+                    var submitCardInfo = item.OriginalItem.TicketCardInfos.Where(x => x.TicketNumber == item.TicketNumber).FirstOrDefault();
                     dbAir.FlightsDescription = item.FlightsDescription;
-                    dbAir.CType = item.CType;
+                    dbAir.CType = item.OriginalItem?.CType ?? 0;
                     dbAir.CustTicketInfos = frontendClients;
                     dbAir.ClientNum = frontendClients.Select(x => x.ClientId).Distinct().Count();
                     dbAir.ClientName = string.Join(",", frontendClients.Select(x => x.ClientId).Distinct());
                     dbAir.Price = currentSegmentPrice;
-                    dbAir.PriceDescription = item.PriceDescription;
+                    dbAir.Currency = item.OriginalItem?.PaymentCurrency ?? 0;
+                    dbAir.PriceDescription = submitCardInfo?.PriceDescription ?? string.Empty;
                     dbAir.RecordType = item.RecordType;
 
-                    dbAir.FlightsCode = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsCode));
-                    dbAir.FlightsCity = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsCity));
-                    dbAir.FlightsDate = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsDate));
-                    dbAir.FlightsTime = string.Join("/", item.AirTicketBasicInfos.Select(x => x.FlightsTime));
-                    dbAir.ArrivedTime = string.Join("/", item.AirTicketBasicInfos.Select(x => x.ArrivedTime));
+                    dbAir.FlightsCode = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsCode));
+                    dbAir.FlightsCity = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsCity));
+                    dbAir.FlightsDate = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsDate));
+                    dbAir.FlightsTime = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.FlightsTime));
+                    dbAir.ArrivedTime = string.Join("/", item.OriginalItem?.AirTicketBasicInfos.Select(x => x.ArrivedTime));
 
 
                     await _sqlSugar.Updateable(dbAir).ExecuteCommandAsync();
@@ -1851,21 +2091,21 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                         dbCard.PayMoney = dbAir.Price;
                         dbCard.PayThenMoney = dbCard.PayMoney;
                         dbCard.UpdateDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-                        decimal effectiveRate = item.DayRate > 0M ? item.DayRate : 1M;
+                        decimal effectiveRate = item.OriginalItem?.DayRate > 0M ? item.OriginalItem.DayRate : 1M;
                         dbCard.RMBPrice = dbAir.Price * effectiveRate;
 
-                        dbCard.PayDId = item.PayDId;
-                        dbCard.ConsumptionPatterns = item.ConsumptionPatterns;
-                        dbCard.ConsumptionDate = item.ConsumptionDate;
-                        dbCard.CTDId = item.CTDId;
-                        dbCard.BankNo = item.BankNo;
-                        dbCard.CardholderName = item.CardholderName;
-                        dbCard.CompanyBankNo = item.CompanyBankNo;
-                        dbCard.OtherBankName = item.OtherBankName;
-                        dbCard.OtherSideNo = item.OtherSideNo;
-                        dbCard.Payee = item.Payee;
-                        dbCard.OrbitalPrivateTransfer = item.OrbitalPrivateTransfer;
-                        dbCard.Remark = item.Remark;
+                        dbCard.PayDId = submitCardInfo?.PayDId ?? 0;
+                        dbCard.ConsumptionPatterns = submitCardInfo?.ConsumptionPatterns;
+                        dbCard.ConsumptionDate = submitCardInfo?.ConsumptionDate;
+                        dbCard.CTDId = submitCardInfo?.CTDId ?? 0;
+                        dbCard.BankNo = submitCardInfo?.BankNo;
+                        dbCard.CardholderName = submitCardInfo?.CardholderName;
+                        dbCard.CompanyBankNo = submitCardInfo?.CompanyBankNo;
+                        dbCard.OtherBankName = submitCardInfo?.OtherBankName;
+                        dbCard.OtherSideNo = submitCardInfo?.OtherSideNo;
+                        dbCard.Payee = submitCardInfo?.Payee;
+                        dbCard.OrbitalPrivateTransfer = submitCardInfo?.OrbitalPrivateTransfer ?? 0;
+                        dbCard.Remark = submitCardInfo?.Remark;
 
                         await _sqlSugar.Updateable(dbCard).ExecuteCommandAsync();
 
@@ -1874,6 +2114,8 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                             appPushBodyInfos.Add(new AppPushBodyInfo { OperationType = 2, AirTicketId = dbAir.Id, CardId = dbCard.Id, CardCNYAmount = dbCard.RMBPrice });
                         }
                     }
+
+                    
                 }
             }
 
@@ -1940,7 +2182,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
 
         var dbCabinTypes = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
             .Where(x => x.DIId == groupId && x.IsDel == 0)
-            .Select(x => new { x.FlightsDescription, x.CType })
+            .Select(x => new { x.FlightsDescription, x.CType })  //这里需要修改成tickerNumber
             .ToListAsync();
 
         foreach (var air in airInfos)
@@ -1965,7 +2207,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
             {
                 var cabinCustomers = air.CustTicketInfos?
                     .Where(x => x.CType == cabinType)
-                    .GroupBy(x => new { x.ClientId,x.CType })
+                    .GroupBy(x => new { x.ClientId, x.CType })
                     .Select(g => g.First())
                     .ToList() ?? new List<CustTicketInfo>();
 
@@ -2023,6 +2265,195 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
         return result;
     }
 
+    /// <summary>
+    /// 20260819,按机票编号整理,蒋金辰
+    /// </summary>
+    private async Task<List<AirTicketFeeOpInfo>> OrganizeAirTicketDataAsyncByTicketNum(List<AirTicketFeeInfo> airInfos, int groupId)
+    {
+        var result = new List<AirTicketFeeOpInfo>();
+        if (airInfos == null || !airInfos.Any()) return result;
+        // ticketNumList 原: dbCabinTypes
+        var reservations = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
+                        .Where(x => x.DIId == groupId && x.IsDel == 0)
+                        .ToListAsync();
+
+        var ticketNumList = new List<dynamic>();
+
+        foreach (var res in reservations)
+        {
+            foreach (var ticket in res.CustTicketInfos)
+            {
+                ticketNumList.Add(new
+                {
+                    FlightsDescription = res.FlightsDescription,
+                    TicketNumber = ticket.TicketNumber
+                });
+            }
+        }
+
+        foreach (var air in airInfos)
+        {
+
+            var currTicketNum = ticketNumList
+                .Where(x => x.FlightsDescription == air.FlightsDescription)
+                .Select(x => x.TicketNumber)
+                .Distinct()
+                .ToList();
+
+            var submitTicketNums = air.CustTicketInfos?.Select(x => x.TicketNumber).Distinct().ToList() ?? new List<string>();
+
+            if (!submitTicketNums.Any())
+            {
+                submitTicketNums.Add("0");
+            }
+
+            var ticketNums = currTicketNum.Union(submitTicketNums).Distinct().ToList();
+
+            foreach (var ticketNum in ticketNums)
+            {
+                var ticketCustomers = air.CustTicketInfos?
+                    .Where(x => x.TicketNumber == ticketNum)
+                    .GroupBy(x => new { x.ClientId, x.TicketNumber })
+                    .Select(g => g.First())
+                    .ToList() ?? new List<CustTicketInfo>();
+
+                if (ticketCustomers.Count < 1) { continue; }
+
+                // 1. 【正常票过滤】:非退票,或者虽然标记了 IsRefund 但 RefundRecord 类根本不存在的归入正常票
+                var normalCustomers = ticketCustomers;
+                if (currTicketNum.Contains(ticketNum) || normalCustomers.Any() || (air.CustTicketInfos == null || !air.CustTicketInfos.Any()))
+                {
+
+                    //需要拆分附加服务费用
+                    //var normalPrice = normalCustomers.Sum(x => (x?.ActualPrice ?? 0M) + (x.AdditionalServices?.Where(y => y != null).Sum(y => y?.Amount ?? 0M) ?? 0M));
+                    var normalPrice = normalCustomers.Sum(x => (x?.ActualPrice ?? 0M));
+                    var clientIds = normalCustomers.Select(x => x.ClientId).ToList();
+                    var airInfo = _mapper.Map<AirTicketFeeOpInfo>(air);
+                    airInfo.DIId = groupId;
+                    airInfo.CType = ticketCustomers.FirstOrDefault()?.CType ?? 0;
+                    airInfo.RecordType = 0;
+                    airInfo.ClientNum = normalCustomers.Count;
+                    airInfo.ClientName = string.Join(",", clientIds);
+                    airInfo.CustTicketInfos = normalCustomers;
+                    airInfo.ClientNameIds = clientIds;
+                    airInfo.Price = normalPrice;
+                    airInfo.PayMoney = normalPrice;
+                    result.Add(airInfo);
+                }
+
+                // 2. 【退票信息拆分】:严格验证 IsRefund 为 true 且 RefundRecord 类非空
+                var refundGroups = ticketCustomers
+                    .Where(x => x.IsRefund && x.RefundRecord != null)
+                    .GroupBy(x => x.RefundRecord.RefundAccount)
+                    .ToList();
+
+                foreach (var refundGroup in refundGroups)
+                {
+                    var refundCustomers = refundGroup.ToList();
+                    var refundAmount = refundCustomers.Sum(x => (x.RefundRecord?.RefundAmount ?? 0M) + (x.RefundRecord?.NonRefundableTax ?? 0M));
+                    var clientIds = refundCustomers.Select(x => x.ClientId).ToList();
+
+                    var airInfo = _mapper.Map<AirTicketFeeOpInfo>(air);
+                    var cabinType = refundCustomers.FirstOrDefault()?.CType ?? 0; // 获取舱位类型,如果没有则默认为 0
+                    airInfo.DIId = groupId;
+                    airInfo.CType = cabinType;
+                    airInfo.RecordType = 1;
+                    airInfo.CTDId = refundGroup.Key;
+                    airInfo.ClientNum = clientIds.Count;
+                    airInfo.ClientName = string.Join(",", clientIds);
+                    airInfo.CustTicketInfos = refundCustomers;
+                    airInfo.ClientNameIds = clientIds;
+                    airInfo.Price = refundAmount;
+                    airInfo.PayMoney = refundAmount;
+                    airInfo.AuditGMOperate = 4; // 退票信息默认自动审核,前端不允许修改;
+                    airInfo.IsAuditGM = 3;      // 退票信息默认自动审核,前端不允许修改;
+                    airInfo.AuditGMDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+                    result.Add(airInfo);
+                }
+            }
+        }
+
+        //foreach (var air in airInfos)
+        //{
+        //    var currDbCabinTypes = dbCabinTypes
+        //        .Where(x => x.FlightsDescription == air.FlightsDescription)
+        //        .Select(x => x.CType)
+        //        .Distinct()
+        //        .ToList();
+
+        //    var submitCabinTypes = air.CustTicketInfos?.Select(x => x.CType).Distinct().ToList() ?? new List<int>();
+
+        //    // 放行空客池占位特征
+        //    if (!submitCabinTypes.Any())
+        //    {
+        //        submitCabinTypes.Add(0);
+        //    }
+
+        //    var cabinTypes = currDbCabinTypes.Union(submitCabinTypes).Distinct().ToList();
+
+        //    foreach (var cabinType in cabinTypes)
+        //    {
+        //        var cabinCustomers = air.CustTicketInfos?
+        //            .Where(x => x.CType == cabinType)
+        //            .GroupBy(x => new { x.ClientId, x.CType })
+        //            .Select(g => g.First())
+        //            .ToList() ?? new List<CustTicketInfo>();
+
+        //        // 1. 【正常票过滤】:非退票,或者虽然标记了 IsRefund 但 RefundRecord 类根本不存在的归入正常票
+        //        var normalCustomers = cabinCustomers;
+
+        //        if (currDbCabinTypes.Contains(cabinType) || normalCustomers.Any() || (air.CustTicketInfos == null || !air.CustTicketInfos.Any()))
+        //        {
+        //            var normalPrice = normalCustomers.Sum(x => (x?.ActualPrice ?? 0M) + (x.AdditionalServices?.Where(y => y != null).Sum(y => y?.Amount ?? 0M) ?? 0M));
+        //            var clientIds = normalCustomers.Select(x => x.ClientId).ToList();
+        //            var airInfo = _mapper.Map<AirTicketFeeOpInfo>(air);
+        //            airInfo.DIId = groupId;
+        //            airInfo.CType = cabinType;
+        //            airInfo.RecordType = 0;
+        //            airInfo.ClientNum = normalCustomers.Count;
+        //            airInfo.ClientName = string.Join(",", clientIds);
+        //            airInfo.CustTicketInfos = normalCustomers;
+        //            airInfo.ClientNameIds = clientIds;
+        //            airInfo.Price = normalPrice;
+        //            airInfo.PayMoney = normalPrice;
+        //            result.Add(airInfo);
+        //        }
+
+        //        // 2. 【退票信息拆分】:严格验证 IsRefund 为 true 且 RefundRecord 类非空
+        //        var refundGroups = cabinCustomers
+        //            .Where(x => x.IsRefund && x.RefundRecord != null)
+        //            .GroupBy(x => x.RefundRecord.RefundAccount)
+        //            .ToList();
+
+        //        foreach (var refundGroup in refundGroups)
+        //        {
+        //            var refundCustomers = refundGroup.ToList();
+        //            var refundAmount = refundCustomers.Sum(x => (x.RefundRecord?.RefundAmount ?? 0M) + (x.RefundRecord?.NonRefundableTax ?? 0M));
+        //            var clientIds = refundCustomers.Select(x => x.ClientId).ToList();
+
+        //            var airInfo = _mapper.Map<AirTicketFeeOpInfo>(air);
+        //            airInfo.DIId = groupId;
+        //            airInfo.CType = cabinType;
+        //            airInfo.RecordType = 1;
+        //            airInfo.CTDId = refundGroup.Key;
+        //            airInfo.ClientNum = clientIds.Count;
+        //            airInfo.ClientName = string.Join(",", clientIds);
+        //            airInfo.CustTicketInfos = refundCustomers;
+        //            airInfo.ClientNameIds = clientIds;
+        //            airInfo.Price = refundAmount;
+        //            airInfo.PayMoney = refundAmount;
+        //            airInfo.AuditGMOperate = 4; // 退票信息默认自动审核,前端不允许修改;
+        //            airInfo.IsAuditGM = 3;      // 退票信息默认自动审核,前端不允许修改;
+        //            airInfo.AuditGMDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+        //            result.Add(airInfo);
+        //        }
+        //    }
+        //}
+
+        return result;
+    }
+
+
     /// <summary>
     /// 判断费用记录是否允许修改
     /// </summary>
@@ -2317,7 +2748,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
             if (targetNormalClient != null)
             {
                 targetNormalClient.IsRefund = false;
-                targetNormalClient.RefundRecord = null; 
+                targetNormalClient.RefundRecord = null;
             }
             else
             {
@@ -2587,7 +3018,7 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
             var clientDatas = await _sqlSugar.Queryable<Grp_TourClientList>()
                 .LeftJoin<Crm_DeleClient>((x, y) => x.ClientId == y.Id)
                 .Where((x, y) => x.IsDel == 0 && x.DiId == dto.DiId)
-                .Select((x, y) => new { y.Id, y.FirstName, y.LastName }) 
+                .Select((x, y) => new { y.Id, y.FirstName, y.LastName })
                 .ToListAsync();
 
             var clientNameList = clientDatas.Select(x =>
@@ -2674,8 +3105,8 @@ public class AirTicketResRepository : BaseRepository<Grp_AirTicketReservations,
                 //// 金额单价防呆计算
                 //if (item.RecordType == 0)
                 //{
-                    // 防止历史脏数据中乘机人数为 0 导致除以零系统崩溃
-                    item.PrePrice = item.ClientNum > 0 ? (item.Price / item.ClientNum) : item.Price;
+                // 防止历史脏数据中乘机人数为 0 导致除以零系统崩溃
+                item.PrePrice = item.ClientNum > 0 ? (item.Price / item.ClientNum) : item.Price;
                 //}
             }