ProcessDataJob.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. <?php
  2. namespace App\Jobs;
  3. use App\Model\DDEmployee;
  4. use App\Model\Inventory;
  5. use App\Model\Record;
  6. use App\Model\U8State;
  7. use App\Model\Vendor;
  8. use App\Service\U8DatabaseServerService;
  9. use Illuminate\Bus\Queueable;
  10. use Illuminate\Contracts\Queue\ShouldQueue;
  11. use Illuminate\Foundation\Bus\Dispatchable;
  12. use Illuminate\Queue\InteractsWithQueue;
  13. use Illuminate\Queue\SerializesModels;
  14. use Symfony\Component\Console\Output\ConsoleOutput;
  15. use Symfony\Component\Console\Output\OutputInterface;
  16. class ProcessDataJob implements ShouldQueue
  17. {
  18. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  19. protected $data;
  20. public $timeout = 30;
  21. public function __construct($data)
  22. {
  23. //record表
  24. $this->data = $data;
  25. }
  26. public function handle()
  27. {
  28. try {
  29. list($bool, $msg) = $this->syncApprovedRecords($this->data);
  30. if(! $bool) $this->finalDo($msg, $this->data);
  31. } catch (\Throwable $e) {
  32. $this->finalDo("异常:" . $e->getMessage());
  33. $this->delete();
  34. }
  35. }
  36. private function finalDo($msg, $record){
  37. Record::where('id', $record['id'])
  38. ->update(['result' => $msg]);
  39. }
  40. private function syncApprovedRecords($record)
  41. {
  42. try {
  43. $LoginType = $record['login_type'];
  44. $database = $record['database'];
  45. $service = new U8DatabaseServerService(['zt_database' => $database]);
  46. if ($service->error) return [false, $service->error];
  47. $time = date("Y-m-d H:i:s");
  48. $time1 = date("Y-m-d 00:00:00");
  49. if($record['del_time'] == 2){
  50. $type = $record['type'];
  51. $order_number = $record['order_number'];
  52. $system_name = "system";
  53. $name = DDEmployee::where('userid', $record['userid'])->where('login_type', $LoginType)->value('name');
  54. if(! empty($name)) $system_name = $name;
  55. if($type == Record::type_one){
  56. $service->db->table("PO_Pomain")
  57. ->where("cPOID", $order_number)
  58. ->update([
  59. "cVerifier" => $system_name,
  60. "iverifystateex" => 1,
  61. "cState" => 1,
  62. "cAuditTime" => $time . ".000",
  63. "cAuditDate" => $time1 . ".000",
  64. ]);
  65. }elseif($type == Record::type_two){
  66. $service->db->table("PU_AppVouch")
  67. ->where("cCode", $order_number)
  68. ->update([
  69. "cVerifier" => $system_name,
  70. "cAuditTime" => $time . ".000",
  71. "cAuditDate" => $time1 . ".000",
  72. ]);
  73. }elseif($type == Record::type_three){
  74. $service->db->table("RdRecord01")
  75. ->where("cCode", $order_number)
  76. ->update([
  77. "cHandler" => $system_name,
  78. "dnverifytime" => $time . ".000",
  79. "dVeriDate" => $time1 . ".000",
  80. ]);
  81. }elseif($type == Record::type_four){
  82. list($status, $msg) = $this->inventoryAddToU8($record, $service->db);
  83. if(! $status) return [false, $msg];
  84. }elseif($type == Record::type_five){
  85. list($status, $msg) = $this->vendorAddToU8($record, $service->db);
  86. if(! $status) return [false, $msg];
  87. }
  88. // 更新本地数据 通过状态
  89. Record::where("id", $record['id'])->update(['del_time' => 3]);
  90. // 更新状态为 通过状态
  91. U8State::updateOrCreate(
  92. ['order_number' => $record['order_number'], 'login_type' => $record['login_type'], 'type' => $record['type']],
  93. ['state' => U8State::state_one]
  94. );
  95. }
  96. $service->close();
  97. } catch (\Throwable $e) {
  98. return [false, $e->getMessage()];
  99. }
  100. return [true, ''];
  101. }
  102. //U8 存货新增到用友
  103. public function inventoryAddToU81($data, $service){
  104. $inventory = Inventory::where('del_time', 0)
  105. ->where('order_number', $data['order_number'])
  106. ->where('login_type', $data['login_type'])
  107. ->first();
  108. if(empty($inventory)) return [false, '存货记录不存在或已被删除'];
  109. $inventory = $inventory->toArray();
  110. $title = $inventory['title'];
  111. $category_code = $inventory['category_code'];
  112. if(! empty($data['code'])){
  113. //用户传入
  114. $no = $inventory['code'];
  115. $flag_title = "重填";
  116. }else{
  117. //生成编码
  118. list($status, $msg) = $this->generate('inventory',$category_code, $service);
  119. if(! $status) return [false, $msg];
  120. $no = $msg;
  121. $flag_title = "重试";
  122. }
  123. $inventoryData = [
  124. 'cInvCode' => $no,
  125. 'cInvName' => $title,
  126. 'cInvCCode' => $category_code,
  127. 'cInvStd' => $inventory['size'] ?? null, // 规格型号
  128. 'bSale' => $inventory['bSale'], //内销
  129. 'bExpSale' => $inventory['bExpSale'], //外销
  130. 'bPurchase' => $inventory['bPurchase'], // 采购
  131. 'bSelf' => $inventory['bSelf'], // 自制
  132. 'bComsume' => $inventory['bComsume'], // 生产耗材
  133. 'iGroupType' => $inventory['unit_group_type'], // 计量单位组类别
  134. 'cGroupCode' => $inventory['unit_group_code'], //计量单位组
  135. 'cComUnitCode' => $inventory['unit_code'], // 主计量单位
  136. 'cShopUnit' => $inventory['unit_code'], // 零售计量单位
  137. 'iImpTaxRate' => $inventory['iImpTaxRate'], // 进项税率
  138. 'iTaxRate' => $inventory['iTaxRate'], // 销项税率
  139. 'dSDate' => date("Y-m-d 00:00:00.000"), // 启用日期
  140. 'cEnterprise' => $inventory['vendor_code_title'] ?? null,
  141. 'iSupplyType' => '0', // 供应类型
  142. 'fConvertRate' => '1.0', // 转换因子
  143. 'bInTotalCost' => '1', // 成本累计否
  144. 'cPlanMethod' => 'R',
  145. 'cSRPolicy' => 'PE',
  146. 'bBomMain' => '0', // 允许BOM母件
  147. 'bBomSub' => '0', // 允许BOM子件
  148. 'bProductBill' => '0', // 允许生产订单
  149. 'iPlanDefault' => '1',
  150. 'iAllocatePrintDgt' => '4',
  151. 'bService' => '0',
  152. 'bAccessary' => '0',
  153. 'iInvAdvance' => '0.0',
  154. 'bInvQuality' => '0',
  155. 'bInvBatch' => '0',
  156. 'bInvEntrust' => '0',
  157. 'bInvOverStock' => '0',
  158. 'bFree1' => '0',
  159. 'bFree2' => '0',
  160. 'bInvType' => '0',
  161. 'bFree3' => '0',
  162. 'bFree4' => '0',
  163. 'bFree5' => '0',
  164. 'bFree6' => '0',
  165. 'bFree7' => '0',
  166. 'bFree8' => '0',
  167. 'bFree9' => '0',
  168. 'bFree10' => '0',
  169. 'cCreatePerson' => 'demo',
  170. 'cModifyPerson' => 'demo', // 变更
  171. 'dModifyDate' => date("Y-m-d H:i:s.000"),
  172. 'bFixExch' => '0',
  173. 'bTrack' => '0',
  174. 'bSerial' => '0',
  175. 'bBarCode' => '0',
  176. 'bSolitude' => '0',
  177. 'bSpecialties' => '0',
  178. 'bPropertyCheck' => '0',
  179. 'iRecipeBatch' => '0',
  180. 'bPromotSales' => '0',
  181. 'bPlanInv' => '0',
  182. 'bProxyForeign' => '0',
  183. 'bATOModel' => '0',
  184. 'bCheckItem' => '0',
  185. 'bPTOModel' => '0',
  186. 'bEquipment' => '0',
  187. 'bMPS' => '0',
  188. 'bROP' => '0',
  189. 'bRePlan' => '0',
  190. 'bBillUnite' => '0',
  191. 'bCutMantissa' => '0',
  192. 'bConfigFree1' => '0',
  193. 'bConfigFree2' => '0',
  194. 'bConfigFree3' => '0',
  195. 'bConfigFree4' => '0',
  196. 'bConfigFree5' => '0',
  197. 'bConfigFree6' => '0',
  198. 'bConfigFree7' => '0',
  199. 'bConfigFree8' => '0',
  200. 'bConfigFree9' => '0',
  201. 'bConfigFree10' => '0',
  202. 'bPeriodDT' => '0',
  203. 'bOutInvDT' => '0',
  204. 'bBackInvDT' => '0',
  205. 'bDTWarnInv' => '0',
  206. 'bImportMedicine' => '0',
  207. 'bFirstBusiMedicine' => '0',
  208. 'bForeExpland' => '0',
  209. 'bInvModel' => '0',
  210. 'bKCCutMantissa' => '0',
  211. 'bReceiptByDT' => '0',
  212. 'bCheckBSATP' => '0',
  213. 'bCheckFree1' => '0',
  214. 'bCheckFree2' => '0',
  215. 'bCheckFree3' => '0',
  216. 'bCheckFree4' => '0',
  217. 'bCheckFree5' => '0',
  218. 'bCheckFree6' => '0',
  219. 'bCheckFree7' => '0',
  220. 'bCheckFree8' => '0',
  221. 'bCheckFree9' => '0',
  222. 'bCheckFree10' => '0',
  223. 'iCheckATP' => '0',
  224. 'bPiece' => '0',
  225. 'bSrvItem' => '0',
  226. 'bSrvFittings' => '0',
  227. 'bSpecialOrder' => '0',
  228. 'bTrackSaleBill' => '0',
  229. 'bCheckBatch' => '0',
  230. 'bMngOldpart' => '0',
  231. ];
  232. $inventorySubData = [
  233. 'cInvSubCode' => $no,
  234. 'iSurenessType' => '1',
  235. 'bIsAttachFile' => '0',
  236. 'bInByProCheck' => '1',
  237. 'iRequireTrackStyle' => '0',
  238. 'iExpiratDateCalcu' => '0',
  239. 'iBOMExpandUnitType' => '1',
  240. 'iDrawType' => $inventory['iDrawType'] ?? 0, // 领用方式
  241. 'fInvCIQExch' => $inventory['customs_change_rate'] ?? 1, // 海关换算率
  242. 'bInvKeyPart' => '1',
  243. 'iAcceptEarlyDays' => '999',
  244. 'dInvCreateDatetime' => date("Y-m-d H:i:s.000"),
  245. 'bPUQuota' => '0',
  246. 'bInvROHS' => '0',
  247. 'bPrjMat' => '0',
  248. 'bInvAsset' => '0',
  249. 'bSrvProduct' => '0',
  250. 'iAcceptDelayDays' => '0',
  251. 'bSCkeyProjections' => '0',
  252. 'iSupplyPeriodType' => '1',
  253. 'iAvailabilityDate' => '1',
  254. 'bImport' => '0',
  255. 'bCheckSubitemCost' => '1',
  256. 'fRoundFactor' => '0.0',
  257. 'bConsiderFreeStock' => '1',
  258. 'bSuitRetail' => '0',
  259. 'bFeatureMatch' => '0',
  260. 'bProduceByFeatureAllocate' => '0',
  261. 'bMaintenance' => '0',
  262. 'iMaintenanceCycleUnit' => '0',
  263. 'bCoupon' => '0',
  264. 'bStoreCard' => '0',
  265. 'bProcessProduct' => '0',
  266. 'bProcessMaterial' => '0',
  267. // 所有的价格自由项默认设为 '0'
  268. 'bPurPriceFree1' => '0', 'bPurPriceFree2' => '0', 'bPurPriceFree3' => '0', 'bPurPriceFree4' => '0', 'bPurPriceFree5' => '0',
  269. 'bPurPriceFree6' => '0', 'bPurPriceFree7' => '0', 'bPurPriceFree8' => '0', 'bPurPriceFree9' => '0', 'bPurPriceFree10' => '0',
  270. 'bOMPriceFree1' => '0', 'bOMPriceFree2' => '0', 'bOMPriceFree3' => '0', 'bOMPriceFree4' => '0', 'bOMPriceFree5' => '0',
  271. 'bOMPriceFree6' => '0', 'bOMPriceFree7' => '0', 'bOMPriceFree8' => '0', 'bOMPriceFree9' => '0', 'bOMPriceFree10' => '0',
  272. 'bSalePriceFree1' => '0', 'bSalePriceFree2' => '0', 'bSalePriceFree3' => '0', 'bSalePriceFree4' => '0', 'bSalePriceFree5' => '0',
  273. 'bSalePriceFree6' => '0', 'bSalePriceFree7' => '0', 'bSalePriceFree8' => '0', 'bSalePriceFree9' => '0', 'bSalePriceFree10' => '0',
  274. // 所有的控制自由项默认设为 '0'
  275. 'bControlFreeRange1' => '0', 'bControlFreeRange2' => '0', 'bControlFreeRange3' => '0', 'bControlFreeRange4' => '0', 'bControlFreeRange5' => '0',
  276. 'bControlFreeRange6' => '0', 'bControlFreeRange7' => '0', 'bControlFreeRange8' => '0', 'bControlFreeRange9' => '0', 'bControlFreeRange10' => '0',
  277. // 所有的批次属性默认设为 '0'
  278. 'bBatchProperty1' => '0', 'bBatchProperty2' => '0', 'bBatchProperty3' => '0', 'bBatchProperty4' => '0', 'bBatchProperty5' => '0',
  279. 'bBatchProperty6' => '0', 'bBatchProperty7' => '0', 'bBatchProperty8' => '0', 'bBatchProperty9' => '0', 'bBatchProperty10' => '0',
  280. 'bBondedInv' => '0',
  281. 'bBatchCreate' => '0',
  282. ];
  283. try {
  284. $service->beginTransaction();
  285. $exists = $service->table('Inventory')
  286. ->where('cInvCode', $inventoryData['cInvCode'])
  287. ->lockForUpdate() // 锁定该行,防止并发冲突
  288. ->exists();
  289. if ($exists) return [false, '存货编码已存在,请' . $flag_title];
  290. $service->table('Inventory')->insert($inventoryData);
  291. $service->table('Inventory_sub')->insert($inventorySubData);
  292. $service->commit();
  293. } catch (\Throwable $exception) {
  294. $service->rollBack();
  295. return [false, "创建用友失败: " . $exception->getMessage()];
  296. }
  297. return [true, ''];
  298. }
  299. /**
  300. * 将存货信息同步到用友 U8 系统
  301. * * @param array $data 包含 order_number 和 login_type 等关键参数
  302. * @param object $service 用友数据库连接实例 (DB Service)
  303. * @return array [bool, string]
  304. */
  305. public function inventoryAddToU8($data, $service)
  306. {
  307. // 1. 获取本地存货记录
  308. $inventory = Inventory::where('del_time', 0)
  309. ->where('order_number', $data['order_number'])
  310. ->where('login_type', $data['login_type'])
  311. ->first();
  312. if (empty($inventory)) {
  313. return [false, '本地存货记录不存在或已被删除'];
  314. }
  315. $inventory = $inventory->toArray();
  316. // 2. 提取核心属性变量并转换为布尔/整数
  317. $bSale = (int)($inventory['bSale'] ?? 0); // 销售(内销)
  318. $bExpSale = (int)($inventory['bExpSale'] ?? 0); // 外销
  319. $bPurchase = (int)($inventory['bPurchase'] ?? 0); // 采购
  320. $bSelf = (int)($inventory['bSelf'] ?? 0); // 自制
  321. $bComsume = (int)($inventory['bComsume'] ?? 0); // 生产耗用
  322. $bProxy = (int)($inventory['bProxy'] ?? 0); // 委外 (若数据库无此字段,建议默认0)
  323. // --- 用友业务规则动态计算开始 ---
  324. // 2.1 计划默认属性 (iPlanDefault)
  325. // 规则:自制=1, 委外=2, 采购=3, 计划品=4。优先级:自制 > 委外 > 采购
  326. $iPlanDefault = 0;
  327. if ($bSelf) {
  328. $iPlanDefault = 1;
  329. } elseif ($bProxy) {
  330. $iPlanDefault = 2;
  331. } elseif ($bPurchase) {
  332. $iPlanDefault = 3;
  333. }
  334. // 2.2 允许BOM母件 (bBomMain)
  335. // 规则:自制、委外、计划品等默认为1;纯采购件默认为0
  336. $bBomMain = ($bSelf || $bProxy) ? 1 : 0;
  337. // 2.3 允许BOM子件 (bBomSub)
  338. // 规则:只要涉及到生产消耗或采购,通常都允许作为子件
  339. $bBomSub = ($bSelf || $bProxy || $bPurchase || $bComsume) ? 1 : 0;
  340. // 2.4 允许生产订单 (bProductBill)
  341. // 规则:只有“自制”属性为“是”时,才允许下达生产订单
  342. $bProductBill = $bSelf ? 1 : 0;
  343. // 2.5 计划方法 (cPlanMethod) 和 成本相关 (bInTotalCost)
  344. // 规则:如果非内外销且非生产耗用,但勾选了BOM子件,属于辅助性物料,不计算MRP
  345. $cPlanMethod = 'R'; // R: MRP/MPS计算
  346. $bInTotalCost = 1; // 默认参与成本累计
  347. if (!$bSale && !$bExpSale && !$bComsume && $bBomSub) {
  348. $cPlanMethod = 'N'; // N: 不计算
  349. $bInTotalCost = 0;
  350. }
  351. // --- 用友业务规则动态计算结束 ---
  352. // 3. 编码生成或使用传入编码
  353. $category_code = $inventory['category_code'];
  354. if (!empty($data['code'])) {
  355. $no = $data['code'];
  356. $flag_title = "重填";
  357. } else {
  358. // 调用编码生成器 (假设方法名为 generate)
  359. list($status, $msg) = $this->generate('inventory', $category_code, $service);
  360. if (!$status) {
  361. return [false, "生成存货编码失败: " . $msg];
  362. }
  363. $no = $msg;
  364. $flag_title = "重试";
  365. }
  366. // 4. 构建 Inventory 主表数据
  367. $inventoryData = [
  368. 'cInvCode' => $no,
  369. 'cInvName' => $inventory['title'],
  370. 'cInvCCode' => $category_code,
  371. 'cInvStd' => $inventory['size'] ?? null, // 规格型号
  372. 'bSale' => $bSale,
  373. 'bExpSale' => $bExpSale,
  374. 'bPurchase' => $bPurchase,
  375. 'bSelf' => $bSelf,
  376. 'bComsume' => $bComsume,
  377. 'bProxyForeign' => $bProxy, // 委外
  378. 'iGroupType' => $inventory['unit_group_type'],
  379. 'cGroupCode' => $inventory['unit_group_code'],
  380. 'cComUnitCode' => $inventory['unit_code'], // 主计量单位
  381. 'cShopUnit' => $inventory['unit_code'], // 零售单位
  382. 'iImpTaxRate' => $inventory['iImpTaxRate'], // 进项税率
  383. 'iTaxRate' => $inventory['iTaxRate'], // 销项税率
  384. 'dSDate' => date("Y-m-d 00:00:00.000"), // 启用日期
  385. 'cEnterprise' => $inventory['vendor_code_title'] ?? null,
  386. 'iSupplyType' => '0', // 领料方式:领用
  387. 'fConvertRate' => '1.0', // 转换因子
  388. 'bInTotalCost' => $bInTotalCost, // 动态计算
  389. 'cPlanMethod' => $cPlanMethod, // 动态计算
  390. 'cSRPolicy' => 'PE', // 供需政策:PE
  391. 'bBomMain' => $bBomMain, // 动态计算
  392. 'bBomSub' => $bBomSub, // 动态计算
  393. 'bProductBill' => $bProductBill, // 动态计算
  394. 'iPlanDefault' => $iPlanDefault, // 动态计算
  395. 'iAllocatePrintDgt' => '4',
  396. 'bService' => '0',
  397. 'bAccessary' => '0',
  398. 'iInvAdvance' => '0.0',
  399. 'bInvQuality' => '0',
  400. 'bInvBatch' => '0',
  401. 'bInvEntrust' => '0',
  402. 'bInvOverStock' => '0',
  403. 'cCreatePerson' => $user['username'] ?? 'admin',
  404. 'dModifyDate' => date("Y-m-d H:i:s.000"),
  405. 'bPlanInv' => '0',
  406. 'bATOModel' => '0',
  407. 'bPTOModel' => '0',
  408. 'bMPS' => ($iPlanDefault == 1 || $iPlanDefault == 2) ? '1' : '0', // 只有自制委外才可能作为MPS件
  409. 'bROP' => '0',
  410. 'bRePlan' => '0',
  411. 'bCutMantissa' => '0',
  412. 'bCheckBSATP' => '0',
  413. 'iCheckATP' => '0',
  414. // 以下为大量自由项/控制位字段,默认设为 0
  415. 'bFree1' => '0', 'bFree2' => '0', 'bFree3' => '0', 'bFree4' => '0', 'bFree5' => '0',
  416. 'bFree6' => '0', 'bFree7' => '0', 'bFree8' => '0', 'bFree9' => '0', 'bFree10' => '0',
  417. 'bConfigFree1' => '0', 'bConfigFree2' => '0', 'bConfigFree3' => '0', 'bConfigFree4' => '0',
  418. 'bConfigFree5' => '0', 'bConfigFree6' => '0', 'bConfigFree7' => '0', 'bConfigFree8' => '0',
  419. 'bConfigFree9' => '0', 'bConfigFree10' => '0',
  420. ];
  421. // 5. 构建 Inventory_sub 表数据
  422. $inventorySubData = [
  423. 'cInvSubCode' => $no,
  424. 'iSurenessType' => '1',
  425. 'bIsAttachFile' => '0',
  426. 'bInByProCheck' => '1',
  427. 'iRequireTrackStyle' => '0',
  428. 'iExpiratDateCalcu' => '0',
  429. 'iBOMExpandUnitType' => '1', // BOM展开单位:主计量
  430. 'iDrawType' => $inventory['iDrawType'] ?? 0, // 领用方式
  431. 'fInvCIQExch' => $inventory['customs_change_rate'] ?? 1,
  432. 'bInvKeyPart' => '1',
  433. 'iAcceptEarlyDays' => '999',
  434. 'dInvCreateDatetime' => date("Y-m-d H:i:s.000"),
  435. 'bPUQuota' => '0',
  436. 'bInvROHS' => '0',
  437. 'bPrjMat' => '0',
  438. 'bInvAsset' => '0',
  439. 'bSrvProduct' => '0',
  440. 'iAcceptDelayDays' => '0',
  441. 'bSCkeyProjections' => '0',
  442. 'iSupplyPeriodType' => '1', // 供应期间类型:天
  443. 'iAvailabilityDate' => '1', // 第一需求日
  444. 'bImport' => '0',
  445. 'bCheckSubitemCost' => '1',
  446. 'fRoundFactor' => '0.0',
  447. 'bConsiderFreeStock' => '1',
  448. 'bSuitRetail' => '0',
  449. 'bFeatureMatch' => '0',
  450. 'bProduceByFeatureAllocate' => '0',
  451. 'bMaintenance' => '0',
  452. 'iMaintenanceCycleUnit' => '0',
  453. 'bCoupon' => '0',
  454. 'bStoreCard' => '0',
  455. 'bProcessProduct' => '0',
  456. 'bProcessMaterial' => '0',
  457. // 自由项价格控制全部设为 0
  458. 'bPurPriceFree1' => '0', 'bOMPriceFree1' => '0', 'bSalePriceFree1' => '0',
  459. 'bControlFreeRange1' => '0', 'bBatchProperty1' => '0',
  460. 'bBondedInv' => '0',
  461. 'bBatchCreate' => '0',
  462. ];
  463. // 6. 执行数据库同步(使用事务)
  464. try {
  465. $service->beginTransaction();
  466. // 检查存货编码是否冲突
  467. $exists = $service->table('Inventory')
  468. ->where('cInvCode', $no)
  469. ->lockForUpdate()
  470. ->exists();
  471. if ($exists) {
  472. $service->rollBack();
  473. return [false, '存货编码 [' . $no . '] 在用友系统中已存在,请' . $flag_title];
  474. }
  475. // 插入主表和从表
  476. $service->table('Inventory')->insert($inventoryData);
  477. $service->table('Inventory_sub')->insert($inventorySubData);
  478. $service->commit();
  479. } catch (\Throwable $exception) {
  480. $service->rollBack();
  481. return [false, "同步用友数据库失败: " . $exception->getMessage()];
  482. }
  483. return [true, '同步成功'];
  484. }
  485. //U8 供应商新增
  486. public function vendorAddToU8($data, $user,$service){
  487. $vendor = Vendor::where('del_time', 0)
  488. ->where('order_number', $data['order_number'])
  489. ->where('login_type', $data['login_type'])
  490. ->first();
  491. if(empty($vendor)) return [false, '供应商记录不存在或已被删除'];
  492. $vendor = $vendor->toArray();
  493. if(! empty($vendor['code'])){
  494. //用户传入
  495. $no = $vendor['code'];
  496. $flag_title = "重填";
  497. }else{
  498. //生成编码
  499. list($status, $msg) = $this->generate('vendor','', $service);
  500. if(! $status) return [false, $msg];
  501. $no = $msg;
  502. $flag_title = "重试";
  503. }
  504. $vendorData = [
  505. 'cVenCode' => $no,
  506. 'cVenName' => $vendor['title'],
  507. 'cVenAbbName' => $vendor['easy_title'],
  508. 'cVCCode' => $vendor['category_code'],
  509. 'dVenDevDate' => date("Y-m-d 00:00:00.000"),
  510. 'iVenDisRate' => '0.0',
  511. 'iVenCreLine' => '0.0',
  512. 'iVenCreDate' => '0',
  513. 'cVenHeadCode' => $no,
  514. 'iAPMoney' => '0.0',
  515. 'iLastMoney' => '0.0',
  516. 'iLRMoney' => '0.0',
  517. 'iFrequency' => '0',
  518. 'bVenTax' => '1',
  519. 'cCreatePerson' => 'demo',
  520. 'cModifyPerson' => 'demo',
  521. 'dModifyDate' => date("Y-m-d H:i:s.000"),
  522. 'iGradeABC' => '-1',
  523. 'bLicenceDate' => '0',
  524. 'bBusinessDate' => '0',
  525. 'bProxyDate' => '0',
  526. 'bPassGMP' => '0',
  527. 'bVenCargo' => '1', // 采购
  528. 'bProxyForeign' => '0', // 委外
  529. 'bVenService' => '0', // 服务
  530. 'bVenOverseas' => '0', // 国外
  531. 'cVenExch_name' => '人民币',
  532. 'iVenGSPType' => '0',
  533. 'iVenGSPAuth' => '-1',
  534. 'bVenAccPeriodMng' => '0',
  535. 'bVenHomeBranch' => '0',
  536. 'dVenCreateDatetime' => date("Y-m-d H:i:s.000"),
  537. 'bIsVenAttachFile' => '0',
  538. 'bRetail' => '0',
  539. ];
  540. try {
  541. $service->beginTransaction();
  542. $exists = $service->table('Vendor')
  543. ->where('cVenCode', $vendorData['cVenCode'])
  544. ->lockForUpdate() // 锁定该行,防止并发冲突
  545. ->exists();
  546. if ($exists) return [false, '供应商编码已存在,请' . $flag_title];
  547. $service->table('Vendor')->insert($vendorData);
  548. $service->commit();
  549. } catch (\Throwable $exception) {
  550. $service->rollBack();
  551. return [false, "创建供应商失败: " . $exception->getMessage()];
  552. }
  553. return [true, ''];
  554. }
  555. // 生成编码
  556. public function generate($type, $classCode = "", $service)
  557. {
  558. try {
  559. // 1. 确定对象映射
  560. $cardNumber = ($type === 'inventory') ? 'inventory' : 'Vendor';
  561. $cardNumber_name = ($type === 'inventory') ? '存货' : '供应商';
  562. $table = ($type === 'inventory') ? 'Inventory' : 'Vendor';
  563. $codeField = ($type === 'inventory') ? 'cInvCode' : 'cVenCode';
  564. // 2. 获取 U8 规则定义
  565. $rule = $service->table('VoucherNumber')
  566. ->where('CardNumber', $cardNumber)
  567. ->first();
  568. if (!$rule) return [false, "未找到 {$cardNumber_name} 的规则定义"];
  569. /**
  570. * 3. 动态解析前缀 (Prefix1, Prefix2, Prefix3)
  571. * U8 的规则通常存放在 PrefixRule 或 Prefix 字段中
  572. */
  573. $prefix = "";
  574. for ($i = 1; $i <= 3; $i++) {
  575. $ruleField = "Prefix{$i}Rule";
  576. $valField = "Prefix{$i}";
  577. // 检查规则描述
  578. $ruleDesc = $rule->$ruleField ?? ''; // 例如 "存货分类编码" 或 "GYS"
  579. $staticVal = $rule->$valField ?? '';
  580. if (str_contains($ruleDesc, '分类') || str_contains($staticVal, '分类')) {
  581. // 如果规则提到“分类”,则填入传入的分类编码
  582. $prefix .= $classCode;
  583. } elseif (!empty($ruleDesc)) {
  584. // 如果规则是具体的字符(如 "GYS"),直接拼接
  585. $prefix .= $ruleDesc;
  586. } elseif (!empty($staticVal) && $staticVal !== '手工输入' && $staticVal !== '存货分类编码') {
  587. // 如果静态值不是提示语,则作为前缀
  588. $prefix .= $staticVal;
  589. }
  590. }
  591. // 4. 获取流水号配置 (Glide)
  592. $glideLen = $rule->GlideLen > 0 ? $rule->GlideLen : 4;
  593. $startNum = $rule->iStartNumber ?? 1;
  594. // 5. 查找当前最大编码
  595. $expectedLen = strlen($prefix) + $glideLen;
  596. $lastCode = $service->table($table)
  597. ->where($codeField, 'like', $prefix . '%')
  598. ->whereRaw("LEN($codeField) = $expectedLen")
  599. ->orderBy($codeField, 'desc')
  600. ->value($codeField);
  601. // 6. 生成编码
  602. if (!$lastCode) {
  603. // 初始:前缀 + 起始值补零
  604. $finalCode = $prefix . str_pad($startNum, $glideLen, '0', STR_PAD_LEFT);
  605. } else {
  606. // 截取流水号自增
  607. $lastSerial = substr($lastCode, -$glideLen);
  608. $nextSerial = ++$lastSerial;
  609. // 溢出检查
  610. if (strlen($nextSerial) > $glideLen) {
  611. return [false, "流水号已溢出"];
  612. }
  613. $finalCode = $prefix . str_pad($nextSerial, $glideLen, '0', STR_PAD_LEFT);
  614. }
  615. return [true, $finalCode];
  616. } catch (\Throwable $e) {
  617. return [false, "生成失败: " . $e->getMessage()];
  618. }
  619. }
  620. protected function echoMessage(OutputInterface $output)
  621. {
  622. //输出消息
  623. $output->writeln(json_encode($this->data));
  624. }
  625. }