add-edit-form.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. <script setup lang="ts">
  2. import { reactive, ref, watch, nextTick, onMounted } from "vue";
  3. import { FormRules, ElForm, ElMessage } from "element-plus";
  4. import { httpAdd } from "/@/api/InvoiceSales/invoiceApply";
  5. import RemoteSelect from "/@/components/RemoteSelect";
  6. import { httpInvoiceList } from "/@/api/InvoiceSales/invoiceApply";
  7. import { httpList as httpCompanylist } from "/@/api/parameter/finance";
  8. import OrderDialog from "./order-dialog.vue";
  9. import EditOrder from "./edit-order.vue";
  10. import { useResponseHandle } from "/@/hooks";
  11. import { useRouter } from "vue-router";
  12. import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
  13. import InvoiceTitle from "./invoice-title.vue";
  14. import { convertInvoiceTitle, convertInvoiceTitleData } from "./columns";
  15. import { useCompany } from "/@/hooks/core/useCompany";
  16. import { addition } from "/@/utils/calc";
  17. import { ADD_EDIT_FORM_RULES } from "./../../config/configs";
  18. import { xs_inv_type_list, useTypeOptions } from "/@/utils/status";
  19. enum FROM_TYPE {
  20. order = "order",
  21. invoice = "invoice"
  22. }
  23. const TYPE = ref<FROM_TYPE>(FROM_TYPE.order);
  24. const { companyList } = useCompany();
  25. const { push } = useRouter();
  26. const modelRef = ref<InstanceType<typeof OrderDialog>>(null);
  27. const editOrderRef = ref<InstanceType<typeof EditOrder>>(null);
  28. const formRef = ref<InstanceType<typeof ElForm>>(null);
  29. const mapSequenceNoToInvfee = ref<Record<string, any>>({});
  30. const responseHandle = useResponseHandle();
  31. const loading = ref(false);
  32. const orderList = ref([]);
  33. const { currentCompany } = useCompany();
  34. //最大开票金额
  35. const denomination = ref(0);
  36. const max = 150;
  37. const initform = {
  38. companyNo: "", //销售方公司抬头
  39. buyNo: "", //购买方公司抬头
  40. invtype: "", //开票类型
  41. email: "", //邮箱
  42. remark: "", //发票备注
  43. exam_remark: "", //申请备注
  44. orderArr: [], //开票销售订单明细 obj:sequenceNo//销售订单编码 inv_fee//销售订单开票金额,
  45. companyType: "01",
  46. platform_type: ""
  47. };
  48. const invoiceTypes = ref([]);
  49. const ruleForm = ref({ ...initform });
  50. const rules = reactive<FormRules>({ ...ADD_EDIT_FORM_RULES });
  51. function handleShowEditModal(_, row: any) { editOrderRef.value.onDisplay({ row }); }
  52. function handleEdit({ row, inv_fee, num }) {
  53. mapSequenceNoToInvfee.value[row.sequenceNo] = {
  54. num,
  55. inv_fee,
  56. platform_type: row.platform_type
  57. };
  58. }
  59. const removeRemarkPo = (poCode: string, index: number) => {
  60. const isLast = index === orderList.value.length - 1;
  61. const isOnly = orderList.value.length === 1;
  62. if (isOnly)
  63. return (ruleForm.value.remark = ruleForm.value.remark
  64. .split(poCode)
  65. .join(""));
  66. ruleForm.value.remark = ruleForm.value.remark
  67. .split(isLast ? ";" + poCode : poCode + ";")
  68. .join("");
  69. };
  70. const handleDelete = (index: number) => {
  71. const { sequenceNo, poCode } = orderList.value[index];
  72. removeRemarkPo(poCode, index);
  73. delete mapSequenceNoToInvfee.value[sequenceNo];
  74. orderList.value.splice(index, 1);
  75. };
  76. function generatorOrderArr() {
  77. const keys = Object.keys(mapSequenceNoToInvfee.value);
  78. const orderArr = keys.map(sequenceNo => {
  79. const { inv_fee, num, platform_type } =
  80. mapSequenceNoToInvfee.value[sequenceNo];
  81. return {
  82. platform_type,
  83. sequenceNo,
  84. inv_fee,
  85. num
  86. };
  87. });
  88. return { orderArr };
  89. }
  90. function handleSave() {
  91. formRef.value.validate(async isValid => {
  92. if (!isValid) return;
  93. if (ruleForm.value.remark.length > 184) {
  94. return ElMessage.error("发票备注不能超过184个字符");
  95. }
  96. if (orderList.value.length > max) {
  97. return ElMessage.error(`订单数量不能超过${max}个`);
  98. }
  99. const { orderArr } = generatorOrderArr();
  100. const noPriceSequenceNos = orderList.value.filter(item => {
  101. const {ainv_fee, inv_fee, apay_fee, sequenceNo } = item
  102. const { inv_fee: current_inv_fee } = orderArr.find((item) => item.sequenceNo === sequenceNo) || {}
  103. const invTotal = Number(current_inv_fee) + Number(ainv_fee) + Number(inv_fee)
  104. console.log(invTotal, apay_fee)
  105. return invTotal > apay_fee
  106. })
  107. if(noPriceSequenceNos.length !== 0){
  108. ElMessage.error(`订单:${noPriceSequenceNos.map(({ sequenceNo }) => sequenceNo)},已开票金额+未开票金额+本次开票金额必须小于或等于已认款金额`);
  109. return
  110. }
  111. const total = orderArr.reduce(
  112. (prev, { inv_fee }) => prev + Number(inv_fee),
  113. 0
  114. );
  115. if (ruleForm.value.invtype === "fully_digitalized_special_electronic" || ruleForm.value.invtype === "fully_digitalized_normal_electronic") {
  116. if (total > (1000 * 10000)) {
  117. return ElMessage.error("订单总金额不能超过1000万");
  118. }
  119. } else {
  120. if (total > denomination.value) {
  121. return ElMessage.error(`订单总金额超过最大开票面额`);
  122. }
  123. }
  124. const { code, message } = await httpAdd({
  125. relaComNo: ruleForm.value.companyNo,
  126. ...ruleForm.value,
  127. ...generatorOrderArr(),
  128. is_comon:'1'
  129. });
  130. responseHandle({
  131. code,
  132. message,
  133. handler: () => push("/InvoiceSales/invoiceApply")
  134. });
  135. });
  136. }
  137. function menu_type_change() {
  138. const { invtype } = ruleForm.value;
  139. rules.email[0].required = (
  140. invtype === "special_electronic" ||
  141. invtype === "electronic" ||
  142. invtype === "fully_digitalized_special_electronic" ||
  143. invtype === "fully_digitalized_normal_electronic");
  144. }
  145. function handleAddModalShow() {
  146. const { companyNo, buyNo ,platform_type } = ruleForm.value;
  147. if (!companyNo || !buyNo) return ElMessage.warning("请选择销售公司抬头和购买方公司抬头");
  148. if (!platform_type) return ElMessage.warning("请选择平台类型");
  149. modelRef.value.show(companyNo, platform_type);
  150. }
  151. function handleAddOrder(list) {
  152. ruleForm.value.remark = "";
  153. list.forEach((item, index) => {
  154. const { sequenceNo, platform_type, winv_fee, winv_num, poCode } = item;
  155. if (!mapSequenceNoToInvfee.value[sequenceNo]) {
  156. mapSequenceNoToInvfee.value[sequenceNo] = {
  157. inv_fee: winv_fee,
  158. num: winv_num,
  159. platform_type
  160. };
  161. orderList.value.push(item);
  162. ruleForm.value.remark += index === 0 ? poCode : ";" + poCode;
  163. }
  164. });
  165. }
  166. //设置发票抬头详情
  167. const sellerInvoiceTitle = ref<Record<string, string>>({});
  168. const purchaserInvoiceTitle = ref<Record<string, string>>({});
  169. function handleInvoiceTitle(
  170. _isSeller: boolean,
  171. invoiceTitle: Record<string, string>
  172. ) {
  173. if (!invoiceTitle) { return _isSeller ? (sellerInvoiceTitle.value = {}) : (purchaserInvoiceTitle.value = {})}
  174. if (_isSeller) {
  175. //支持的开票方式
  176. const { invoiceType, denomination: _denomination } = invoiceTitle;
  177. denomination.value = Number(_denomination) * 10000;
  178. const chunks = invoiceType.split(",");
  179. ruleForm.value.invtype = "";
  180. invoiceTypes.value = xs_inv_type_list.filter(({ value }) => chunks.includes(value));
  181. }
  182. _isSeller
  183. ? (sellerInvoiceTitle.value = convertInvoiceTitleData(convertInvoiceTitle(invoiceTitle)))
  184. : (purchaserInvoiceTitle.value = convertInvoiceTitleData(convertInvoiceTitle(invoiceTitle)));
  185. }
  186. const setSellerInvoiceTitle = handleInvoiceTitle.bind(null, true);
  187. const setPurchaserInvoiceTitle = handleInvoiceTitle.bind(null, false);
  188. async function handleCompanyChange(companyNo) {
  189. ruleForm.value.companyNo = companyNo;
  190. const { code, data, message } = await httpCompanylist({ companyNo });
  191. nextTick(() => {
  192. if (formRef.value) {
  193. formRef.value.validateField("companyNo");
  194. }
  195. });
  196. responseHandle({
  197. code,
  198. message,
  199. handler: () => setSellerInvoiceTitle(data.list[0])
  200. });
  201. }
  202. watch(
  203. () => ruleForm.value.platform_type,
  204. () => {
  205. ruleForm.value.remark = "";
  206. orderList.value = [];
  207. mapSequenceNoToInvfee.value = {};
  208. },
  209. {
  210. immediate: true
  211. }
  212. );
  213. async function handleSupplierChange(companyNo) {
  214. // ruleForm.value.buyer = companyName;
  215. const { code, data, message } = await httpCompanylist({ noRela: true, size:1000 });
  216. console.log(data, '---')
  217. responseHandle({
  218. code,
  219. message,
  220. handler: () => {
  221. const item = data.list.find(({ companyNo: _code }) => _code === companyNo);
  222. setPurchaserInvoiceTitle(item || {});
  223. }
  224. })
  225. }
  226. onMounted(async () => {
  227. await handleCompanyChange(currentCompany.value.companyNo);
  228. ruleForm.value.buyNo = 'GS2404151642335170'
  229. handleSupplierChange('GS2404151642335170')
  230. });
  231. const totalPrice = ref('0.00')
  232. watch(() => mapSequenceNoToInvfee.value, (newVal) => {
  233. totalPrice.value = '0.00'
  234. const keys = Object.keys(mapSequenceNoToInvfee.value);
  235. const orderArr = keys.map(sequenceNo => {
  236. const { inv_fee } = mapSequenceNoToInvfee.value[sequenceNo];
  237. totalPrice.value = Number(addition(totalPrice.value, inv_fee)).toFixed(2)
  238. })
  239. }, {
  240. deep: true
  241. })
  242. </script>
  243. <template>
  244. <div class="addEditForm">
  245. <el-form
  246. ref="formRef"
  247. v-loading="loading"
  248. :model="ruleForm"
  249. :rules="rules"
  250. status-icon
  251. size="small"
  252. >
  253. <el-row>
  254. <el-col :span="12">
  255. <el-form-item
  256. label="销售方公司抬头"
  257. prop="companyNo"
  258. >
  259. <el-select
  260. style="width: 100%"
  261. placeholder="销售方公司抬头"
  262. disabled
  263. v-model="ruleForm.companyNo"
  264. >
  265. <el-option
  266. v-for="c in companyList"
  267. :key="c.companyCode"
  268. :value="c.companyCode"
  269. :label="c.companyName"
  270. />
  271. </el-select>
  272. </el-form-item>
  273. <InvoiceTitle :detail="sellerInvoiceTitle" />
  274. </el-col>
  275. <el-col :span="12">
  276. <el-form-item
  277. label="购买方公司抬头"
  278. prop="companyNo"
  279. >
  280. <el-select
  281. style="width: 100%"
  282. placeholder="购买方公司抬头"
  283. v-model="ruleForm.buyNo"
  284. disabled
  285. >
  286. <el-option
  287. v-for="c in companyList"
  288. :key="c.companyCode"
  289. :value="c.companyCode"
  290. :label="c.companyName"
  291. />
  292. </el-select>
  293. </el-form-item>
  294. <InvoiceTitle v-if="ruleForm.buyNo" :detail="purchaserInvoiceTitle" />
  295. </el-col>
  296. <el-col :span="6">
  297. <el-form-item label="发票类型" prop="invtype">
  298. <el-select
  299. v-model="ruleForm.invtype"
  300. style="width: 100%"
  301. @change="menu_type_change"
  302. placeholder="发票类型"
  303. no-data-text="请选择其他销售方公司"
  304. >
  305. <el-option
  306. v-for="si in invoiceTypes"
  307. :key="si.value"
  308. :label="si.label"
  309. :value="si.value"
  310. />
  311. </el-select>
  312. </el-form-item>
  313. <el-form-item label="电子邮箱" prop="email">
  314. <el-input v-model="ruleForm.email" placeholder="电子邮箱" />
  315. </el-form-item>
  316. <el-form-item
  317. label="平台类型"
  318. prop="platform_type"
  319. >
  320. <el-switch
  321. v-model="ruleForm.platform_type"
  322. size="small"
  323. active-text="toC"
  324. inactive-text="toB"
  325. active-value="2"
  326. inactive-value="1"
  327. />
  328. </el-form-item>
  329. </el-col>
  330. <el-col :span="18">
  331. <el-form-item label="申请备注" prop="exam_remark">
  332. <el-input
  333. w-full
  334. v-model="ruleForm.exam_remark"
  335. :rows="5"
  336. type="textarea"
  337. maxlength="2000"
  338. placeholder="申请备注"
  339. show-word-limit
  340. />
  341. </el-form-item>
  342. </el-col>
  343. <el-col :span="24" v-show="TYPE === 'order'">
  344. <el-form-item label="订单列表">
  345. <div flex justify-between w-full mb-2>
  346. <div class="flex">
  347. <el-tag :type="orderList.length > max ? 'danger' : 'info'">
  348. {{ orderList.length }} / {{ max }}
  349. </el-tag>
  350. <el-tag style="margin-left: 10px" type="primary" size="small">{{totalPrice}}元</el-tag>
  351. </div>
  352. <el-button type="primary" @click="handleAddModalShow"
  353. >添加</el-button
  354. >
  355. </div>
  356. <el-table :data="orderList" stripe border max-height="300">
  357. <el-table-column
  358. prop="sequenceNo"
  359. label="销售订单编码"
  360. show-overflow-tooltip
  361. width="150"
  362. />
  363. <el-table-column
  364. prop="sequenceNo"
  365. label="销售订单主编码"
  366. show-overflow-tooltip
  367. width="150"
  368. />
  369. <el-table-column
  370. prop="poCode"
  371. label="平台订单编码"
  372. show-overflow-tooltip
  373. width="150"
  374. />
  375. <el-table-column
  376. prop="customerNo"
  377. label="购买方公司编码"
  378. width="150"
  379. show-overflow-tooltip
  380. />
  381. <el-table-column
  382. prop="customerName"
  383. label="购买方公司名称"
  384. show-overflow-tooltip
  385. width="200"
  386. />
  387. <el-table-column
  388. prop="plat_pay_fee"
  389. label="平台公司已回款"
  390. show-overflow-tooltip
  391. width="110"
  392. />
  393. <el-table-column
  394. prop="inv_cat_code"
  395. label="类目编码"
  396. show-overflow-tooltip
  397. width="110"
  398. />
  399. <el-table-column
  400. prop="inv_cat_name"
  401. label="货物和劳务名称"
  402. show-overflow-tooltip
  403. width="110"
  404. />
  405. <el-table-column
  406. prop="inv_good_name"
  407. label="开票商品名称"
  408. show-overflow-tooltip
  409. width="110"
  410. />
  411. <el-table-column
  412. prop="inv_tax"
  413. label="开票税率"
  414. show-overflow-tooltip
  415. width="80"
  416. >
  417. <template #="{ row }">
  418. {{ row.inv_tax ? (Number(row.inv_tax) * 100) + '%' : '' }}
  419. </template>
  420. </el-table-column>
  421. <el-table-column
  422. prop="winv_num"
  423. label="未开票数量"
  424. show-overflow-tooltip
  425. width="110"
  426. />
  427. <el-table-column
  428. prop="winv_fee"
  429. label="未开票金额"
  430. show-overflow-tooltip
  431. width="110"
  432. />
  433. <el-table-column
  434. label="平台类型"
  435. width="110"
  436. show-overflow-tooltip
  437. >
  438. <template #="{ row }">
  439. <el-tag size="small">{{
  440. useTypeOptions.find(
  441. p => p.value === String(row.platform_type)
  442. )?.label
  443. }}</el-tag>
  444. </template>
  445. </el-table-column>
  446. <el-table-column
  447. label="开票金额"
  448. width="110"
  449. show-overflow-tooltip
  450. >
  451. <template #="{ row }">{{
  452. mapSequenceNoToInvfee[row.sequenceNo].inv_fee
  453. }}</template>
  454. </el-table-column>
  455. <el-table-column label="开票数量" show-overflow-tooltip>
  456. <template #="{ row }">{{
  457. mapSequenceNoToInvfee[row.sequenceNo].num
  458. }}</template>
  459. </el-table-column>
  460. <el-table-column
  461. label="业务员"
  462. show-overflow-tooltip
  463. prop="ownerName"
  464. width="80"
  465. />
  466. <el-table-column fixed="right" width="70">
  467. <template #header>
  468. <span>操作</span>
  469. </template>
  470. <template #default="scope">
  471. <el-button
  472. size="small"
  473. type="primary"
  474. link
  475. :icon="useRenderIcon('edits')"
  476. @click="handleShowEditModal(scope.$index, scope.row)"
  477. />
  478. <el-button
  479. size="small"
  480. type="danger"
  481. link
  482. :icon="useRenderIcon('delete')"
  483. @click="handleDelete(scope.$index)"
  484. />
  485. </template>
  486. </el-table-column>
  487. </el-table>
  488. </el-form-item>
  489. </el-col>
  490. <el-col :span="24">
  491. <el-form-item label="发票备注" prop="remark">
  492. <div w-full>
  493. <el-input
  494. w-full
  495. v-model="ruleForm.remark"
  496. :rows="3"
  497. type="textarea"
  498. maxlength="2000"
  499. show-word-limit
  500. placeholder="发票备注"
  501. />
  502. </div>
  503. </el-form-item>
  504. </el-col>
  505. <el-col :span="24" flex justify-end>
  506. <el-button :loading="loading" type="primary" @click="handleSave"
  507. >保存</el-button
  508. >
  509. </el-col>
  510. </el-row>
  511. </el-form>
  512. <OrderDialog ref="modelRef" @save-btn-click="handleAddOrder" />
  513. <EditOrder ref="editOrderRef" @save-btn-click="handleEdit" />
  514. </div>
  515. </template>
  516. <style lang="scss" scoped>
  517. :deep(.el-descriptions) {
  518. padding: 0px !important;
  519. padding-left: 30px !important;
  520. padding-bottom: 10px !important;
  521. }
  522. </style>