feat:bill manual payment completed

This commit is contained in:
LittleBoy 2024-07-29 00:02:37 +08:00
parent 895c032532
commit 21ec84f68d
11 changed files with 343 additions and 142 deletions

View File

@ -132,7 +132,7 @@ body #root{
} }
// input // input
.semi-input-wrapper, .semi-select,.semi-datepicker-range-input { .semi-input-wrapper, .semi-select,.semi-datepicker-range-input,.semi-input-textarea-wrapper {
background-color: transparent; background-color: transparent;
border: 1px var(--semi-color-fill-2) solid; border: 1px var(--semi-color-fill-2) solid;
border-radius: var(--semi-border-radius-small); border-radius: var(--semi-border-radius-small);

View File

@ -41,6 +41,7 @@
"reconciliation_status_pending": "UNCHECKED", "reconciliation_status_pending": "UNCHECKED",
"reconciliation_status_submitted": "CHECKED", "reconciliation_status_submitted": "CHECKED",
"require_student_number": "Search Student Number", "require_student_number": "Search Student Number",
"set_bill_paid": "Set Bill Paid",
"sort_asc": "ASC", "sort_asc": "ASC",
"sort_desc": "DESC", "sort_desc": "DESC",
"title_actual_payment_amount": "Actually Paid", "title_actual_payment_amount": "Actually Paid",
@ -53,11 +54,13 @@
"title_operate": "Operation", "title_operate": "Operation",
"title_paid_at": "Transaction Date", "title_paid_at": "Transaction Date",
"title_pay_amount": "Pay Amount", "title_pay_amount": "Pay Amount",
"title_pay_channel": "Payment Channel",
"title_pay_method": "Pay Method", "title_pay_method": "Pay Method",
"title_pay_sort": "Sort By", "title_pay_sort": "Sort By",
"title_program_id": "Program ID", "title_program_id": "Program ID",
"title_program_name": "Program", "title_program_name": "Program",
"title_reconciliation_status": "Reconciliation", "title_reconciliation_status": "Reconciliation",
"title_remark": "Remark",
"title_semester": "Semester", "title_semester": "Semester",
"title_service_charge": "Service Charge", "title_service_charge": "Service Charge",
"title_student_name": "Student Name", "title_student_name": "Student Name",

View File

@ -41,6 +41,7 @@
"reconciliation_status_pending": "未对账", "reconciliation_status_pending": "未对账",
"reconciliation_status_submitted": "已对账", "reconciliation_status_submitted": "已对账",
"require_student_number": "请输入查询学号", "require_student_number": "请输入查询学号",
"set_bill_paid": "设置账单支付完成",
"sort_asc": "升序", "sort_asc": "升序",
"sort_desc": "降序", "sort_desc": "降序",
"title_actual_payment_amount": "实付金额", "title_actual_payment_amount": "实付金额",
@ -53,11 +54,13 @@
"title_operate": "操作", "title_operate": "操作",
"title_paid_at": "支付时间", "title_paid_at": "支付时间",
"title_pay_amount": "应付金额", "title_pay_amount": "应付金额",
"title_pay_channel": "支付渠道",
"title_pay_method": "支付方式", "title_pay_method": "支付方式",
"title_pay_sort": "排序方式", "title_pay_sort": "排序方式",
"title_program_id": "专业ID", "title_program_id": "专业ID",
"title_program_name": "就读专业", "title_program_name": "就读专业",
"title_reconciliation_status": "对账状态", "title_reconciliation_status": "对账状态",
"title_remark": "备注",
"title_semester": "学期", "title_semester": "学期",
"title_service_charge": "手续费", "title_service_charge": "手续费",
"title_student_name": "学生姓名", "title_student_name": "学生姓名",

View File

@ -41,6 +41,7 @@
"reconciliation_status_pending": "未對帳", "reconciliation_status_pending": "未對帳",
"reconciliation_status_submitted": "已對帳", "reconciliation_status_submitted": "已對帳",
"require_student_number": "請輸入查詢學號", "require_student_number": "請輸入查詢學號",
"set_bill_paid": "設定帳單支付完成",
"sort_asc": "升序", "sort_asc": "升序",
"sort_desc": "降序", "sort_desc": "降序",
"title_actual_payment_amount": "實付金額", "title_actual_payment_amount": "實付金額",
@ -53,11 +54,13 @@
"title_operate": "操作", "title_operate": "操作",
"title_paid_at": "付款時間", "title_paid_at": "付款時間",
"title_pay_amount": "應付金額", "title_pay_amount": "應付金額",
"title_pay_channel": "支付渠道",
"title_pay_method": "付款方式", "title_pay_method": "付款方式",
"title_pay_sort": "排序方式", "title_pay_sort": "排序方式",
"title_program_id": "專業ID", "title_program_id": "專業ID",
"title_program_name": "就讀專業", "title_program_name": "就讀專業",
"title_reconciliation_status": "對帳狀態", "title_reconciliation_status": "對帳狀態",
"title_remark": "備註",
"title_semester": "學期", "title_semester": "學期",
"title_service_charge": "手續費", "title_service_charge": "手續費",
"title_student_name": "學生姓名", "title_student_name": "學生姓名",

View File

@ -0,0 +1,147 @@
import {BillDetailItems} from "@/components/bill";
import {Button, Col, Form, Modal, Notification, Row, Space, Toast} from "@douyinfe/semi-ui";
import React from "react";
import {useTranslation} from "react-i18next";
import {finishBill, modifyBillStatus} from "@/service/api/bill.ts";
import {BizError} from "@/service/types.ts";
import {useSetState} from "ahooks";
type BillPaidModalProps = {
bill: BillModel;
open?: boolean;
onConfirm: () => void
onCancel: () => void
}
export const BillPaidModal: React.FC<BillPaidModalProps> = (props) => {
const {t} = useTranslation()
const [state, setState] = useSetState<{
loading?: boolean
}>({})
const onConfirmPaid = () => {
if (!props.bill) return;
setState({
loading: true
})
modifyBillStatus(props.bill.id, 'PAID').then(() => {
setState({loading: false})
Notification.success({title: 'Notice', content: t('base.operate_success')})
props.onConfirm()
}).catch((e: BizError) => {
setState({loading: false})
Toast.error({
content: `${t('base.operate_fail')}:${e.message}`,
duration: 3
})
})
}
const onSubmit = (values: BillUpdateParams) => {
if (!props.bill) return;
finishBill({bill:props.bill,param:values}).then(props.onConfirm)
// setState({
// loading: true
// })
// modifyBillStatus(props.bill.id, 'PAID', values).then(() => {
// setState({loading: false})
// Notification.success({title: 'Notice', content: t('base.operate_success')})
// props.onConfirm()
// }).catch((e: BizError) => {
// setState({loading: false})
// Toast.error({
// content: `${t('base.operate_fail')}:${e.message}`,
// duration: 3
// })
// })
}
return <Modal
title={t('bill.set_bill_paid') + ` - ${props.bill?.id}`}
visible={props.open}
closeOnEsc={true}
onCancel={props.onCancel}
confirmLoading={state.loading}
onOk={onConfirmPaid}
footer={null}
width={600}
okText={t('base.confirm_paid')}
maskClosable={false}
>
{props.bill && <div><BillDetailItems bill={props.bill}/></div>}
<Form<BillUpdateParams> onSubmit={onSubmit} initValues={{
payment_channel: 'FLYWIRE',
payment_method: 'card',
merchant_ref: props.bill?.merchant_ref,
payment_amount: props.bill?.amount,
actual_payment_amount: props.bill?.amount
}}>
<Row gutter={20}>
<Col span={12}>
<Form.Select
rules={[
{required: true, message: 'required error'},
]}
showClear field="payment_channel" label={t('bill.title_pay_channel')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
<Form.Select.Option value="FLYWIRE">FLYWIRE</Form.Select.Option>
{/*<Form.Select.Option value="ASIAPAY">ASIAPAY</Form.Select.Option>*/}
{/*<Form.Select.Option value="PPS">PPS</Form.Select.Option>*/}
</Form.Select>
</Col>
<Col span={12}>
<Form.Select
rules={[
{required: true, message: 'required error'},
]}
showClear field="payment_method" label={t('bill.title_pay_method')}
placeholder={t('base.please_select')} style={{width: '100%'}}>
<Form.Select.Option value="card">Card(VISA,MasterCard,UnionPay,JCB...)</Form.Select.Option>
<Form.Select.Option value="wechat">Wechat</Form.Select.Option>
<Form.Select.Option value="alipay">Alipay</Form.Select.Option>
<Form.Select.Option value="other">Other</Form.Select.Option>
{/*<Form.Select.Option value="ASIAPAY">ASIAPAY</Form.Select.Option>*/}
{/*<Form.Select.Option value="PPS">PPS</Form.Select.Option>*/}
</Form.Select>
</Col>
</Row>
<Row gutter={20}>
<Col span={12}>
<Form.Input
rules={[
{required: true, message: 'required error'},
]}
showClear field="merchant_ref" label="Merchant Ref"
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
<Col span={6}>
<Form.Input
rules={[
{required: true, message: 'required error'},
]} type={'number'} showClear
field="payment_amount" label={t('bill.title_pay_amount')}
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
<Col span={6}>
<Form.Input
type={'number'}
showClear field="actual_payment_amount" label={t('bill.title_actual_payment_amount')}
placeholder={t('base.please_enter')} style={{width: '100%'}}/>
</Col>
</Row>
<Row gutter={20}>
<Col span={24}>
<Form.TextArea
rules={[{required: true, message: 'required error'}]}
showClear field="remark" label={t('bill.title_remark')} placeholder={t('base.please_enter')}
style={{width: '100%'}}/>
</Col>
</Row>
{/*<p style={{marginTop: 10}}>{t('bill.paid_confirm')}</p>*/}
<div className={'text-right'} style={{margin: '10px 0 20px'}}>
<Space spacing={12}>
<Button onClick={props.onCancel} type={'tertiary'}>{t('base.cancel')}</Button>
<Button loading={state.loading} htmlType={'submit'} theme={'solid'} type={'primary'}>{t('base.confirm_paid')}</Button>
</Space>
</div>
</Form>
</Modal>
}

View File

@ -9,9 +9,9 @@ import BillDetail from "@/components/bill/detail.tsx";
import {billList, BillQueryParams, modifyBillStatus} from "@/service/api/bill.ts"; import {billList, BillQueryParams, modifyBillStatus} from "@/service/api/bill.ts";
import {BillStatus, BizError} from "@/service/types.ts"; import {BillStatus, BizError} from "@/service/types.ts";
import {useDownloadReceiptPDF} from "@/service/generate-pdf.ts"; import {useDownloadReceiptPDF} from "@/service/generate-pdf.ts";
import useAuth from "@/hooks/useAuth.ts";
import {BillDetailItems} from "@/components/bill"; import {BillDetailItems} from "@/components/bill";
import MoneyFormat from "@/components/money-format.tsx"; import MoneyFormat from "@/components/money-format.tsx";
import {BillPaidModal} from "@/pages/bill/components/bill_paid_modal.tsx";
const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => { const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => {
@ -54,27 +54,6 @@ const BillQuery = () => {
}) })
} }
const onConfirmPaid = () => {
if (!state.updateBill) return;
setState({
updateLoading: true
})
modifyBillStatus(state.updateBill.id, 'PAID').then(() => {
setState({
updateBill: undefined, updateLoading: false
})
Notification.success({title: 'Notice', content: t('base.operate_success')})
refresh()
}).catch((e: BizError) => {
setState({
updateLoading: false
})
Toast.error({
content: `${t('base.operate_fail')}:${e.message}`,
duration: 3
})
})
}
const operation = (bill: BillModel) => { const operation = (bill: BillModel) => {
return (<div className={'table-operation-render'}> return (<div className={'table-operation-render'}>
@ -128,21 +107,15 @@ const BillQuery = () => {
> >
{showBill && <BillDetail bill={showBill} onCancel={() => setShowBill(undefined)}/>} {showBill && <BillDetail bill={showBill} onCancel={() => setShowBill(undefined)}/>}
</Modal> </Modal>
<Modal <BillPaidModal
title="Notice" open={!!state.updateBill}
visible={!!state.updateBill} onCancel={()=>setState({updateBill:undefined})}
closeOnEsc={true} bill={state.updateBill!}
onCancel={() => { onConfirm={()=>{
setState({updateBill: undefined, updateLoading: false}) setState({updateBill:undefined})
refresh()
}} }}
confirmLoading={state.updateLoading} />
onOk={onConfirmPaid}
okText={t('base.confirm_paid')}
maskClosable={false}
>
{state.updateBill && <div><BillDetailItems bill={state.updateBill}/></div>}
<p style={{marginTop: 10}}>{t('bill.paid_confirm')}</p>
</Modal>
<Modal <Modal
title="Confirm Bill Type" title="Confirm Bill Type"
visible={!!state.confirmBill} visible={!!state.confirmBill}
@ -166,9 +139,8 @@ const BillQuery = () => {
</div> </div>
</Space> </Space>
<div className="confirm-item-btn"> <div className="confirm-item-btn">
{!!it.confirm_status ? <Tag color='light-blue'>CONFIRMED</Tag> : {it.confirm_status == 'CONFIRMED' ? <Tag color='light-blue'>CONFIRMED</Tag> :
<Button>{t('base.confirm')}</Button>} <Button>{t('base.confirm')}</Button>}
</div> </div>
</div>)) </div>))
} }

View File

@ -10,98 +10,102 @@ import useAuth from "@/hooks/useAuth.ts";
import {BizError} from "@/service/types.ts"; import {BizError} from "@/service/types.ts";
const BillReconciliation = () => { const BillReconciliation = () => {
const {t} = useTranslation() const {t} = useTranslation()
const {user} = useAuth(); const {user} = useAuth();
const [queryParams, setBillQueryParams] = useState<BillQueryParams>({ const [queryParams, setBillQueryParams] = useState<BillQueryParams>({
apply_status:'UNCHECKED' apply_status: 'UNCHECKED'
}); });
const {data, loading, refresh} = useRequest(() => billList({ const {data, loading, refresh} = useRequest(() => billList({
...queryParams, ...queryParams,
status: 'PAID', status: 'PAID',
department: user?.department == 'RO' ? 'RO' : 'FO', confirm_status: 'CONFIRMED',
}), { department: user?.department == 'RO' ? 'RO' : 'FO',
refreshDeps: [queryParams], }), {
onError: (e:Error) => { refreshDeps: [queryParams],
Toast.error({ onError: (e: Error) => {
content: `${t('base.query_bill')}:${e.message}`, Toast.error({
duration: 3 content: `${t('base.query_bill')}:${e.message}`,
}) duration: 3
} })
}) }
})
const [selectKeys, setSelectedKeys] = useState<(string | number)[]>([]) const [selectKeys, setSelectedKeys] = useState<(string | number)[]>([])
const [state,setState] = useSetState({ const [state, setState] = useSetState({
checkingId: -1 checkingId: -1
}) })
const confirmBill = (records: number[]) => { const confirmBill = (records: number[]) => {
if (records.length == 0) { if (records.length == 0) {
Notification.error({title: 'Notice', content: t('bill.confirm_select_empty')}) Notification.error({title: 'Notice', content: t('bill.confirm_select_empty')})
return return
} }
setState({checkingId: records.length > 1 ? 0 : records[0] }) setState({checkingId: records.length > 1 ? 0 : records[0]})
confirmBills(records).then(() => { confirmBills(records).then(() => {
Notification.success({title: 'Notice', content: t('bill.confirm_success')}) Notification.success({title: 'Notice', content: t('bill.confirm_success')})
refresh() refresh()
}).catch((e:BizError) => { }).catch((e: BizError) => {
Toast.error({ Toast.error({
content: `${t('base.operate_fail')}:${e.message}`, content: `${t('base.operate_fail')}:${e.message}`,
duration: 3 duration: 3
}) })
}).finally(() => {setState({checkingId: -1})}) }).finally(() => {
} setState({checkingId: -1})
const operation = (_record: BillModel) => { })
return (<Space> }
<Popconfirm const operation = (_record: BillModel) => {
title={'Notice'} return (<Space>
content={t('bill.confirm_confirm_title')} <Popconfirm
onConfirm={() => confirmBill([_record.id])} title={'Notice'}
> content={t('bill.confirm_confirm_title')}
<Button loading={state.checkingId == _record.id} size={'small'} theme={'solid'} onConfirm={() => confirmBill([_record.id])}
type={'primary'}>{t('bill.confirm')}</Button> >
</Popconfirm> <Button
</Space>) loading={state.checkingId == _record.id} size={'small'} theme={'solid'}
} type={'primary'}>{t('bill.confirm')}</Button>
</Popconfirm>
</Space>)
}
return (<div> return (<div>
<SearchForm <SearchForm
searchHeader={( searchHeader={(
<Tabs className={'no-border'} onChange={(apply_status) => setBillQueryParams({ <Tabs className={'no-border'} onChange={(apply_status) => setBillQueryParams({
apply_status, apply_status,
status: 'PAID' status: 'PAID'
})}> })}>
<TabPane tab={<span>{t('bill.reconciliation_status_pending')}</span>} itemKey="UNCHECKED"/> <TabPane tab={<span>{t('bill.reconciliation_status_pending')}</span>} itemKey="UNCHECKED"/>
<TabPane tab={<span>{t('bill.reconciliation_status_submitted')}</span>} itemKey="CHECKED"/> <TabPane tab={<span>{t('bill.reconciliation_status_submitted')}</span>} itemKey="CHECKED"/>
</Tabs> </Tabs>
)} )}
loading={loading} loading={loading}
onSearch={(params) => { onSearch={(params) => {
setBillQueryParams({...params, apply_status: queryParams.apply_status}) setBillQueryParams({...params, apply_status: queryParams.apply_status})
}} }}
/> />
<BillList <BillList
source={data} type={'reconciliation'} source={data} type={'reconciliation'}
operationRender={queryParams.apply_status == 'CHECKED' ? undefined : operation} operationRender={queryParams.apply_status == 'CHECKED' ? undefined : operation}
onRowSelection={queryParams.apply_status == 'CHECKED' ? undefined : (keys: (number | string)[]) => { onRowSelection={queryParams.apply_status == 'CHECKED' ? undefined : (keys: (number | string)[]) => {
setSelectedKeys(keys); setSelectedKeys(keys);
}} }}
loading={loading} loading={loading}
onPageChange={(page_number) => setBillQueryParams({ onPageChange={(page_number) => setBillQueryParams({
...queryParams, ...queryParams,
page_number page_number
})} })}
tableFooter={queryParams.apply_status != 'CHECKED' && selectKeys?.length > 0 && ( tableFooter={queryParams.apply_status != 'CHECKED' && selectKeys?.length > 0 && (
<Popconfirm <Popconfirm
title={'Notice'} title={'Notice'}
content={`${t('bill.cancel_confirm_bills')}?`} content={`${t('bill.cancel_confirm_bills')}?`}
onConfirm={() => confirmBill(selectKeys as number[])} onConfirm={() => confirmBill(selectKeys as number[])}
> >
<Button style={{marginRight: 10}}> <Button style={{marginRight: 10}}>
{t('bill.confirm_batch')} {t('bill.confirm_batch')}
</Button> </Button>
</Popconfirm> </Popconfirm>
)} )}
/> />
</div>) </div>)
} }
export default BillReconciliation export default BillReconciliation

View File

@ -1,4 +1,5 @@
import {get, post, put} from "@/service/request.ts"; import {get, post, put} from "@/service/request.ts";
import dayjs from "dayjs";
export type BillQueryResult = { export type BillQueryResult = {
result: BillModel[]; result: BillModel[];
@ -63,3 +64,57 @@ export function updateBillPaymentSuccess(bill_id: number, merchant_ref: string,
export function createExternalBill(params: ExternalCreateParamsType) { export function createExternalBill(params: ExternalCreateParamsType) {
return post<BillModel>('/bill', params) return post<BillModel>('/bill', params)
} }
type BillUpdateFormParams = {
bill:BillModel;
param:BillUpdateParams
}
export async function finishAsiapay({bill,param}: BillUpdateFormParams){
const paramUrl = `?prc=0&src=0&Ord=12345678&Ref=${param.merchant_ref}&PayRef=123456&successcode=0&Amt=10.00&Cur=344&Holder=Test Card&AuthId=123456&AlertCode=&remark=
&eci=07&payerAuth=U&sourceIp=192.1.1.1&ipCountry=HK&payMethod=VISA
x&cardIssuingCountry=HK&channelType=SPC&`
const ret = await post<string>(`/flywire/feedback?${paramUrl}`, {},true)
return ret?.toLowerCase() == 'ok'
}
export async function finishFlywire({bill,param}: BillUpdateFormParams){
const eventData = {
"event_type": "guaranteed",
"event_date": dayjs().format('YYYY-MM-DDTHH:mm:ss[Z]'),
"event_resource": "payments",
"data": {
"remark": param.remark,
"payment_id": param.merchant_ref,
"amount_from": param.actual_payment_amount,
"currency_from": "HKD",
"amount_to": param.payment_amount,
"currency_to": "HKD",
"status": "guaranteed",
"expiration_date": dayjs().format('YYYY-MM-DDTHH:mm:ss[Z]'),
"external_reference": bill.id,
"country": "CN",
"payment_method": {
"type": param.payment_channel
},
"fields": {
"student_email": bill.student_email,
"student_last_name": bill.student_last_name || bill.student_english_name,
"student_id": bill.student_number,
"resident_no": null,
"contact_no": "",
"payment_type_other": null,
"payment_type": null
}
}
}
const ret = await post<string>('/flywire/feedback', eventData,true)
return ret?.toLowerCase() == 'ok'
}
export function finishBill(params: BillUpdateFormParams) {
if(params.param.payment_channel === 'flywire'){
return finishAsiapay(params)
}
return finishFlywire(params)
}

View File

@ -27,7 +27,7 @@ export function GeneratePdf(bill: BillModel) {
doc.setFont('Helvetica', 'normal', 'normal'); doc.setFont('Helvetica', 'normal', 'normal');
drawItem(doc, {title: 'Student Name:', content: bill.student_english_name || bill.student_chinese_name}, 40) drawItem(doc, {title: 'Student Name:', content: bill.student_english_name || bill.student_chinese_name}, 40)
drawItem(doc, {title: 'Reference Number:', content: `${bill.application_number}`}, 40, "right") drawItem(doc, {title: 'Reference Number:', content: `${bill.id}`}, 40, "right")
drawItem(doc, {title: 'Student Number:', content: `${bill.student_number || bill.application_number}`}, 48) drawItem(doc, {title: 'Student Number:', content: `${bill.student_number || bill.application_number}`}, 48)
drawItem(doc, {title: 'Print Date:', content: dayjs().format('YYYY-MM-DD')}, 48, "right") drawItem(doc, {title: 'Print Date:', content: dayjs().format('YYYY-MM-DD')}, 48, "right")
@ -58,10 +58,10 @@ export function GeneratePdf(bill: BillModel) {
body: [ body: [
...(bill.details.map(it=>{ ...(bill.details.map(it=>{
return [ return [
`#${bill.id}${it.id?'-' + it.id:''}`, `#${it.id}`,
dayjs(bill.paid_at).format('YYYY-MM-DD'), dayjs(bill.paid_at).format('YYYY-MM-DD'),
it.bill_type, it.bill_type,
`${bill.payment_channel}(${bill.payment_method})`, `${bill.payment_channel}` + bill.payment_channel != bill.payment_method ? `(${bill.payment_method})` : '',
`${it.amount}` `${it.amount}`
]; ];
})), })),

View File

@ -38,13 +38,13 @@ export function request<T>(url: string, method: RequestMethod, data: AllType = n
reject(new BizError("Service Internal Exception,Please Try Later!", res.status)) reject(new BizError("Service Internal Exception,Please Try Later!", res.status))
return; return;
} }
if (getOriginResult) {
resolve(res.data as unknown as T)
return;
}
// const // const
const {code, message, data,request_id} = res.data const {code, message, data,request_id} = res.data
if (code == 0) { if (code == 0) {
if (getOriginResult) {
resolve(res.data as unknown as T)
return;
}
resolve(data as unknown as T) resolve(data as unknown as T)
} else { } else {
reject(new BizError(message, code,request_id, data as unknown as AllType)) reject(new BizError(message, code,request_id, data as unknown as AllType))

26
src/types/bill.d.ts vendored
View File

@ -1,6 +1,8 @@
declare type BaseDate = number | Date | string | null; declare type BaseDate = number | Date | string | null;
declare type BillStatus = 'PAID' | 'CANCELLED' | 'PENDING' declare type BillStatus = 'PAID' | 'CANCELLED' | 'PENDING'
declare type ConfirmStatus = 'UNCONFIRMED' | 'CONFIRMED'
declare type SortOrderType = 'ASC'|'DESC' | string;
// 现场支付账单数据 // 现场支付账单数据
declare type ManualCreateBillParam = { declare type ManualCreateBillParam = {
@ -12,10 +14,9 @@ declare type BillDetail = {
id: string | number; id: string | number;
bill_id: string | number; bill_id: string | number;
bill_type: string; bill_type: string;
confirm_status?: number; confirm_status: ConfirmStatus;
amount: decimal; amount: decimal;
} }
declare type SortOrderType = 'ASC'|'DESC' | string;
/** /**
* *
*/ */
@ -31,6 +32,7 @@ declare type BillQueryParam = {
start_date:string; start_date:string;
end_date:string; end_date:string;
department:string; department:string;
confirm_status: ConfirmStatus;
sort_field:string; sort_field:string;
sort_order:SortOrderType; sort_order:SortOrderType;
} }
@ -57,19 +59,22 @@ declare type BillModel = {
service_charge?: number; service_charge?: number;
payment_amount?: number; payment_amount?: number;
actual_payment_amount?: null | number; actual_payment_amount?: null | number;
payment_method?: null | string | number; payment_method?: null | string;
currency?: string; currency?: string;
payment_channel?: null | string | number; payment_channel?: null | string ;
expiration_time?: BaseDate; expiration_time?: BaseDate;
status: string; status: string;
apply_status: string; apply_status: string;
paid_area?: null | string | number; paid_area?: null | string | number;
paid_at?:BaseDate; paid_at?:BaseDate;
merchant_ref: null | string; merchant_ref?: string;
payment_id?: null | string | number; payment_id?: null | string | number;
create_at: BaseDate; create_at: BaseDate;
update_at: BaseDate; update_at: BaseDate;
student_first_name: string;
student_last_name: string;
remark: string;
confirm_status: ConfirmStatus;
details: BillDetail[] details: BillDetail[]
} }
@ -91,3 +96,12 @@ declare type AsiaPayModel = {
type ExternalCreateParamsType = { type ExternalCreateParamsType = {
[key: string]: string | null | BillDetail[]; [key: string]: string | null | BillDetail[];
} }
type BillUpdateParams = {
payment_channel: string;
payment_method: string;
actual_payment_amount?: number | string;
remark?: string;
merchant_ref?: string;
payment_amount?: number | string;
}