Commit 6ea190d2 authored by Bill's avatar Bill

feat: 添加结算功能

parent 32a68cc0
.balanceInfoName {
font-size: 14px;
line-height: 16px;
}
.wrapContainer {
background-color: #FAFBFC;
padding: 12px;
margin-top: 8px;
font-size: 14px;
.item {
margin-bottom: 16px;
&-title {
color: #909399;
}
&-value {
color: #252D37;
margin-left: 8px;
}
}
}
.payChannel {
margin-top: 8px;
.title {
margin-bottom: 12px;
}
.panChanneList {
display: flex;
flex-direction: row;
align-items: center;
.item {
margin-right: 16px;
}
}
}
import { getSettleAccountsPlatformSettlementChannelList } from '@/services/SettleV2Api';
import { Modal, Radio } from 'antd';
import React, { useState, useEffect, useCallback } from 'react';
import styles from './index.less';
import PayItem from './payItem';
import alipay_icon from '@/assets/icons/alipay_icon.png'
import balance_icon from '@/assets/icons/balance_icon.png'
import unionpay_icon from '@/assets/icons/unionpay_icon.png'
import wechat_icon from '@/assets/icons/wechat_icon.png'
import quicklypay_icon from '@/assets/icons/quicklypay_icon.png'
interface Iprops {
visible: boolean,
onConfirm: (params: { payChannel: number }) => void,
onClose: () => void
confirmLoading: boolean,
/** 结算方信息 */
balanceInfo: {
/** 结算方名字 */
name: string,
/** 结算金额 */
amount: number,
payMethods: string,
}
}
const ICON_ENUM = {
11: wechat_icon,
12: alipay_icon,
13: quicklypay_icon,
14: unionpay_icon,
15: balance_icon
}
const OtherPayModal: React.FC<Iprops> = (props: Iprops) => {
const {visible, balanceInfo, onConfirm, onClose, confirmLoading} = props;
const [payChannelOptions, setPayChannelOptions] = useState<{label: string, value: number}[]>([]);
const [activeChannel, setActiveChannel] = useState<number | null>(null);
const handleClose = () => {
onClose?.()
}
const list = [
{
title: '结算方',
value: balanceInfo?.name
},
{
title: '结算金额',
value: balanceInfo.amount
},
{
title: '支付方式',
value: balanceInfo.payMethods
}
]
const getPayChannel = useCallback(async () => {
const { data, code } = await getSettleAccountsPlatformSettlementChannelList();
if (code === 1000) {
setPayChannelOptions(data.map((_item) => ({
label: _item.payChannelName,
value: _item.payChannel,
icon: ICON_ENUM[_item.payChannel] || ''
})))
}
}, [])
useEffect(() => {
if (!visible) {
return;
}
getPayChannel()
}, [visible])
const handleRadioChange = (item: { value: number }) => {
setActiveChannel(item.value)
}
const handleOnOk = () => {
onConfirm?.({
payChannel: activeChannel
})
}
return (
<Modal
width={548}
title="付款"
onCancel={handleClose}
visible={visible}
okText={"确定付款"}
onOk={handleOnOk}
confirmLoading={confirmLoading}
>
<div className={styles.wrapContainer}>
{
list.map((_item) => {
return (
<div className={styles.item} key={_item.title}>
<span className={styles['item-title']}>{_item.title}</span>
<span className={styles['item-value']}>{_item.value}</span>
</div>
)
})
}
{/* <Radio.Group options={payChannelOptions} onChange={handleRadioChange} value={activeChannel} /> */}
</div>
<div className={styles.payChannel}>
<div className={styles.title}>支付渠道</div>
<div className={styles.panChanneList}>
{
payChannelOptions.map((_item) => {
return (
<div className={styles.item} key={_item.value}>
<PayItem {..._item} isActive={_item.value === activeChannel} onClick={handleRadioChange} />
</div>
)
})
}
</div>
</div>
</Modal>
)
}
export default OtherPayModal;
.container {
display: flex;
flex-direction: row;
align-items: center;
font-size: 12px;
padding: 8px 16px;
background-color: #F5F6F7;
border-radius: 8px;
border: 1px solid #F5F6F7;
.img {
width: 16px;
height: 16px;
margin-right: 8px;
}
}
.active {
border: 1px solid #00A98F;
color: #00A98F;
background: rgba(0,169,143,0.04);
}
import { useMemo } from 'react';
import React from 'react';
import styles from './payItem.less';
import classnames from 'classnames';
interface Iprops {
icon?: string,
label: string,
value: number,
isActive: boolean,
onClick?: (data: {value: number}) => void
}
const PayItem: React.FC<Iprops> = (props: Iprops) => {
const { icon, label, value, isActive, onClick } = props;
const handleClick = () => {
onClick?.({ value })
}
return (
<div
className={classnames(styles.container, {
[styles.active]: isActive
})}
onClick={handleClick}
>
<img className={styles.img} src={icon} />
<div>{label}</div>
</div>
)
}
export default PayItem;
import EyePreview from '@/components/EyePreview';
// import StatusTag from '../components/StatusTag';
// import { TO_BE_RECONCILED, TO_BE_PAY, TO_BE_COLLECTED, COMPLETED, PRODUCT_NOTICE_SETTLEMENT_DETAIL, LOGISTICS_DETAIL, ORDER_DETAIL, CONTRACT_FUND_BILL } from '../common/constants';
import { priceFormat } from '@/utils/numberFomat'
import StatusTag from '@/components/StatusTag';
import { STATUS_TEXT } from '@/constants/balance';
import React, { useMemo, useState } from 'react';
// import { payStatus } from '../common';
import moment from 'moment';
import { COMPLETED, CONTRACT_FUND_BILL, LOGISTICS_DETAIL, ORDER_DETAIL, PRODUCT_NOTICE_SETTLEMENT_DETAIL, TO_BE_COLLECTED, TO_BE_PAY, TO_BE_RECONCILED, IS_UNIVERSAL_PAY } from '@/pages/balance/common/constants';
import { useCallback } from 'react';
import { GetSettleAccountsMemberSettlementPagePayableSettlementResponseDetail } from '@/services/SettleV2Api';
import { ColumnsType } from 'antd/lib/table';
const format = "YYYY-MM-DD HH:mm:ss"
const URL_MAP = {
[PRODUCT_NOTICE_SETTLEMENT_DETAIL]: "productNoticeSettlementDetail",
[LOGISTICS_DETAIL]: "logisticsDetail",
[ORDER_DETAIL]: "orderDetail",
[CONTRACT_FUND_BILL]: '',
}
const STATUS_COLOR = ["default", "warning", "primary", "danger", "success"];
type ModalsType = {
/** 查看付款 */
viewPay: boolean,
/** 查看通联支付 */
viewUniversalPay: boolean,
/** 手动结算 */
manualSettlement: boolean,
/** 上传付款凭证 */
uploadPayVoucher: boolean,
/** 通联支付付款 */
universalPay: boolean
}
function useHandleSettlementList() {
const [itemInfo, setItemInfo] = useState<GetSettleAccountsMemberSettlementPagePayableSettlementResponseDetail | null>(null)
const [modals, setModals] = useState<ModalsType>({
viewPay: false,
viewUniversalPay: false,
manualSettlement: false,
uploadPayVoucher: false,
universalPay: false
})
const handleOpen = useCallback((key: keyof ModalsType) => {
setModals({
...modals,
[key]: true
})
}, [])
const handleClose = useCallback((key: keyof ModalsType) => {
setModals({
...modals,
[key]: false
})
}, [])
const handleAction = (record: GetSettleAccountsMemberSettlementPagePayableSettlementResponseDetail, key: keyof ModalsType) => {
setItemInfo(record);
console.log("key", key);
handleOpen(key);
/** 通联支付 */
// if (record.payWay === 2 && record.status === COMPLETED) {
// handleOpen('viewUniversalPay');
// return;
// }
// const KEY_MAP = {
// [TO_BE_RECONCILED]: 'confirmReconciliation',
// [TO_BE_COLLECTED]: 'confirmPay',
// [COMPLETED]: 'viewPay'
// }
// handleOpen(KEY_MAP[record.status])
}
const columns: ColumnsType<GetSettleAccountsMemberSettlementPagePayableSettlementResponseDetail> = [
{
title: '结算单号',
dataIndex: 'settlementNo',
render: (text, record) => {
const prefix = `/memberCenter/balance/accountsPayable/settlementList/` ;
const url = URL_MAP[record.orderType];
let fullUrl = `${prefix}${url}?id=${record.id}`;
if(record.orderType === CONTRACT_FUND_BILL) {
fullUrl = `/memberCenter/contract/funds/bill/details?applyId=${record.orderId}`;
}
return (
<EyePreview url={fullUrl} >
{record.settlementNo}
</EyePreview>
)
}
},
{ title: '结算方式', dataIndex: 'settlementWayName' },
{ title: '结算方', dataIndex: 'settlementName'},
{
title: '结算单据类型',
dataIndex: 'orderTypeName',
filters: [
{
text: '生产通知单',
value: 1,
},
{
text: '物流单',
value: 2,
},
{
text: '订单',
value: 3,
},
{
text: '请款单',
value: 6
}
],
// onFilter: (value: number, record: any) => record.orderType === value,
},
{
title: '结算金额',
dataIndex: 'amount',
sorter: (a, b) => a.amount - b.amount,
render: (text, record) => {
return (
<div>{`¥${priceFormat(record.amount)}`}</div>
)
}
},
{
title: '总单数',
dataIndex: 'totalCount',
sorter: (a, b) => a.totalCount - b.totalCount,
},
{
title: '结算时间',
dataIndex: 'settlementTime',
sorter: (a, b) => moment(a.settlementTime, format).valueOf() - moment(b.settlementTime, format).valueOf(),
},
// {title: '结算日期', dataIndex: 'settlementDate'},
{ title: '预计付款日期', dataIndex: 'prePayTime' },
{ title: '实际付款日期', dataIndex: 'payTime' },
{
title: '结算状态', dataIndex: 'status',
// filters: payStatus,
// onFilter: (value: number, record: any) => record.status == value,
render: (text: string, record: any) => {
return (
<StatusTag type={STATUS_COLOR[record.status] as 'success'} title={STATUS_TEXT[record.status]} />
)
}
},
{
title: '支付方式',
dataIndex: 'payWayName',
// filters: [
// {
// text: '线上支付',
// value: 1,
// },
// {
// text: '线下转账线上确认',
// value: 2,
// },
// {
// text: '授信',
// value: 3,
// },
// {
// text: '货到付款',
// value: 4
// }
// ],
// onFilter: (value: number, record: any) => record.payWay === value,
},
{
title: '操作',
render: (text: string, record: any) => {
const isUniversalPay = record.payWay === IS_UNIVERSAL_PAY;
// 待对账的时候可以手动结算
if (record.status === TO_BE_RECONCILED) {
if (record.orderType !== CONTRACT_FUND_BILL) {
return <a onClick={() => handleAction(record, "manualSettlement")}>手动结算</a>
}
return null
}
if (record.status === TO_BE_PAY) {
return (
<a onClick={() => handleAction(record, isUniversalPay ? 'universalPay' : 'uploadPayVoucher')}>
付款
</a>
)
}
return <a onClick={() => handleAction(record, isUniversalPay ? 'viewUniversalPay' : 'viewPay')}>查看付款凭证</a>
}
}
]
const cacheItemInfo = useMemo(() => itemInfo, [itemInfo])
return {
columns,
handleOpen,
handleClose,
itemInfo: cacheItemInfo,
modals,
}
}
export default useHandleSettlementList
/*
* @Author: Bill
* @Date: 2020-10-20 11:04:07
* @Description: 应付账款结算
*/
import React, { useRef, useCallback, useEffect, useState } from 'react';
import React, { useRef, useCallback, useEffect, useState, useMemo } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { Button, Card, DatePicker, Modal} from 'antd';
import NiceForm from '@/components/NiceForm';
......@@ -16,12 +10,14 @@ import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { schema } from './schema';
import { Moment } from 'moment';
import { fetchOptions } from '../../common'
import useFetchColumns from '../../hooks/useFetchColumns';
import UploadVoucherModal from '../../components/UploadVoucherModal';
import Voucher from '../../components/Voucher';
import useSetSearchValueInTable from '@/hooks/useSetSearchValueInTable';
import { getSettleAccountsCommonGetSettlementOrderType, getSettleAccountsCommonGetSettlementStatus, getSettleAccountsMemberSettlementGetPayablePayProve, getSettleAccountsMemberSettlementPagePayableSettlement, GetSettleAccountsMemberSettlementPagePayableSettlementRequest, postSettleAccountsMemberSettlementPay } from '@/services/SettleV2Api';
import { getSettleAccountsCommonGetSettlementOrderType, getSettleAccountsCommonGetSettlementStatus, getSettleAccountsMemberSettlementGetPayablePayProve, getSettleAccountsMemberSettlementPagePayableSettlement, GetSettleAccountsMemberSettlementPagePayableSettlementRequest, postSettleAccountsMemberSettlementCommunicationPay, postSettleAccountsMemberSettlementPay } from '@/services/SettleV2Api';
import { postReportSettlementMemberManualSettlement } from '@/services/ReportV2Api';
import OtherPayModal from '../components/OtherPayModal';
import useHandleSettlementList from './hooks/useHandleSettlementList';
import ViewUniversalPay from '../../components/ViewUniversalPay';
const { onFieldValueChange$ } = FormEffectHooks
const { RangePicker } = DatePicker;
......@@ -36,15 +32,30 @@ interface SearchParams {
status?: number,
current: number,
pageSize: number,
}
const SettlementList = () => {
const ref = useRef<any>({})
const { searchData, formatInitialValue, clear } = useSetSearchValueInTable();
const { columns, manualStatus, payModalVisible, balanceInfo, payModalOnCancel, viewModalonCancel, viewVisible, payVoucherInfo } = useFetchColumns("payable");
const [files, setFiles] = useState<any>([]);
const [uploadSubmitLoading, setUploadSubmitLoading] = useState<boolean>()
const [uploadSubmitLoading, setUploadSubmitLoading] = useState<boolean>();
const [universalPayLoading, setUniversalPayLoading] = useState<boolean>(false)
const { itemInfo, modals, handleClose, columns } = useHandleSettlementList();
const [files, setFiles] = useState([]);
const universalPayInfo1 = useMemo(() => ({
name: itemInfo?.settlementName,
amount: itemInfo?.amount,
statusName: itemInfo?.statusName,
payWayName: '通联支付',
settlementDate: itemInfo?.settlementDate
}), [itemInfo])
const universalPayInfo = useMemo(() => ({
name: itemInfo?.payName,
amount: itemInfo?.amount,
payMethods: '通联支付',
}), [itemInfo])
/**
* 分页查询
......@@ -60,6 +71,7 @@ const SettlementList = () => {
status: searchParams.status || '0',
orderType: searchParams.orderType || '0',
}
const { data } = await getSettleAccountsMemberSettlementPagePayableSettlement(postData);
return data
}, [searchData])
......@@ -70,15 +82,21 @@ const SettlementList = () => {
const fetchManualSettlement = useCallback(async (id: number) => {
const { code, data } = await postReportSettlementMemberManualSettlement({id})
if(code === 1000) {
// handleManualStatus(null)
handleClose("manualSettlement")
formActions.submit();
}
}, [])
useEffect(() => {
if (manualStatus !== null) {
fetchManualSettlement(manualStatus)
if (itemInfo !== null && modals['manualSettlement']) {
fetchManualSettlement(itemInfo.id)
return
}
}, [manualStatus])
if (itemInfo !== null && modals['viewPay']) {
fetchVouchers(itemInfo.id);
}
}, [itemInfo, modals])
const fetchVouchers = useCallback(async (id: number) => {
const { code, data } = await getSettleAccountsMemberSettlementGetPayablePayProve({id: id.toString()})
......@@ -87,26 +105,38 @@ const SettlementList = () => {
}
}, [])
useEffect(() => {
if (payVoucherInfo !== null) {
fetchVouchers(payVoucherInfo.id);
}
}, [payVoucherInfo])
/**
* 上传凭证
* @param params
*/
const handleUploadVoucher = async (params: { fileList: {name: string, proveUrl: string}[]}) => {
setUploadSubmitLoading(true)
const { data, code } = await postSettleAccountsMemberSettlementPay({id: balanceInfo.id, proveList: params.fileList})
const { data, code } = await postSettleAccountsMemberSettlementPay({id: itemInfo?.id!, proveList: params.fileList})
setUploadSubmitLoading(false)
if(code === 1000) {
payModalOnCancel();
// handlePayModalClose();
handleClose('uploadPayVoucher')
formActions.submit();
}
}
/** 通联支付, 付款 */
const handleUniversalPay = async (params: {payChannel: number}) => {
try {
setUniversalPayLoading(true)
const { data, code } = await postSettleAccountsMemberSettlementCommunicationPay({
id: itemInfo.id,
payChannelType: params.payChannel,
})
if (code === 1000) {
// handlePayModalClose();
handleClose('universalPay')
}
} finally {
setUniversalPayLoading(true)
}
}
/**
* 搜索
*/
......@@ -155,18 +185,36 @@ const SettlementList = () => {
/>
</Card>
<UploadVoucherModal
visible={payModalVisible}
visible={modals.uploadPayVoucher}
// id={balanceInfo?.id}
roleId={balanceInfo?.roleId}
settlementId={balanceInfo?.settlementId}
roleId={itemInfo?.roleId}
settlementId={itemInfo?.memberId}
handleUpload={handleUploadVoucher}
onCancel={payModalOnCancel}
onCancel={() => handleClose('uploadPayVoucher')}
confirmLoading={uploadSubmitLoading}
/>
<Modal width={548} title="查看付款凭证" onCancel={viewModalonCancel} visible={viewVisible} footer={null}>
{/* <WrapVoucher id={props.id} type={props.type} /> */}
<Modal
width={548}
title="查看付款凭证"
onCancel={() => handleClose('viewPay')}
visible={modals['viewPay']}
footer={null}
>
<Voucher files={files} />
</Modal>
<OtherPayModal
visible={modals.universalPay}
balanceInfo={universalPayInfo}
onClose={() => handleClose('universalPay')}
onConfirm={handleUniversalPay}
confirmLoading={universalPayLoading}
/>
<ViewUniversalPay
visible={modals['viewUniversalPay']}
balanceInfo={universalPayInfo1}
onClose={() => handleClose('viewUniversalPay')}
onOk={() => handleClose('viewUniversalPay')}
/>
</PageHeaderWrapper>
)
}
......
/*
* @Author: Bill
* @Date: 2020-10-20 11:04:07
* @Description: 应付账款结算
*/
import React, { useRef, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { Card, Button, DatePicker, Tag, Badge, Drawer } from 'antd';
import { Card, Button, DatePicker, Tag, Badge, Drawer, message } from 'antd';
import NiceForm from '@/components/NiceForm';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { createFormActions } from '@formily/antd';
......@@ -14,7 +8,7 @@ import { StandardTable } from 'god';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { schema } from './schema';
import InvoiceCreate from '../../components/InvoiceCreate'
// import InvoiceCreate from '../../components/InvoiceCreate'
// import StatusTag from '../../components/StatusTag';
import StatusTag, { STATUS_TYPE } from '@/components/StatusTag'
import { fetchOptions } from '../../common';
......@@ -23,8 +17,17 @@ import { numFormat, priceFormat } from '@/utils/numberFomat';
import {
getSettleAccountsCommonGetPayStatus,
getSettleAccountsCommonGetReceiptInvoiceStatus,
getSettleAccountsMemberSettlementPageReceiptInvoice
getSettleAccountsMemberSettlementGetInvoiceProveDetail,
GetSettleAccountsMemberSettlementGetInvoiceProveDetailResponse,
getSettleAccountsMemberSettlementGetReturnInvoiceProveDetail,
GetSettleAccountsMemberSettlementGetReturnInvoiceProveDetailResponse,
getSettleAccountsMemberSettlementPageReceiptInvoice,
GetSettleAccountsMemberSettlementPageReceiptInvoiceResponseDetail,
postSettleAccountsMemberSettlementInvoiceByReturn,
postSettleAccountsMemberSettlementInvoiceProve
} from '@/services/SettleV2Api';
// import { ColumnType } from 'antd/lib/table';
import InvoiceDrawerInfo from '../../components/InvoiceCreate/invoiceDrawerInfo'
const RangePicker = DatePicker.RangePicker;
const formActions = createFormActions();
......@@ -32,15 +35,15 @@ const formActions = createFormActions();
const SettlementList = () => {
const ref = useRef<any>({});
const [visible, setVisible] = useState(false);
const [activeData, setActiveData] = useState({});
const [activeData, setActiveData] = useState<GetSettleAccountsMemberSettlementPageReceiptInvoiceResponseDetail>(null);
const { searchData, formatInitialValue } = useSetSearchValueInTable();
const [invoiceInfo, setInvoiceInfo] = useState<any>(null)
/**
* 获取开票管理列表
* @param params
*/
const fetchListData = async (params) => {
// /settle/accounts/member/settlement/pageReceiptInvoice
const searchParams = {
...params,
invoiceStatus: params?.invoiceStatus || 2 ,
......@@ -51,7 +54,7 @@ const SettlementList = () => {
return data
}
/**
/**
* 搜索
*/
const handleSearch = (values) => {
......@@ -63,33 +66,113 @@ const SettlementList = () => {
ref.current.reload({...values, payStartTime, payEndTime, orderStartTime, orderEndTime});
}
const handleShow = (record) => {
document.body.parentNode.style.overflowY = "hidden";
const handleShow = async (record: GetSettleAccountsMemberSettlementPageReceiptInvoiceResponseDetail) => {
(document.body.parentNode as HTMLBodyElement).style.overflowY = "hidden";
setVisible(true)
setActiveData({
id: record.id,
type: record.invoiceStatus,
settlementOrderType: record.settlementOrderType || 5,
// payRoleId: record.payRoleId,
// payMemberId: record.payMemberId
})
setActiveData(record)
// setActiveData({
// id: record.id,
// type: record.invoiceStatus,
// settlementOrderType: record.settlementOrderType || 5,
// // payRoleId: record.payRoleId,
// // payMemberId: record.payMemberId
// })
/** 退货类型 */
const service = record.settlementOrderType === 5
? getSettleAccountsMemberSettlementGetReturnInvoiceProveDetail
: getSettleAccountsMemberSettlementGetInvoiceProveDetail
const { code, data, message: msg } = await service({ id: record.id.toString() });
if (code !== 1000) {
message.error(msg);
return
}
if (record.settlementOrderType === 5) {
if ((data as GetSettleAccountsMemberSettlementGetReturnInvoiceProveDetailResponse).length === 0) {
message.error("无发票信息");
return
}
setInvoiceInfo(data);
return;
}
setInvoiceInfo([(data as GetSettleAccountsMemberSettlementGetInvoiceProveDetailResponse)]);
}
const handleOnCancel = () => {
document.body.parentNode.style.overflowY = "auto";
(document.body.parentNode as HTMLBodyElement).style.overflowY = "auto";
setVisible(false)
setActiveData({})
setActiveData(null)
}
const handleForm = async (value) => {
const keys = Object.keys(value);
if(keys.length === 0) {
message.error({
content: '请添加发票信息'
})
return;
}
let res = {}
keys.forEach((item) => {
const array = value[item];
// res[item] = array.map((_row) => {
// const { invoiceDate, ...rest } = _row;
// return { invoiceDate: invoiceDate.format('YYYY-MM-DD'), ...rest}
// })
res[item] = array
})
/** 退货类型 */
if (activeData.settlementOrderType === 5) {
const postDataList = {
receiptInvoiceId: activeData.id,
addList: invoiceInfo.map((_item, key) => {
const { kindName, typeName, ...rest } = _item;
return {
...rest,
// TODO, 修改
kind: kindName == '增值税普通发票' ? 1 : 2,
type: typeName == '企业' ? 1 : 2,
proveList: res[`list-${key}`]
}
})
}
const { data, code, message: msg } = await postSettleAccountsMemberSettlementInvoiceByReturn(postDataList);
if (code !== 1000) {
message.error(msg);
return
}
formActions.submit();
setVisible(false)
return;
}
/** 普通发票开具 */
const { kindName, typeName, ...rest } = invoiceInfo[0]
const defaultPostData = {
receiptInvoiceId: activeData.id,
...rest,
kind: kindName == '增值税普通发票' ? 1 : 2,
type: typeName == '企业' ? 1 : 2,
proveList: res[`list-0`]
}
const { data, code, message: msg } = await postSettleAccountsMemberSettlementInvoiceProve(defaultPostData);
if (code === 1000) {
setVisible(false)
formActions.submit();
}
message.error(msg);
}
const columns = [
{
title: '订单号/类型',
title: '单据号/类型',
dataIndex: 'orderNo',
render: (text, record: any) => {
render: (text, record) => {
const typeNum = record.settlementOrderType % STATUS_TYPE.length;
return (
<div>
<p>{record.orderNo}</p>
<StatusTag title={record.settlementOrderTypeName} type={STATUS_TYPE[record.settlementOrderType] as "success"} />
<StatusTag title={record.settlementOrderTypeName} type={STATUS_TYPE[typeNum] as "success"} />
</div>
)
}
......@@ -189,7 +272,7 @@ const SettlementList = () => {
tableProps={{
rowKey: 'id',
}}
columns={columns}
columns={columns as any}
currentRef={ref}
fetchTableData={(params: any) => fetchListData(params)}
controlRender={
......@@ -228,14 +311,14 @@ const SettlementList = () => {
}
/>
</Card>
<InvoiceCreate
{...activeData as any}
title="开具发票"
width={800}
<InvoiceDrawerInfo
mode={activeData.invoiceStatus === 1 ? 'edit' : 'view'}
visible={visible}
invoiceInfoData={invoiceInfo}
onCancel={handleOnCancel}
refresh={() => formActions.submit()}
onSubmit={handleForm}
/>
</PageHeaderWrapper>
)
}
......
import EyePreview from '@/components/EyePreview';
// import StatusTag from '../components/StatusTag';
// import { TO_BE_RECONCILED, TO_BE_PAY, TO_BE_COLLECTED, COMPLETED, PRODUCT_NOTICE_SETTLEMENT_DETAIL, LOGISTICS_DETAIL, ORDER_DETAIL, CONTRACT_FUND_BILL } from '../common/constants';
import { priceFormat } from '@/utils/numberFomat'
import StatusTag from '@/components/StatusTag';
import { STATUS_TEXT } from '@/constants/balance';
import React, { useMemo, useState } from 'react';
// import { payStatus } from '../common';
import moment from 'moment';
import { COMPLETED, CONTRACT_FUND_BILL, LOGISTICS_DETAIL, ORDER_DETAIL, PRODUCT_NOTICE_SETTLEMENT_DETAIL, TO_BE_COLLECTED, TO_BE_RECONCILED } from '@/pages/balance/common/constants';
import { useCallback } from 'react';
import { GetSettleAccountsMemberSettlementPageReceivableSettlementResponseDetail } from '@/services/SettleV2Api';
import { ColumnsType } from 'antd/lib/table';
const format = "YYYY-MM-DD HH:mm:ss"
const URL_MAP = {
[PRODUCT_NOTICE_SETTLEMENT_DETAIL]: "productNoticeSettlementDetail",
[LOGISTICS_DETAIL]: "logisticsDetail",
[ORDER_DETAIL]: "orderDetail",
[CONTRACT_FUND_BILL]: '',
}
const KEY_MAP_TEXT = {
[TO_BE_RECONCILED]: '确认对账完成',
[TO_BE_COLLECTED]: '确认付款凭证',
[COMPLETED]: '查看付款凭证'
}
const STATUS_COLOR = ["default", "warning", "primary", "danger", "success"];
type ModalsType = {
/**确认对账 */
confirmReconciliation: boolean,
/** 确认付款凭证 */
confirmPay: boolean,
/** 查看付款 */
viewPay: boolean,
/** 查看通联支付 */
viewUniversalPay: boolean
}
function useHandleSettlementList() {
const [itemInfo, setItemInfo] = useState<GetSettleAccountsMemberSettlementPageReceivableSettlementResponseDetail>([])
const [modals, setModals] = useState<ModalsType>({
confirmReconciliation: false,
confirmPay: false,
viewPay: false,
viewUniversalPay: false,
})
const handleOpen = useCallback((key: keyof ModalsType) => {
setModals({
...modals,
[key]: true
})
}, [])
const handleClose = useCallback((key: keyof ModalsType) => {
setModals({
...modals,
[key]: false
})
}, [])
const handleAction = (record: GetSettleAccountsMemberSettlementPageReceivableSettlementResponseDetail) => {
setItemInfo(record);
/** 通联支付 */
if (record.payWay === 2 && record.status === COMPLETED) {
handleOpen('viewUniversalPay');
return;
}
const KEY_MAP = {
[TO_BE_RECONCILED]: 'confirmReconciliation',
[TO_BE_COLLECTED]: 'confirmPay',
[COMPLETED]: 'viewPay'
}
handleOpen(KEY_MAP[record.status])
}
const columns: ColumnsType<GetSettleAccountsMemberSettlementPageReceivableSettlementResponseDetail> = [
{
title: '结算单号',
dataIndex: 'settlementNo',
render: (text, record) => {
const prefix = '/memberCenter/balance/accountsReceivable/settlementList/';
const url = URL_MAP[record.orderType];
let fullUrl = `${prefix}${url}?id=${record.id}`;
if(record.orderType === CONTRACT_FUND_BILL) {
fullUrl = `/memberCenter/contract/funds/bill/details?applyId=${record.orderId}`;
}
return (
<EyePreview url={fullUrl} >
{record.settlementNo}
</EyePreview>
)
}
},
{ title: '结算方式', dataIndex: 'settlementWayName' },
{ title: '付款方', dataIndex: 'payName'},
{
title: '结算单据类型',
dataIndex: 'orderTypeName',
filters: [
{
text: '生产通知单',
value: 1,
},
{
text: '物流单',
value: 2,
},
{
text: '订单',
value: 3,
},
{
text: '请款单',
value: 6
}
],
onFilter: (value: number, record: any) => record.orderType === value,
},
{
title: '结算金额',
dataIndex: 'amount',
sorter: (a, b) => a.amount - b.amount,
render: (text, record) => {
return (
<div>{`¥${priceFormat(record.amount)}`}</div>
)
}
},
{
title: '总单数',
dataIndex: 'totalCount',
sorter: (a, b) => a.totalCount - b.totalCount,
},
{
title: '结算时间',
dataIndex: 'settlementTime',
sorter: (a, b) => moment(a.settlementTime, format).valueOf() - moment(b.settlementTime, format).valueOf(),
},
{ title: '预计付款日期', dataIndex: 'prePayTime' },
{ title: '实际付款日期', dataIndex: 'payTime' },
{
title: '结算状态', dataIndex: 'status',
render: (text: string, record: GetSettleAccountsMemberSettlementPageReceivableSettlementResponseDetail) => {
return (
<StatusTag type={STATUS_COLOR[record.status] as 'success'} title={STATUS_TEXT[record.status]} />
)
}
},
{
title: '支付方式',
dataIndex: 'payWayName',
},
{
title: '操作',
render: (text: string, record: GetSettleAccountsMemberSettlementPageReceivableSettlementResponseDetail) => {
const keyText = KEY_MAP_TEXT[record.status];
return (<a onClick={() => handleAction(record)}>{keyText}</a>)
}
}
];
const cacheItemInfo = useMemo(() => itemInfo, [itemInfo])
return {
columns,
handleOpen,
handleClose,
itemInfo: cacheItemInfo,
modals,
}
}
export default useHandleSettlementList
/*
* @Author: Bill
* @Date: 2020-10-20 11:04:07
* @Description: 应付账款结算
*/
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { Card, DatePicker, Modal, Space, Button} from 'antd';
......@@ -16,13 +10,13 @@ import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilte
import { useAsyncSelect } from '@/formSchema/effects/useAsyncSelect';
import { schema } from './schema';
import { fetchOptions } from '../../common';
import useFetchColumns from '../../hooks/useFetchColumns'
import Voucher from '../../components/Voucher';
import ConfirmAccount from '../../components/ConfirmAccount';
import { usePageStatus } from '@/hooks/usePageStatus';
import useSetSearchValueInTable from '@/hooks/useSetSearchValueInTable';
import { Moment } from 'moment';
import { getSettleAccountsCommonGetSettlementOrderType, getSettleAccountsCommonGetSettlementStatus, getSettleAccountsMemberSettlementGetReceivablePayProve, getSettleAccountsMemberSettlementPageReceivableSettlement, postSettleAccountsMemberSettlementConfirmAccountComplete, postSettleAccountsMemberSettlementConfirmPayProve } from '@/services/SettleV2Api';
import useHandleSettlementList from './hooks/useHandleSettlementList';
import ViewUniversalPay from '../../components/ViewUniversalPay';
const { RangePicker } = DatePicker;
......@@ -44,7 +38,20 @@ const SettlementList = () => {
const ref = useRef<any>({})
const { searchData, formatInitialValue, clear } = useSetSearchValueInTable();
const [files, setFiles] = useState<any>([]);
const { columns, confirmPayInfo, confirmPayVisible, reconciliationInfo, reconciliationVisible, viewVisible, payVoucherInfo, viewModalonCancel, confirmPayOnCancel, reconciliationOnCancel } = useFetchColumns("receiveable")
const {
columns,
modals,
itemInfo,
handleClose,
handleOpen
} = useHandleSettlementList()
const universalPayInfo = useMemo(() => ({
name: itemInfo.settlementName,
amount: itemInfo.amount,
statusName: itemInfo.statusName,
payWayName: '通联支付',
settlementDate: itemInfo.settlementDate
}), [itemInfo])
const fetchListData = async (params: requestDataType) => {
const searchParams = {
......@@ -66,7 +73,8 @@ const SettlementList = () => {
const handleConfirm = async (params: { id: number }) => {
const { code, data } = await postSettleAccountsMemberSettlementConfirmAccountComplete({settlementId: params.id});
if (code === 1000) {
reconciliationOnCancel();
// reconciliationOnCancel();
handleClose("confirmReconciliation")
formActions.submit();
}
}
......@@ -77,7 +85,8 @@ const SettlementList = () => {
const handleConfirmPayStatus = async (params: { status: 0 | 1, id: number }) => {
const { code } = await postSettleAccountsMemberSettlementConfirmPayProve({id: params.id, status: params.status});
if (code) {
confirmPayOnCancel();
// confirmPayOnCancel();
handleClose('confirmPay')
formActions.submit();
}
}
......@@ -89,10 +98,10 @@ const SettlementList = () => {
}, [])
useEffect(() => {
if (payVoucherInfo !== null) {
fetchVouchers(payVoucherInfo.id);
if (itemInfo !== null && (itemInfo.status === 4 || itemInfo.status === 3)) {
fetchVouchers(itemInfo.id);
}
}, [payVoucherInfo])
}, [itemInfo])
/**
* 搜索
......@@ -144,32 +153,32 @@ const SettlementList = () => {
<Modal
width={400}
title="确认对账完成"
visible={reconciliationVisible}
onCancel={reconciliationOnCancel}
onOk={() => handleConfirm({ id: reconciliationInfo?.id})}
visible={modals['confirmReconciliation']}
onCancel={() => handleClose('confirmReconciliation')}
onOk={() => handleConfirm({ id: itemInfo?.id})}
>
<ConfirmAccount settlementDate={reconciliationInfo?.settlementDate} payName={reconciliationInfo?.payName} />
<ConfirmAccount settlementDate={itemInfo?.settlementDate} payName={itemInfo?.payName} />
</Modal>
<Modal width={548} title="查看付款凭证" onCancel={viewModalonCancel} visible={viewVisible} footer={null}>
<Modal width={548} title="查看付款凭证" onCancel={() => handleClose('viewPay')} visible={modals['viewPay']} footer={null}>
<Voucher files={files} />
</Modal>
<Modal
width={548}
title="确认付款凭证"
onCancel={confirmPayOnCancel}
visible={confirmPayVisible}
onCancel={() => handleClose('confirmPay')}
visible={modals['confirmPay']}
footer={(
<Space>
<Button onClick={confirmPayOnCancel}>取消</Button>
<Button onClick={() => handleClose('confirmPay')}>取消</Button>
<Button
danger
onClick={() => handleConfirmPayStatus({status: 0, id: confirmPayInfo.id})}
onClick={() => handleConfirmPayStatus({status: 0, id: itemInfo.id})}
>
确认未到款
</Button>
<Button
type={"primary"}
onClick={() => handleConfirmPayStatus({status: 1, id: confirmPayInfo.id})}
onClick={() => handleConfirmPayStatus({status: 1, id: itemInfo.id})}
>
确认到款
</Button>
......@@ -178,6 +187,12 @@ const SettlementList = () => {
>
<Voucher files={files} />
</Modal>
<ViewUniversalPay
visible={modals['viewUniversalPay']}
balanceInfo={universalPayInfo}
onClose={() => handleClose('viewUniversalPay')}
onOk={() => handleClose('viewUniversalPay')}
/>
</PageHeaderWrapper>
)
}
......
......@@ -67,3 +67,10 @@ export const ORDER_DETAIL = 3
* 结算单据类型-合同请款单
*/
export const CONTRACT_FUND_BILL = 6
/**
* 应付账款管理, 应收账款管理, 平台代收账款管理列表 支付方式
* 1. 线下支付, 2通联支付
*/
export const IS_UNIVERSAL_PAY = 2
/*
* @Author: Bill
* @Date: 2020-10-28 13:46:19
* @Description: 开票管理, 发票自增
*/
import React from 'react';
import { toArr, isFn, FormPath } from '@formily/shared'
import { Button, Input } from 'antd'
import styled from 'styled-components'
import {
SchemaForm,
SchemaField,
SchemaMarkupField as Field
} from '@formily/antd'
import { ArrayList } from '@formily/react-shared-components';
import { PlusOutlined, MinusOutlined } from '@ant-design/icons'
const ArrayComponents = {
CircleButton: props => <Button {...props} />,
TextButton: props => <Button {...props} />,
AdditionIcon: () => <div>+</div>,
RemoveIcon: () => <div>-</div>,
MoveDownIcon: () => <div>Down</div>,
MoveUpIcon: () => <div>Up</div>
}
const RowStyleLayout = styled(props => <div {...props} />)`
.ant-btn {
margin-right: 16px;
}
.ant-form-item {
display: inline-flex;
margin-right: 16px;
margin-bottom: 10px;
}
> .ant-form-item {
margin-bottom: 0;
margin-right: 0;
}
`
const InvoiceArray = (props) => {
const { value, schema, className, editable, path, mutators } = props
const {
renderAddition,
renderRemove,
renderMoveDown,
renderMoveUp,
renderEmpty,
renderExtraOperations,
...componentProps
} = schema.getExtendsComponentProps() || {}
const onAdd = () => {
const items = Array.isArray(schema.items)
? schema.items[schema.items.length - 1]
: schema.items
mutators.push(items.getEmptyValue())
}
const length = toArr(value).length;
return (
<ArrayList
value={value}
minItems={schema.minItems}
maxItems={schema.maxItems}
editable={editable}
components={ArrayComponents}
renders={{
renderAddition,
renderRemove,
renderMoveDown,
renderMoveUp,
renderEmpty // 允许开发者覆盖默认
}}
>
{toArr(value).map((item, index) => {
return (
<RowStyleLayout {...componentProps} key={index}>
<div style={{display: 'flex', position: 'relative'}}>
<SchemaField path={FormPath.parse(path).concat(index)} />
{
length - 1 === index
? <Button onClick={onAdd} style={{marginLeft: '-16px'}}><PlusOutlined width={44}/></Button>
: null
}
<Button
style={{marginLeft: length - 1 === index ? '0' : '-16px'}}
onClick={() => mutators.remove(index)}
>
<MinusOutlined width={44}/>
</Button>
</div>
</RowStyleLayout>
)
})}
<ArrayList.Empty>
{({ children }) => {
return (
<div className="array-cards-addition" onClick={onAdd}>
{/* {children} */}
<Button>添加</Button>
</div>
)
}}
</ArrayList.Empty>
<ArrayList.Addition>
{({ children, isEmpty }) => {
if (!isEmpty) {
return null
}
}}
</ArrayList.Addition>
</ArrayList>
)
}
InvoiceArray.isFieldComponent = true;
export default InvoiceArray
/*
* @Author: Bill
* @Date: 2020-10-28 14:11:55
* @Description: 开票详情 drawer
*/
import React, { useEffect, useState } from 'react';
import { Drawer, Input, DatePicker, message } from 'antd';
import InvoiceInfo from './InvoiceInfo';
import {
SchemaForm,
SchemaMarkupField as Field
} from '@formily/antd'
import InvoiceArray from './InvoiceArray';
import { Button, Table } from 'antd';
import { createFormActions, FormMegaLayout, FormSlot, createVirtualBox } from '@formily/antd';
import { InvoiceInfoProps, ProveListProps } from '../../common/type';
import { Moment } from 'moment';
import styles from './index.less';
import {
getSettleAccountsMemberSettlementGetInvoiceProveDetail,
getSettleAccountsMemberSettlementGetReturnInvoiceProveDetail,
postSettleAccountsMemberSettlementInvoiceByReturn,
postSettleAccountsMemberSettlementInvoiceProve
} from '@/services/SettleV2Api';
const formActions = createFormActions();
interface Iprops {
visible: boolean,
title: string,
width: number,
type: number, // 0 -> 开票, 1-> 查看
id: string, // 开票管理id,
/** 1-生产通知单,2-物流单,3-订单,4-积分订单,5-退货申请单 */
settlementOrderType: 1 | 2 | 3 | 4 | 5,
// payRoleId: number,
// payMemberId: number,
onCancel: () => void,
refresh: () => void
}
interface SubmitProps {
[key: string]: {
number: string,
remark: string,
invoiceDate: Moment
}[]
}
enum TypeEnum {
edit = 0,
view = 1
}
const columns = [
{title: '发票号码', dataIndex:'number'},
{title: '发票时间', dataIndex:'invoiceDate'},
{title: '备注', dataIndex:'remark'},
]
const InvoiceComponent = createVirtualBox('InvoiceComponent', ({ infos }) => {
return (
<div >
<InvoiceInfo infos={infos} />
</div>
)
})
const TabelHeader = createVirtualBox('TabelHeader', ({ infos }) => {
return (
<div >
<div className={styles.formHeader}>
<div className={styles.invoiceNum}>发票号码<span style={{color: "#ff4d4f"}}>*</span></div>
<div className={styles.invoiceDate}>开票日期<span style={{color: "#ff4d4f"}}>*</span></div>
<div>备注</div>
</div>
</div>
)
})
const InvoiceDrawer: React.FC<Iprops> = (props) => {
const { visible, title, onCancel, width, type, id, settlementOrderType} = props;
const [invoiceInfo, setInvoiceInfo] = useState<any[]>([]);
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
const isEdit = type === TypeEnum.edit;
const handleSubmit = () => {
// // 如果当前type==0为开票, 1为查看,那么直接关闭
if(type === TypeEnum.edit) {
formActions.submit()
} else {
// 查看类型的话,按确认直接关掉窗口
onCancel();
}
}
const handleForm = (value: SubmitProps) => {
console.log(value);
const keys = Object.keys(value);
if(keys.length === 0) {
message.error({
content: '请添加发票信息'
})
return;
}
let res = {}
keys.map((item) => {
const array = value[item];
res[item] = array.map((_row) => {
const { number, remark, invoiceDate } = _row;
return {number: number, remark: remark, invoiceDate: invoiceDate.format('YYYY-MM-DD')}
})
})
if(settlementOrderType === 5) {
console.log(value);
const postDataList = {
receiptInvoiceId: parseInt(id),
addList: invoiceInfo.map((_item, key) => {
const { kindName, typeName, ...rest } = _item;
return {
...rest,
kind: kindName == '增值税普通发票' ? 1 : 2,
type: typeName == '企业' ? 1 : 2,
proveList: res[`list-${key}`]
}
})
}
console.log(postDataList);
postSettleAccountsMemberSettlementInvoiceByReturn(postDataList)
.then(({code, data}) => {
if(code === 1000) {
setSubmitLoading(false)
props.refresh();
onCancel();
}
})
return ;
}
const { kindName, typeName, ...rest } = invoiceInfo[0]
const postData = {
...rest,
proveList: res[`list-0`],
kind: kindName == '增值税普通发票' ? 1 : 2,
type: typeName == '企业' ? 1 : 2,
receiptInvoiceId: parseInt(id)
}
setSubmitLoading(true)
console.log(postData);
// /settle/accounts/member/settlement/invoiceProve
postSettleAccountsMemberSettlementInvoiceProve(postData)
.then((data) => {
if(data.code === 1000) {
setSubmitLoading(false)
props.refresh();
onCancel();
}
})
}
useEffect(() => {
if(props.visible) {
console.log(settlementOrderType)
if(settlementOrderType === 5) {
getSettleAccountsMemberSettlementGetReturnInvoiceProveDetail({id: id})
.then(({code, data}) => {
if (code === 1000) {
setInvoiceInfo(data)
}
})
return ;
}
getSettleAccountsMemberSettlementGetInvoiceProveDetail({id: id})
.then((data) => {
if(data.code === 1000) {
setInvoiceInfo([data.data])
}
})
}
}, [id, visible, settlementOrderType])
// const tableList = invoiceInfo?.proveList?.map((item, key) => ({...item, id: key}));
const renderFooter = () => {
if(type === TypeEnum.view) {
return null
}
return (
<div style={{textAlign: 'right'}}>
<Button onClick={onCancel} style={{ marginRight: 8 }}>
取消
</Button>
<Button onClick={handleSubmit} type="primary">
确认
</Button>
</div>
)
}
const renderOnly = () => {
return (
<SchemaForm onSubmit={handleForm} actions={formActions} components={{ InvoiceArray, Input, DatePicker }} >
{
invoiceInfo.map((item, unique) => {
const infos = item;
const tableList = item?.proveList?.map((item, key) => ({...item, id: key}));
return (
<div key={unique}>
<InvoiceComponent infos={infos} />
{
isEdit
? (
<>
<TabelHeader />
<Field
name={`list-${unique}`}
type="array"
x-component="InvoiceArray"
>
<Field type="object">
<Field name="number" x-component="Input" x-rules={[{required: true, message: '请填写发票号'}]} />
<Field name="invoiceDate" x-component-props={{
getPopupContainer: (triggernode) => triggernode.parentElement
}} x-component="DatePicker" x-rules={[{required: true, message: '请选择发票日期'}]} />
<Field name="remark" x-component="Input" x-component-props={{style: {width: '240px'}}} />
</Field>
</Field>
</>
)
: (
<Table
columns={columns}
dataSource={tableList}
rowKey={record => record.id}
/>
)
}
</div>
)
})
}
</SchemaForm>
)
}
return (
<Drawer
getContainer={false}
destroyOnClose
visible={visible}
bodyStyle={{overflow: 'auto'}}
title={title}
width={width}
onClose={onCancel}
footer={renderFooter()}
>
<div style={{display: 'flex', flexDirection: 'column'}}>
{
renderOnly()
}
</div>
</Drawer>
)
}
export default InvoiceDrawer
.container {
display: flex;
flex-direction: row;
// align-items: center;
:global {
.ant-form-item {
margin-bottom: 12px;
}
.ant-input,
.ant-picker {
background-color: #F5F6F7;
border-color: #F5F6F7;
&:focus {
border-color: #F5F6F7;
box-shadow: none;
}
}
.ant-input-focused,
.ant-picker-focused {
border-color: #F5F6F7;
box-shadow: none;
}
}
.remove {
background-color: #F5F6F7;
width: 32px;
height: 32px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-left: 12px;
cursor: pointer;
}
}
.add {
width: 100%;
background-color: #F5F6F7;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 12px;
color: #5C626A;
padding: 8px;
cursor: pointer;
.addText {
margin-left: 8px;
}
}
import React from 'react'
import {
SchemaField,
SchemaMarkupField as Field
} from '@formily/antd'
import { ArrayList } from '@formily/react-shared-components'
import { toArr, isFn, FormPath } from '@formily/shared'
import styles from './arrayList.less';
import { MinusOutlined, PlusOutlined, PlusSquareOutlined } from '@ant-design/icons'
const ArrayCustom = props => {
const { value, schema, className, editable, path, mutators } = props
const xComponentProps = props.props['x-component-props'];
const onAdd = () => {
const items = Array.isArray(schema.items)
? schema.items[schema.items.length - 1]
: schema.items
mutators.push(items.getEmptyValue())
}
return (
<ArrayList
value={value}
minItems={schema.minItems}
maxItems={schema.maxItems}
editable={editable}
>
{xComponentProps?.header || null}
{toArr(value).map((item, index) => {
return (
<div className={styles.container} key={index}>
<SchemaField path={FormPath.parse(path).concat(index)} />
<div className={styles.remove} onClick={() => mutators.remove(index)}>
<MinusOutlined />
</div>
</div>
)
})}
<div className={styles.add} onClick={onAdd}>
<PlusOutlined size={12} />
<span className={styles.addText}>添加</span>
</div>
</ArrayList>
)
}
ArrayCustom.isFieldComponent = true
export default ArrayCustom;
import InvoiceDrawer from './InvoiceDrawer'
export default InvoiceDrawer
/*
* @Author: Bill
* @Date: 2020-10-28 13:46:39
* @Description: 开票详情 内容
*/
import React from 'react';
import { Row, Col } from 'antd';
import { InvoiceInfoProps } from '../../common/type';
import styles from './index.less';
// import { InvoiceInfoProps } from '../../common/type';
import styles from './invoiceInfo.less';
interface Iprops {
infos: InvoiceInfoProps
infos: {
typeName: string,
kindName: string,
invoiceTitle: string,
taxNo: string,
bankOfDeposit: string,
account: string,
address: string,
tel: string,
originalProveList?: {
number: number,
invoiceDate: string,
invoiceAmount: string,
remark: string
}[]
}
}
const infoList = [
......@@ -24,7 +33,7 @@ const infoList = [
{ title: '电话', dataIndex: 'tel' }
]
const InvoiceInfo: React.FC<Iprops> = (props) => {
const InvoiceInfo: React.FC<Iprops> & { isVirtualFieldComponent: boolean } = (props) => {
const { infos } = props;
return (
......@@ -35,12 +44,43 @@ const InvoiceInfo: React.FC<Iprops> = (props) => {
<Row className={styles.row} key={item.dataIndex}>
<Col span={3} className={styles.label}>{item.title}</Col>
<Col span={16}>{infos && infos[item.dataIndex]}</Col>
</Row>
</Row>
)
})
}
{
infos?.originalProveList && (
<div className={styles.originalInvoice}>
<p className={styles.title}>原订单发票信息</p>
<div className={styles.header}>
<div className={styles.defaultWidth}>原订单发票号码</div>
<div className={styles.defaultWidth}>原订单开票日期</div>
<div className={styles.defaultWidth}>原订单开票金额</div>
<div className={styles.specialWidth}>备注</div>
</div>
<div className={styles.list}>
{
infos.originalProveList.map((_item, key) => {
return (
<div className={styles.item} key={key}>
<div className={styles.defaultWidth}>{_item.number}</div>
<div className={styles.defaultWidth}>{_item.invoiceDate}</div>
<div className={styles.defaultWidth}>{_item.invoiceAmount}</div>
<div className={styles.specialWidth}>{_item.remark}</div>
</div>
)
})
}
</div>
</div>
)
}
</>
)
}
export default InvoiceInfo
\ No newline at end of file
InvoiceInfo.isVirtualFieldComponent = true
export default InvoiceInfo
.header {
display: flex;
flex-direction: row;
align-items: center;
color: #91959B;
margin-bottom: 8px;
.header-item {
width: 128px;
margin-right: 20px;
}
}
.required {
color: #F25767;
}
import {
createFormActions,
FormMegaLayout,
SchemaForm,
SchemaMarkupField as Field
} from '@formily/antd'
import { Button, Drawer } from 'antd';
import React from 'react';
import ArrayList from './arrayList';
import { Input, DatePicker } from '@formily/antd-components';
import styles from './invoiceDrawerInfo.less';
import InvoiceInfo from './invoice'
const formActions = createFormActions();
interface Iprops {
visible: boolean,
onCancel: () => void,
onSubmit?: (values: any) => void,
mode?: 'edit' | 'view',
invoiceInfoData: any
}
const InvoiceDrawerInfo: React.FC<Iprops> = (props: Iprops) => {
const { visible, onCancel, invoiceInfoData, onSubmit } = props;
const handleCancel = () => {
onCancel?.()
}
const renderFooter = () => {
return (
<div style={{textAlign: 'right'}}>
<Button onClick={handleCancel} style={{ marginRight: 8 }}>
取消
</Button>
<Button onClick={() => formActions.submit()} type="primary">
确认
</Button>
</div>
)
}
const handleSubmit = (values) => {
console.log(values)
onSubmit?.(values)
}
return (
<Drawer
visible={visible}
width={800}
title="开具发票"
footer={renderFooter()}
>
<SchemaForm
onSubmit={handleSubmit}
components={{ ArrayCustom: ArrayList, Input: Input, DatePicker }}
actions={formActions}
>
{
invoiceInfoData?.map((_item, key) => {
return (
<div key={key}>
<InvoiceInfo infos={_item} />
<Field
name={`list-${key}`}
type="array"
x-component="ArrayCustom"
x-component-props={{
header: (
<div className={styles.header}>
<div className={styles['header-item']}>
发票管理<span className={styles.required}>*</span>
</div>
<div className={styles['header-item']}>
开票日期
<span className={styles.required}>*</span>
</div>
<div className={styles['header-item']}>
开票金额(元)
<span className={styles.required}>*</span>
</div>
<div className={styles['header-item']}>备注</div>
</div>
)
}}
>
<Field type="object">
<FormMegaLayout inline>
<Field
name="number"
x-component="Input"
x-component-props={{
style: {
width: '128px'
}
}}
x-rules={
[
{required: true, message: '请填写发票号码'}
]
}
/>
<Field
name="invoiceDate"
x-component="DatePicker"
x-rules={
[
{required: true, message: '请填写开票日期'}
]
}
/>
<Field
name="invoiceAmount"
x-component="Input"
x-component-props={{
style: {
width: '128px'
}
}}
x-rules={
[
{required: true, message: '请填写开票金额(元)'}
]
}
/>
<Field
name="remark"
x-component="Input"
x-component-props={{
style: {
width: '268px'
}
}}
/>
</FormMegaLayout>
</Field>
</Field>
</div>
)
})
}
</SchemaForm>
</Drawer>
)
}
export default InvoiceDrawerInfo
......@@ -3,7 +3,7 @@
@margin-top: 50px;
.row {
margin-bottom: @margin-lg;
margin-bottom: @margin-lg;
font-size: @font-size;
.label {
......@@ -24,4 +24,40 @@
width: 140px;
margin-right: @margin-md;
}
}
\ No newline at end of file
}
.originalInvoice {
display: flex;
flex-direction: column;
margin-top: 8px;
.title {
font-size: 12px;
}
.header {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 8px;
}
.list {
display: flex;
flex-direction: column;
.item {
display: flex;
flex-direction: row;
align-items: center;
}
}
.defaultWidth {
width: 128px;
margin-right: 8px;
}
.specialWidth {
flex: 1;
}
}
/*
* @Author: bILL
* @Date: 2020-10-20 17:39:16
* @Description: 新增会员结算结算方式
*/
import React, { useEffect, useState } from 'react';
import { Radio, Input, Tooltip } from 'antd';
import { QuestionCircleOutlined } from '@ant-design/icons';
......
.balanceInfoName {
font-size: 14px;
line-height: 16px;
}
.wrapContainer {
background-color: #FAFBFC;
border-radius: 8px;
padding: 12px 12px 4px 12px;
margin-top: 8px;
font-size: 14px;
.item {
margin-bottom: 8px;
&-title {
color: #909399;
}
&-value {
color: #252D37;
margin-left: 16px;
}
}
}
import { Modal } from 'antd';
import React from 'react';
import styles from './index.less';
interface Iprops {
visible: boolean,
balanceInfo: {
/** 结算方名字 */
name: string,
amount: number,
/** 付款状态, 成功失败 */
/** 支付状态 */
statusName: string,
/** 支付时间 */
settlementDate: string,
/** 支付方式 */
payWayName: string,
},
onOk: () => void,
onClose: () => void
}
/** 查看通联支付付款 */
const ViewUniversalPay: React.FC<Iprops> = (props: Iprops) => {
const { visible, balanceInfo, onClose, onOk } = props;
const handleClose = () => {
onClose?.()
}
const handleOnOk = () => {
onOk?.();
}
const list = [
{
title: '结算金额',
value: balanceInfo?.amount
},
{
title: '付款方式',
value: balanceInfo?.payWayName
},
{
title: '付款时间',
value: balanceInfo?.settlementDate
},
{
title: '付款状态',
value: balanceInfo?.statusName
}
]
return (
<Modal
width={548}
title="查看付款"
onCancel={handleClose}
visible={visible}
onOk={handleOnOk}
>
<div className={styles.balanceInfoName}>结算方: {balanceInfo?.name}</div>
<div className={styles.wrapContainer}>
{
list.map((_item) => {
return (
<div className={styles.item} key={_item.title}>
<span className={styles['item-title']}>{_item.title}</span>
<span className={styles['item-value']}>{_item.value}</span>
</div>
)
})
}
</div>
</Modal>
)
}
export default ViewUniversalPay
......@@ -233,7 +233,7 @@ function useFetchColumns(mode: 'payable' | 'receiveable') {
}
return <a onClick={() => handleViewPayModal({id: record.id})}>查看付款凭证</a>
}
// 待收张狂结算
// 待收账款结算
if (record.status === TO_BE_RECONCILED) {
return <a onClick={() => handleReconciledComfirm({ id: record.id, payName: record.payName, settlementDate: record.settlementDate})}>确认对账完成</a>
}
......
import { getSettleAccountsPlatformSettlementGetReceivablePayProve } from "@/services/SettleV2Api";
import { useCallback, useState } from "react";
type FileType = {
name: string
/**
* 凭证地址
*/
proveUrl: string
}
type ConfirmPayInfoType = {
id: number,
/** 支付状态 */
statusName: string,
/** 支付时间 */
settlementDate: string,
/** 结算金额 */
amount: number
/** 结算方 */
name: string,
/** 1. 线下支付, 2.通联支付 */
payWay: 1 | 2,
/** 支付方式 */
payWayName: string,
}
/** 确认付款凭证 */
function useConfirmPayInfo() {
const [payFiles, setPayFiles] = useState<FileType[]>([]);
const [visible, setVisible] = useState<boolean>(false);
const [confirmPayInfo, setConfirmPayInfo] = useState<ConfirmPayInfoType | null>(null)
const fetchVouchers = useCallback(async (params: {id: number}) => {
const { code, data } = await getSettleAccountsPlatformSettlementGetReceivablePayProve({id: params.id.toString()})
if (code === 1000) {
setPayFiles(data);
}
}, [])
const handleOpen = (params: ConfirmPayInfoType) => {
setConfirmPayInfo(params)
fetchVouchers({id: params.id}).then(() => {
setVisible(true)
})
}
const handleClose = () => {
setVisible(false)
}
return {
payFiles,
confirmPayInfo,
confirmPayVisible: visible,
handleConfirmCompletePaymentStatus: handleOpen,
confirmPayOnCancel: handleClose,
}
}
export default useConfirmPayInfo
import { useState, useEffect } from 'react';
type ReconciliationInfoType = {
/** 结算id */
id: number,
/** 结算时间 */
settlementDate: string,
/** 付款方名字 */
payName: string
}
/** 确认对账完成 */
function useConfirmReconciliation() {
const [visible, setVisible] = useState<boolean>(false);
const [reconciliationInfo, setReconciliationInfo] = useState<ReconciliationInfoType | null>(null)
const handleReconciliationOpen = (params: ReconciliationInfoType) => {
setReconciliationInfo(params)
setVisible(true)
}
const handleReconciliationClose = () => {
setVisible(false);
}
return {
reconciliationModalVisible: visible,
handleReconciliationClose,
handleReconciliationOpen,
reconciliationInfo
}
}
export default useConfirmReconciliation
import { getSettleAccountsPlatformSettlementGetReceivablePayProve } from '@/services/SettleV2Api';
import { useState, useEffect, useCallback } from 'react'
type RecordType = {
id: number
}
type PayInfoType = {
id: number,
/** 支付状态 */
statusName: string,
/** 支付时间 */
settlementDate: string,
/** 结算金额 */
amount: number
/** 结算方 */
name: string,
/** 1. 线下支付, 2.通联支付 */
payWay: 1 | 2,
/** 支付方式 */
payWayName: string,
}
/** 查看付款凭证 */
function useViewPayInfo() {
const [visible, setVisible] = useState<boolean>(false);
const [files, setFiles] = useState<any>([]);
const [payInfo, setPayInfo] = useState<PayInfoType | null>(null)
const fetchVouchers = useCallback(async (params: {id: number}) => {
const { code, data } = await getSettleAccountsPlatformSettlementGetReceivablePayProve({id: params.id.toString()})
if (code === 1000) {
setFiles(data);
}
}, [])
const handleOpen = (params: PayInfoType) => {
setPayInfo(params)
if (params.payWay === 1) {
fetchVouchers({id: params.id}).then(() => {
setVisible(true)
})
return;
}
setVisible(true)
}
const handleClose = () => {
setVisible(false)
}
return {
files,
payInfo,
viewVisible: visible,
viewModalonCancel: handleClose,
handleViewPayModal: handleOpen
}
}
export default useViewPayInfo;
/*
* @Author: your name
* @Date: 2020-10-20 09:37:53
* @Description: 平台代收账款结算
*/
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { Card, DatePicker, Modal, Space, Button } from 'antd';
......@@ -27,6 +21,10 @@ import { STATUS_TEXT } from '@/constants/balance';
import { numFormat, priceFormat } from '@/utils/numberFomat';
import useSetSearchValueInTable from '@/hooks/useSetSearchValueInTable';
import { getSettleAccountsCommonGetSettlementStatus, getSettleAccountsPlatformSettlementGetReceivablePayProve, getSettleAccountsPlatformSettlementPageReceivableSettlement, GetSettleAccountsPlatformSettlementPageReceivableSettlementRequest, postSettleAccountsPlatformSettlementConfirmAccountComplete, postSettleAccountsPlatformSettlementConfirmPayProve } from '@/services/SettleV2Api';
import useConfirmReconciliation from './hooks/useConfirmReconciliation';
import useViewPayInfo from './hooks/useViewPayInfo';
import useConfirmPayInfo from './hooks/useConfirmPayInfo';
import ViewUniversalPay from '../../components/ViewUniversalPay';
const formActions = createFormActions();
const { RangePicker } = DatePicker;
......@@ -36,21 +34,10 @@ const PLATFORM_BENEFICIARY = 3;
const AccountReceivable = () => {
const ref = useRef<any>({})
const {
confirmPayInfo,
confirmPayVisible,
reconciliationInfo,
reconciliationVisible,
viewVisible,
payVoucherInfo,
viewModalonCancel,
confirmPayOnCancel,
reconciliationOnCancel,
handleReconciledComfirm,
handleComfirmCompletePaymentStatus,
handleViewPayModal
} = useFetchColumns("receiveable");
const [files, setFiles] = useState([]);
const { reconciliationInfo, reconciliationModalVisible, handleReconciliationOpen, handleReconciliationClose } = useConfirmReconciliation();
const { files, viewVisible, payInfo, viewModalonCancel, handleViewPayModal } = useViewPayInfo();
const { confirmPayInfo, payFiles, handleConfirmCompletePaymentStatus, confirmPayOnCancel, confirmPayVisible } = useConfirmPayInfo();
// const [files, setFiles] = useState([]);
const { searchData, formatInitialValue, clear } = useSetSearchValueInTable();
const columns = [
......@@ -87,15 +74,15 @@ const AccountReceivable = () => {
{
title: '操作',
render: (text, record) => {
// 待收张狂结算
// 待收账款结算
if (record.status === TO_BE_RECONCILED) {
return <a onClick={() => handleReconciledComfirm({ id: record.id, payName: '平台', settlementDate: record.settlementDate})}>确认对账完成</a>
return <a onClick={() => handleReconciliationOpen({ id: record.id, payName: '平台', settlementDate: record.settlementDate})}>确认对账完成</a>
}
if (record.status === TO_BE_COLLECTED) {
return <a onClick={() => handleComfirmCompletePaymentStatus({id: record.id})}>确认付款凭证</a>
return <a onClick={() => handleConfirmCompletePaymentStatus(record)}>确认付款凭证</a>
}
if (record.status === COMPLETED) {
return <a onClick={() => handleViewPayModal({id: record.id})}>查看付款凭证</a>
return <a onClick={() => handleViewPayModal(record)}>查看付款凭证</a>
}
}
}
......@@ -122,14 +109,15 @@ const AccountReceivable = () => {
const handleConfirm = async (params: {id: number}) => {
const { code } = await postSettleAccountsPlatformSettlementConfirmAccountComplete({settlementId: params.id})
if(code === 1000) {
reconciliationOnCancel();
// reconciliationOnCancel();
handleReconciliationClose();
formActions.submit();
}
}
/**
* 确认付款凭证
*/
* 确认付款凭证
*/
const handleConfirmPayStatus = async (params: { status: 0 | 1, id: number }) => {
const { code } = await postSettleAccountsPlatformSettlementConfirmPayProve({id: params.id, status: params.status});
if (code) {
......@@ -138,18 +126,6 @@ const AccountReceivable = () => {
}
}
const fetchVouchers = useCallback(async (id: number) => {
const { code, data } = await getSettleAccountsPlatformSettlementGetReceivablePayProve({id: id.toString()})
if (code === 1000) {
setFiles(data);
}
}, [])
useEffect(() => {
if (payVoucherInfo !== null) {
fetchVouchers(payVoucherInfo.id);
}
}, [payVoucherInfo])
/**
* 搜索
......@@ -197,15 +173,13 @@ const AccountReceivable = () => {
<Modal
width={400}
title="确认对账完成"
visible={reconciliationVisible}
onCancel={reconciliationOnCancel}
visible={reconciliationModalVisible}
onCancel={handleReconciliationClose}
onOk={() => handleConfirm({ id: reconciliationInfo?.id})}
>
<ConfirmAccount settlementDate={reconciliationInfo?.settlementDate} payName={reconciliationInfo?.payName} />
</Modal>
<Modal width={548} title="查看付款凭证" onCancel={viewModalonCancel} visible={viewVisible} footer={null}>
<Voucher files={files} />
</Modal>
<Modal
width={548}
title="确认付款凭证"
......@@ -229,6 +203,24 @@ const AccountReceivable = () => {
</Space>
)}
>
<Voucher files={payFiles} />
</Modal>
{/* 通联支付付款凭证 */}
<ViewUniversalPay
visible={viewVisible && payInfo.payWay === 2}
balanceInfo={confirmPayInfo}
onOk={viewModalonCancel}
onClose={viewModalonCancel}
/>
<Modal
width={548}
title="查看付款凭证"
onCancel={viewModalonCancel}
visible={viewVisible && payInfo.payWay === 1}
footer={null}
>
<Voucher files={files} />
</Modal>
</PageHeaderWrapper>
......
......@@ -26,11 +26,13 @@ import { fetchOptions } from '../../common';
import {
getSettleAccountsCommonGetStrategySettlementOrderType,
getSettleAccountsPlatformConfigGetMemberSettlementStrategyDetail,
getSettleAccountsPlatformSettlementTypeList,
postSettleAccountsPlatformConfigAddMemberSettlementStrategy,
postSettleAccountsPlatformConfigUpdateMemberSettlementStrategy
} from '@/services/SettleV2Api';
import { getMemberManageLowerPageBynamerole, getMemberManageRoleSubList, getMemberManageUpperPage } from '@/services/MemberV2Api';
import { getManageRuleConfigurationList } from '@/services/ManageV2Api';
import { getOrderPlatformSettlementTypeList } from '@/services/OrderNewV2Api';
const formActions = createFormActions();
......@@ -162,6 +164,7 @@ const MemberSettleAdd: React.FC = () => {
settlementWay: value.settlementWay.active,
settlementDays: value.settlementWay.otherValues[0],
settlementDate: value.settlementWay.otherValues[1],
settlementPaymentType: value.settlementPaymentType,
}
const serviceActions = isAdd
? postSettleAccountsPlatformConfigAddMemberSettlementStrategy
......@@ -222,7 +225,8 @@ const MemberSettleAdd: React.FC = () => {
settlementWay: {
active: res.data.settlementWay,
otherValues: [res.data.settlementDays, res.data.settlementDate],
}
},
settlementPaymentType: res.data.settlementOrderType.toString(),
})
const list = res.data.memberList.map((item) => ({
...item,
......@@ -240,7 +244,15 @@ const MemberSettleAdd: React.FC = () => {
}
getInfo();
}
}, [id])
}, [])
const fetchPaymentType = async () => {
const { data, code } = await getSettleAccountsPlatformSettlementTypeList();
if (code === 1000) {
return data.map((_item) => ({value: _item.methodCode, label: _item.methodName}))
}
return []
}
return (
<PageHeaderWrapper
......@@ -269,6 +281,10 @@ const MemberSettleAdd: React.FC = () => {
useAsyncSelect(
"settlementOrderType",
fetchOptions(getSettleAccountsCommonGetStrategySettlementOrderType)
),
useAsyncSelect(
"settlementPaymentType",
fetchPaymentType
)
}}
/>
......
......@@ -110,6 +110,14 @@ export const addSchema = {
'x-rules': [
{required: true, message: '请填写结算单据类型'},
]
},
settlementPaymentType: {
type: 'string',
enum: [],
title: '结算支付方式',
'x-rules': [
{required: true, message: '请填写结算支付方式'},
]
}
},
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment