Quote.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <?php
  2. namespace addons\qingdongams\controller;
  3. use addons\qingdongams\model\ExamineRecord;
  4. use addons\qingdongams\model\JointFollow;
  5. use addons\qingdongams\model\Quote as QuoteModel;
  6. use addons\qingdongams\model\QuoteFile;
  7. use addons\qingdongams\model\QuoteOther;
  8. use addons\qingdongams\model\Message;
  9. use addons\qingdongams\model\QuoteProduct;
  10. use addons\qingdongams\model\Staff;
  11. use addons\qingdongams\model\Customer;
  12. use addons\qingdongams\model\File;
  13. use addons\qingdongams\model\Product;
  14. use PhpOffice\PhpWord\TemplateProcessor;
  15. use think\Db;
  16. use think\Exception;
  17. /**
  18. * 报价单
  19. */
  20. class Quote extends StaffApi
  21. {
  22. protected $noNeedLogin = ['*'];
  23. protected $noNeedRight = [];
  24. //获取待审批费用数量
  25. public function getQuoteCheckNumber()
  26. {
  27. $faqi = QuoteModel::where([
  28. ])->where(function ($query) {
  29. $query->where('create_staff_id|owner_staff_id', 'in', Staff::getMyStaffIds());
  30. })->count();
  31. $shenpi = QuoteModel::where([
  32. 'check_status' => ['in', [0, 1]],
  33. ])->count();
  34. $this->success('请求成功', ['faqi' => $faqi, 'shenpi' => $shenpi]);
  35. }
  36. //获取报价单记录
  37. public function getList()
  38. {
  39. $limit = input("limit/d", 10);
  40. $params = $this->request->post();
  41. $where = [];
  42. $order = 'id desc';
  43. $check_status = input('check_status');//1审核中、2审核通过、3审核未通过
  44. $type_tab = input('type_tab', 0);
  45. $whereFlow = $followWhere = $whereOr = [];
  46. if ($check_status) {
  47. //1审核中、2审核通过、3审核未通过、4撤销
  48. if ($check_status == 1) {
  49. $where['check_status'] = ['in', [0, 1]];
  50. $followWhere[] = ['exp', Db::raw('FIND_IN_SET(' . $this->auth->id . ',check_staff_ids)')];
  51. } elseif ($check_status == 2) {
  52. $where['check_status'] = 2;
  53. $where['status'] = ['neq', 9];
  54. $followWhere[] = ['exp', Db::raw('FIND_IN_SET(' . $this->auth->id . ',check_staff_ids)')];
  55. } elseif ($check_status == 3) {
  56. $where['check_status|status'] = 3;
  57. $followWhere[] = ['exp', Db::raw('FIND_IN_SET(' . $this->auth->id . ',check_staff_ids)')];
  58. } elseif ($check_status == 4) {
  59. $where['check_status'] = 4;
  60. $followWhere[] = ['exp', Db::raw('FIND_IN_SET(' . $this->auth->id . ',check_staff_ids)')];
  61. } elseif ($check_status == 9) {
  62. $where['check_status'] = 9;
  63. $followWhere[] = ['exp', Db::raw('FIND_IN_SET(' . $this->auth->id . ',check_staff_ids)')];
  64. }
  65. }
  66. if ($type_tab == 1) {//我发起的
  67. $whereFlow['owner_staff_id'] =['in', Staff::getMyStaffIds()];
  68. } elseif ($type_tab == 2) {//我审批的
  69. $whereFlow = $followWhere;
  70. }
  71. if (isset($params['name']) && $params['name']) {//产品名称
  72. $where['number'] = ['like', "%{$params['name']}%"];
  73. }
  74. if (isset($params['customer_id']) && $params['customer_id']) {
  75. $where['customer_id'] = $params['customer_id'];
  76. }
  77. if (isset($params['status']) && is_numeric($params['status'])) {
  78. $where['status'] = $params['status'];
  79. }
  80. if (isset($params['times']) && !empty($params['times'])) {
  81. $where['createtime'] = ['between',setTimes($params['times'], 'time')];
  82. }
  83. if (isset($params['staff_id']) && !empty($params['staff_id'])) {
  84. $whereFlow=[];
  85. $whereFlow['owner_staff_id']=$params['staff_id'];
  86. }
  87. if (isset($params['type']) && $params['type']) {//客户分类
  88. $whereFlow=[];
  89. if ($params['type'] == 1) {//我的创建
  90. $where['owner_staff_id'] = $this->auth->id;
  91. } elseif ($params['type'] == 2) {//下属创建
  92. $where['owner_staff_id'] = ['in', Staff::getLowerStaffId()];
  93. }else{
  94. $where['owner_staff_id'] = ['in', Staff::getMyStaffIds()];
  95. }
  96. }
  97. if (isset($params['createtime']) && $params['createtime']) {
  98. $where['createtime'] = ['between',setTimes($params['createtime'], 'time')];
  99. }
  100. if(isset($params['id_list'])){//日志 查询id列表
  101. $where=[];
  102. $whereFlow=[];
  103. $where['id']=['in',explode(',',$params['id_list'])];
  104. }
  105. $list = QuoteModel::where($where)->where($whereFlow)->with([
  106. 'ownerStaff', 'customer', 'contacts'
  107. ])->order($order)->paginate($limit);
  108. $this->success('请求成功', $list);
  109. }
  110. //添加报价单
  111. public function addQuote()
  112. {
  113. $params = $this->request->post();
  114. // 表单验证
  115. if (($result = $this->qingdongamsValidate($params,get_class(), 'create')) !== true) {
  116. $this->error($result);
  117. }
  118. try {
  119. $result = QuoteModel::createQuote($params);
  120. $customerModel = new Customer();
  121. $customerInfo = $customerModel->where(['id' => $params['customer_id']])->find();
  122. if (!in_array($customerInfo['follow'], ['准备购买', '准备付款', '已经购买'])) {
  123. $customerModel->where(['id' => $params['customer_id']])->update(['follow' => '准备购买']);
  124. }
  125. Db::commit();
  126. } catch (Exception $e) {
  127. Db::rollback();
  128. $this->error($e->getMessage());
  129. }
  130. if ($result) {
  131. $this->success('新增报价单成功');
  132. }
  133. }
  134. //修改报价单
  135. public function editQuote()
  136. {
  137. $id = input('id');
  138. $params = $this->request->post();
  139. $row = QuoteModel::where(['id' => $id])->find();
  140. if (empty($row)) {
  141. $this->error('修改报价单信息不存在');
  142. }
  143. // 表单验证
  144. if (($result = $this->qingdongamsValidate($params,get_class(), 'edit')) !== true) {
  145. $this->error($result);
  146. }
  147. Db::startTrans();
  148. try {
  149. $params['status'] = 0;
  150. $result = QuoteModel::updateQuote($params);
  151. Db::commit();
  152. } catch (Exception $e) {
  153. Db::rollback();
  154. $this->error($e->getMessage());
  155. }
  156. $this->success('修改报价单信息成功');
  157. }
  158. //作废报价单
  159. public function tovoidQuote()
  160. {
  161. $id = input('id');
  162. $row = QuoteModel::where(['id' => $id])->find();
  163. if (empty($row)) {
  164. $this->error('报价单信息不存在');
  165. }
  166. if($row['owner_staff_id'] != $this->auth->id){
  167. $this->error('只有负责人可以作废报价单');
  168. }
  169. try {
  170. $result = QuoteModel::where(['id' => $id])->update(['status' => 9, 'check_status' => 9]);
  171. ExamineRecord::cancelExaminse(ExamineRecord::QUOTE_TYPE,$id);
  172. } catch (Exception $e) {
  173. $this->error($e->getMessage());
  174. }
  175. if ($result) {
  176. $this->success('作废报价单成功');
  177. }
  178. $this->error('作废失败');
  179. }
  180. /**
  181. * 转接报价单
  182. */
  183. public function transferQuote()
  184. {
  185. $id = input('id');
  186. $staff_id = input('staff_id');
  187. $desc = input('desc', '无');
  188. if (empty($staff_id)) {
  189. $this->error('转接销售不能为空');
  190. }
  191. $row = QuoteModel::where(['id' => $id])->with(['customer', 'ownerStaff'])->find();
  192. if (empty($row)) {
  193. $this->error('报价单信息不存在');
  194. }
  195. if ($staff_id == $row['owner_staff_id']) {
  196. $this->error('当前员工已经是报价单负责人');
  197. }
  198. $newStaff = Staff::get($staff_id);
  199. if (empty($newStaff)) {
  200. $this->error('新负责人不存在');
  201. }
  202. try {
  203. QuoteModel::transferQuote($id,$staff_id,$desc);
  204. } catch (Exception $e) {
  205. $this->error($e->getMessage());
  206. }
  207. $this->success('更换负责人成功');
  208. }
  209. /**
  210. * 报价单详情
  211. *
  212. */
  213. public function quoteDetail()
  214. {
  215. header('content-type:text/html;charset=utf-8');
  216. $id = input('id');
  217. $quote = QuoteModel::where(['id' => $id])->with([
  218. 'createStaff','orderStaff', 'product', 'customer', 'contacts'
  219. ])->find();
  220. if (empty($quote)) {
  221. $this->error('信息不存在');
  222. }
  223. $quote = $quote->toArray();
  224. $quote['product_type'] = json_decode($quote['product_type']);
  225. $quote['clause'] = htmlspecialchars_decode(html_entity_decode($quote['clause']));
  226. $quote['is_operation'] = 0;
  227. if (in_array($quote['create_staff_id'], Staff::getLowerStaffId())) {
  228. //是否可以操作
  229. $quote['is_operation'] = 1;
  230. }
  231. if ($quote['check_status'] == 0 || $quote['check_status'] == 1) {//审核
  232. $quote['is_examine'] = ExamineRecord::isExaminse(ExamineRecord::QUOTE_TYPE, $id);
  233. } else {
  234. $quote['is_examine'] = 0;
  235. }
  236. $quote['auth_login_id'] = $this->auth->id;
  237. $quote['files'] = File::where(['id' => ['in', explode(',', $quote['file_ids'])]])->field('id,types,name,file_path')->select();
  238. $quote = QuoteOther::getOther($quote);
  239. Message::setRead(Message::QUOTE_TYPE, $id, $this->auth->id);
  240. $this->success('请求成功', $quote);
  241. }
  242. //获取报价单编号
  243. public function getQuoteNumber()
  244. {
  245. $this->success('请求成功', ['number' => getItemNumber('quote')]);
  246. }
  247. //预览pdf
  248. public function previewPdf()
  249. {
  250. $params = $this->request->post();
  251. // 表单验证
  252. if (($result = $this->qingdongamsValidate($params,get_class(), 'create')) !== true) {
  253. $this->error($result);
  254. }
  255. try {
  256. unset($params['flow_staff_ids']);
  257. $lastId = QuoteModel::createQuote($params);
  258. } catch (Exception $e) {
  259. $this->error($e->getMessage());
  260. }
  261. QuoteModel::destroy($lastId);
  262. return $this->downloadQuote($lastId);
  263. }
  264. //生成pdf
  265. public function downloadQuote($id = null)
  266. {
  267. $type = input('type', 'word');
  268. if(!$id){
  269. $id = input('id', '');
  270. }
  271. $quote = QuoteModel::withTrashed()->where(['id' => $id])
  272. ->with(['customer', 'contacts','orderStaff'])->find();
  273. if (empty($quote)) {
  274. $this->error('报价单不存在');
  275. }
  276. $quote = $quote->toArray();
  277. $quoteProduct = QuoteProduct::where(['quote_id' => $id])->with(['product'])->select();
  278. $product_type = json_decode($quote['product_type'], true);
  279. $product_type_name = [];
  280. foreach ($product_type as $t) {
  281. $product_type_name[] = $t['name'] ?? '';
  282. }
  283. $product_type_name = implode(',', $product_type_name);
  284. $tmp = new TemplateProcessor('assets/addons/qingdongams/phpword/baojia1.docx');
  285. \PhpOffice\PhpWord\Settings::setCompatibility(true);
  286. \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true);
  287. $tmp->setValue('product_type', $product_type_name);//替换变量name
  288. //对齐变量
  289. $name = $quote['customer']['name'] ?? '';
  290. $name = $name . @str_repeat(' ', 13 - mb_strlen($name));
  291. $address = $quote['customer']['address'] ?? '';
  292. $address = $address . @str_repeat(' ', 13 - mb_strlen($address));
  293. $contacts = $quote['contacts']['name'] ?? '';
  294. $contacts = $contacts . @str_repeat(' ', 13 - mb_strlen($contacts));
  295. $mobile = $quote['contacts']['mobile'] ?? '';
  296. $mobile = $mobile . @str_repeat(' ', 13 - ceil(mb_strlen($mobile) / 2));
  297. $tmp->setValue('name', $name);//替换变量name
  298. $tmp->setValue('address', $address);//替换变量address
  299. $tmp->setValue('contacts', $contacts);//替换变量contacts
  300. $tmp->setValue('mobile', $mobile);//替换变量mobile
  301. $tmp->setValue('order_contacts', $quote['order_staff']['name']);//替换变量contacts
  302. $tmp->setValue('order_mobile', $quote['order_staff']['mobile']);//替换变量mobile
  303. // $tmp->setValue('date', date('Y年m月d日'));//替换变量date
  304. $rows = count($quoteProduct);//总行数
  305. $tmp->cloneRow('num', $rows);//复制行
  306. $content = '';
  307. for ($i = 0; $i < $rows; $i++) {
  308. $tmp->setValue("num#" . ($i + 1), $i + 1);//序号
  309. $tmp->setValue("one#" . ($i + 1), $quoteProduct[$i]['name'] ?? '');//名称
  310. $tmp->setValue("two#" . ($i + 1), $quoteProduct[$i]['product_type']['name'] ?? '');//规格/说明
  311. $tmp->setValue("three#" . ($i + 1), $quoteProduct[$i]['config_desc'] ?? '');//配置说明
  312. $tmp->setValue("four#" . ($i + 1), $quoteProduct[$i]['number'] ?? '');//数量
  313. $tmp->setValue("five#" . ($i + 1), $quoteProduct[$i]['unit'] ?? '');//单位
  314. $tmp->setValue("sex#" . ($i + 1), $quoteProduct[$i]['price'] ?? '');//单价
  315. $tmp->setValue("eight#" . ($i + 1), $quoteProduct[$i]['price']*$quoteProduct[$i]['number']?? '');//总价
  316. $tmp->setValue("remarks#" . ($i + 1), $quoteProduct[$i]['remarks'] ?? '');//备注
  317. $content .= "<br>" . $quoteProduct[$i]['description'] ?? '';
  318. }
  319. $tmp->setValue('discount_rate', $quote['discount_amount']);//替换变量mobile
  320. $tmp->setValue('price', $quote['quote_amount']);//替换变量mobile
  321. $tmp->setValue('price_big', convertAmountToCn($quote['quote_amount']));//替换变量mobile
  322. //start 配置
  323. $configs = [];
  324. foreach ($quoteProduct as $v) {
  325. if ($v['config']) {
  326. $pconfig = [];
  327. foreach ($v['config'] as $vrc) {
  328. $vrc['num'] = $vrc['num'] ?? 0 * $v['number'] ?? 0;
  329. $pconfig[] = $vrc;
  330. }
  331. $configs = array_merge($configs, $pconfig);
  332. }
  333. }
  334. $tmp->cloneRow('i', count($configs));//复制行
  335. $block = [];
  336. foreach ($configs as $k => $ves) {
  337. $block['i#' . ($k + 1)] = $k + 1;
  338. $block['cname#' . ($k + 1)] = $ves['name'];
  339. $block['cnum#' . ($k + 1)] = $ves['num'];
  340. $block['cremark#' . ($k + 1)] = $ves['remark'];
  341. }
  342. $tmp->cloneBlock('config', 1, true, false, [$block]);
  343. //end 配置
  344. //start 特约条款
  345. $clause = strip_tags(html_entity_decode($quote['clause']));
  346. $clause = preg_replace("/(\s|\&nbsp\;| |\xc2\xa0)/", " ", strip_tags($clause)); //去除空格和换行
  347. $clauseList = explode('·', $clause);
  348. $replacements = [];
  349. foreach ($clauseList as $v) {
  350. if ($v) {
  351. $replacements[] = ['clause' => $v];
  352. }
  353. }
  354. $tmp->cloneBlock('clauseBlock', 0, true, false, $replacements);
  355. if(empty($clause)){
  356. $tmp->cloneBlock('isClause', 0);
  357. }else{
  358. $tmp->cloneBlock('isClause');
  359. }
  360. //end 特约条款
  361. $showNames = [];
  362. foreach ($quoteProduct as $v) {
  363. $showNames[] = $v['name'];
  364. }
  365. $products = Product::where([])->order('id asc')->column('name');
  366. foreach ($products as $v) {
  367. if (in_array($v, $showNames)) {
  368. //显示
  369. $tmp->cloneBlock($v);
  370. } else {
  371. $tmp->cloneBlock($v, 0);
  372. }
  373. }
  374. $filename = $quote['customer']['name'] . date('YmdHis') . '.docx';
  375. $fileurl = './docx/' . date('Ymd') . '/';
  376. if (!file_exists($fileurl)) {
  377. mkdir($fileurl);
  378. }
  379. $tmp->saveAs($fileurl . $filename);//另存为
  380. $model = new File();
  381. $data = [
  382. 'types' => 'file',
  383. 'name' => $filename,
  384. 'save_name' => $fileurl . $filename,
  385. 'size' => filesize($fileurl . $filename),
  386. 'file_path' => trim($fileurl, '.') . $filename,
  387. ];
  388. $model->save($data);
  389. $lastid = $model->getLastInsID();
  390. $file = cdnurl($model::getUrl($lastid), true);
  391. if ($type == 'pdf') {
  392. $docfile = $_SERVER['DOCUMENT_ROOT'] . trim($data['save_name'], '.'); // word文件
  393. $pdfdir = $_SERVER['DOCUMENT_ROOT'] . trim($fileurl, '.'); // pdf文件
  394. $cmd = "export HOME=/tmp && libreoffice --headless -convert-to pdf {$docfile} -outdir {$pdfdir}";
  395. @exec($cmd);
  396. $pathinfo = pathinfo($file);
  397. $file = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.pdf';
  398. $filename = $pathinfo['filename'] . '.pdf';
  399. }
  400. $this->success('请求成功', ['file' => $file, 'id' => $lastid, 'filename' => $filename]);;
  401. }
  402. //获取附件列表
  403. public function getFilesList() {
  404. $id = input('quote_id');
  405. $files = QuoteFile::where(['quote_id' => $id])->field('file_id')->with(['file'])->select();
  406. $this->success('请求成功', $files);
  407. }
  408. //获取附件列表
  409. public function addFiles() {
  410. $quote_id = input('quote_id');
  411. $files = input('files');
  412. $files = QuoteFile::addFiles($files, $quote_id);
  413. $this->success('添加成功');
  414. }
  415. //删除附件
  416. public function deleteFiles() {
  417. $id = input('id');
  418. $files = QuoteFile::where(['file_id'=>$id])->delete();
  419. $this->success('删除成功');
  420. }
  421. //特约条款
  422. public function getClauseText()
  423. {
  424. $content = "· 交货期:合同生效后,5个、10个、15个、25个、30工作日内发货。<br>
  425. · 交货地点:贵司工厂(仅限大陆工厂)/客户提供指定安装地点<br>
  426. · 保修期:12个月或24个月<br>
  427. · 有效期:30天、60天、90天。<br>
  428. · 付款方式:全款发货/五五/三六一。<br>
  429. · 含税方式:不含税/含13%增值税。";
  430. $this->success('请求成功', $content);
  431. }
  432. }