ApplyForInvoicingModal.tsx 11.6 KB
import { RESPONSE_CODE } from '@/constants/enum';
import { postServiceOrderApplyInvoicing } from '@/services';
import { FloatAdd, enumToSelect, getAliYunOSSFileNameFromUrl } from '@/utils';
import {
  ModalForm,
  ProFormMoney,
  ProFormSelect,
  ProFormText,
  ProFormTextArea,
  ProFormUploadDragger,
} from '@ant-design/pro-components';
import { Form, message } from 'antd';
import { useEffect, useState } from 'react';
import { PAYEE_OPTIONS } from '../constant';

// 定义选项类型
interface SelectOption {
  label: string;
  value: string;
}

export default ({ setCheckVisible, isEdit, subOrders, onClose }) => {
  const [isUrgent, setIsUrgent] = useState('');
  const [receivingCompanyOptions, setReceivingCompanyOptions] = useState<
    SelectOption[]
  >([]);

  let ids = subOrders?.map((item) => {
    return item.id;
  });

  // 定义返回类型接口
  interface MainOrderData {
    value: string | number;
    totalPayment: number;
    invoiceIssuedAmount: number;
    availableAmount: number;
  }

  // 获取唯一的主订单ID及其相关金额信息
  const getUniqueMainOrderIds = (): MainOrderData[] => {
    const mainOrderIds = subOrders?.map((item: any) => item.mainOrderId);
    const uniqueIds = [...new Set(mainOrderIds)].filter(Boolean);

    return uniqueIds.map((id) => {
      // 获取该主订单下所有子订单
      const orderSubOrders = subOrders.filter(
        (item: any) => item.mainOrderId === id,
      );

      // 计算该主订单的总金额
      let totalPayment = 0;
      orderSubOrders.forEach((item: any) => {
        totalPayment = FloatAdd(totalPayment, item.totalPayment || 0);
      });

      // 计算已开票金额(如果有的话)
      let invoiceIssuedAmount = 0;
      orderSubOrders.forEach((item: any) => {
        invoiceIssuedAmount = FloatAdd(
          invoiceIssuedAmount,
          item.invoiceIssuedAmount || 0,
        );
      });

      // 计算可开票金额
      const availableAmount = Math.max(0, totalPayment - invoiceIssuedAmount);

      return {
        value: String(id),
        totalPayment,
        invoiceIssuedAmount,
        availableAmount,
      };
    });
  };

  let mainIdSet = new Set();
  subOrders?.forEach((item: { mainOrderId: unknown }) => {
    mainIdSet.add(item.mainOrderId);
  });

  let mainIds = Array.from(mainIdSet).join(',');

  let newListAnnex = [];

  //回显,子订单可以编辑备注跟附件
  if (isEdit) {
    newListAnnex = subOrders.afterAnnexList?.map((path) => {
      let i = 0;
      return {
        uid: i++,
        name: getAliYunOSSFileNameFromUrl(path),
        status: 'uploaded',
        url: path,
        response: { data: [path] },
      };
    });
    subOrders.filePaths = newListAnnex;
  }

  const [form] = Form.useForm<{
    applyInvoicingNotes: string;
    filePaths: any;
    subIds: any[];
    afterInvoicingUpdate: boolean;
    receivingCompany: string;
    isUrgent: boolean;
    deadline: string;
    invoiceType: string; // 新增发票类型字段
  }>();

  useEffect(() => {
    //显示拼接的主订单id
    form.setFieldValue('applyInvoicingNotes', mainIds);

    // 设置每个主订单的可开票金额初始值
    const mainOrders = getUniqueMainOrderIds();
    mainOrders.forEach((order) => {
      form.setFieldValue(
        `invoiceAvailableAmount_${order.value}`,
        order.availableAmount,
      );
    });

    // 转换收款单位选项并保存
    const options = enumToSelect(PAYEE_OPTIONS);
    setReceivingCompanyOptions(options);
  }, []);

  return (
    <ModalForm<{
      applyInvoicingNotes: string;
      filePaths: any;
      subIds: any[];
      afterInvoicingUpdate: boolean;
    }>
      width={500}
      open
      title={isEdit ? '修改信息' : '申请开票'}
      initialValues={subOrders}
      form={form}
      autoFocusFirstInput
      modalProps={{
        okText: '确认',
        cancelText: '取消',
        destroyOnClose: true,
        onCancel: () => {
          setCheckVisible(false);
        },
      }}
      submitter={{
        render: (props, defaultDoms) => {
          return defaultDoms;
        },
      }}
      submitTimeout={2000}
      onFinish={async (values) => {
        values.subIds = ids;
        //附件处理
        values.filePaths = values.filePaths?.map((item) => {
          return { url: item.response.data[0] };
        });

        if (isEdit) {
          values.afterInvoicingUpdate = true;
        } else {
          values.afterInvoicingUpdate = false;
        }

        // 收集主订单可开票金额
        const invoiceOrderAmounts = getUniqueMainOrderIds().map((item) => ({
          orderId: Number(item.value), // 确保为 number 类型
          availableAmount: values[`invoiceAvailableAmount_${item.value}`],
        }));
        // 校验所有主订单可开票金额之和等于price字段
        const price = values.price;
        let sumAvailable = 0;
        invoiceOrderAmounts.forEach((item) => {
          sumAvailable = FloatAdd(sumAvailable, item.availableAmount || 0);
        });
        if (Math.abs(sumAvailable - price) > 0.01) {
          message.error(
            `所有主订单可开票金额之和(${sumAvailable})必须等于开票金额(${price})`,
          );
          return;
        }

        // 获取receivingCompany对应的名称
        const selectedOption = receivingCompanyOptions.find(
          (option) => option.value === values.receivingCompany,
        );
        const receivingCompanyName = selectedOption ? selectedOption.label : '';

        // 添加到请求数据中
        const reqData = {
          ...values,
          receivingCompanyName, // 添加收款单位名称
          mainOrderIds: mainIds, // 使用已有的主订单ID字符串
          invoiceOrderAmounts,
        };
        const res = await postServiceOrderApplyInvoicing({ data: reqData });

        if (res.result === RESPONSE_CODE.SUCCESS) {
          message.success(res.message);
          onClose();
        }
      }}
      onOpenChange={setCheckVisible}
    >
      {/* 主订单金额表格,任何情况都显示 */}
      <div style={{ marginBottom: 24 }}>
        <div
          style={{
            display: 'flex',
            fontWeight: 'bold',
            marginBottom: 8,
            padding: '8px 0',
            borderBottom: '1px solid #f0f0f0',
          }}
        >
          <div style={{ flex: 25 }}>订单号</div>
          <div style={{ flex: 18, textAlign: 'right' }}>订单金额</div>
          <div style={{ flex: 18, textAlign: 'right' }}>已开票金额</div>
          <div style={{ flex: 39, textAlign: 'right' }}>可开票金额</div>
        </div>
        {getUniqueMainOrderIds().map((item, index) => {
          const maxAvailable = Math.max(
            0,
            item.totalPayment - item.invoiceIssuedAmount,
          );
          return (
            <div
              key={index}
              style={{
                display: 'flex',
                marginBottom: 8,
                padding: '8px 0',
                borderBottom: '1px solid #f0f0f0',
              }}
            >
              <div style={{ flex: 25 }}>{item.value}</div>
              <div style={{ flex: 18, textAlign: 'right' }}>
                ¥ {item.totalPayment.toFixed(2)}
              </div>
              <div style={{ flex: 18, textAlign: 'right' }}>
                ¥ {item.invoiceIssuedAmount.toFixed(2)}
              </div>
              <div style={{ flex: 39, textAlign: 'right' }}>
                <ProFormMoney
                  name={`invoiceAvailableAmount_${item.value}`}
                  locale="zh-CN"
                  fieldProps={{
                    precision: 2,
                    style: { width: '70%' },
                  }}
                  initialValue={item.availableAmount}
                  rules={[
                    { required: true, message: '请填写可开票金额!' },
                    {
                      validator: (_, value) => {
                        if (value > maxAvailable) {
                          return Promise.reject(
                            `可开票金额不能超过${maxAvailable.toFixed(2)}`,
                          );
                        } else if (value === 0) {
                          return Promise.reject(`可开票金额不能为0`);
                        }
                        return Promise.resolve();
                      },
                    },
                  ]}
                />
              </div>
            </div>
          );
        })}
      </div>

      <div className="mb-1">
        如果需要合并订单,请将需要合并的订单id写在备注中,id之间用英文逗号隔开。
      </div>
      <ProFormTextArea
        width="lg"
        name="applyInvoicingNotes"
        key="applyInvoicingNotes"
        placeholder="请输入备注"
        onMetaChange={(val) => {
          console.log(val);
        }}
        proFieldProps={{
          onchange: () => {
            message.info('change');
          },
        }}
      />
      <ProFormText
        width="lg"
        name="purchaser"
        label="抬头名称"
        key="purchaser"
        placeholder="请输入抬头名称"
        rules={[{ required: true, message: '抬头名称必填' }]}
      />
      <ProFormSelect
        placeholder="选择收款单位"
        name="receivingCompany"
        width="lg"
        key="receivingCompany"
        label={
          <div>
            <span>开票收款单位</span>
            <span className="pl-2 text-xs text-gray-400">
              财务开票将依据这个字段,选择对应的公司开票(若对[收款单位]没有要求,请任意选择一个)
            </span>
          </div>
        }
        options={receivingCompanyOptions}
        rules={[{ required: true, message: '开票收款单位必填' }]}
      />

      {/* 新增发票类型选择 */}
      <ProFormSelect
        placeholder="选择发票类型"
        name="invoiceType"
        width="lg"
        key="invoiceType"
        label="发票类型"
        options={[
          { label: '普票', value: 'ORDINARY_TICKET' },
          { label: '专票', value: 'SPECIAL_TICKET' },
        ]}
        rules={[{ required: true, message: '发票类型必填' }]}
      />

      <ProFormSelect
        placeholder="选择是否加急"
        name="isUrgent"
        width="lg"
        key="isUrgent"
        label="是否加急"
        options={[
          { label: '是', value: 'true' },
          { label: '否', value: 'false' },
        ]}
        rules={[{ required: true, message: '是否加急必填' }]}
        onChange={(val: any) => {
          setIsUrgent(val);
        }}
      />

      {/* <ProFormDatePicker
        key="deadline"
        label="期望开票时间"
        name="deadline"
        rules={[{ required: isUrgent === 'true', message: '期望开票时间必填' }]}
        hidden={isUrgent !== 'true'}
      /> */}

      <ProFormTextArea
        key="invoicingUrgentCause"
        label="加急开票原因"
        name="invoicingUrgentCause"
        rules={[{ required: isUrgent === 'true', message: '加急开票原因' }]}
        hidden={isUrgent !== 'true'}
      />

      <ProFormUploadDragger
        key="2"
        label={
          <div>
            <span>开票明细确认表</span>
            <span className="pl-2 text-xs text-gray-400">
              如果开票信息有变更,如开票内容跟下单内容不一致、下单抬头和付款抬头不一致,请上传开票明细确认表。
            </span>
          </div>
        }
        name="filePaths"
        action="/api/service/order/fileProcess"
        fieldProps={{
          headers: { Authorization: localStorage.getItem('token') },
        }}
      />
    </ModalForm>
  );
};