OutsourceOrderService.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  1. namespace Admin.NET.Plugin.AiDOP.Supply;
  2. /// <summary>
  3. /// 委外加工订单服务
  4. /// </summary>
  5. [ApiDescriptionSettings(Order = 310, Description = "委外加工订单")]
  6. [Route("api/Supply")]
  7. [AllowAnonymous]
  8. [NonUnify]
  9. public class OutsourceOrderService : IDynamicApiController, ITransient
  10. {
  11. private const int PwPurOrdSerialWidth = 4;
  12. private readonly ISqlSugarClient _db;
  13. private readonly UserManager _userManager;
  14. public OutsourceOrderService(ISqlSugarClient db, UserManager userManager)
  15. {
  16. _db = db;
  17. _userManager = userManager;
  18. }
  19. [DisplayName("委外加工订单列表")]
  20. [HttpGet("outsource-order/list")]
  21. public async Task<object> GetOrderList([FromQuery] OutsourceOrderListInput input)
  22. {
  23. var page = input.Page <= 0 ? 1 : input.Page;
  24. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  25. var offset = (page - 1) * pageSize;
  26. var where = new List<string>
  27. {
  28. "p.Potype='PW'",
  29. "p.IsActive=1",
  30. "IFNULL(p.`Usage`,'')='委外加工'"
  31. };
  32. var pars = new List<SugarParameter>();
  33. if (_userManager.TenantId > 0)
  34. {
  35. where.Add("p.tenant_id = @TenantId");
  36. pars.Add(new SugarParameter("@TenantId", _userManager.TenantId));
  37. }
  38. if (!string.IsNullOrWhiteSpace(input.PurOrd))
  39. {
  40. where.Add("p.PurOrd LIKE @PurOrd");
  41. pars.Add(new SugarParameter("@PurOrd", $"%{input.PurOrd.Trim()}%"));
  42. }
  43. if (!string.IsNullOrWhiteSpace(input.Buyer))
  44. {
  45. where.Add("p.Buyer=@Buyer");
  46. pars.Add(new SugarParameter("@Buyer", input.Buyer.Trim()));
  47. }
  48. if (!string.IsNullOrWhiteSpace(input.Supp))
  49. {
  50. where.Add("p.Supp=@Supp");
  51. pars.Add(new SugarParameter("@Supp", input.Supp.Trim()));
  52. }
  53. var tenantJoin = _userManager.TenantId > 0 ? " AND s.tenant_id=p.tenant_id" : "";
  54. var fromSql = $"""
  55. FROM PurOrdMaster p
  56. LEFT JOIN SuppMaster s ON p.Supp=s.Supp{tenantJoin}
  57. LEFT JOIN DepartmentMaster d ON p.Department=d.Department{(_userManager.TenantId > 0 ? " AND d.tenant_id=p.tenant_id" : "")}
  58. LEFT JOIN EmployeeMaster e ON p.Buyer=e.Employee{(_userManager.TenantId > 0 ? " AND e.tenant_id=p.tenant_id" : "")}
  59. LEFT JOIN (SELECT DISTINCT PurOrd FROM PurOrdDetail WHERE IFNULL(RctQty,0)>0) pdhr ON pdhr.PurOrd=p.PurOrd
  60. WHERE {string.Join(" AND ", where)}
  61. """;
  62. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) {fromSql}", pars);
  63. var list = await _db.Ado.SqlQueryAsync<OutsourceOrderListRow>(
  64. $"""
  65. SELECT
  66. p.RecID AS Id,
  67. p.PurOrd AS PurOrd,
  68. CONCAT(TRIM(IFNULL(p.Supp,'')),' ',TRIM(IFNULL(s.SortName,''))) AS SuppName,
  69. p.Potype AS Potype,
  70. p.ReqBy AS ReqBy,
  71. CONCAT(TRIM(IFNULL(p.Buyer,'')),' ',TRIM(IFNULL(e.Name,''))) AS Buyer,
  72. CONCAT(TRIM(IFNULL(p.Department,'')),' ',TRIM(IFNULL(d.Descr,''))) AS DepartmentDescr,
  73. p.OrdDate AS OrdDate,
  74. p.DueDate AS DueDate,
  75. p.Curr AS Curr,
  76. CASE
  77. WHEN IFNULL(LENGTH(p.Status),0)=0 OR p.Buyer IS NULL THEN 'R'
  78. ELSE p.Status
  79. END AS Status,
  80. p.Supp AS Supp,
  81. p.Buyer AS BuyerCode,
  82. p.Department AS Department,
  83. p.`Usage` AS `Usage`,
  84. p.Remark AS Remark,
  85. CASE WHEN pdhr.PurOrd IS NOT NULL THEN 1 ELSE 0 END AS HasReceived
  86. {fromSql}
  87. ORDER BY {BuildOrderBy(input.SortField, input.SortOrder)}
  88. LIMIT {pageSize} OFFSET {offset}
  89. """,
  90. pars);
  91. return new { total, page, pageSize, list };
  92. }
  93. [DisplayName("委外加工订单详情")]
  94. [HttpGet("outsource-order/{id:int}")]
  95. public async Task<object> GetOrderDetail(int id)
  96. {
  97. var tenantWhere = _userManager.TenantId > 0 ? " AND p.tenant_id=@TenantId" : string.Empty;
  98. var tenantJoin = _userManager.TenantId > 0 ? " AND s.tenant_id=p.tenant_id" : string.Empty;
  99. var tenantJoinD = _userManager.TenantId > 0 ? " AND d.tenant_id=p.tenant_id" : string.Empty;
  100. var tenantJoinE = _userManager.TenantId > 0 ? " AND e.tenant_id=p.tenant_id" : string.Empty;
  101. var detailPars = _userManager.TenantId > 0
  102. ? new[] { new SugarParameter("@Id", id), new SugarParameter("@TenantId", _userManager.TenantId) }
  103. : new[] { new SugarParameter("@Id", id) };
  104. var row = (await _db.Ado.SqlQueryAsync<OutsourceOrderListRow>(
  105. $"""
  106. SELECT
  107. p.RecID AS Id,
  108. p.PurOrd AS PurOrd,
  109. CONCAT(TRIM(IFNULL(p.Supp,'')),' ',TRIM(IFNULL(s.SortName,''))) AS SuppName,
  110. p.Potype AS Potype,
  111. p.ReqBy AS ReqBy,
  112. CONCAT(TRIM(IFNULL(p.Buyer,'')),' ',TRIM(IFNULL(e.Name,''))) AS Buyer,
  113. CONCAT(TRIM(IFNULL(p.Department,'')),' ',TRIM(IFNULL(d.Descr,''))) AS DepartmentDescr,
  114. p.OrdDate AS OrdDate,
  115. p.DueDate AS DueDate,
  116. p.Curr AS Curr,
  117. CASE WHEN IFNULL(LENGTH(p.Status),0)=0 OR p.Buyer IS NULL THEN 'R' ELSE p.Status END AS Status,
  118. p.Supp AS Supp,
  119. p.Buyer AS BuyerCode,
  120. p.Department AS Department,
  121. p.`Usage` AS `Usage`,
  122. p.Remark AS Remark
  123. FROM PurOrdMaster p
  124. LEFT JOIN SuppMaster s ON p.Supp=s.Supp{tenantJoin}
  125. LEFT JOIN DepartmentMaster d ON p.Department=d.Department{tenantJoinD}
  126. LEFT JOIN EmployeeMaster e ON p.Buyer=e.Employee{tenantJoinE}
  127. WHERE p.RecID=@Id{tenantWhere}
  128. LIMIT 1
  129. """,
  130. detailPars)).FirstOrDefault();
  131. return row ?? throw Oops.Oh("记录不存在");
  132. }
  133. [DisplayName("保存委外加工订单")]
  134. [HttpPost("outsource-order/save")]
  135. public async Task<object> SaveOrder([FromBody] OutsourceOrderSaveInput input)
  136. {
  137. var now = DateTime.Now;
  138. var ordDate = ParseDate(input.OrdDate) ?? now.Date;
  139. var buyer = string.IsNullOrWhiteSpace(input.Buyer) ? "110" : input.Buyer.Trim();
  140. var usage = string.IsNullOrWhiteSpace(input.Usage) ? "委外加工" : input.Usage.Trim();
  141. var reqBy = string.IsNullOrWhiteSpace(input.ReqBy) ? "DO" : input.ReqBy.Trim();
  142. if (string.IsNullOrWhiteSpace(input.Supp)) throw Oops.Oh("请选择供应商");
  143. if (string.IsNullOrWhiteSpace(input.Department)) throw Oops.Oh("请选择部门");
  144. if (input.Id is null or <= 0)
  145. {
  146. try
  147. {
  148. _db.Ado.BeginTran();
  149. var purOrd = await PurOrdNumberGenerator.NextAsync(_db, "PW", DateTime.Today, PwPurOrdSerialWidth);
  150. await _db.Ado.ExecuteCommandAsync(
  151. """
  152. INSERT INTO PurOrdMaster
  153. (
  154. Confirming, CreditTermsInt, Disc, ExchRate, EstVal, ExchRate1, ExchRate2,
  155. FixedPrice, FixedRate, Frt, PartialOK, AmtPrepaid, PrintPO, PST, Recurr,
  156. `Release`, Revision, Scheduled, ServiceCharge, SpecialCharge, Taxable,
  157. Tax1, Tax2, Tax3, TransportDays, IsActive, IsConfirm, Potype, IsChanged,
  158. TaxIn, Amt, IsPriceChanged,
  159. PurOrd, OrdDate, Supp, ReqBy, Buyer, Department, `Usage`, Remark, Status,
  160. Domain, FSTID, CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id
  161. )
  162. VALUES
  163. (
  164. 0, 0, 0, 1, 0, 1, 1,
  165. 0, 0, 0, 1, 0, 0, 0, 0,
  166. 0, 0, 0, 0, 0, 1,
  167. 0, 0, 0, 0, 1, 1, 'PW', 0,
  168. 1, 0, 0,
  169. @PurOrd, @OrdDate, @Supp, @ReqBy, @Buyer, @Department, @Usage, @Remark, 'R',
  170. @Domain, @FSTID, @CreateUser, @CreateTime, @UpdateUser, @UpdateTime, @TenantId
  171. )
  172. """,
  173. new SugarParameter("@PurOrd", purOrd),
  174. new SugarParameter("@OrdDate", ordDate),
  175. new SugarParameter("@Supp", input.Supp.Trim()),
  176. new SugarParameter("@ReqBy", reqBy),
  177. new SugarParameter("@Buyer", buyer),
  178. new SugarParameter("@Department", input.Department.Trim()),
  179. new SugarParameter("@Usage", usage),
  180. new SugarParameter("@Remark", input.Remark?.Trim()),
  181. new SugarParameter("@Domain", "100"),
  182. new SugarParameter("@FSTID", "1"),
  183. new SugarParameter("@CreateUser", _userManager.Account),
  184. new SugarParameter("@CreateTime", now),
  185. new SugarParameter("@UpdateUser", _userManager.Account),
  186. new SugarParameter("@UpdateTime", now),
  187. new SugarParameter("@TenantId", _userManager.TenantId <= 0 ? null : _userManager.TenantId)
  188. );
  189. var id = await _db.Ado.GetIntAsync(
  190. "SELECT IFNULL(MAX(RecID),0) FROM PurOrdMaster WHERE PurOrd=@PurOrd",
  191. new SugarParameter("@PurOrd", purOrd));
  192. _db.Ado.CommitTran();
  193. return new { id, purOrd, message = "新增成功" };
  194. }
  195. catch
  196. {
  197. _db.Ado.RollbackTran();
  198. throw;
  199. }
  200. }
  201. var exists = await _db.Ado.GetIntAsync("SELECT COUNT(1) FROM PurOrdMaster WHERE RecID=@Id" + (_userManager.TenantId > 0 ? " AND tenant_id=@TenantId" : ""),
  202. _userManager.TenantId > 0
  203. ? new[] { new SugarParameter("@Id", input.Id.Value), new SugarParameter("@TenantId", _userManager.TenantId) }
  204. : new[] { new SugarParameter("@Id", input.Id.Value) });
  205. if (exists <= 0) throw Oops.Oh("记录不存在");
  206. await _db.Ado.ExecuteCommandAsync(
  207. """
  208. UPDATE PurOrdMaster
  209. SET OrdDate=@OrdDate,Supp=@Supp,ReqBy=@ReqBy,Buyer=@Buyer,Department=@Department,`Usage`=@Usage,Remark=@Remark,UpdateUser=@UpdateUser,UpdateTime=@UpdateTime
  210. WHERE RecID=@Id
  211. """,
  212. new SugarParameter("@Id", input.Id.Value),
  213. new SugarParameter("@OrdDate", ordDate),
  214. new SugarParameter("@Supp", input.Supp.Trim()),
  215. new SugarParameter("@ReqBy", reqBy),
  216. new SugarParameter("@Buyer", buyer),
  217. new SugarParameter("@Department", input.Department.Trim()),
  218. new SugarParameter("@Usage", usage),
  219. new SugarParameter("@Remark", input.Remark?.Trim()),
  220. new SugarParameter("@UpdateUser", _userManager.Account),
  221. new SugarParameter("@UpdateTime", now)
  222. );
  223. return new { id = input.Id, message = "编辑成功" };
  224. }
  225. [DisplayName("删除委外加工订单")]
  226. [HttpPost("outsource-order/delete/{id:int}")]
  227. public async Task<object> DeleteOrder(int id, [FromQuery] string purOrd)
  228. {
  229. if (string.IsNullOrWhiteSpace(purOrd)) throw Oops.Oh("缺少采购订单号");
  230. var hasReceipt = await _db.Ado.GetIntAsync(
  231. "SELECT COUNT(1) FROM PurOrdRctDetail WHERE OrdNbr=@PurOrd AND IFNULL(QtyReceived,0)>0" + (_userManager.TenantId > 0 ? " AND tenant_id=@TenantId" : ""),
  232. _userManager.TenantId > 0
  233. ? new[] { new SugarParameter("@PurOrd", purOrd.Trim()), new SugarParameter("@TenantId", _userManager.TenantId) }
  234. : new[] { new SugarParameter("@PurOrd", purOrd.Trim()) });
  235. if (hasReceipt > 0) throw Oops.Oh("存在收货数量,不允许删除");
  236. var hasShip = await _db.Ado.GetIntAsync(
  237. "SELECT COUNT(1) FROM scm_shdzb WHERE po_bill=@PurOrd" + (_userManager.TenantId > 0 ? " AND tenant_id=@TenantId" : ""),
  238. _userManager.TenantId > 0
  239. ? new[] { new SugarParameter("@PurOrd", purOrd.Trim()), new SugarParameter("@TenantId", _userManager.TenantId) }
  240. : new[] { new SugarParameter("@PurOrd", purOrd.Trim()) });
  241. if (hasShip > 0) throw Oops.Oh("存在发货单,不允许删除");
  242. try
  243. {
  244. _db.Ado.BeginTran();
  245. await _db.Ado.ExecuteCommandAsync("DELETE FROM PurOrdDetailBatch WHERE PurOrd=@PurOrd", new SugarParameter("@PurOrd", purOrd.Trim()));
  246. await _db.Ado.ExecuteCommandAsync("DELETE FROM PurOrdDetail WHERE PurOrd=@PurOrd", new SugarParameter("@PurOrd", purOrd.Trim()));
  247. await _db.Ado.ExecuteCommandAsync("DELETE FROM PurOrdMaster WHERE RecID=@Id AND PurOrd=@PurOrd", new SugarParameter("@Id", id), new SugarParameter("@PurOrd", purOrd.Trim()));
  248. _db.Ado.CommitTran();
  249. }
  250. catch
  251. {
  252. _db.Ado.RollbackTran();
  253. throw;
  254. }
  255. return new { message = "删除成功" };
  256. }
  257. [DisplayName("委外采购明细列表")]
  258. [HttpGet("outsource-order/detail/list")]
  259. public async Task<object> GetDetailList([FromQuery] OutsourceOrderDetailListInput input)
  260. {
  261. var page = input.Page <= 0 ? 1 : input.Page;
  262. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  263. var offset = (page - 1) * pageSize;
  264. var where = new List<string> { "d.Potype='PW'" };
  265. var pars = new List<SugarParameter>();
  266. if (_userManager.TenantId > 0)
  267. {
  268. where.Add("d.tenant_id = @TenantId");
  269. pars.Add(new SugarParameter("@TenantId", _userManager.TenantId));
  270. }
  271. if (!string.IsNullOrWhiteSpace(input.PurOrd))
  272. {
  273. where.Add("d.PurOrd LIKE @PurOrd");
  274. pars.Add(new SugarParameter("@PurOrd", $"%{input.PurOrd.Trim()}%"));
  275. }
  276. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  277. {
  278. where.Add("d.ItemNum LIKE @ItemNum");
  279. pars.Add(new SugarParameter("@ItemNum", $"%{input.ItemNum.Trim()}%"));
  280. }
  281. if (!string.IsNullOrWhiteSpace(input.Location))
  282. {
  283. where.Add("d.Location=@Location");
  284. pars.Add(new SugarParameter("@Location", input.Location.Trim()));
  285. }
  286. if (!string.IsNullOrWhiteSpace(input.WorkOrd))
  287. {
  288. where.Add("d.WorkOrd LIKE @WorkOrd");
  289. pars.Add(new SugarParameter("@WorkOrd", $"%{input.WorkOrd.Trim()}%"));
  290. }
  291. if (!string.IsNullOrWhiteSpace(input.LotSerial))
  292. {
  293. where.Add("d.LotSerial LIKE @LotSerial");
  294. pars.Add(new SugarParameter("@LotSerial", $"%{input.LotSerial.Trim()}%"));
  295. }
  296. var fromSql = $"""
  297. FROM PurOrdDetail d
  298. LEFT JOIN ItemMaster i ON d.ItemNum=i.ItemNum
  299. WHERE {string.Join(" AND ", where)}
  300. """;
  301. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) {fromSql}", pars);
  302. var list = await _db.Ado.SqlQueryAsync<OutsourceOrderDetailRow>(
  303. $"""
  304. SELECT
  305. d.PurOrd AS PurOrd,d.ItemNum AS ItemNum,d.QtyOrded AS QtyOrded,d.QtyReceived AS QtyReceived,d.QtyReleased AS QtyReleased,
  306. d.DueDate AS DueDate,d.Line AS Line,d.Location AS Location,d.UM AS UM,d.RecID AS Id,d.Rev AS Rev,d.Drawing AS Drawing,
  307. d.WorkOrd AS WorkOrd,d.LotSerial AS LotSerial,d.Status AS Status,IFNULL(i.Descr,'') AS Descr,d.PurOrdRecID AS PurOrdRecID
  308. {fromSql}
  309. ORDER BY {BuildDetailOrderBy(input.SortField, input.SortOrder)}
  310. LIMIT {pageSize} OFFSET {offset}
  311. """,
  312. pars);
  313. return new { total, page, pageSize, list };
  314. }
  315. [DisplayName("委外采购明细详情")]
  316. [HttpGet("outsource-order/detail/{id:int}")]
  317. public async Task<object> GetDetail(int id)
  318. {
  319. var tenantWhere = _userManager.TenantId > 0 ? " AND d.tenant_id=@TenantId" : string.Empty;
  320. var detailPars = _userManager.TenantId > 0
  321. ? new[] { new SugarParameter("@Id", id), new SugarParameter("@TenantId", _userManager.TenantId) }
  322. : new[] { new SugarParameter("@Id", id) };
  323. var detail = (await _db.Ado.SqlQueryAsync<OutsourceOrderDetailRow>(
  324. $"""
  325. SELECT
  326. d.PurOrd AS PurOrd,d.ItemNum AS ItemNum,d.QtyOrded AS QtyOrded,d.QtyReceived AS QtyReceived,d.QtyReleased AS QtyReleased,
  327. d.DueDate AS DueDate,d.Line AS Line,d.Location AS Location,d.UM AS UM,d.RecID AS Id,d.Rev AS Rev,d.Drawing AS Drawing,
  328. d.WorkOrd AS WorkOrd,d.LotSerial AS LotSerial,d.Status AS Status,IFNULL(i.Descr,'') AS Descr,d.PurOrdRecID AS PurOrdRecID
  329. FROM PurOrdDetail d
  330. LEFT JOIN ItemMaster i ON d.ItemNum=i.ItemNum
  331. WHERE d.RecID=@Id{tenantWhere}
  332. LIMIT 1
  333. """,
  334. detailPars)).FirstOrDefault();
  335. if (detail == null) throw Oops.Oh("记录不存在");
  336. var batches = await _db.Ado.SqlQueryAsync<OutsourceOrderBatchRow>(
  337. """
  338. SELECT
  339. b.Batch AS Batch,b.ItemNum AS ItemNum,b.SuppItem AS SuppItem,b.UM AS UM,b.Location AS Location,b.QtyOrded AS QtyOrded,
  340. b.QtyBO AS QtyBO,b.QtyReleased AS QtyReleased,b.QtyReceived AS QtyReceived,b.QtyReturned AS QtyReturned,b.LotSerial AS LotSerial
  341. FROM PurOrdDetailBatch b
  342. WHERE b.PurOrdDetailRecID=@RecId
  343. ORDER BY b.Batch
  344. """,
  345. new SugarParameter("@RecId", detail.Id));
  346. return new { detail, batches };
  347. }
  348. [DisplayName("保存委外采购明细")]
  349. [HttpPost("outsource-order/detail/save")]
  350. public async Task<object> SaveDetail([FromBody] OutsourceOrderDetailSaveInput input)
  351. {
  352. if (string.IsNullOrWhiteSpace(input.PurOrd)) throw Oops.Oh("采购订单号不能为空");
  353. if (input.PurOrdRecID <= 0) throw Oops.Oh("采购订单主键值不能为空");
  354. if (string.IsNullOrWhiteSpace(input.ItemNum)) throw Oops.Oh("请选择物料");
  355. if (!input.QtyOrded.HasValue || input.QtyOrded.Value <= 0) throw Oops.Oh("订单数量必须大于0");
  356. var item = (await _db.Ado.SqlQueryAsync<ItemLookupRow>(
  357. """
  358. SELECT ItemNum,Descr,UM,Location,Rev,Drawing
  359. FROM ItemMaster
  360. WHERE ItemNum=@ItemNum
  361. LIMIT 1
  362. """,
  363. new SugarParameter("@ItemNum", input.ItemNum.Trim()))).FirstOrDefault();
  364. if (item == null) throw Oops.Oh("物料不存在");
  365. try
  366. {
  367. _db.Ado.BeginTran();
  368. int detailId;
  369. int line;
  370. if (input.Id is null or <= 0)
  371. {
  372. line = input.Line ?? await _db.Ado.GetIntAsync("SELECT IFNULL(MAX(Line),0)+1 FROM PurOrdDetail WHERE PurOrd=@PurOrd", new SugarParameter("@PurOrd", input.PurOrd.Trim()));
  373. await _db.Ado.ExecuteCommandAsync(
  374. """
  375. INSERT INTO PurOrdDetail
  376. (
  377. QtyBO, RctCost, CreditTermsInt, UpdateCurrentCost, CumReceived1, CumReceived2,
  378. CumReceived3, CumReceived4, Disc, FixedPrice, InspectReq, SingleLot, SupplyPer,
  379. PurOrd, PST, PackingSlipQty, PayUMConv, PurCost, RctQty, QtyOrded, QtyReceived,
  380. QtyReturned, Active, QtyReleased, RctUMConversion, Scheduled, ScheduledChanged,
  381. SchedMRPReq, SafetyDays, SafetyHours, StdCost, Taxable, TaxIn, MaxTaxableAmt,
  382. TransportHours, UMConversion, VAT, IsActive, IsConfirm, Potype, IsChanged,
  383. TaxRate, IsRounding, ReceiptQty, BarCodeQty, IsClosed, QtyReturnedRefund, CumQtyBO,
  384. Line, ItemNum, Descr, UM, Rev, Drawing, Location, DueDate, LotSerial, PurOrdRecID, Status,
  385. CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id
  386. )
  387. VALUES
  388. (
  389. 0, 0, 0, 0, 0, 0,
  390. 0, 0, 0, 0, 0, 0, 0,
  391. @PurOrd, 0, 0, 1, 0, 0, @QtyOrded, 0,
  392. 0, 1, 0, 1, 0, 0,
  393. 0, 0, 0, 0, 1, 1, 0,
  394. 0, 1, 0, 1, 1, 'PW', 0,
  395. 0, 0, 0, 0, 0, 0, 0,
  396. @Line, @ItemNum, @Descr, @UM, @Rev, @Drawing, @Location, @DueDate, @LotSerial, @PurOrdRecID, 'R',
  397. @CreateUser, @CreateTime, @UpdateUser, @UpdateTime, @TenantId
  398. )
  399. """,
  400. new SugarParameter("@PurOrd", input.PurOrd.Trim()),
  401. new SugarParameter("@Line", line),
  402. new SugarParameter("@ItemNum", item.ItemNum),
  403. new SugarParameter("@Descr", item.Descr),
  404. new SugarParameter("@UM", item.UM),
  405. new SugarParameter("@Rev", item.Rev),
  406. new SugarParameter("@Drawing", item.Drawing),
  407. new SugarParameter("@Location", item.Location),
  408. new SugarParameter("@QtyOrded", input.QtyOrded.Value),
  409. new SugarParameter("@DueDate", ParseDate(input.DueDate)),
  410. new SugarParameter("@LotSerial", string.Empty),
  411. new SugarParameter("@PurOrdRecID", input.PurOrdRecID),
  412. new SugarParameter("@CreateUser", _userManager.Account),
  413. new SugarParameter("@CreateTime", DateTime.Now),
  414. new SugarParameter("@UpdateUser", _userManager.Account),
  415. new SugarParameter("@UpdateTime", DateTime.Now),
  416. new SugarParameter("@TenantId", _userManager.TenantId <= 0 ? null : _userManager.TenantId)
  417. );
  418. detailId = await _db.Ado.GetIntAsync("SELECT IFNULL(MAX(RecID),0) FROM PurOrdDetail WHERE PurOrd=@PurOrd AND Line=@Line", new SugarParameter("@PurOrd", input.PurOrd.Trim()), new SugarParameter("@Line", line));
  419. }
  420. else
  421. {
  422. detailId = input.Id.Value;
  423. line = input.Line ?? await _db.Ado.GetIntAsync("SELECT IFNULL(Line,0) FROM PurOrdDetail WHERE RecID=@Id", new SugarParameter("@Id", detailId));
  424. await _db.Ado.ExecuteCommandAsync(
  425. """
  426. UPDATE PurOrdDetail
  427. SET ItemNum=@ItemNum,Descr=@Descr,UM=@UM,Rev=@Rev,Drawing=@Drawing,Location=@Location,QtyOrded=@QtyOrded,DueDate=@DueDate,UpdateUser=@UpdateUser,UpdateTime=@UpdateTime
  428. WHERE RecID=@Id
  429. """,
  430. new SugarParameter("@Id", detailId),
  431. new SugarParameter("@ItemNum", item.ItemNum),
  432. new SugarParameter("@Descr", item.Descr),
  433. new SugarParameter("@UM", item.UM),
  434. new SugarParameter("@Rev", item.Rev),
  435. new SugarParameter("@Drawing", item.Drawing),
  436. new SugarParameter("@Location", item.Location),
  437. new SugarParameter("@QtyOrded", input.QtyOrded.Value),
  438. new SugarParameter("@DueDate", ParseDate(input.DueDate)),
  439. new SugarParameter("@UpdateUser", _userManager.Account),
  440. new SugarParameter("@UpdateTime", DateTime.Now)
  441. );
  442. }
  443. await _db.Ado.ExecuteCommandAsync("DELETE FROM PurOrdDetailBatch WHERE PurOrdDetailRecID=@RecID", new SugarParameter("@RecID", detailId));
  444. var batchNo = 1;
  445. foreach (var b in input.Batches)
  446. {
  447. if (string.IsNullOrWhiteSpace(b.SuppItem) && string.IsNullOrWhiteSpace(b.ItemNum))
  448. continue;
  449. var batchItem = (await _db.Ado.SqlQueryAsync<ItemLookupRow>(
  450. """
  451. SELECT ItemNum,Descr,UM,Location,Rev,Drawing
  452. FROM ItemMaster
  453. WHERE ItemNum=@ItemNum
  454. LIMIT 1
  455. """,
  456. new SugarParameter("@ItemNum", (b.SuppItem ?? b.ItemNum)!.Trim()))).FirstOrDefault();
  457. await _db.Ado.ExecuteCommandAsync(
  458. """
  459. INSERT INTO PurOrdDetailBatch
  460. (
  461. Domain, PurOrd, Potype, Line, Batch, ItemNum, SuppItem, UM, Location,
  462. QtyOrded, QtyBO, QtyReleased, QtyReceived, QtyReturned, LotSerial, PurOrdDetailRecID,
  463. CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id
  464. )
  465. VALUES
  466. (
  467. '', @PurOrd, 'PW', @Line, @Batch, @ItemNum, @SuppItem, @UM, @Location,
  468. @QtyOrded, @QtyBO, @QtyReleased, @QtyReceived, @QtyReturned, @LotSerial, @PurOrdDetailRecID,
  469. @CreateUser, @CreateTime, @UpdateUser, @UpdateTime, @TenantId
  470. )
  471. """,
  472. new SugarParameter("@PurOrd", input.PurOrd.Trim()),
  473. new SugarParameter("@Line", line),
  474. new SugarParameter("@Batch", b.Batch ?? batchNo),
  475. new SugarParameter("@ItemNum", batchItem?.ItemNum ?? b.ItemNum?.Trim()),
  476. new SugarParameter("@SuppItem", b.SuppItem?.Trim()),
  477. new SugarParameter("@UM", b.UM ?? batchItem?.UM),
  478. new SugarParameter("@Location", b.Location ?? batchItem?.Location),
  479. new SugarParameter("@QtyOrded", b.QtyOrded ?? 0),
  480. new SugarParameter("@QtyBO", b.QtyBO.GetValueOrDefault(0)),
  481. new SugarParameter("@QtyReleased", b.QtyReleased ?? 0),
  482. new SugarParameter("@QtyReceived", b.QtyReceived ?? 0),
  483. new SugarParameter("@QtyReturned", b.QtyReturned ?? 0),
  484. new SugarParameter("@LotSerial", b.LotSerial?.Trim()),
  485. new SugarParameter("@PurOrdDetailRecID", detailId),
  486. new SugarParameter("@CreateUser", _userManager.Account),
  487. new SugarParameter("@CreateTime", DateTime.Now),
  488. new SugarParameter("@UpdateUser", _userManager.Account),
  489. new SugarParameter("@UpdateTime", DateTime.Now),
  490. new SugarParameter("@TenantId", _userManager.TenantId <= 0 ? null : _userManager.TenantId)
  491. );
  492. batchNo++;
  493. }
  494. _db.Ado.CommitTran();
  495. return new { id = detailId, message = "保存成功" };
  496. }
  497. catch
  498. {
  499. _db.Ado.RollbackTran();
  500. throw;
  501. }
  502. }
  503. [DisplayName("删除委外采购明细")]
  504. [HttpPost("outsource-order/detail/delete/{id:int}")]
  505. public async Task<object> DeleteDetail(int id)
  506. {
  507. var status = await _db.Ado.GetStringAsync("SELECT Status FROM PurOrdDetail WHERE RecID=@Id" + (_userManager.TenantId > 0 ? " AND tenant_id=@TenantId" : "") + " LIMIT 1",
  508. _userManager.TenantId > 0
  509. ? new[] { new SugarParameter("@Id", id), new SugarParameter("@TenantId", _userManager.TenantId) }
  510. : new[] { new SugarParameter("@Id", id) });
  511. if (string.Equals(status, "C", StringComparison.OrdinalIgnoreCase))
  512. throw Oops.Oh("关闭状态不允许删除");
  513. try
  514. {
  515. _db.Ado.BeginTran();
  516. await _db.Ado.ExecuteCommandAsync("DELETE FROM PurOrdDetailBatch WHERE PurOrdDetailRecID=@Id", new SugarParameter("@Id", id));
  517. await _db.Ado.ExecuteCommandAsync("DELETE FROM PurOrdDetail WHERE RecID=@Id", new SugarParameter("@Id", id));
  518. _db.Ado.CommitTran();
  519. }
  520. catch
  521. {
  522. _db.Ado.RollbackTran();
  523. throw;
  524. }
  525. return new { message = "删除成功" };
  526. }
  527. [DisplayName("委外订单新增明细初始化")]
  528. [HttpGet("outsource-order/detail/init")]
  529. public async Task<object> GetDetailInit([FromQuery] string purOrd)
  530. {
  531. if (string.IsNullOrWhiteSpace(purOrd)) throw Oops.Oh("采购订单号不能为空");
  532. var tenantWhere = _userManager.TenantId > 0 ? " AND p.tenant_id=@TenantId" : string.Empty;
  533. var initPars = _userManager.TenantId > 0
  534. ? new[] { new SugarParameter("@PurOrd", purOrd.Trim()), new SugarParameter("@TenantId", _userManager.TenantId) }
  535. : new[] { new SugarParameter("@PurOrd", purOrd.Trim()) };
  536. var row = (await _db.Ado.SqlQueryAsync<DetailInitRow>(
  537. $"""
  538. SELECT p.`Usage` AS `Usage`, IFNULL(MAX(d.Line),0)+1 AS Line, p.RecID AS PurOrdRecID, p.PurOrd AS PurOrd
  539. FROM PurOrdMaster p
  540. LEFT JOIN PurOrdDetail d ON p.PurOrd=d.PurOrd
  541. WHERE p.PurOrd=@PurOrd{tenantWhere}
  542. GROUP BY p.`Usage`, p.RecID, p.PurOrd
  543. LIMIT 1
  544. """,
  545. initPars)).FirstOrDefault();
  546. return row ?? throw Oops.Oh("采购订单不存在");
  547. }
  548. [DisplayName("选择物料列表")]
  549. [HttpGet("outsource-order/items")]
  550. public async Task<object> GetItemList([FromQuery] ItemSelectListInput input)
  551. {
  552. var page = input.Page <= 0 ? 1 : input.Page;
  553. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  554. var offset = (page - 1) * pageSize;
  555. var where = new List<string> { "1=1" };
  556. var pars = new List<SugarParameter>();
  557. if (_userManager.TenantId > 0)
  558. {
  559. where.Add("tenant_id = @TenantId");
  560. pars.Add(new SugarParameter("@TenantId", _userManager.TenantId));
  561. }
  562. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  563. {
  564. where.Add("ItemNum LIKE @ItemNum");
  565. pars.Add(new SugarParameter("@ItemNum", $"%{input.ItemNum.Trim()}%"));
  566. }
  567. if (!string.IsNullOrWhiteSpace(input.Descr))
  568. {
  569. where.Add("Descr LIKE @Descr");
  570. pars.Add(new SugarParameter("@Descr", $"%{input.Descr.Trim()}%"));
  571. }
  572. var fromSql = $"FROM ItemMaster WHERE {string.Join(" AND ", where)}";
  573. var total = await _db.Ado.GetIntAsync($"SELECT COUNT(1) {fromSql}", pars);
  574. var list = await _db.Ado.SqlQueryAsync<ItemLookupRow>(
  575. $"""
  576. SELECT RecID, ItemNum, Descr, Descr1, UM, Location, Rev, Drawing
  577. {fromSql}
  578. ORDER BY {BuildItemOrderBy(input.SortField, input.SortOrder)}
  579. LIMIT {pageSize} OFFSET {offset}
  580. """,
  581. pars);
  582. return new { total, page, pageSize, list };
  583. }
  584. [DisplayName("采购组下拉")]
  585. [HttpGet("outsource-order/options/buyers")]
  586. public async Task<object> GetBuyerOptions()
  587. {
  588. var (where, pars) = BuildTenantFilter();
  589. var list = await _db.Ado.SqlQueryAsync<OptionRow>(
  590. $"""
  591. SELECT Employee AS Value, CONCAT(TRIM(Employee),' ',TRIM(IFNULL(Name,''))) AS Label
  592. FROM EmployeeMaster
  593. WHERE 1=1{where}
  594. ORDER BY Employee
  595. """,
  596. pars);
  597. return new { list };
  598. }
  599. [DisplayName("供应商下拉")]
  600. [HttpGet("outsource-order/options/suppliers")]
  601. public async Task<object> GetSupplierOptions()
  602. {
  603. var (where, pars) = BuildTenantFilter();
  604. var list = await _db.Ado.SqlQueryAsync<OptionRow>(
  605. $"""
  606. SELECT Supp AS Value, CONCAT(TRIM(Supp),' ',TRIM(IFNULL(SortName,''))) AS Label
  607. FROM SuppMaster
  608. WHERE 1=1{where}
  609. ORDER BY Supp
  610. """,
  611. pars);
  612. return new { list };
  613. }
  614. [DisplayName("部门下拉")]
  615. [HttpGet("outsource-order/options/departments")]
  616. public async Task<object> GetDepartmentOptions()
  617. {
  618. var (where, pars) = BuildTenantFilter();
  619. var list = await _db.Ado.SqlQueryAsync<OptionRow>(
  620. $"""
  621. SELECT Department AS Value, CONCAT(TRIM(Department),' ',TRIM(IFNULL(Descr,''))) AS Label
  622. FROM DepartmentMaster
  623. WHERE 1=1{where}
  624. ORDER BY Department
  625. """,
  626. pars);
  627. return new { list };
  628. }
  629. [DisplayName("库位下拉")]
  630. [HttpGet("outsource-order/options/locations")]
  631. public async Task<object> GetLocationOptions()
  632. {
  633. var (where, pars) = BuildTenantFilter();
  634. var list = await _db.Ado.SqlQueryAsync<OptionRow>(
  635. $"""
  636. SELECT location AS Value, CONCAT(TRIM(location),' ',TRIM(IFNULL(descr,''))) AS Label
  637. FROM LocationMaster
  638. WHERE IFNULL(typed,'')<>'Supp'{where}
  639. ORDER BY location
  640. """,
  641. pars);
  642. return new { list };
  643. }
  644. private (string Where, List<SugarParameter> Pars) BuildTenantFilter()
  645. {
  646. var tenantId = _userManager.TenantId;
  647. if (tenantId > 0)
  648. return (" AND tenant_id = @TenantId", new List<SugarParameter> { new("@TenantId", tenantId) });
  649. return ("", new List<SugarParameter>());
  650. }
  651. [DisplayName("BOM组件查询")]
  652. [HttpGet("outsource-order/options/bom")]
  653. public async Task<object> GetBomComponents([FromQuery] string itemNum)
  654. {
  655. if (string.IsNullOrWhiteSpace(itemNum)) return new { list = new List<object>() };
  656. var list = await _db.Ado.SqlQueryAsync<BomComponentRow>(
  657. """
  658. SELECT
  659. psm.ComponentItem AS SuppItem,
  660. CAST(psm.Qty AS DECIMAL(18,5)) AS QtyBO,
  661. IFNULL(psm.UM, im.UM) AS UM,
  662. im.Descr AS ItemName
  663. FROM ProductStructureMaster psm
  664. LEFT JOIN ItemMaster im ON psm.ComponentItem = im.ItemNum
  665. WHERE psm.ParentItem = @itemNum
  666. ORDER BY psm.ComponentItem
  667. """,
  668. new { itemNum = itemNum.Trim() });
  669. return new { list };
  670. }
  671. private static DateTime? ParseDate(string? v) => DateTime.TryParse(v, out var d) ? d : null;
  672. private static string BuildOrderBy(string? sortField, string? sortOrder)
  673. {
  674. var dir = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  675. return sortField?.ToLowerInvariant() switch
  676. {
  677. "purord" => $"p.PurOrd {dir}",
  678. "suppname" => $"p.Supp {dir}",
  679. "buyer" => $"p.Buyer {dir}",
  680. "orddate" => $"p.OrdDate {dir}",
  681. "duedate" => $"p.DueDate {dir}",
  682. "curr" => $"p.Curr {dir}",
  683. "status" => $"p.Status {dir}",
  684. _ => "p.RecID DESC"
  685. };
  686. }
  687. private static string BuildDetailOrderBy(string? sortField, string? sortOrder)
  688. {
  689. var dir = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  690. return sortField?.ToLowerInvariant() switch
  691. {
  692. "purord" => $"d.PurOrd {dir}",
  693. "line" => $"d.Line {dir}",
  694. "itemnum" => $"d.ItemNum {dir}",
  695. "descr" => $"i.Descr {dir}",
  696. "um" => $"d.UM {dir}",
  697. "rev" => $"d.Rev {dir}",
  698. "drawing" => $"d.Drawing {dir}",
  699. "qtyorded" => $"d.QtyOrded {dir}",
  700. "qtyreceived" => $"d.QtyReceived {dir}",
  701. "qtyreleased" => $"d.QtyReleased {dir}",
  702. "duedate" => $"d.DueDate {dir}",
  703. "location" => $"d.Location {dir}",
  704. "workord" => $"d.WorkOrd {dir}",
  705. "lotserial" => $"d.LotSerial {dir}",
  706. "status" => $"d.Status {dir}",
  707. _ => "d.RecID DESC"
  708. };
  709. }
  710. private static string BuildItemOrderBy(string? sortField, string? sortOrder)
  711. {
  712. var dir = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  713. return sortField?.ToLowerInvariant() switch
  714. {
  715. "itemnum" => $"ItemNum {dir}",
  716. "descr" => $"Descr {dir}",
  717. "descr1" => $"Descr1 {dir}",
  718. "um" => $"UM {dir}",
  719. "location" => $"Location {dir}",
  720. "rev" => $"Rev {dir}",
  721. "drawing" => $"Drawing {dir}",
  722. _ => "RecID DESC"
  723. };
  724. }
  725. private sealed class OutsourceOrderListRow
  726. {
  727. public int Id { get; set; }
  728. public string? PurOrd { get; set; }
  729. public string? SuppName { get; set; }
  730. public string? Potype { get; set; }
  731. public string? ReqBy { get; set; }
  732. public string? Buyer { get; set; }
  733. public string? DepartmentDescr { get; set; }
  734. public DateTime? OrdDate { get; set; }
  735. public DateTime? DueDate { get; set; }
  736. public string? Curr { get; set; }
  737. public string? Status { get; set; }
  738. public string? Supp { get; set; }
  739. public string? BuyerCode { get; set; }
  740. public string? Department { get; set; }
  741. public string? Usage { get; set; }
  742. public string? Remark { get; set; }
  743. public int HasReceived { get; set; }
  744. }
  745. private sealed class OutsourceOrderDetailRow
  746. {
  747. public int Id { get; set; }
  748. public string? PurOrd { get; set; }
  749. public int? Line { get; set; }
  750. public string? ItemNum { get; set; }
  751. public string? Descr { get; set; }
  752. public string? UM { get; set; }
  753. public string? Rev { get; set; }
  754. public string? Drawing { get; set; }
  755. public decimal? QtyOrded { get; set; }
  756. public decimal? QtyReceived { get; set; }
  757. public decimal? QtyReleased { get; set; }
  758. public DateTime? DueDate { get; set; }
  759. public string? Location { get; set; }
  760. public string? WorkOrd { get; set; }
  761. public string? LotSerial { get; set; }
  762. public string? Status { get; set; }
  763. public int? PurOrdRecID { get; set; }
  764. }
  765. private sealed class OutsourceOrderBatchRow
  766. {
  767. public int? Batch { get; set; }
  768. public string? ItemNum { get; set; }
  769. public string? SuppItem { get; set; }
  770. public string? UM { get; set; }
  771. public string? Location { get; set; }
  772. public decimal? QtyOrded { get; set; }
  773. public decimal? QtyBO { get; set; }
  774. public decimal? QtyReleased { get; set; }
  775. public decimal? QtyReceived { get; set; }
  776. public decimal? QtyReturned { get; set; }
  777. public string? LotSerial { get; set; }
  778. }
  779. private sealed class ItemLookupRow
  780. {
  781. public long RecID { get; set; }
  782. public string? ItemNum { get; set; }
  783. public string? Descr { get; set; }
  784. public string? Descr1 { get; set; }
  785. public string? UM { get; set; }
  786. public string? Location { get; set; }
  787. public string? Rev { get; set; }
  788. public string? Drawing { get; set; }
  789. }
  790. private sealed class OptionRow
  791. {
  792. public string? Value { get; set; }
  793. public string? Label { get; set; }
  794. }
  795. private sealed class DetailInitRow
  796. {
  797. public string? Usage { get; set; }
  798. public int Line { get; set; }
  799. public int PurOrdRecID { get; set; }
  800. public string? PurOrd { get; set; }
  801. }
  802. private sealed class BomComponentRow
  803. {
  804. public string? SuppItem { get; set; }
  805. public decimal QtyBO { get; set; }
  806. public string? UM { get; set; }
  807. public string? ItemName { get; set; }
  808. }
  809. }