addReceivableDialog.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. <template>
  2. <ele-modal
  3. custom-class="ele-dialog-form long-dialog-form"
  4. :centered="true"
  5. :visible="visible"
  6. :title="title"
  7. :append-to-body="true"
  8. :close-on-click-modal="false"
  9. :maxable="true"
  10. :resizable="true"
  11. width="85%"
  12. :before-close="cancel"
  13. >
  14. <div v-loading="loading">
  15. <!--基本信息-->
  16. <headerTitle title="基本信息" style="margin-top: 30px"></headerTitle>
  17. <el-form ref="form" :model="dataForm" :rules="rules" label-width="120px">
  18. <el-row>
  19. <el-col :span="8">
  20. <el-form-item label="会计科目:" prop="accountingSubjectId">
  21. <ele-tree-select
  22. clearable
  23. :disabled="dialogType == 'view'"
  24. filterable
  25. ref="treeSelect"
  26. :data="accountingSubjectList"
  27. v-model="dataForm.accountingSubjectId"
  28. valueKey="id"
  29. labelKey="name"
  30. placeholder="请选择"
  31. @change="changeSubjectInfo"
  32. default-expand-all
  33. />
  34. </el-form-item>
  35. </el-col>
  36. <el-col :span="8">
  37. <el-form-item
  38. label="应收日期"
  39. prop="receivableDate"
  40. >
  41. <el-date-picker
  42. :disabled="dialogType == 'view'"
  43. v-model="dataForm.receivableDate"
  44. value-format="yyyy-MM-dd"
  45. type="date"
  46. style="width: 100%"
  47. >
  48. </el-date-picker>
  49. </el-form-item>
  50. </el-col>
  51. <el-col :span="8">
  52. <el-form-item prop="receivableTotalPrice" label="应收金额">
  53. <el-input :disabled="dialogType == 'view'" v-model="dataForm.receivableTotalPrice"></el-input>
  54. </el-form-item>
  55. </el-col>
  56. </el-row>
  57. <el-row>
  58. <el-col :span="8">
  59. <el-form-item prop="invoiceCode" label="发票编码">
  60. <el-input disabled v-model="dataForm.invoiceCode"></el-input>
  61. </el-form-item>
  62. </el-col>
  63. <el-col :span="8">
  64. <el-form-item prop="invoiceNos" label="发票号">
  65. <el-input disabled v-model="dataForm.invoiceNos"></el-input>
  66. </el-form-item>
  67. </el-col>
  68. <el-col :span="8">
  69. <el-form-item prop="invoiceCategory" label="发票种类">
  70. <el-select
  71. :disabled="true"
  72. clearable
  73. class="ele-block"
  74. v-model="dataForm.invoiceCategory"
  75. placeholder="请选择"
  76. >
  77. <el-option label="增值税专用发票" :value="1"></el-option>
  78. <el-option label="增值税普通发票" :value="2"></el-option>
  79. </el-select>
  80. </el-form-item>
  81. </el-col>
  82. </el-row>
  83. <el-row>
  84. <el-col :span="8">
  85. <el-form-item prop="contactName" label="公司名称">
  86. <el-input disabled v-model="dataForm.contactName"></el-input>
  87. </el-form-item>
  88. </el-col>
  89. <el-col :span="8">
  90. <el-form-item label="来源类型" prop="sourceType">
  91. <el-select
  92. :disabled="true"
  93. clearable
  94. class="ele-block"
  95. v-model="dataForm.sourceType"
  96. placeholder="请选择"
  97. >
  98. <el-option label="对账单" :value="1" />
  99. <el-option label="合同" :value="2"/>
  100. <el-option label="售后管理" :value="4" />
  101. <el-option label="销售订单" :value="3" />
  102. <el-option label="发货单" :value="5" />
  103. <el-option label="收款计划单" :value="7" />
  104. <el-option label="采购收货单" :value="6" />
  105. <el-option label="付款计划单" :value="8" />
  106. <el-option label="其他" :value="99" />
  107. <el-option label="其他" :value="9" />
  108. </el-select>
  109. </el-form-item>
  110. </el-col>
  111. <el-col :span="8">
  112. <el-form-item prop="sourceCode" label="来源编码">
  113. <el-input disabled v-model="dataForm.sourceCode"></el-input>
  114. </el-form-item>
  115. </el-col>
  116. </el-row>
  117. <el-row>
  118. <el-col :span="8">
  119. <el-form-item prop="remark" label="备注">
  120. <el-input :disabled="dialogType == 'view'" v-model="dataForm.remark"></el-input>
  121. </el-form-item>
  122. </el-col>
  123. <el-col :span="12">
  124. <el-form-item prop="files" label="附件">
  125. <fileMain v-model="dataForm.files" :type="dialogType == 'view' ? 'view' : 'add'"></fileMain>
  126. </el-form-item>
  127. </el-col>
  128. </el-row>
  129. </el-form>
  130. <headerTitle title="应收信息" style="margin-top: 30px"></headerTitle>
  131. <receivable-info
  132. :dataForm.sync="dataForm"
  133. :receivableList.sync="receivableList"
  134. dialogType="view"
  135. @addAdvanceReceipt="addAdvanceReceipt"
  136. ref="receivableInfoRef"
  137. ></receivable-info>
  138. <headerTitle title="对账明细" style="margin-top: 30px"></headerTitle>
  139. <account-detail-table
  140. ref="accountDetailTableRef"
  141. :datasource.sync="datasource"
  142. dialogType="view"
  143. :queryDimension="dataForm.queryDimension"
  144. @totalChange="handleTotalChange"
  145. @countChange="handleCountChange"
  146. ></account-detail-table>
  147. </div>
  148. <advance-receipt-select-dialog
  149. ref="advanceReceiptSelectRef"
  150. :contactId="dataForm.contactId"
  151. @changeParent="onAdvanceReceiptSelected"
  152. ></advance-receipt-select-dialog>
  153. <div slot="footer" class="footer">
  154. <el-button
  155. v-if="dialogType !== 'view'"
  156. type="primary"
  157. @click="save"
  158. v-click-once
  159. >保存
  160. </el-button>
  161. <el-button
  162. v-if="dialogType !== 'view'"
  163. type="primary"
  164. @click="save('sub')"
  165. v-click-once
  166. >提交
  167. </el-button>
  168. <el-button @click="cancel">返回</el-button>
  169. </div>
  170. <process-submit-dialog
  171. :processSubmitDialogFlag.sync="processSubmitDialogFlag"
  172. v-if="processSubmitDialogFlag"
  173. ref="processSubmitDialogRef"
  174. @reload="reload"
  175. ></process-submit-dialog>
  176. </ele-modal>
  177. </template>
  178. <script>
  179. import AccountDetailTable from '@/views/saleManage/saleOrder/accountstatement/components/accountDetailTable.vue';
  180. import ReceivableInfo from '@/views/saleManage/saleOrder/accountstatement/components//receivableInfo.vue';
  181. import AdvanceReceiptSelectDialog from '@/views/saleManage/saleOrder/accountstatement/components//advanceReceiptSelectDialog.vue';
  182. import saleForm from '@/views/saleManage/saleOrder/accountstatement/components/saleForm.vue';
  183. import {
  184. getStatementRecordListAPI,
  185. infoAccountStatementAPI,
  186. saveAccountStatementAPI,
  187. createAccountStatementAPI,
  188. updateAccountStatementAPI,
  189. accountstatementInfoAPI,
  190. accountstatementUpdateAPI
  191. } from '@/api/saleManage/accountstatement';
  192. import { getFinReceivableInitInfoAPI, getFinReceivableInfoAPI, saveReceivableAPI, updateReceivableAPI } from '@/api/financialManage/receivableManage';
  193. import processSubmitDialog from '@/BIZComponents/processSubmitDialog/processSubmitDialog.vue';
  194. import { paymentTypeOp } from '@/enum/dict';
  195. import { getTreeByPid } from '@/api/classifyManage';
  196. export default {
  197. name: 'addReceivableDialog',
  198. components: {
  199. processSubmitDialog,
  200. AccountDetailTable,
  201. ReceivableInfo,
  202. AdvanceReceiptSelectDialog,
  203. saleForm
  204. },
  205. //客户管理数据
  206. props: {
  207. visible: {
  208. type: Boolean,
  209. default: false
  210. },
  211. contactData: {
  212. type: Object,
  213. default: () => {
  214. return {};
  215. }
  216. },
  217. saleOrderData: {
  218. type: Object,
  219. default: () => {
  220. return {};
  221. }
  222. }
  223. },
  224. data() {
  225. return {
  226. paymentTypeOp,
  227. fullscreen: false,
  228. loading: false,
  229. saveLoading: false,
  230. datasource: [],
  231. recorpayList: [],
  232. receivableList: [],
  233. accountingSubjectList: [],
  234. isDetail: false,
  235. statReceipt: null,
  236. dataForm: {
  237. accountingSubjectId: '',
  238. accountingSubjectCode: '',
  239. accountingSubjectName: '',
  240. sourceType: 1,
  241. dateType: 1,
  242. queryDimension: 1,
  243. dateValue: '',
  244. contactId: '',
  245. contactName: '',
  246. sourceName: '',
  247. sourceId: '',
  248. type: '',
  249. year: '',
  250. quarter: '',
  251. month: '',
  252. dateTimeRange: [],
  253. orderNo: '',
  254. orderId: '',
  255. id: '',
  256. startDate: '',
  257. endDate: '',
  258. repliedFiles: [],
  259. // 应收信息字段
  260. fundType: 2,
  261. transactionMethod: 1,
  262. collectionTerms: '',
  263. receiptStatus: 0,
  264. totalAdvanceConfirmAmount: 0,
  265. totalReceivableAmount: 0
  266. },
  267. rules: {
  268. accountingSubjectId: { required: true, message: '请选择', trigger: 'change' },
  269. receivableDate: { required: true, message: '请选择', trigger: 'change' },
  270. receivableTotalPrice: { required: true, message: '请输入', trigger: 'change' },
  271. // sourceType: { required: true, message: '请选择', trigger: 'change' },
  272. // contactId: { required: true, message: '请选择', trigger: 'change' },
  273. // projectId: { required: true, message: '请选择', trigger: 'change' },
  274. // orderId: { required: true, message: '请选择', trigger: 'change' },
  275. // dateType: { required: true, message: '请选择', trigger: 'change' },
  276. // year: { required: true, message: '请选择', trigger: 'change' },
  277. // quarter: { required: true, message: '请选择', trigger: 'change' },
  278. // month: { required: true, message: '请选择', trigger: 'change' },
  279. // dateTimeRange: {
  280. // required: true,
  281. // message: '请选择',
  282. // trigger: 'change'
  283. // },
  284. // sourceId: { required: true, message: '请选择', trigger: 'change' },
  285. // contractId: { required: true, message: '请选择', trigger: 'change' },
  286. // queryDimension: {
  287. // required: true,
  288. // message: '请选择',
  289. // trigger: 'change'
  290. // }
  291. },
  292. title: '',
  293. processSubmitDialogFlag: false,
  294. dialogType: '',
  295. businessId: ''
  296. };
  297. },
  298. computed: {
  299. // isNonDirectionalSale() {
  300. // return this.datasource.some((item) => item.orderCategory == 4);
  301. // }
  302. },
  303. methods: {
  304. changeSubjectInfo(val) {
  305. const { code, name } = this.$refs.treeSelect.getNodeByValue(val);
  306. this.dataForm.accountingSubjectCode = code;
  307. this.dataForm.accountingSubjectName = name;
  308. },
  309. //获取分类管理中的数据
  310. async getClassifyList(id, listName) {
  311. let res = await getTreeByPid(id);
  312. this[listName] = res.data;
  313. },
  314. handleTotalChange(total) {
  315. this.dataForm.amountTotalPrice = total;
  316. },
  317. handleCountChange(count) {
  318. this.dataForm.totalStatementCount = count;
  319. },
  320. // 新增预收款:打开预收款选择弹窗
  321. addAdvanceReceipt() {
  322. this.$refs.advanceReceiptSelectRef.open();
  323. },
  324. // 预收款选择器回调:将选中的预收款映射为应收信息行
  325. onAdvanceReceiptSelected(row) {
  326. this.receivableList.push({
  327. saleOrderNo: row.saleOrderNo || '',
  328. advanceReceiptCode: row.advanceReceiptCode || '',
  329. fundTypeName: this.fundTypeLabel(row.fundType),
  330. unConfirmAmount: +row.unConfirmAmount || 0,
  331. // 本次对账预收款确收金额上限取「未对账确收金额」
  332. statementAmount: +row.unConfirmAmount || 0,
  333. currentConfirmAmount: 0,
  334. currentReceivableAmount: 0
  335. });
  336. },
  337. fundTypeLabel(val) {
  338. const item = (this.paymentTypeOp || []).find((i) => i.value === val);
  339. return item ? item.label : val != null && val !== '' ? String(val) : '';
  340. },
  341. async open(dialogType, row, type = '') {
  342. this.dialogType = dialogType;
  343. this.title =
  344. dialogType == 'add'
  345. ? '新增'
  346. : dialogType == 'update'
  347. ? '修改'
  348. : '详情';
  349. // this.dataForm.type = type;
  350. await this.getClassifyList(24, 'accountingSubjectList');
  351. // this.$nextTick(() => {
  352. // if (this.contactData.id) {
  353. // this.$refs.saleFormRef.getCusInfo(this.contactData);
  354. // }
  355. // if (this.saleOrderData.id) {
  356. // this.$refs.saleFormRef.getOrderInfo(this.saleOrderData);
  357. // }
  358. // });
  359. if (this.dialogType !== 'add') {
  360. this.businessId = row.id;
  361. this.getInfo(row);
  362. } else {
  363. let data = await getFinReceivableInitInfoAPI({
  364. statementId: row.id
  365. });
  366. this.dataForm = {
  367. ...this.dataForm,
  368. ...data,
  369. invoiceCategory: Number(data?.invoiceCategory) || 1
  370. };
  371. this.statReceipt = data.statReceipt || null;
  372. this.receivableList =
  373. (data.statReceipt && data.statReceipt.statReceiptDetailList) || [];
  374. this.datasource = data.productList || [];
  375. console.log('data~~~~~~~', data);
  376. }
  377. },
  378. // 应收
  379. // handleAddReceivable(type, row) {
  380. // this.addReceivableDialogFlag = true;
  381. // this.$nextTick(() => {
  382. // this.$refs.addReceivableDialogRef.open(type, row);
  383. // });
  384. // },
  385. //获取对账单详情
  386. async getInfo(row) {
  387. let data = await getFinReceivableInfoAPI(row.id);
  388. console.log('getInfo~~~', data);
  389. this.dataForm = {
  390. ...this.dataForm,
  391. ...data,
  392. invoiceCategory: Number(data?.invoiceCategory) || 1
  393. };
  394. this.statReceipt = data.statReceipt || null;
  395. this.receivableList =
  396. (data.statReceipt && data.statReceipt.statReceiptDetailList) || [];
  397. this.datasource = data.productList || [];
  398. // this.recorpayList = data.recorpayList || [];
  399. // 解决小数精度丢失问题,使用分进行计算
  400. // data.orderTotalAmount =
  401. // data.orderList.reduce(
  402. // (pre, cur) => pre + Math.round(+cur.orderAmount * 100),
  403. // 0
  404. // ) / 100;
  405. // data.amountTotalPrice =
  406. // data.orderList.reduce(
  407. // (pre, cur) => pre + Math.round(+cur.statementAmount * 100),
  408. // 0
  409. // ) / 100;
  410. // data.amountCompletePrice =
  411. // data.orderList.reduce(
  412. // (pre, cur) => pre + Math.round(+cur.statementedAmount * 100),
  413. // 0
  414. // ) / 100;
  415. // data.amountUnCompletePrice =
  416. // data.orderList.reduce(
  417. // (pre, cur) => pre + Math.round(+cur.unStatementAmount * 100),
  418. // 0
  419. // ) / 100;
  420. // data.orderList = data.orderList.map((item) => {
  421. // item.deliveryProducts = item.deliveryProducts.map((i) => {
  422. // return {
  423. // ...i,
  424. // taxRate: i.taxRate || 0,
  425. // discountRatio: i.discountRatio || 100,
  426. // singlePrice: i.singlePrice || 0,
  427. // originalTotalCount: i.totalCount // 保存原始数量值用于校验
  428. // };
  429. // });
  430. // return item;
  431. // });
  432. // this.datasource = data.orderList || [];
  433. // this.receivableList = data.receivableList || [];
  434. // this.dataForm = {
  435. // ...this.dataForm,
  436. // ...data,
  437. // fundType: data.fundType || 2,
  438. // transactionMethod: data.transactionMethod || 1,
  439. // collectionTerms: data.collectionTerms || '',
  440. // receiptStatus: data.receiptStatus || 0,
  441. // totalAdvanceConfirmAmount: data.totalAdvanceConfirmAmount || 0,
  442. // totalReceivableAmount: data.totalReceivableAmount || 0
  443. // };
  444. // switch (this.dataForm.dateType) {
  445. // case 1:
  446. // // this.dataForm.year = this.dataForm.dateValue;
  447. // this.$set(this.dataForm, 'year', this.dataForm.dateValue);
  448. // break;
  449. // case 2: {
  450. // //2023年-四季度
  451. // let data = this.dataForm.dateValue.split('年-');
  452. // this.$set(this.dataForm, 'year', data[0]);
  453. // this.$set(this.dataForm, 'quarter', data[1]);
  454. // break;
  455. // }
  456. // case 3:
  457. // this.$set(this.dataForm, 'month', this.dataForm.dateValue);
  458. // break;
  459. // default:
  460. // this.dataForm.dateValue = '';
  461. // this.$set(this.dataForm, 'dateTimeRange', [
  462. // this.dataForm.startDate,
  463. // this.dataForm.endDate
  464. // ]);
  465. // }
  466. this.$forceUpdate();
  467. },
  468. //关闭弹窗
  469. cancel() {
  470. this.$emit('update:visible', false);
  471. },
  472. //查询获取订单数据
  473. async handleSearch(params) {
  474. // console.log('this.$refs.inventoryTableRef~~~', this.$refs.inventoryTableref);
  475. let searchQuery = {
  476. endDate: params.endDate,
  477. sourceId: params.sourceId,
  478. sourceType: params.sourceType,
  479. startDate: params.startDate,
  480. queryDimension: params.queryDimension,
  481. type: 1
  482. };
  483. // const searchQuery = ["SALES20251225007","SALES20251225005","SALES20251225009"];
  484. this.loading = true;
  485. try {
  486. let data = await getStatementRecordListAPI(searchQuery);
  487. console.log(data, 'data');
  488. this.loading = false;
  489. data = data.map((item) => {
  490. item.deliveryProducts = item.deliveryProducts.map((i) => {
  491. return {
  492. ...i,
  493. taxRate: i.taxRate || 0,
  494. discountRatio: i.discountRatio || 100,
  495. singlePrice: i.singlePrice || 0,
  496. originalTotalCount: i.totalCount // 保存原始数量值用于校验
  497. };
  498. });
  499. return item;
  500. });
  501. this.datasource = data || [];
  502. console.log(this.datasource, 'this.datasource~~~');
  503. // this.recorpayList = data.recorpayList || [];
  504. this.dataForm = {
  505. ...this.dataForm,
  506. // amountPayablePrice: data.amountPayablePrice,
  507. // amountReceivablePrice: data.amountReceivablePrice,
  508. // amountTotalPrice: data.amountTotalPrice,
  509. // amountPayablePass: data.amountPayablePass,
  510. // amountReceivablePass: data.amountReceivablePass
  511. orderTotalAmount:
  512. data.reduce(
  513. (pre, cur) => pre + Math.round(+cur.orderAmount * 100),
  514. 0
  515. ) / 100,
  516. amountTotalPrice:
  517. data.reduce(
  518. (pre, cur) => pre + Math.round(+cur.statementAmount * 100),
  519. 0
  520. ) / 100,
  521. amountCompletePrice:
  522. data.reduce(
  523. (pre, cur) => pre + Math.round(+cur.statementedAmount * 100),
  524. 0
  525. ) / 100,
  526. amountUnCompletePrice:
  527. data.reduce(
  528. (pre, cur) => pre + Math.round(+cur.unStatementAmount * 100),
  529. 0
  530. ) / 100
  531. };
  532. this.$forceUpdate();
  533. } catch (error) {
  534. this.loading = false;
  535. }
  536. // if (!this.datasource.length) this.$message.warning('暂无订单信息');
  537. },
  538. //保存
  539. async save(is) {
  540. if (!this.datasource.length)
  541. return this.$message.warning('暂无对账信息');
  542. try {
  543. // 校验明细表与应收信息表单
  544. await this.$refs.accountDetailTableRef.getValidForm();
  545. await this.$refs.receivableInfoRef.validate();
  546. console.log(this.dialogType, 'this.dialogType~~~');
  547. let api =
  548. this.dialogType == 'add'
  549. ? saveReceivableAPI
  550. : updateReceivableAPI;
  551. // const totalAdvanceConfirmAmount =
  552. // this.receivableList.reduce(
  553. // (pre, cur) =>
  554. // pre + Math.round((+cur.currentConfirmAmount || 0) * 100),
  555. // 0
  556. // ) / 100;
  557. // const totalReceivableAmount =
  558. // Math.round(
  559. // ((+this.dataForm.amountTotalPrice || 0) -
  560. // totalAdvanceConfirmAmount) *
  561. // 100
  562. // ) / 100;
  563. let params = {
  564. ...this.dataForm,
  565. // totalAdvanceConfirmAmount,
  566. // totalReceivableAmount,
  567. productList: this.datasource,
  568. statReceipt: this.statReceipt
  569. // recorpayList: this.recorpayList
  570. };
  571. this.saveLoading = true;
  572. let data = await api(params);
  573. if (is == 'sub') {
  574. await this.submitApprove(data);
  575. return;
  576. // await submitAccountStatementApproveAPI({
  577. // businessId: data,
  578. // type: this.dataForm.type
  579. // });
  580. }
  581. this.$message.success('操作成功');
  582. this.reload();
  583. this.saveLoading = false;
  584. } catch (error) {
  585. console.error('保存失败:', error);
  586. // 错误提示已经在 getValidForm 方法中处理
  587. this.saveLoading = false;
  588. }
  589. },
  590. async submitApprove(res) {
  591. console.log(res, 'res~~~');
  592. console.log(this.businessId || res, 'this.businessId || res');
  593. const data = await getFinReceivableInfoAPI(this.businessId || res);
  594. console.log(data, 'data~~~');
  595. this.processSubmitDialogFlag = true;
  596. this.$nextTick(() => {
  597. let params = {
  598. businessId: data.id,
  599. businessKey: 'fin_receivable_approve',
  600. formCreateUserId: data.createUserId,
  601. variables: {
  602. businessCode: data.code,
  603. businessName: data.contactName,
  604. businessType: '应收'
  605. }
  606. };
  607. this.$refs.processSubmitDialogRef.init(params);
  608. });
  609. },
  610. reload() {
  611. this.cancel();
  612. //刷新主页面
  613. this.$emit('done');
  614. }
  615. }
  616. };
  617. </script>
  618. <style scoped lang="scss">
  619. ::v-deep.el-divider {
  620. margin: 10px;
  621. font-weight: bold;
  622. }
  623. </style>