diff --git a/SGGL/BLL/CLGL/TwInOutplandetailRelationService.cs b/SGGL/BLL/CLGL/TwInOutplandetailRelationService.cs index 7ce7ca5e..5f05d8c3 100644 --- a/SGGL/BLL/CLGL/TwInOutplandetailRelationService.cs +++ b/SGGL/BLL/CLGL/TwInOutplandetailRelationService.cs @@ -102,6 +102,7 @@ namespace BLL MaterialCode = newtable.MaterialCode, PrefabricatedComponents = newtable.PrefabricatedComponents, Number = newtable.Number, + // 领料出库由焊接任务单焊口驱动,关联明细需要保存焊口ID用于后续追溯。 WeldJointId = newtable.WeldJointId, }; Funs.DB.Tw_InOutPlanDetail_Relation.InsertOnSubmit(table); @@ -134,6 +135,7 @@ namespace BLL table.MaterialCode = newtable.MaterialCode; table.PrefabricatedComponents = newtable.PrefabricatedComponents; table.Number = newtable.Number; + // 更新关联明细时同步维护焊口ID,避免出库申请修改后丢失焊口归属。 table.WeldJointId = newtable.WeldJointId; Funs.DB.SubmitChanges(); } diff --git a/SGGL/BLL/CLGL/TwInOutplanmasterService.cs b/SGGL/BLL/CLGL/TwInOutplanmasterService.cs index 539252f2..0a610bb6 100644 --- a/SGGL/BLL/CLGL/TwInOutplanmasterService.cs +++ b/SGGL/BLL/CLGL/TwInOutplanmasterService.cs @@ -992,7 +992,8 @@ namespace BLL .Select(e => e.WeldJointId) .Distinct() .ToList(); - //领料出库需要排除散件材料 + // 领料出库按当前任务单焊口取料,避免同一管线其他未下任务单焊口的材料被带出。 + // 这里只取已反写 MaterialCode 的材料;未匹配主编码的材料无法生成库存出库明细。 var allMaterDatial = (from x in db.HJGL_PipeLineMat join y in db.HJGL_MaterialCodeLib on x.MaterialCode equals y.MaterialCode where pipelineList.Contains(x.PipelineId) @@ -1063,6 +1064,7 @@ namespace BLL }; TwInOutplandetailService.Add(detail); } + // 管件申请单只保存单位为“个”的材料关联,并同步记录焊口ID,便于追溯出库材料归属。 var twinoutplandetailRelationList = MaterDatial.Where(x => x.MaterialUnit.Contains("个")).Select(x => new Tw_InOutPlanDetail_Relation { PipelineId = x.PipelineId, @@ -1103,6 +1105,7 @@ namespace BLL }; TwInOutplandetailService.Add(detail); } + // 管段申请单只保存单位为“米”的材料关联,并同步记录焊口ID,避免关联明细跨单据混写。 var twinoutplandetailRelationList = MaterDatial.Where(x => x.MaterialUnit.Contains("米")).Select(x => new Tw_InOutPlanDetail_Relation { PipelineId = x.PipelineId, diff --git a/SGGL/FineUIPro.Web/File/Excel/DataIn/焊接日报导入模板.xlsx b/SGGL/FineUIPro.Web/File/Excel/DataIn/焊接日报导入模板.xlsx new file mode 100644 index 00000000..580097e0 Binary files /dev/null and b/SGGL/FineUIPro.Web/File/Excel/DataIn/焊接日报导入模板.xlsx differ diff --git a/SGGL/FineUIPro.Web/FineUIPro.Web.csproj b/SGGL/FineUIPro.Web/FineUIPro.Web.csproj index dd0f9e5d..29e8444e 100644 --- a/SGGL/FineUIPro.Web/FineUIPro.Web.csproj +++ b/SGGL/FineUIPro.Web/FineUIPro.Web.csproj @@ -1699,6 +1699,7 @@ + @@ -11413,6 +11414,13 @@ WeldingPlanIn.aspx + + WeldReportIn.aspx + ASPXCodeBehind + + + WeldReportIn.aspx + WeldJointBatchEdit.aspx ASPXCodeBehind @@ -17182,7 +17190,7 @@ - + @@ -17208,4 +17216,4 @@ --> - \ No newline at end of file + diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldMatMatch.aspx.cs b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldMatMatch.aspx.cs index b188911d..a3b0c7c2 100644 --- a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldMatMatch.aspx.cs +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldMatMatch.aspx.cs @@ -405,6 +405,10 @@ namespace FineUIPro.Web.HJGL.WeldingManage Grid1.DataSource = dt; Grid1.DataBind(); } + /// + /// 绑定管线匹配汇总。重新匹配会刷新 Grid2,需要把用户原来勾选的管线恢复回来。 + /// + /// 需要保留选中状态的管线ID void BindGrid2(IEnumerable keepSelectedPipelineIds = null) { //var result = tw_PipeMatMatchOutputs @@ -435,6 +439,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage Rate = Math.Round((decimal)output.Average(item => item.MatchRate) * 100, 2); lbRate.Text = "匹配率:" + Rate.ToString() + "%"; }*/ + // “优先匹配选中焊口”会重新计算匹配结果,重新绑定后要继续保留原管线选中状态。 var keepSelectedList = keepSelectedPipelineIds == null ? new List() : keepSelectedPipelineIds.Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList(); var selectList = new List(); for (int i = 0; i < Grid2.Rows.Count; i++) @@ -503,6 +508,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage private void BindMaterialDetail(string pipelineId) { + // 任务单只允许从未生成任务单的焊口材料中选择,避免重复生成焊接任务。 var tb = GetNoTaskMaterialDetails() .Where(x => x.PipelineId == pipelineId) .ToList(); @@ -532,6 +538,9 @@ namespace FineUIPro.Web.HJGL.WeldingManage Grid1.SelectedRowIDArray = selectList.ToArray(); } + /// + /// 获取还没有生成焊接任务单的材料匹配明细。 + /// private List GetNoTaskMaterialDetails() { var matchOutputs = tw_PipeMatMatchOutputs ?? new List(); @@ -545,6 +554,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage return matchOutputs; } + // HJGL_WeldTask 已存在的焊口不再显示到待生成任务单的材料明细里。 var taskWeldJointIds = Funs.DB.HJGL_WeldTask .Where(x => weldJointIds.Contains(x.WeldJointId)) .Select(x => x.WeldJointId) @@ -572,6 +582,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage private void RefreshMatchResult() { + // 重新匹配库存会刷新管线汇总表,先暂存选择状态,绑定完成后再恢复。 var keepSelectedPipelineIds = Grid2.SelectedRowIDArray == null ? new List() : Grid2.SelectedRowIDArray.ToList(); tw_PipeMatMatchOutputs = TwArrivalStatisticsService.GetPipeMatMatch( this.CurrUser.LoginProjectId, @@ -756,6 +767,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage return; } + // 页面上的焊口选择来自 Grid1 材料明细;Grid3 是左侧已加入匹配的管线列表。 var selectedWeldJoints = new HashSet(); foreach (int rowIndex in Grid1.SelectedRowIndexArray) { @@ -781,6 +793,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage priorityWeldJoints.Remove(selectedPipelineId); } + // 优先焊口会触发重新匹配,当前管线也必须继续保持选中,方便后续生成任务单。 var keepSelectedPipelineIds = Grid2.SelectedRowIDArray == null ? new List() : Grid2.SelectedRowIDArray.ToList(); if (!keepSelectedPipelineIds.Contains(selectedPipelineId)) { @@ -865,6 +878,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage var weldingRods = from x in Funs.DB.Base_Consumables where x.ConsumablesType == "2" select x; var weldingWires = from x in Funs.DB.Base_Consumables where x.ConsumablesType == "1" select x; var selectedWeldJointIdList = selectedWeldJointIds.ToList(); + // 任务单按用户在材料匹配明细中勾选的焊口生成,不再按整条管线生成。 var selectRowId = (from x in Funs.DB.View_HJGL_NoWeldJointFind where selectedWeldJointIdList.Contains(x.WeldJointId) && x.WeldingDailyId == null && x.WeldTaskId == null && x.WeldingMethodCode != null @@ -873,6 +887,7 @@ namespace FineUIPro.Web.HJGL.WeldingManage Dictionary unitworkTaskCode = new Dictionary(); Dictionary unitworkSerialNumber = new Dictionary(); Dictionary matchPipeline = new Dictionary(); + // 生成任务单仍保留管线在匹配结果中的排序,用于任务单列表按管线顺序展示。 for (int rowIndex = 0; rowIndex < Grid2.Rows.Count; rowIndex++) { matchPipeline.Add(rowIndex + 1, Grid2.Rows[rowIndex].RowID); @@ -996,6 +1011,9 @@ namespace FineUIPro.Web.HJGL.WeldingManage return true; } + /// + /// 从材料匹配明细 Grid1 中提取用户选中的焊口ID。 + /// private HashSet GetSelectedWeldJointIdsFromGrid1() { var selectedWeldJointIds = new HashSet(); diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx index 7653e2cb..fac609da 100644 --- a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx @@ -298,6 +298,9 @@ + + diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.cs b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.cs index 4f99ed04..269c9052 100644 --- a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.cs +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.cs @@ -481,6 +481,21 @@ namespace FineUIPro.Web.HJGL.WeldingManage } } + /// + /// 焊接日报导入 + /// + protected void btnMenuImport_Click(object sender, EventArgs e) + { + if (CommonService.GetAllButtonPowerList(this.CurrUser.LoginProjectId, this.CurrUser.PersonId, Const.HJGL_WeldReportMenuId, Const.BtnAdd)) + { + PageContext.RegisterStartupScript(Window1.GetShowReference("WeldReportIn.aspx", "导入焊接日报")); + } + else + { + ShowNotify("您没有这个权限,请与管理员联系!", MessageBoxIcon.Warning); + } + } + /// /// 删除按钮 /// diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.designer.cs index ba616267..d8bee4a9 100644 --- a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.designer.cs +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReport.aspx.designer.cs @@ -380,6 +380,15 @@ namespace FineUIPro.Web.HJGL.WeldingManage { /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 /// protected global::FineUIPro.MenuButton btnMenuEdit; + + /// + /// btnMenuImport 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.MenuButton btnMenuImport; /// /// btnMenuOut 控件。 diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx new file mode 100644 index 00000000..45cad033 --- /dev/null +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx @@ -0,0 +1,93 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WeldReportIn.aspx.cs" Inherits="FineUIPro.Web.HJGL.WeldingManage.WeldReportIn" %> + + + + + + + 焊接日报导入 + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx.cs b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx.cs new file mode 100644 index 00000000..feecd54f --- /dev/null +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx.cs @@ -0,0 +1,745 @@ +using MiniExcelLibs; +using BLL; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; + +namespace FineUIPro.Web.HJGL.WeldingManage +{ + public partial class WeldReportIn : PageBase + { + private const string TemplateRelativePath = @"File\Excel\DataIn\焊接日报导入模板.xlsx"; + + // 基础日报字段始终必填,点口相关字段按是否点口单独校验。 + private static readonly string[] RequiredColumns = + { + "管线号", "焊口号", "盖面焊工", "打底焊工", "焊接日期", "焊接位置", "是否点口" + }; + + // 只有是否点口为 是/1/true 时,才要求委托信息完整;评定级别允许为空,填写时再校验格式。 + private static readonly string[] PointRequiredColumns = + { + "委托单号", "委托日期", "检测单位" + }; + + // 模板表头必须完整,避免用户使用旧模板或列名被误改后发生错列导入。 + private static readonly string[] RequiredHeaders = + { + "管线号", "焊口号", "盖面焊工", "打底焊工", "焊接日期", "焊接位置", "是否点口", "委托单号", "委托日期", "检测单位", + "检测单号", "检测日期", "报告日期", "检测总数", "合格数", "是否合格", "评定级别", "缺陷", "返修位置" + }; + + private string initPath = Const.ExcelUrl; + + protected void Page_Load(object sender, EventArgs e) + { + if (!IsPostBack) + { + hdFileName.Text = string.Empty; + BindGrid(new List()); + } + } + + protected void btnAudit_Click(object sender, EventArgs e) + { + try + { + string filePath = SaveUploadFile(); + if (string.IsNullOrEmpty(filePath)) + { + return; + } + + var rows = ReadAndValidate(filePath); + BindGrid(rows); + string errlog = JoinErrors(rows); + if (string.IsNullOrEmpty(errlog)) + { + ShowNotify("审核完成,请点击导入!", MessageBoxIcon.Success); + } + else + { + Alert.ShowInTop(errlog, MessageBoxIcon.Warning); + } + } + catch (Exception ex) + { + Alert.ShowInTop(ex.Message, MessageBoxIcon.Warning); + } + } + + protected void btnImport_Click(object sender, EventArgs e) + { + try + { + if (string.IsNullOrEmpty(hdFileName.Text.Trim())) + { + ShowNotify("请先审核要导入的文件!", MessageBoxIcon.Warning); + return; + } + + string filePath = Path.Combine(Server.MapPath("~/"), initPath, hdFileName.Text.Trim()); + var rows = ReadAndValidate(filePath); + BindGrid(rows); + string errlog = JoinErrors(rows); + if (!string.IsNullOrEmpty(errlog)) + { + Alert.ShowInTop("请先将错误数据修正,再重新导入提交!" + errlog, MessageBoxIcon.Warning); + return; + } + + foreach (ImportRow row in rows) + { + ImportOneRow(row); + } + + BLL.LogService.AddLog(this.CurrUser.PersonId, "导入焊接日报"); + ShowNotify("导入成功!", MessageBoxIcon.Success); + PageContext.RegisterStartupScript(ActiveWindow.GetHidePostBackReference()); + } + catch (Exception ex) + { + Alert.ShowInTop(ex.Message, MessageBoxIcon.Warning); + } + } + + protected void btnDownLoad_Click(object sender, EventArgs e) + { + string path = Funs.RootPath + TemplateRelativePath; + FileInfo info = new FileInfo(path); + if (!info.Exists) + { + Alert.ShowInTop("未找到导入模板!", MessageBoxIcon.Warning); + return; + } + + string filename = "焊接日报导入模板.xlsx"; + long fileSize = info.Length; + System.Web.HttpContext.Current.Response.Clear(); + System.Web.HttpContext.Current.Response.ContentType = "application/x-zip-compressed"; + System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + System.Web.HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8)); + System.Web.HttpContext.Current.Response.AddHeader("Content-Length", fileSize.ToString()); + System.Web.HttpContext.Current.Response.TransmitFile(path, 0, fileSize); + System.Web.HttpContext.Current.Response.Flush(); + System.Web.HttpContext.Current.Response.Close(); + } + + private string SaveUploadFile() + { + if (!fuAttachUrl.HasFile) + { + ShowNotify("请您选择Excel文件!", MessageBoxIcon.Warning); + return string.Empty; + } + + string extension = Path.GetExtension(fuAttachUrl.FileName).Trim().ToLower(); + if (extension != ".xlsx") + { + ShowNotify("只可以选择xlsx文件!", MessageBoxIcon.Warning); + return string.Empty; + } + + string rootPath = Server.MapPath("~/"); + string initFullPath = Path.Combine(rootPath, initPath); + if (!Directory.Exists(initFullPath)) + { + Directory.CreateDirectory(initFullPath); + } + + hdFileName.Text = BLL.Funs.GetNewFileName() + extension; + string filePath = Path.Combine(initFullPath, hdFileName.Text); + fuAttachUrl.PostedFile.SaveAs(filePath); + return filePath; + } + + private List ReadAndValidate(string filePath) + { + if (!File.Exists(filePath)) + { + throw new Exception("导入文件不存在,请重新上传!"); + } + + // 使用模板第一行作为表头读取;后续取值按列名匹配,不依赖 Excel 列顺序。 + DataTable table = MiniExcel.QueryAsDataTable(filePath, useHeaderRow: true); + ValidateHeaders(table); + + var rows = new List(); + for (int i = 0; i < table.Rows.Count; i++) + { + DataRow dataRow = table.Rows[i]; + if (IsEmptyRow(dataRow)) + { + continue; + } + + // RowIndex 保留 Excel 原始行号,便于把错误信息定位回用户文件。 + ImportRow row = new ImportRow + { + SerialNo = rows.Count + 1, + RowIndex = i + 2, + PipelineCode = GetCell(dataRow, "管线号"), + WeldJointCode = GetCell(dataRow, "焊口号"), + CoverWelderCode = GetCell(dataRow, "盖面焊工"), + BackingWelderCode = GetCell(dataRow, "打底焊工"), + WeldingDateText = GetCell(dataRow, "焊接日期"), + WeldingLocationCode = GetCell(dataRow, "焊接位置"), + IsPointText = GetCell(dataRow, "是否点口"), + TrustBatchCode = GetCell(dataRow, "委托单号"), + TrustDateText = GetCell(dataRow, "委托日期"), + NDEUnitName = GetCell(dataRow, "检测单位"), + NDECode = GetCell(dataRow, "检测单号"), + NDEDateText = GetCell(dataRow, "检测日期"), + ReportDateText = GetCell(dataRow, "报告日期"), + TotalFilmText = GetCell(dataRow, "检测总数"), + PassFilmText = GetCell(dataRow, "合格数"), + CheckResultText = GetCell(dataRow, "是否合格"), + JudgeGrade = GetCell(dataRow, "评定级别"), + DefectNames = GetCell(dataRow, "缺陷"), + RepairLocation = GetCell(dataRow, "返修位置") + }; + rows.Add(row); + } + + if (rows.Count == 0) + { + throw new Exception("导入数据为空!"); + } + + ValidateRows(rows); + return rows; + } + + private void ValidateHeaders(DataTable table) + { + foreach (string header in RequiredHeaders) + { + if (!table.Columns.Cast().Any(x => x.ColumnName.Trim() == header)) + { + throw new Exception("导入Excel格式错误,缺少列:" + header); + } + } + } + + private void ValidateRows(List rows) + { + var duplicateKeys = rows.GroupBy(x => x.PipelineCode + "|" + x.WeldJointCode) + .Where(x => !string.IsNullOrEmpty(x.Key.Trim('|')) && x.Count() > 1) + .SelectMany(x => x.Select(y => y.RowIndex)) + .ToList(); + + foreach (ImportRow row in rows) + { + // 先校验基础必填项,保证后续业务查询有最小输入条件。 + foreach (string col in RequiredColumns) + { + if (string.IsNullOrEmpty(GetValueByColumn(row, col))) + { + row.AddError(col + "为必填项"); + } + } + + if (duplicateKeys.Contains(row.RowIndex)) + { + row.AddError("文件内存在重复管线号和焊口号"); + } + + // 点口行才进入委托必填校验;非点口行允许只导入焊接日报数据。 + row.IsPoint = ParseBoolean(row.IsPointText, row); + if (row.IsPoint == true) + { + foreach (string col in PointRequiredColumns) + { + if (string.IsNullOrEmpty(GetValueByColumn(row, col))) + { + row.AddError(col + "为必填项"); + } + } + } + + row.WeldingDate = ParseDate(row.WeldingDateText, "焊接日期", row); + row.TrustDate = ParseDate(row.TrustDateText, "委托日期", row, row.IsPoint == true); + row.NDEDate = ParseDate(row.NDEDateText, "检测日期", row, false); + row.ReportDate = ParseDate(row.ReportDateText, "报告日期", row, false); + row.TotalFilm = ParseInt(row.TotalFilmText, "检测总数", row, false); + row.PassFilm = ParseInt(row.PassFilmText, "合格数", row, false); + row.CheckResult = ParseCheckResult(row.CheckResultText, row); + ValidateJudgeGrade(row); + + ValidateBusinessData(row); + } + } + + // 校验 Excel 中的业务引用是否能匹配系统现有基础数据。 + private void ValidateBusinessData(ImportRow row) + { + if (string.IsNullOrEmpty(row.PipelineCode) || string.IsNullOrEmpty(row.WeldJointCode)) + { + return; + } + + row.Joint = Funs.DB.View_HJGL_WeldJoint.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId + && x.PipelineCode == row.PipelineCode && x.WeldJointCode == row.WeldJointCode); + if (row.Joint == null) + { + row.AddError("管线号和焊口号对应的焊口不存在"); + return; + } + if (!string.IsNullOrEmpty(row.Joint.WeldingDailyId)) + { + row.AddError("焊口已生成焊接日报"); + } + + row.Pipeline = Funs.DB.HJGL_Pipeline.FirstOrDefault(x => x.PipelineId == row.Joint.PipelineId); + if (row.Pipeline == null) + { + row.AddError("焊口关联管线不存在"); + } + + row.CoverWelder = Funs.DB.SitePerson_Person.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId && x.WelderCode == row.CoverWelderCode); + if (row.CoverWelder == null) + { + row.AddError("盖面焊工不存在"); + } + + row.BackingWelder = Funs.DB.SitePerson_Person.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId && x.WelderCode == row.BackingWelderCode); + if (row.BackingWelder == null) + { + row.AddError("打底焊工不存在"); + } + + row.WeldingLocation = Funs.DB.Base_WeldingLocation.FirstOrDefault(x => x.WeldingLocationCode == row.WeldingLocationCode); + if (row.WeldingLocation == null) + { + row.AddError("焊接位置不存在"); + } + + // 检测单位只服务点口委托链路,非点口行不因检测单位为空或不存在报错。 + if (row.IsPoint == true && !string.IsNullOrEmpty(row.NDEUnitName)) + { + row.NDEUnit = (from x in Funs.DB.Base_Unit + join y in Funs.DB.Project_ProjectUnit on x.UnitId equals y.UnitId + where y.ProjectId == this.CurrUser.LoginProjectId + && y.UnitType == BLL.Const.ProjectUnitType_5 + && x.UnitName == row.NDEUnitName + select x).FirstOrDefault(); + if (row.NDEUnit == null) + { + row.AddError("检测单位不存在"); + } + } + + // 缺陷名称允许多个,用中英文逗号分隔,导入前转换为缺陷 ID 集合。 + if (!string.IsNullOrEmpty(row.DefectNames)) + { + string[] names = row.DefectNames.Split(new[] { ',', ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string name in names.Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x))) + { + var defect = Funs.DB.Base_Defect.FirstOrDefault(x => x.DefectName == name); + if (defect == null) + { + row.AddError("缺陷[" + name + "]不存在"); + } + else + { + row.DefectIds.Add(defect.DefectId.ToString()); + } + } + } + + // 已存在的委托单必须与当前焊口所在单位工程和检测单位一致。 + if (row.IsPoint == true && !string.IsNullOrEmpty(row.TrustBatchCode)) + { + var trust = Funs.DB.HJGL_Batch_BatchTrust.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId && x.TrustBatchCode == row.TrustBatchCode); + if (trust != null && row.Pipeline != null) + { + if (trust.UnitWorkId != row.Pipeline.UnitWorkId || trust.NDEUnit != row.NDEUnit?.UnitId) + { + row.AddError("委托单号已存在且单位工程或检测单位不一致"); + } + } + } + + // 检测单号可复用,但复用时检测单位不能冲突。 + if (row.IsPoint == true && !string.IsNullOrEmpty(row.NDECode)) + { + var nde = Funs.DB.HJGL_Batch_NDE.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId && x.NDECode == row.NDECode); + if (nde != null && row.NDEUnit != null && nde.NDEUnit != row.NDEUnit.UnitId) + { + row.AddError("检测单号已存在且检测单位不一致"); + } + } + } + + private void ImportOneRow(ImportRow row) + { + var daily = GetOrCreateWeldingDaily(row); + var weldJoint = Funs.DB.HJGL_WeldJoint.First(x => x.WeldJointId == row.Joint.WeldJointId); + // 导入直接生成正式日报,同时把焊工、焊接位置等结果回填到焊口。 + weldJoint.WeldingDailyId = daily.WeldingDailyId; + weldJoint.WeldingDailyCode = daily.WeldingDailyCode; + weldJoint.CoverWelderId = row.CoverWelder.PersonId; + weldJoint.BackingWelderId = row.BackingWelder.PersonId; + weldJoint.CoverWelderTeamGroupId = row.CoverWelder.TeamGroupId; + weldJoint.BackingWelderTeamGroupId = row.BackingWelder.TeamGroupId; + weldJoint.WeldingLocationId = row.WeldingLocation.WeldingLocationId; + Funs.DB.SubmitChanges(); + + BLL.WeldJointService.UpdateWeldJointAddG(weldJoint.WeldJointId, weldJoint.JointAttribute, Const.BtnAdd); + var batchC = BLL.Project_SysSetService.GetSysSetBySetId("5", this.CurrUser.LoginProjectId); + if (batchC == null) + { + throw new Exception("请设置项目的组批条件"); + } + + string batchErr = BLL.PointBatchService.AddBatchByWeldJointId(weldJoint.WeldJointId, row.WeldingDate, batchC.SetValue); + if (!string.IsNullOrEmpty(batchErr)) + { + throw new Exception(batchErr); + } + BLL.HJGL_PipelineComponentjointService.UpdateStateByWeldJointId(weldJoint.WeldJointId, row.WeldingDate.Value); + BLL.PipelineService.UpdataDateByWeldJointId(weldJoint.WeldJointId); + + // 点口行继续生成或复用委托、检测链路;非点口行到日报回填即结束。 + if (row.IsPoint == true) + { + ImportPointTrustAndNDE(row, weldJoint); + } + } + + private Model.HJGL_WeldingDaily GetOrCreateWeldingDaily(ImportRow row) + { + var daily = Funs.DB.HJGL_WeldingDaily.FirstOrDefault(x => x.UnitWorkId == row.Joint.UnitWorkId + && x.WeldingDate >= row.WeldingDate.Value.Date + && x.WeldingDate < row.WeldingDate.Value.Date.AddDays(1)); + if (daily != null) + { + return daily; + } + + string personName = BLL.Person_PersonsService.GetPersonsNameById(this.CurrUser.PersonId); + string perfix = string.Format("{0:yyyyMMdd}", row.WeldingDate.Value) + "-" + personName + "-"; + daily = new Model.HJGL_WeldingDaily + { + WeldingDailyId = Guid.NewGuid().ToString(), + WeldingDailyCode = BLL.SQLHelper.RunProcNewId("SpGetThreeNumber", "dbo.HJGL_WeldingDaily", "WeldingDailyCode", this.CurrUser.LoginProjectId, perfix), + WeldingDate = row.WeldingDate.Value.Date, + ProjectId = this.CurrUser.LoginProjectId, + UnitWorkId = row.Joint.UnitWorkId, + UnitId = row.Joint.UnitId, + Tabler = this.CurrUser.PersonId, + TableDate = DateTime.Now.Date, + Remark = "焊接日报导入" + }; + BLL.WeldingDailyService.AddWeldingDaily(daily); + return daily; + } + + private void ImportPointTrustAndNDE(ImportRow row, Model.HJGL_WeldJoint weldJoint) + { + // 组批服务已按焊口生成点口批明细,这里只负责把点口状态推进到已点口。 + var pointItem = Funs.DB.HJGL_Batch_PointBatchItem.FirstOrDefault(x => x.WeldJointId == weldJoint.WeldJointId); + if (pointItem == null) + { + throw new Exception("焊口【" + row.WeldJointCode + "】未生成点口批明细"); + } + + pointItem.PointState = "1"; + pointItem.PointDate = DateTime.Now; + Funs.DB.SubmitChanges(); + + var pointBatch = Funs.DB.HJGL_Batch_PointBatch.First(x => x.PointBatchId == pointItem.PointBatchId); + var trust = GetOrCreateTrust(row, pointBatch); + var trustItem = Funs.DB.HJGL_Batch_BatchTrustItem.FirstOrDefault(x => x.TrustBatchId == trust.TrustBatchId && x.WeldJointId == weldJoint.WeldJointId); + // 同一委托单下同一焊口只保留一条委托明细,重复导入前已由日报校验拦截。 + if (trustItem == null) + { + trustItem = new Model.HJGL_Batch_BatchTrustItem + { + TrustBatchItemId = SQLHelper.GetNewID(typeof(Model.HJGL_Batch_BatchTrustItem)), + TrustBatchId = trust.TrustBatchId, + PointBatchItemId = pointItem.PointBatchItemId, + WeldJointId = weldJoint.WeldJointId, + CreateDate = DateTime.Now + }; + Funs.DB.HJGL_Batch_BatchTrustItem.InsertOnSubmit(trustItem); + } + + pointItem.IsBuildTrust = true; + if (string.IsNullOrEmpty(trust.TopointBatch)) + { + trust.TopointBatch = pointBatch.PointBatchId; + } + else if (!trust.TopointBatch.Split(',').Contains(pointBatch.PointBatchId)) + { + trust.TopointBatch += "," + pointBatch.PointBatchId; + } + Funs.DB.SubmitChanges(); + + // 检测单号为空时只生成点口和委托信息,不创建检测结果。 + if (!string.IsNullOrEmpty(row.NDECode)) + { + ImportNDE(row, trust, trustItem); + } + } + + private Model.HJGL_Batch_BatchTrust GetOrCreateTrust(ImportRow row, Model.HJGL_Batch_PointBatch pointBatch) + { + // 同项目内委托单号唯一,模板中相同委托单号应复用已有委托主表。 + var trust = Funs.DB.HJGL_Batch_BatchTrust.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId && x.TrustBatchCode == row.TrustBatchCode); + if (trust != null) + { + return trust; + } + + trust = new Model.HJGL_Batch_BatchTrust + { + TrustBatchId = SQLHelper.GetNewID(typeof(Model.HJGL_Batch_BatchTrust)), + TrustBatchCode = row.TrustBatchCode, + TrustDate = row.TrustDate, + ProjectId = pointBatch.ProjectId, + UnitId = pointBatch.UnitId, + UnitWorkId = pointBatch.UnitWorkId, + DetectionTypeId = pointBatch.DetectionTypeId, + DetectionRateId = pointBatch.DetectionRateId, + NDEUnit = row.NDEUnit.UnitId, + PointBatchId = pointBatch.PointBatchId, + TopointBatch = pointBatch.PointBatchId, + IsAudit = true + }; + Funs.DB.HJGL_Batch_BatchTrust.InsertOnSubmit(trust); + Funs.DB.SubmitChanges(); + return trust; + } + + private void ImportNDE(ImportRow row, Model.HJGL_Batch_BatchTrust trust, Model.HJGL_Batch_BatchTrustItem trustItem) + { + // 检测单主表按检测单号复用,明细按委托明细关联到具体焊口。 + var nde = Funs.DB.HJGL_Batch_NDE.FirstOrDefault(x => x.ProjectId == this.CurrUser.LoginProjectId && x.NDECode == row.NDECode); + if (nde == null) + { + nde = new Model.HJGL_Batch_NDE + { + NDEID = SQLHelper.GetNewID(typeof(Model.HJGL_Batch_NDE)), + TrustBatchId = trust.TrustBatchId, + ProjectId = trust.ProjectId, + UnitId = trust.UnitId, + UnitWorkId = trust.UnitWorkId, + NDEUnit = row.NDEUnit.UnitId, + NDECode = row.NDECode, + NDEDate = row.NDEDate, + AuditDate = row.ReportDate, + NDEMan = this.CurrUser.PersonId + }; + Funs.DB.HJGL_Batch_NDE.InsertOnSubmit(nde); + Funs.DB.SubmitChanges(); + } + + var ndeItem = Funs.DB.HJGL_Batch_NDEItem.FirstOrDefault(x => x.TrustBatchItemId == trustItem.TrustBatchItemId); + if (ndeItem == null) + { + ndeItem = new Model.HJGL_Batch_NDEItem + { + NDEItemID = SQLHelper.GetNewID(typeof(Model.HJGL_Batch_NDEItem)), + NDEID = nde.NDEID, + TrustBatchItemId = trustItem.TrustBatchItemId + }; + Funs.DB.HJGL_Batch_NDEItem.InsertOnSubmit(ndeItem); + } + + ndeItem.DetectionTypeId = trust.DetectionTypeId; + ndeItem.RequestDate = row.TrustDate; + ndeItem.RepairLocation = row.RepairLocation; + ndeItem.TotalFilm = row.TotalFilm; + ndeItem.PassFilm = row.PassFilm; + ndeItem.CheckResult = row.CheckResult; + ndeItem.NDEReportNo = row.NDECode; + ndeItem.FilmDate = row.NDEDate; + ndeItem.ReportDate = row.ReportDate; + ndeItem.SubmitDate = DateTime.Now; + ndeItem.CheckDefects = row.DefectIds.Count > 0 ? string.Join(",", row.DefectIds) : null; + ndeItem.JudgeGrade = row.JudgeGrade; + Funs.DB.SubmitChanges(); + } + + private void BindGrid(List rows) + { + Grid1.DataSource = rows; + Grid1.DataBind(); + } + + private string JoinErrors(List rows) + { + var errors = rows.Where(x => !string.IsNullOrEmpty(x.ErrorMessage)) + .Select(x => "第" + x.RowIndex + "行:" + x.ErrorMessage); + return string.Join("
", errors); + } + + private string GetCell(DataRow row, string columnName) + { + // 模板列名可能带尾随空格,取值时统一 Trim 后按列名匹配。 + DataColumn column = row.Table.Columns.Cast().FirstOrDefault(x => x.ColumnName.Trim() == columnName); + if (column == null || row[column] == null) + { + return string.Empty; + } + return row[column].ToString().Trim(); + } + + private bool IsEmptyRow(DataRow row) + { + return RequiredHeaders.All(x => string.IsNullOrEmpty(GetCell(row, x))); + } + + private string GetValueByColumn(ImportRow row, string columnName) + { + switch (columnName) + { + case "管线号": return row.PipelineCode; + case "焊口号": return row.WeldJointCode; + case "盖面焊工": return row.CoverWelderCode; + case "打底焊工": return row.BackingWelderCode; + case "焊接日期": return row.WeldingDateText; + case "焊接位置": return row.WeldingLocationCode; + case "是否点口": return row.IsPointText; + case "委托单号": return row.TrustBatchCode; + case "委托日期": return row.TrustDateText; + case "检测单位": return row.NDEUnitName; + case "评定级别": return row.JudgeGrade; + default: return string.Empty; + } + } + + private DateTime? ParseDate(string value, string columnName, ImportRow row, bool required = true) + { + if (string.IsNullOrEmpty(value)) + { + if (required) + { + row.AddError(columnName + "为必填项"); + } + return null; + } + DateTime? date = Funs.GetNewDateTime(value); + if (date == null) + { + row.AddError(columnName + "格式错误"); + } + return date?.Date; + } + + private int? ParseInt(string value, string columnName, ImportRow row, bool required = true) + { + if (string.IsNullOrEmpty(value)) + { + if (required) + { + row.AddError(columnName + "为必填项"); + } + return null; + } + int? result = Funs.GetNewInt(value); + if (result == null) + { + row.AddError(columnName + "格式错误"); + } + return result; + } + + private bool? ParseBoolean(string value, ImportRow row) + { + // 是否点口只接受明确布尔值,避免“是点口”的业务链路被模糊文本误触发。 + if (value == "是" || value == "1" || value.Equals("true", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + if (value == "否" || value == "0" || value.Equals("false", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + row.AddError("是否点口格式错误"); + return null; + } + + private string ParseCheckResult(string value, ImportRow row) + { + if (string.IsNullOrEmpty(value)) + { + return null; + } + if (value == "合格") + { + return "1"; + } + if (value == "不合格") + { + return "2"; + } + row.AddError("是否合格格式错误"); + return null; + } + + private void ValidateJudgeGrade(ImportRow row) + { + string[] grades = { "Ⅰ", "Ⅱ", "Ⅲ", "Ⅳ", "Ⅴ" }; + if (!string.IsNullOrEmpty(row.JudgeGrade) && !grades.Contains(row.JudgeGrade)) + { + row.AddError("评定级别错误"); + } + } + + private class ImportRow + { + public int SerialNo { get; set; } + public int RowIndex { get; set; } + public string PipelineCode { get; set; } + public string WeldJointCode { get; set; } + public string CoverWelderCode { get; set; } + public string BackingWelderCode { get; set; } + public string WeldingDateText { get; set; } + public string WeldingLocationCode { get; set; } + public string IsPointText { get; set; } + public string TrustBatchCode { get; set; } + public string TrustDateText { get; set; } + public string NDEUnitName { get; set; } + public string NDECode { get; set; } + public string NDEDateText { get; set; } + public string ReportDateText { get; set; } + public string TotalFilmText { get; set; } + public string PassFilmText { get; set; } + public string CheckResultText { get; set; } + public string JudgeGrade { get; set; } + public string DefectNames { get; set; } + public string RepairLocation { get; set; } + public DateTime? WeldingDate { get; set; } + public DateTime? TrustDate { get; set; } + public DateTime? NDEDate { get; set; } + public DateTime? ReportDate { get; set; } + public int? TotalFilm { get; set; } + public int? PassFilm { get; set; } + public bool? IsPoint { get; set; } + public string CheckResult { get; set; } + public Model.View_HJGL_WeldJoint Joint { get; set; } + public Model.HJGL_Pipeline Pipeline { get; set; } + public Model.SitePerson_Person CoverWelder { get; set; } + public Model.SitePerson_Person BackingWelder { get; set; } + public Model.Base_WeldingLocation WeldingLocation { get; set; } + public Model.Base_Unit NDEUnit { get; set; } + public List DefectIds { get; } = new List(); + public string ErrorMessage { get; private set; } + + public void AddError(string error) + { + if (string.IsNullOrEmpty(ErrorMessage)) + { + ErrorMessage = error; + } + else + { + ErrorMessage += ";" + error; + } + } + } + } +} diff --git a/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx.designer.cs new file mode 100644 index 00000000..6fdcd3b3 --- /dev/null +++ b/SGGL/FineUIPro.Web/HJGL/WeldingManage/WeldReportIn.aspx.designer.cs @@ -0,0 +1,116 @@ +//------------------------------------------------------------------------------ +// <自动生成> +// 此代码由工具生成。 +// +// 对此文件的更改可能导致不正确的行为,如果 +// 重新生成代码,则所做更改将丢失。 +// +//------------------------------------------------------------------------------ + +namespace FineUIPro.Web.HJGL.WeldingManage +{ + + + public partial class WeldReportIn + { + + /// + /// form1 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::System.Web.UI.HtmlControls.HtmlForm form1; + + /// + /// PageManager1 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.PageManager PageManager1; + + /// + /// SimpleForm1 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Form SimpleForm1; + + /// + /// Toolbar2 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Toolbar Toolbar2; + + /// + /// hdFileName 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.HiddenField hdFileName; + + /// + /// btnDownLoad 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Button btnDownLoad; + + /// + /// btnAudit 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Button btnAudit; + + /// + /// btnImport 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Button btnImport; + + /// + /// fuAttachUrl 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.FileUpload fuAttachUrl; + + /// + /// lblBottom 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Label lblBottom; + + /// + /// Grid1 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Grid Grid1; + } +}