king
2024-04-28 2e5fe5427d6db393e0495598ff43d90a052f4791
src/tabviews/custom/components/module/invoice/index.jsx
@@ -1,13 +1,13 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Select, Form, Input, Button, Modal, Spin } from 'antd'
import { Select, Form, Input, Button, Modal, Spin, notification } from 'antd'
import { EllipsisOutlined } from '@ant-design/icons'
import moment from 'moment'
import Api from '@/api'
import UtilsDM from '@/utils/utils-datamanage.js'
// import Utils from '@/utils/utils.js'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import InvoiceTable from './invoiceTable'
import SubTable from './subTable'
@@ -20,6 +20,8 @@
  state = {
    BID: '',
    ID: Utils.getuuid(),
    io: '',
    invTypes: [
      {value: '1', label: '电子发票(增值税专用发票)'},
      {value: '2', label: '电子发票(普通发票)'},
@@ -50,9 +52,13 @@
    reviewer: '',
    drawer: '',
    details: [],
    oriDetails: [],
    book: null,
    loading: false,
    tax_type: ''
    saveType: '',
    tax_type: '',
    reqfields: [],
    requireds: []
  }
  UNSAFE_componentWillMount () {
@@ -143,7 +149,7 @@
          cell.field = 'tax_rate'
          cell.label = '税率'
        }
        if (['Description', 'id', 'small_tax_rate'].includes(cell.field)) {
        if (['Description', 'id', 'small_tax_rate', 'free_tax_mark', 'vat_special_management'].includes(cell.field)) {
          cell.Hide = 'true'
        } else if (['spec'].includes(cell.field)) {
          cell.Width = 150
@@ -242,8 +248,17 @@
  }
  resetParam = (book) => {
    let invTypes = book.invoice_type || []
    let invoice_type = sessionStorage.getItem('pre_invoice_type') || ''
    if (invoice_type && invTypes.findIndex(item => item.value === invoice_type) === -1) {
      invoice_type = ''
    }
    this.getRequired(invoice_type)
    return {
      invTypes: book.invoice_type || [],
      invoice_type,
      invTypes: invTypes,
      orgname: book.orgname || '',
      tax_no: book.tax_no || '',
      addr: book.addr || '',
@@ -280,6 +295,10 @@
    if (result.status) {
      this.setState({
        ID: result.data[0][config.setting.primaryKey] || Utils.getuuid(),
        io: '',
        details: [],
        oriDetails: [],
        loading: false
      })
@@ -295,7 +314,301 @@
  }
  changeType = (val) => {
    sessionStorage.setItem('pre_invoice_type', val)
    this.setState({invoice_type: val})
    this.getRequired(val)
  }
  getRequired = (invoice_type) => {
    if (!invoice_type) return
    let reqfields = []
    let requireds = []
    let rds = [
      {value: 'from_to_name', label: '购买方名称'},
      {value: 'from_to_tax_no', label: '购买方纳税人识别号'},
      {value: 'from_to_addr', label: '购买方地址'},
      {value: 'from_to_tel', label: '购买方电话'},
      {value: 'from_to_bank_name', label: '购买方开户行'},
      {value: 'from_to_account_no', label: '购买方账号'},
      {value: 'from_to_mob', label: '购买方手机号'},
      {value: 'from_to_email', label: '购买方邮箱'},
      {value: 'orgname', label: '销售方名称'},
      {value: 'tax_no', label: '销售方纳税人识别号'},
      {value: 'addr', label: '销售方地址'},
      {value: 'tel', label: '销售方电话'},
      {value: 'bank_name', label: '销售方开户行'},
      {value: 'account_no', label: '销售方账号'},
    ]
    if (invoice_type === 'e_general') {
      reqfields = ['from_to_name', 'from_to_tax_no', 'orgname', 'tax_no']
      rds.forEach(item => {
        if (reqfields.includes(item.value)) {
          requireds.push(item)
        }
      })
    } else if (invoice_type === 'e_special') {
      reqfields = ['from_to_name', 'from_to_tax_no', 'from_to_addr', 'from_to_tel', 'from_to_bank_name', 'from_to_account_no', 'orgname', 'tax_no', 'addr', 'tel', 'bank_name', 'account_no']
      rds.forEach(item => {
        if (reqfields.includes(item.value)) {
          requireds.push(item)
        }
      })
    }
    this.setState({
      reqfields,
      requireds
    })
  }
  saveBill = () => {
    const { config, book, saveType } = this.state
    if (saveType) return
    setTimeout(() => {
      this.getBillMsg().then(() => {
        let sql = this.getPreSql(config.billSaveBtn)
        let param = {
          func: 'sPC_TableData_InUpDe',
          LText: sql,
          exec_type: window.GLOB.execType || 'y',
          timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
          BID: book.id
        }
        param.secretkey = Utils.encrypt('', param.timestamp)
        param.LText = Utils.formatOptions(param.LText, param.exec_type)
        this.setState({
          saveType: 'bill'
        })
        Api.genericInterface(param).then(res => {
          if (res.status) {
            notification.success({
              top: 92,
              message: '保存成功。',
              duration: 5
            })
          } else {
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
          }
          this.setState({
            saveType: ''
          })
        })
      }, (error) => {
        notification.warning({
          top: 92,
          message: error,
          duration: 5
        })
        return
      })
    }, 20)
  }
  outBill = () => {
    const { config, book, saveType } = this.state
    if (saveType) return
    setTimeout(() => {
      this.getBillMsg().then(() => {
        let sql = this.getPreSql(config.billOutBtn)
        let param = {
          func: 'sPC_TableData_InUpDe',
          LText: sql,
          key_back_type: 'Y',
          exec_type: window.GLOB.execType || 'y',
          timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
          BID: book.id
        }
        param.secretkey = Utils.encrypt('', param.timestamp)
        param.LText = Utils.formatOptions(param.LText, param.exec_type)
        console.info(sql)
      }, (error) => {
        notification.warning({
          top: 92,
          message: error,
          duration: 5
        })
        return
      })
    })
  }
  getPreSql = (btn) => {
    const { book, ID, io, details, oriDetails, invoice_type, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, from_to_code, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee } = this.state
    let BID = book.id
    let userName = sessionStorage.getItem('User_Name') || ''
    let fullName = sessionStorage.getItem('Full_Name') || ''
    let RoleID = sessionStorage.getItem('role_id') || ''
    let departmentcode = sessionStorage.getItem('departmentcode') || ''
    let organization = sessionStorage.getItem('organization') || ''
    let mk_user_type = sessionStorage.getItem('mk_user_type') || ''
    let nation = sessionStorage.getItem('nation') || ''
    let province = sessionStorage.getItem('province') || ''
    let city = sessionStorage.getItem('city') || ''
    let district = sessionStorage.getItem('district') || ''
    let address = sessionStorage.getItem('address') || ''
    let options = fromJS(oriDetails).toJS()
    let price = 0
    let tax = 0
    let lines = details.map(line => {
      let _sql = `Select '${line.productcode}', '${line.productname}', '${line.spec}', '${line.unit}', ${line.bill_count}, ${line.unitprice}, ${line.amount_line}, '${line.tax_classify_code}', '${line.tax_classify_name}', ${line.tax_rate}, ${line.tax_amount}, '${line.invoice_lp || ''}', '${line.uuid}'`
      let data_type = 'add'
      price += line.amount_line * 100
      tax += line.tax_amount * 100
      if (options.length) {
        options = options.filter(option => {
          if (option.uuid === line.uuid) {
            data_type = 'upt'
            return false
          }
          return true
        })
      }
      return _sql + `, '${data_type}'`
    })
    let _total = (price - tax) / 100
    price = price / 100
    tax = tax / 100
    if (options.length) {
      options.forEach(line => {
        lines.push(`Select '${line.productcode}', '${line.productname}', '${line.spec}', '${line.unit}', ${line.bill_count}, ${line.unitprice}, ${line.amount_line}, '${line.tax_classify_code}', '${line.tax_classify_name}', ${line.tax_rate}, ${line.tax_amount}, '${line.invoice_lp || ''}', '${line.uuid}', 'del'`)
      })
    }
    lines = lines.join(' union all ')
    let _script = ''
    btn.scripts.forEach(item => {
      if (item.status === 'false') return
      _script += `
      ${item.sql}
      `
    })
    let sql = `/* 系统字段 */
      Declare @UserName nvarchar(50), @FullName nvarchar(50), @RoleID nvarchar(512), @mk_departmentcode nvarchar(512), @mk_organization nvarchar(512), @mk_user_type nvarchar(20), @mk_nation nvarchar(50), @mk_province nvarchar(50), @mk_city nvarchar(50), @mk_district nvarchar(50), @mk_address nvarchar(100), @ErrorCode nvarchar(50), @retmsg nvarchar(4000), @account_id nvarchar(50), @account_year_id nvarchar(50), @account_code nvarchar(50), @account_year_code nvarchar(50), @bid nvarchar(50), @tbid nvarchar(50)
      Select @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @mk_departmentcode='${departmentcode}', @mk_organization='${organization}', @mk_user_type='${mk_user_type}', @mk_nation='${nation}', @mk_province='${province}', @mk_city='${city}', @mk_district='${district}', @mk_address='${address}', @ErrorCode='', @retmsg='', @account_id='${book.account_id || ''}', @account_year_id='${book.account_year_id || ''}', @account_code='${book.account_code || ''}', @account_year_code='${book.account_year_code || ''}', @bid=''
      /* 发票主表字段 */
      Declare @invoice_type Nvarchar(50), @from_to_name Nvarchar(50), @from_to_tax_no Nvarchar(50), @from_to_addr Nvarchar(100), @from_to_tel Nvarchar(50), @from_to_bank_name Nvarchar(50), @from_to_account_no Nvarchar(50), @from_to_mob Nvarchar(50), @from_to_email Nvarchar(50), @from_to_code Nvarchar(50), @orgname Nvarchar(50), @tax_no Nvarchar(50), @addr Nvarchar(100), @tel Nvarchar(50), @bank_name Nvarchar(50), @account_no Nvarchar(50), @remark Nvarchar(512), @payee Nvarchar(50), @reviewer Nvarchar(50), @drawer Nvarchar(50), @io Nvarchar(50), @orgcode Nvarchar(50), @total_net_amount Decimal(18,2), @total_tax Decimal(18,2), @total_amount Decimal(18,2)
      Select @invoice_type='${invoice_type}', @from_to_name='${from_to_name}', @from_to_tax_no='${from_to_tax_no}', @from_to_addr='${from_to_addr}', @from_to_tel='${from_to_tel}', @from_to_bank_name='${from_to_bank_name}', @from_to_account_no='${from_to_account_no}', @from_to_mob='${from_to_mob}', @from_to_email='${from_to_email}', @from_to_code='${from_to_code}', @orgname='${orgname}', @tax_no='${tax_no}', @addr='${addr}', @tel='${tel}', @bank_name='${bank_name}', @account_no='${account_no}', @remark='${remark}', @payee='${payee}', @reviewer='${reviewer}', @drawer='${drawer}', @io='${io}', @orgcode='${book.orgcode || ''}', @total_net_amount=${_total}, @total_tax=${tax}, @total_amount=${price}
      /* 发票明细临时表 */
      Declare @details_list table (productcode Nvarchar(50), productname Nvarchar(50), spec Nvarchar(50), unit Nvarchar(50), bill_count Decimal(18,10), unitprice Decimal(18,10), amount_line Decimal(18,2), tax_classify_code Nvarchar(50), tax_classify_name Nvarchar(50), tax_rate Decimal(18,2), tax_amount Decimal(18,2), invoice_lp Nvarchar(50), jskey Nvarchar(50), data_type Nvarchar(50))
      Insert into @details_list (productcode, productname, spec, unit, bill_count, unitprice, amount_line, tax_classify_code, tax_classify_name, tax_rate, tax_amount, invoice_lp, jskey, data_type)
      ${lines}
      /* 自定义脚本 */
      ${_script}
      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
    sql = sql.replace(/@ID@/ig, `'${ID}'`)
    sql = sql.replace(/@BID@/ig, `'${BID}'`)
    sql = sql.replace(/@LoginUID@/ig, `'${sessionStorage.getItem('LoginUID') || ''}'`)
    sql = sql.replace(/@SessionUid@/ig, `'${localStorage.getItem('SessionUid') || ''}'`)
    sql = sql.replace(/@UserID@/ig, `'${sessionStorage.getItem('UserID') || ''}'`)
    sql = sql.replace(/@Appkey@/ig, `'${window.GLOB.appkey || ''}'`)
    sql = sql.replace(/@typename@/ig, `'admin'`)
    if (window.GLOB.externalDatabase !== null) {
      sql = sql.replace(/@db@/ig, window.GLOB.externalDatabase)
    }
    if (sessionStorage.getItem('dataM') === 'true') { // 数据权限
      sql = sql.replace(/\$@/ig, '/*').replace(/@\$/ig, '*/').replace(/@datam@/ig, `'Y'`)
    } else {
      sql = sql.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, `''`)
    }
    if (window.GLOB.debugger === true) {
      console.info(sql.replace(/\n\s{6}/ig, '\n'))
    }
    return sql
  }
  getBillMsg = () => {
    const { requireds, invoice_type, details, remark, from_to_addr, addr } = this.state
    return new Promise((resolve, reject) => {
      let error = ''
      if (!invoice_type) {
        error = '请选择发票类型!'
      }
      if (!error) {
        requireds.forEach(item => {
          if (!this.state[item.value] && !error) {
            error = '请输入' + item.label + '!'
          }
        })
      }
      if (!error && remark.length > 512) {
        error = '备注不可超过512个字符!'
      }
      if (!error && from_to_addr.length > 100) {
        error = '购买方地址不可超过100个字符!'
      }
      if (!error && addr.length > 100) {
        error = '销售方地址不可超过100个字符!'
      }
      if (!error) {
        if (details.length === 0) {
          error = '请添加明细!'
        } else {
          details.forEach((line, index) => {
            if (error) return
            if (line.productcode) {
              if (!line.bill_count) {
                error = '明细第' + (index + 1) + '行,请输入数量!'
              } else if (!line.unitprice) {
                error = '明细第' + (index + 1) + '行,请输入单价!'
              }
            } else {
              error = '明细第' + (index + 1) + '行,请选择货物或应税劳务、服务名称!'
            }
          })
        }
      }
      if (error) {
        reject(error)
      } else {
        resolve()
      }
    })
  }
  changeBuyer = (item) => {
@@ -314,7 +627,7 @@
  }
  render() {
    const { config, book, loading, invTypes, date, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee, details, visible, tax_type } = this.state
    const { config, book, loading, invTypes, reqfields, saveType, date, invoice_type, from_to_name, from_to_tax_no, from_to_addr, from_to_tel, from_to_bank_name, from_to_account_no, from_to_mob, from_to_email, orgname, tax_no, addr, tel, bank_name, account_no, remark, reviewer, drawer, payee, details, visible, tax_type } = this.state
    if (!book || (config.wrap.datatype === 'dynamic' && !tax_no)) {
      return <div className="menu-invoice-wrap" style={config.style}>
@@ -334,15 +647,19 @@
          </div> : null
        }
        <div className="inv-action">
          <Button className="mk-bill">保存单据</Button>
          <Button className="mk-submit">提交开票</Button>
          <Button className="mk-bill" loading={saveType === 'bill'} onClick={this.saveBill}>保存单据</Button>
          <Button className="mk-submit" loading={saveType === 'out'} onClick={this.outBill}>提交开票</Button>
        </div>
        <div className="inv-header">
          <Select placeholder="请选择发票种类" onChange={this.changeType} dropdownClassName="inv-type-select">
          {invoice_type ? <Select defaultValue={invoice_type} onChange={this.changeType} dropdownClassName="inv-type-select">
            {invTypes.map(item => (
              <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
            ))}
          </Select>
          </Select> : <Select placeholder="请选择发票种类" onChange={this.changeType} dropdownClassName="inv-type-select">
            {invTypes.map(item => (
              <Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
            ))}
          </Select>}
          <div className="date">开票日期:{date}</div>
        </div>
        <div className="inv-body">
@@ -350,17 +667,17 @@
            <div className="inv-buyer">
              <div className="inv-label">购买方</div>
              <div className="inv-content">
                <Form.Item label={<>名<span></span>称</>} extra={<EllipsisOutlined onClick={() => this.setState({visible: true})}/>}>
                <Form.Item required={reqfields.includes('from_to_name')} label={<>名<span></span>称</>} extra={<EllipsisOutlined onClick={() => this.setState({visible: true})}/>}>
                  <Input placeholder="请输入购买方名称" allowClear value={from_to_name} autoComplete="off" onChange={(e) => this.setState({from_to_name: e.target.value})}/>
                </Form.Item>
                <Form.Item label="纳税人识别号">
                <Form.Item required={reqfields.includes('from_to_tax_no')} label="纳税人识别号">
                  <Input placeholder="请输入购买方纳税人识别号" allowClear value={from_to_tax_no} autoComplete="off" onChange={(e) => this.setState({from_to_tax_no: e.target.value})}/>
                </Form.Item>
                <Form.Item className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}>
                <Form.Item required={reqfields.includes('from_to_addr')} className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}>
                  <Input placeholder="请输入购买方地址" allowClear value={from_to_addr} autoComplete="off" onChange={(e) => this.setState({from_to_addr: e.target.value})}/>
                  <Input placeholder="请输入购买方电话" allowClear value={from_to_tel} autoComplete="off" onChange={(e) => this.setState({from_to_tel: e.target.value})}/>
                </Form.Item>
                <Form.Item className="mutil-input" label="开户行及账号">
                <Form.Item required={reqfields.includes('from_to_bank_name')} className="mutil-input" label="开户行及账号">
                  <Input placeholder="请输入购买方开户行" allowClear value={from_to_bank_name} autoComplete="off" onChange={(e) => this.setState({from_to_bank_name: e.target.value})}/>
                  <Input placeholder="请输入购买方账号" allowClear value={from_to_account_no} autoComplete="off" onChange={(e) => this.setState({from_to_account_no: e.target.value})}/>
                </Form.Item>
@@ -369,10 +686,10 @@
            <div className="inv-notice">
              <div className="inv-label">通知到</div>
              <div className="inv-content">
                <Form.Item label={<>手<span></span>机<span></span>号</>}>
                <Form.Item required={reqfields.includes('from_to_mob')} label={<>手<span></span>机<span></span>号</>}>
                  <Input placeholder="请输入购买方手机号" allowClear value={from_to_mob} autoComplete="off" onChange={(e) => this.setState({from_to_mob: e.target.value})}/>
                </Form.Item>
                <Form.Item label={<>邮<span></span>箱</>}>
                <Form.Item required={reqfields.includes('from_to_email')} label={<>邮<span></span>箱</>}>
                  <Input placeholder="请输入购买方邮箱" allowClear value={from_to_email} autoComplete="off" onChange={(e) => this.setState({from_to_email: e.target.value})}/>
                </Form.Item>
              </div>
@@ -385,17 +702,17 @@
            <div className="inv-buyer">
              <div className="inv-label">销售方</div>
              <div className="inv-content">
                <Form.Item label={<>名<span></span>称</>}>
                <Form.Item required={reqfields.includes('orgname')} label={<>名<span></span>称</>}>
                  <Input placeholder="请输入销售方名称" value={orgname} autoComplete="off" onChange={(e) => this.setState({orgname: e.target.value})}/>
                </Form.Item>
                <Form.Item label="纳税人识别号">
                <Form.Item required={reqfields.includes('tax_no')} label="纳税人识别号">
                  <Input placeholder="请输入销售方纳税人识别号" disabled value={tax_no} autoComplete="off"/>
                </Form.Item>
                <Form.Item className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}>
                <Form.Item required={reqfields.includes('addr')} className="mutil-input" label={<>地<span></span>址<span></span>、<span></span>电<span></span>话</>}>
                  <Input placeholder="请输入销售方地址" value={addr} autoComplete="off" onChange={(e) => this.setState({addr: e.target.value})}/>
                  <Input placeholder="请输入销售方电话" value={tel} autoComplete="off" onChange={(e) => this.setState({tel: e.target.value})}/>
                </Form.Item>
                <Form.Item className="mutil-input" label="开户行及账号">
                <Form.Item required={reqfields.includes('bank_name')} className="mutil-input" label="开户行及账号">
                  <Input placeholder="请输入销售方开户行" value={bank_name} autoComplete="off" onChange={(e) => this.setState({bank_name: e.target.value})}/>
                  <Input placeholder="请输入销售方账号" value={account_no} autoComplete="off" onChange={(e) => this.setState({account_no: e.target.value})}/>
                </Form.Item>