From c90498a588fa1caa6741234a0434c6ad018fe460 Mon Sep 17 00:00:00 2001 From: zhoukeda <1315948824@qq.com> Date: Wed, 23 Aug 2023 17:35:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=8D=E5=90=91=E8=BF=BD=E6=BA=AF=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Consts/WebApiConst.cs | 5 +- .../Dto/PrdManage/PrdMoFromOneListOutput.cs | 101 +++++++++++ .../Dto/PrdManage/PrdMoFromQueryInput.cs | 2 +- .../Entity/PrdMaterialReceiptD.cs | 5 + .../Entity/PrdReport.cs | 6 + .../PrdMaterialReceiptService.cs | 1 + .../Tnb.ProductionMgr/PrdMoService.cs | 170 +++++++++++++++--- .../Tnb.ProductionMgr/PrdMoTaskService.cs | 3 +- .../ProductionReportRecordService.cs | 2 +- .../Tnb.Common/Utils/DateTimeJsonConverter.cs | 27 +++ 10 files changed, 289 insertions(+), 33 deletions(-) create mode 100644 common/Tnb.Common/Utils/DateTimeJsonConverter.cs diff --git a/BasicData/Tnb.BasicData.Entities/Consts/WebApiConst.cs b/BasicData/Tnb.BasicData.Entities/Consts/WebApiConst.cs index c4488373..e29717ed 100644 --- a/BasicData/Tnb.BasicData.Entities/Consts/WebApiConst.cs +++ b/BasicData/Tnb.BasicData.Entities/Consts/WebApiConst.cs @@ -15,8 +15,11 @@ namespace Tnb.BasicData //齐套出库接口 public const string MES_KITTING_OUT_STK = "/api/wms/wmskitting-out/mes-kitting-out-stk"; + + //物料标签查询接口 + public const string MES_FETCH_IN_OUT_STOCK_INFO_BY_BAR_CODE = "/api/wms/wms-in-stock/mes-fetch-in-out-stock-info-by-bar-code"; #endregion - + } } \ No newline at end of file diff --git a/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromOneListOutput.cs b/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromOneListOutput.cs index 89421dde..5f235c1b 100644 --- a/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromOneListOutput.cs +++ b/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromOneListOutput.cs @@ -136,4 +136,105 @@ namespace Tnb.ProductionMgr.Entities.Dto.PrdManage } + + public class PrdMoReverseFromMaterialOutput + { + /// + /// 物料ID + /// + public string material_id { get; set; } + /// + /// 物料编号 + /// + public string material_code { get; set; } + /// + /// 批次 + /// + public string code_batch { get; set;} + /// + /// 采购单号 + /// + public string purchase_code { get; set; } + /// + /// 供应商 + /// + public string supplier_id { get; set; } + /// + /// 条码数量 + /// + public decimal barcode_qty { get; set; } + /// + /// 单位ID + /// + public string unit_id { get; set; } + /// + /// 入库单号 + /// + public string in_bill_code { get; set; } + /// + /// 入库时间 + /// + public DateTime instock_time { get; set; } + /// + /// 出库单号 + /// + public string out_bill_code { get; set; } + /// + /// 出库时间 + /// + public DateTime? outstock_time { get; set; } + /// + /// 仓库ID + /// + public string warehouse_id { get; set; } + /// + /// 仓库名称 + /// + public string warehouse_name { get; set; } + /// + /// 库位ID + /// + public string location_id { get; set; } + /// + /// 库位编号 + /// + public string location_code { get; set; } + /// + /// 已投数量 + /// + public decimal feeding_num { get; set; } + } + + /// + /// 物料反向追溯投料信息输出父类 + /// + public class PrdMoReverseFromFeedingOutput + { + public string mo_task_id { get; set; } + public string mo_task_code { get; set; } + public string mo_code { get; set; } + public string material_code { get; set; } + public string material_name { get; set; } + public string material_standard { get; set; } + public List children { get; set; } = new List(); + + } + + /// + /// 物料反向追溯投料信息输出字类 + /// + public class PrdMoReverseFromFeedingDetailOutput + { + /// + /// 投料子表id + /// + public string feeding_detail_id { get; set; } + public string feeding_time { get; set; } + public decimal num { get; set; } + public string check_conclusion { get; set; } + public string feeding_name { get; set; } + public string station_name { get; set; } + public string process_name { get; set; } + + } } \ No newline at end of file diff --git a/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromQueryInput.cs b/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromQueryInput.cs index ece9e52f..f7884670 100644 --- a/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromQueryInput.cs +++ b/ProductionMgr/Tnb.ProductionMgr.Entities/Dto/PrdManage/PrdMoFromQueryInput.cs @@ -16,6 +16,6 @@ namespace Tnb.ProductionMgr.Entities.Dto.PrdManage public string? mo_task_code { get; set; } - public string? batch { get; set; } + public string? barcode { get; set; } } } \ No newline at end of file diff --git a/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdMaterialReceiptD.cs b/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdMaterialReceiptD.cs index 47711d1b..d2d2c3ff 100644 --- a/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdMaterialReceiptD.cs +++ b/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdMaterialReceiptD.cs @@ -38,6 +38,11 @@ public partial class PrdMaterialReceiptD : BaseEntity /// 单位id /// public string? unit_id { get; set; } + + /// + /// 条码编号 + /// + public string? barcode { get; set; } /// /// 载具id diff --git a/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdReport.cs b/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdReport.cs index f6875100..f6a573a3 100644 --- a/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdReport.cs +++ b/ProductionMgr/Tnb.ProductionMgr.Entities/Entity/PrdReport.cs @@ -84,6 +84,12 @@ public partial class PrdReport : BaseEntity /// public string? batch { get; set; } + /// + /// 条码 + /// + public string? barcode { get; set; } + + /// /// 设备id /// diff --git a/ProductionMgr/Tnb.ProductionMgr/PrdMaterialReceiptService.cs b/ProductionMgr/Tnb.ProductionMgr/PrdMaterialReceiptService.cs index f442741c..b956c738 100644 --- a/ProductionMgr/Tnb.ProductionMgr/PrdMaterialReceiptService.cs +++ b/ProductionMgr/Tnb.ProductionMgr/PrdMaterialReceiptService.cs @@ -181,6 +181,7 @@ namespace Tnb.ProductionMgr batch = item["batch"]?.ToString(), unit_id = item["unit_id"]?.ToString(), carry_id = input.carry_id, + barcode = item["barcode"]?.ToString(), is_all_feeding = 0, member_carry_id = item["member_carry_id"]?.ToString(), member_carry_code = item["member_carry_code"]?.ToString(), diff --git a/ProductionMgr/Tnb.ProductionMgr/PrdMoService.cs b/ProductionMgr/Tnb.ProductionMgr/PrdMoService.cs index fbc0b711..78ba69d1 100644 --- a/ProductionMgr/Tnb.ProductionMgr/PrdMoService.cs +++ b/ProductionMgr/Tnb.ProductionMgr/PrdMoService.cs @@ -1,10 +1,14 @@ -using JNPF.Common.Core.Manager; +using JNPF; +using JNPF.Common.Core.Manager; using JNPF.Common.Extension; using JNPF.Common.Filter; using JNPF.Common.Security; using JNPF.DependencyInjection; using JNPF.DynamicApiController; +using JNPF.Extras.CollectiveOAuth.Models; +using JNPF.Extras.CollectiveOAuth.Utils; using JNPF.FriendlyException; +using JNPF.Logging; using JNPF.Systems.Entitys.Permission; using JNPF.Systems.Entitys.System; using JNPF.Systems.Interfaces.System; @@ -12,6 +16,7 @@ using JNPF.VisualDev; using JNPF.VisualDev.Entitys.Dto.VisualDevModelData; using JNPF.VisualDev.Interfaces; using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; using SqlSugar; using Tnb.BasicData; using Tnb.BasicData.Entities; @@ -21,6 +26,8 @@ using Tnb.ProductionMgr.Entities.Dto; using Tnb.ProductionMgr.Entities.Dto.PrdManage; using Tnb.ProductionMgr.Entities.Enums; using Tnb.ProductionMgr.Interfaces; +using AuthResponse = JNPF.Extras.CollectiveOAuth.Models.AuthResponse; +using StringExtensions = JNPF.Common.Extension.StringExtensions; namespace Tnb.ProductionMgr { @@ -204,7 +211,7 @@ namespace Tnb.ProductionMgr { throw new ArgumentNullException(nameof(input)); } - if (input.Behavior.IsNullOrWhiteSpace()) + if (StringExtensions.IsNullOrWhiteSpace(input.Behavior)) { throw new ArgumentException($"{nameof(input.Behavior)},not be null or empty"); } @@ -359,7 +366,7 @@ namespace Tnb.ProductionMgr #endregion - #region 工单追溯相关 + #region 正向工单追溯相关 /// /// 工单追溯一级列表 @@ -377,15 +384,15 @@ namespace Tnb.ProductionMgr ids1 = await _db.Queryable().Where(x => x.mo_task_code.Contains(x.mo_task_code)).Select(x => x.mo_id).ToListAsync(); ids = ids1; } - if (!string.IsNullOrEmpty(input.batch)) + if (!string.IsNullOrEmpty(input.barcode)) { ids2 = await _db.Queryable() .LeftJoin((a,b)=>a.mo_task_id==b.id) - .Where((a,b) => a.batch.Contains(input.batch)).Select((a,b) => b.mo_id).ToListAsync(); + .Where((a,b) => a.barcode.Contains(input.barcode)).Select((a,b) => b.mo_id).ToListAsync(); ids = ids2; } - if (!string.IsNullOrEmpty(input.mo_task_code) && !string.IsNullOrEmpty(input.batch)) + if (!string.IsNullOrEmpty(input.mo_task_code) && !string.IsNullOrEmpty(input.barcode)) { ids = ids1.Intersect(ids2).ToList(); } @@ -419,11 +426,11 @@ namespace Tnb.ProductionMgr { string mo_id = dic.ContainsKey("mo_id") ? dic["mo_id"] : ""; string mo_task_code = dic.ContainsKey("mo_task_code") ? dic["mo_task_code"] : ""; - string batch = dic.ContainsKey("batch") ? dic["batch"] : ""; + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; List ids = new List(); - if (!string.IsNullOrEmpty(batch)) + if (!string.IsNullOrEmpty(barcode)) { - ids = await _db.Queryable().Where(x => x.batch.Contains(batch)).Select(x=>x.mo_task_id).ToListAsync(); + ids = await _db.Queryable().Where(x => x.barcode.Contains(barcode)).Select(x=>x.mo_task_id).ToListAsync(); } PrdMo prdMo = await _repository.GetSingleAsync(x => x.id == mo_id); @@ -491,11 +498,11 @@ namespace Tnb.ProductionMgr { string mo_task_id = dic.ContainsKey("mo_task_id") ? dic["mo_task_id"] : ""; string mo_task_code = dic.ContainsKey("mo_task_code") ? dic["mo_task_code"] : ""; - string batch = dic.ContainsKey("batch") ? dic["batch"] : ""; + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; List ids = new List(); - if (!string.IsNullOrEmpty(batch)) + if (!string.IsNullOrEmpty(barcode)) { - ids = await _db.Queryable().Where(x => x.batch.Contains(batch)).Select(x=>x.mo_task_id).ToListAsync(); + ids = await _db.Queryable().Where(x => x.barcode.Contains(barcode)).Select(x=>x.mo_task_id).ToListAsync(); } PrdMoTask prdMoTask = await _db.Queryable().SingleAsync(x => x.id == mo_task_id); @@ -540,18 +547,18 @@ namespace Tnb.ProductionMgr public async Task PrdMoFromManList(Dictionary dic) { string mo_task_id = dic.ContainsKey("mo_task_id") ? dic["mo_task_id"] : ""; - string batch = dic.ContainsKey("batch") ? dic["batch"] : ""; + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; List ids = new List(); - if (!string.IsNullOrEmpty(batch)) + if (!string.IsNullOrEmpty(barcode)) { - ids = await _db.Queryable().Where(x => x.batch.Contains(batch)).Select(x=>x.mo_task_id).ToListAsync(); + ids = await _db.Queryable().Where(x => x.barcode.Contains(barcode)).Select(x=>x.mo_task_id).ToListAsync(); } return await _db.Queryable() .LeftJoin((a, b) => a.create_id == b.Id) .WhereIF(!string.IsNullOrEmpty(mo_task_id), (a, b) => a.mo_task_id == mo_task_id) // .WhereIF(ids!=null && ids.Count>0,(a,b)=>ids.Contains(a.mo_task_id)) - .WhereIF(!string.IsNullOrEmpty(batch),(a,b)=>a.batch.Contains(batch)) + .WhereIF(!string.IsNullOrEmpty(barcode),(a,b)=>a.barcode.Contains(barcode)) .GroupBy((a,b)=>new {a.create_id,employee_name = b.RealName}) .Select((a, b) => new PrdMoFromManListOutput() { @@ -575,11 +582,11 @@ namespace Tnb.ProductionMgr public async Task PrdMoFromEquipList(Dictionary dic) { string mo_task_id = dic.ContainsKey("mo_task_id") ? dic["mo_task_id"] : ""; - string batch = dic.ContainsKey("batch") ? dic["batch"] : ""; + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; List ids = new List(); - if (!string.IsNullOrEmpty(batch)) + if (!string.IsNullOrEmpty(barcode)) { - ids = await _db.Queryable().Where(x => x.batch.Contains(batch)).Select(x=>x.mo_task_id).ToListAsync(); + ids = await _db.Queryable().Where(x => x.barcode.Contains(barcode)).Select(x=>x.mo_task_id).ToListAsync(); } return await _db.Queryable() @@ -603,11 +610,11 @@ namespace Tnb.ProductionMgr public async Task PrdMoFromMaterialList(Dictionary dic) { string mo_task_id = dic.ContainsKey("mo_task_id") ? dic["mo_task_id"] : ""; - string batch = dic.ContainsKey("batch") ? dic["batch"] : ""; + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; List ids = new List(); - if (!string.IsNullOrEmpty(batch)) + if (!string.IsNullOrEmpty(barcode)) { - ids = await _db.Queryable().Where(x => x.batch.Contains(batch)).Select(x=>x.mo_task_id).ToListAsync(); + ids = await _db.Queryable().Where(x => x.barcode.Contains(barcode)).Select(x=>x.mo_task_id).ToListAsync(); } PrdMoTask prdMoTask = await _db.Queryable().SingleAsync(x => x.id == mo_task_id); PrdMo prdMo = await _db.Queryable().SingleAsync(x => x.id == prdMoTask.mo_id); @@ -617,7 +624,7 @@ namespace Tnb.ProductionMgr * 3 物料二维码为精确搜索时 按照先进先出的规则计算投入物料 * 4 组装包装可投入物料组成 1)直接从生产线上流转下来的物料 2)投入的物料 */ - if (string.IsNullOrEmpty(batch)) + if (string.IsNullOrEmpty(barcode)) { if (prdMoTask.schedule_type == 1) { @@ -645,8 +652,8 @@ namespace Tnb.ProductionMgr } else { - PrdReport prdReport = await _db.Queryable().Where(x => x.batch == batch).FirstAsync(); - List prdReports = await _db.Queryable().Where(x=>x.create_time<=prdReport.create_time && x.batch!=batch && x.mo_task_id==mo_task_id).ToListAsync(); + PrdReport prdReport = await _db.Queryable().Where(x => x.barcode == barcode).FirstAsync(); + List prdReports = await _db.Queryable().Where(x=>x.create_time<=prdReport.create_time && x.barcode!=barcode && x.mo_task_id==mo_task_id).ToListAsync(); int? beforeReportNum = prdReports.Sum(x => x.reported_qty); List prdFeedingIds = new List(); if (prdMoTask.schedule_type == 1) @@ -813,6 +820,7 @@ namespace Tnb.ProductionMgr material_code = b.code, material_name = b.name, batch = a.batch, + supplier_name = "自制" }); return await _db.UnionAll(queryable1, queryable2).ToListAsync(); @@ -825,13 +833,117 @@ namespace Tnb.ProductionMgr } } - - - return null; - } #endregion + + #region 反向工单追溯相关 + + /// + /// 物料反向追溯物料信息 + /// + /// + /// + [HttpPost] + public async Task PrdMoReverseFromMaterialInfo(Dictionary dic) + { + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; + string domain = (App.HttpContext.Request.IsHttps ? "https://" : "http://") + App.HttpContext.Request.Host; + Dictionary header = new Dictionary() + { + ["Authorization"] = App.HttpContext.Request.Headers["Authorization"] + }; + Dictionary postData = new Dictionary() + { + ["org_id"] = _userManager.GetUserInfo().Result.organizeId, + ["barcode"] = new List() { barcode }, + ["currentPage"] = 1, + ["pageSize"] = int.MaxValue, + }; + var sendResult = HttpUtils.RequestPost(domain + WebApiConst.MES_FETCH_IN_OUT_STOCK_INFO_BY_BAR_CODE,JsonConvert.SerializeObject(postData),header); + Log.Information(sendResult); + + AuthResponse authResponse = JsonConvert.DeserializeObject(sendResult); + if (authResponse.code != 200) + { + throw Oops.Bah(authResponse.msg); + } + else + { + PageResult output = JsonConvert.DeserializeObject>(authResponse.data.ToString(),new Tnb.Common.Utils.DateTimeJsonConverter()); + if (output.list != null && output.list.Count > 0) + { + output.list[0].feeding_num = await _db.Queryable() + .LeftJoin((a, b) => a.id == b.material_receipt_detail_id) + .Where((a, b) => a.barcode == barcode).SumAsync((a, b) => b.num); + return output.list[0]; + } + } + + return Array.Empty(); + + } + + /// + /// 物料反向追溯投料信息 + /// + /// + /// + [HttpPost] + public async Task PrdMoReverseFromFeedingInfo(Dictionary dic) + { + string barcode = dic.ContainsKey("barcode") ? dic["barcode"] : ""; + List ids = await _db.Queryable() + .LeftJoin((a, b) => a.id == b.material_receipt_detail_id) + .LeftJoin((a, b, c) => b.feeding_id == c.id) + .Where(a => a.barcode == barcode) + .Select((a, b, c) => c.mo_task_id).ToListAsync(); + + return await _db.Queryable() + .LeftJoin((a, b) => a.mo_id == b.id) + .LeftJoin((a,b,c)=>a.material_id==c.id) + .Where((a,b,c)=>ids.Contains(a.id)) + .Select((a, b, c) => new PrdMoReverseFromFeedingOutput + { + mo_task_id = a.id, + mo_task_code = a.mo_task_code, + mo_code = b.mo_code, + material_code = c.code, + material_name = c.name, + material_standard = c.material_standard, + children = SqlFunc.Subqueryable() + .LeftJoin((x,y)=>x.feeding_id==y.id) + .LeftJoin((x,y,z)=>y.create_id==z.Id) + .LeftJoin((x,y,z,org)=>y.station_id==org.Id) + .LeftJoin((x,y,z,org,process)=>y.process_id==process.id) + .LeftJoin((x,y,z,org,process,mp)=>x.material_receipt_detail_id==mp.id) + .Where((x,y,z)=>y.mo_task_id==a.id).ToList((x,y,z,org,process,mp)=>new PrdMoReverseFromFeedingDetailOutput + { + feeding_detail_id = x.id, + feeding_time = y.create_time==null ? "" : y.create_time.Value.ToString("yyyy-MM-dd HH:mm:ss"), + num = x.num, + check_conclusion = mp.check_conclusion, + feeding_name = z.RealName, + station_name = org.FullName, + process_name = process.process_name + }), + }).ToListAsync(); + } + + /// + /// 物料反向追溯查出信息 + /// + /// + /// + [HttpPost] + public async Task PrdMoReverseFromOutInfo(Dictionary dic) + { + string feeding_detail_id = dic.ContainsKey("feeding_detail_id") ? dic["feeding_detail_id"] : ""; + + return null; + } + + #endregion } } diff --git a/ProductionMgr/Tnb.ProductionMgr/PrdMoTaskService.cs b/ProductionMgr/Tnb.ProductionMgr/PrdMoTaskService.cs index d7d81073..71dcbc55 100644 --- a/ProductionMgr/Tnb.ProductionMgr/PrdMoTaskService.cs +++ b/ProductionMgr/Tnb.ProductionMgr/PrdMoTaskService.cs @@ -1404,7 +1404,8 @@ namespace Tnb.ProductionMgr report.reported_qty = input.reported_qty; report.create_id = _userManager.UserId; report.create_time = DateTime.Now; - report.batch = input.mo_task_code + DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); + // report.batch = input.mo_task_code + DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); + report.barcode = input.mo_task_code + DateTimeOffset.Now.ToUnixTimeSeconds().ToString(); report.equip_id = prdMoTask.eqp_id; report.mbom_process_id = prdMoTask.mbom_process_id; report.station = input.station; diff --git a/ProductionMgr/Tnb.ProductionMgr/ProductionReportRecordService.cs b/ProductionMgr/Tnb.ProductionMgr/ProductionReportRecordService.cs index 40f896d0..59ac0e14 100644 --- a/ProductionMgr/Tnb.ProductionMgr/ProductionReportRecordService.cs +++ b/ProductionMgr/Tnb.ProductionMgr/ProductionReportRecordService.cs @@ -127,7 +127,7 @@ namespace Tnb.ProductionMgr create_id = y.RealName, create_id_id = x.create_id, create_time = x.create_time==null ? "" : x.create_time.Value.ToString("yyyy-MM-dd HH:mm"), - batch = x.batch + batch = x.barcode }) }).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(result); diff --git a/common/Tnb.Common/Utils/DateTimeJsonConverter.cs b/common/Tnb.Common/Utils/DateTimeJsonConverter.cs new file mode 100644 index 00000000..5a5d3a37 --- /dev/null +++ b/common/Tnb.Common/Utils/DateTimeJsonConverter.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace Tnb.Common.Utils +{ + public class DateTimeJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(DateTime); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + TypeCode typeCode = Type.GetTypeCode(reader.Value.GetType()); + if (typeCode == TypeCode.DateTime) + return reader.Value; + + var t = long.Parse((string)(reader.Value.ToString())); + return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(t); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file