458 lines
14 KiB
TypeScript
458 lines
14 KiB
TypeScript
import request, { pickBxData } from '@/utils/http/bx'
|
|
|
|
export type CertType = 'purchase' | 'transport'
|
|
export interface PurchaseReference {
|
|
id?: string
|
|
referenceType: 'purchase_permit' | 'purchase_filing' | 'export_permit' | 'unknown'
|
|
referenceNo: string
|
|
purchaseCertId?: string | null
|
|
remark?: string | null
|
|
orderNum?: number | null
|
|
}
|
|
export interface CertBody {
|
|
certNo: string
|
|
issueDate?: string | null
|
|
productNameOnCert: string
|
|
totalQty: string | number
|
|
quantityUnit: string
|
|
validFrom?: string | null
|
|
validTo?: string | null
|
|
validUseText: string
|
|
entryMode: 'manual' | 'image_parse'
|
|
attachmentUrls?: string | null
|
|
status?: 'draft' | 'suspended' | 'void'
|
|
remark?: string | null
|
|
orderNum?: number | null
|
|
buyerName?: string | null
|
|
buyerAddress?: string | null
|
|
buyerLegalRepresentative?: string | null
|
|
buyerPhone?: string | null
|
|
sellerName?: string | null
|
|
sellerAddress?: string | null
|
|
sellerLegalRepresentative?: string | null
|
|
sellerPhone?: string | null
|
|
purchasePurpose?: string | null
|
|
shipperName?: string | null
|
|
shipperAddress?: string | null
|
|
shipperLegalRepresentative?: string | null
|
|
shipperPhone?: string | null
|
|
packagingText?: string | null
|
|
carrierName?: string | null
|
|
carrierAddressPhone?: string | null
|
|
transportMode?: string | null
|
|
vehicleNumbersJson?: string[]
|
|
routeText?: string | null
|
|
consigneeName?: string | null
|
|
applicantName?: string | null
|
|
contractBindings?: CertificateContractBinding[]
|
|
purchaseRefs?: PurchaseReference[]
|
|
}
|
|
export interface QuotaCert extends Omit<CertBody, 'status'> {
|
|
id: string
|
|
status: 'draft' | 'active' | 'suspended' | 'void'
|
|
bindingsEditable?: boolean
|
|
referencesEditable?: boolean
|
|
revision: number
|
|
reservedQty: string | number
|
|
usedQty: string | number
|
|
availableQty: string | number
|
|
verifiedTime?: string | null
|
|
openingConfirmed: boolean
|
|
usagePolicy: 'once' | 'multiple' | 'needs_confirmation'
|
|
createTime?: string
|
|
}
|
|
export interface ScopeProduct {
|
|
id?: string
|
|
contractProductId: string
|
|
scopeStatus?: 'active' | 'inactive'
|
|
remark?: string | null
|
|
orderNum?: number | null
|
|
}
|
|
export interface CertBinding {
|
|
id?: string
|
|
certType: CertType
|
|
certId: string
|
|
linkStatus?: 'active' | 'inactive'
|
|
products: ScopeProduct[]
|
|
scopeRevision?: number
|
|
remark?: string | null
|
|
orderNum?: number | null
|
|
}
|
|
export interface ContractOption {
|
|
id: string
|
|
contractNo: string
|
|
contractName: string
|
|
contractType?: string
|
|
}
|
|
export interface ContractProduct {
|
|
id: string
|
|
productId: string
|
|
productName: string
|
|
model?: string
|
|
unit?: string
|
|
quantity?: string | number
|
|
}
|
|
export interface LegalDocument {
|
|
id: string
|
|
planNo: string
|
|
archiveStatus: string
|
|
transportStatus: string
|
|
products: {
|
|
id: string
|
|
productId: string
|
|
productName: string
|
|
model?: string
|
|
legalUseCertUrl: string | null
|
|
documentStatus: 'uploaded' | 'pending'
|
|
contexts?: {
|
|
stopType: string
|
|
stopProductId: string
|
|
partyName?: string
|
|
address?: string
|
|
contractId?: string
|
|
contractNo?: string
|
|
contractProductId?: string
|
|
}[]
|
|
}[]
|
|
}
|
|
const base = '/transport/quota-certs'
|
|
export const quotaCertApi = {
|
|
list: (kind: CertType, params: { keyword?: string; status?: string } = {}) =>
|
|
request.get(`${base}/${kind}`, { params }).then(pickBxData<QuotaCert[]>),
|
|
get: (kind: CertType, id: string) =>
|
|
request.get(`${base}/${kind}/${id}`).then(pickBxData<QuotaCert>),
|
|
save: (kind: CertType, body: CertBody, id?: string) =>
|
|
(id ? request.put(`${base}/${kind}/${id}`, body) : request.post(`${base}/${kind}`, body)).then(
|
|
pickBxData<QuotaCert>
|
|
),
|
|
remove: (kind: CertType, id: string) => request.delete(`${base}/${kind}/${id}`),
|
|
confirm: (
|
|
kind: CertType,
|
|
id: string,
|
|
body: {
|
|
operationId: string
|
|
usedQty: string
|
|
singleUseConsumed: boolean
|
|
reason: string
|
|
sourceNote?: string
|
|
evidenceUrls?: string
|
|
}
|
|
) => request.post(`${base}/${kind}/${id}/confirm`, body).then(pickBxData<QuotaCert>),
|
|
verify: (kind: CertType, id: string, body: { operationId: string; reason: string }) =>
|
|
request.post(`${base}/${kind}/${id}/verify`, body).then(pickBxData<QuotaCert>),
|
|
recognize: (kind: CertType, attachmentUrl: string) =>
|
|
request.post(`${base}/${kind}/recognize`, { attachmentUrl }, { timeout: 90000 }).then(
|
|
pickBxData<{
|
|
candidates: Partial<CertBody>
|
|
invalidFields: Record<string, string>
|
|
requiresConfirmation: boolean
|
|
}>
|
|
),
|
|
contracts: (keyword = '') =>
|
|
request.get(`${base}/contracts`, { params: { keyword } }).then(pickBxData<ContractOption[]>),
|
|
products: (id: string) =>
|
|
request.get(`${base}/contracts/${id}/products`).then(pickBxData<ContractProduct[]>),
|
|
bindings: (id: string) =>
|
|
request.get(`${base}/contracts/${id}/bindings`).then(pickBxData<CertBinding[]>),
|
|
saveBindings: (id: string, bindings: CertBinding[]) =>
|
|
request.put(`${base}/contracts/${id}/bindings`, { bindings }).then(pickBxData<CertBinding[]>),
|
|
certificateContracts: (kind: CertType, id: string) =>
|
|
request.get(`${base}/${kind}/${id}/contracts`).then(pickBxData<ContractOption[]>)
|
|
}
|
|
const legalBase = '/transport/legal-use-documents'
|
|
export interface StopLegalDocumentItem {
|
|
stopType: 'load' | 'delivery'
|
|
stopId: string
|
|
stopOrder: number
|
|
stopProductId: string
|
|
productId: string
|
|
productName: string
|
|
model?: string
|
|
unit?: string
|
|
contractId?: string | null
|
|
contractNo?: string | null
|
|
contractProductId?: string | null
|
|
partyName?: string | null
|
|
address?: string | null
|
|
legalUseCertUrl: string
|
|
documentStatus: 'pending' | 'uploaded'
|
|
uploadedBy?: string | null
|
|
uploadedTime?: string | null
|
|
}
|
|
export interface StopLegalDocument {
|
|
id: string
|
|
planNo: string
|
|
planDate?: string
|
|
licensePlate?: string
|
|
transportStatus: string
|
|
archiveStatus: string
|
|
requiredCount: number
|
|
uploadedCount: number
|
|
documentStatus: 'pending' | 'uploaded' | 'not_required'
|
|
items: StopLegalDocumentItem[]
|
|
legacyProducts: {
|
|
id: string
|
|
productId: string
|
|
productName: string
|
|
model?: string
|
|
legalUseCertUrl?: string | null
|
|
}[]
|
|
}
|
|
export const legalDocumentApi = {
|
|
listStops: (params: {
|
|
keyword?: string
|
|
planNo?: string
|
|
status?: 'pending' | 'uploaded'
|
|
contractId?: string
|
|
page?: number
|
|
pageSize?: number
|
|
}) =>
|
|
request
|
|
.get(`${legalBase}/stops`, { params })
|
|
.then(
|
|
pickBxData<{ list: StopLegalDocument[]; total: number; page: number; pageSize: number }>
|
|
),
|
|
getStops: (id: string, contractId?: string) =>
|
|
request
|
|
.get(`${legalBase}/stops/${id}`, { params: { contractId } })
|
|
.then(pickBxData<StopLegalDocument>),
|
|
saveStops: (
|
|
id: string,
|
|
items: Pick<StopLegalDocumentItem, 'stopType' | 'stopProductId' | 'legalUseCertUrl'>[],
|
|
contractId?: string
|
|
) =>
|
|
request
|
|
.patch(`${legalBase}/stops/${id}`, { items }, { params: { contractId } })
|
|
.then(pickBxData<StopLegalDocument>),
|
|
list: (params: { keyword?: string; status?: string }) =>
|
|
request.get(legalBase, { params }).then(pickBxData<LegalDocument[]>),
|
|
get: (id: string) => request.get(`${legalBase}/${id}`).then(pickBxData<LegalDocument>),
|
|
save: (id: string, products: { id: string; legalUseCertUrl?: string | null }[]) =>
|
|
request.patch(`${legalBase}/${id}`, { products }).then(pickBxData<LegalDocument>)
|
|
}
|
|
|
|
export type MeasurementVersion = 'per_stop_v2' | 'legacy_single_side'
|
|
export interface QuotaSourceOption {
|
|
sourceStopId?: string
|
|
stopOrder?: number
|
|
contractNo?: string | null
|
|
bindingsReadable?: boolean
|
|
certificates?: { certType: CertType; certId: string; certNo: string; quantityUnit: string }[]
|
|
isToxic?: boolean
|
|
isExplosive?: boolean
|
|
sourceStopType: 'load' | 'delivery'
|
|
sourceStopProductId: string
|
|
planProductId: string
|
|
productName: string
|
|
model?: string
|
|
quantity: string | number
|
|
unit: string
|
|
contractId: string
|
|
contractProductId: string
|
|
partyName?: string
|
|
}
|
|
export interface QuotaSourcePair {
|
|
sourceStopType: 'load' | 'delivery'
|
|
sourceStopProductId: string
|
|
relatedStopProductIds: string[]
|
|
}
|
|
export interface ManualQuotaQuantity {
|
|
permitKind: CertType
|
|
sourceId: string
|
|
permitId: string
|
|
sourceQuantity: string | number
|
|
permitQuantity: string | number
|
|
note: string
|
|
}
|
|
export interface QuotaCondition {
|
|
certType: CertType
|
|
certId: string
|
|
certRevision: number
|
|
sourceFingerprint: string
|
|
partiesConfirmed: boolean
|
|
routeConfirmed: boolean
|
|
note: string
|
|
}
|
|
export interface QuotaConfiguration {
|
|
measurementVersion?: MeasurementVersion
|
|
sourceMapping?: QuotaSourcePair[] | null
|
|
purchaseCertIds: string[]
|
|
transportCertIds: string[]
|
|
manualAllocations: ManualQuotaQuantity[]
|
|
conditions: QuotaCondition[]
|
|
}
|
|
export interface PlanQuota {
|
|
measurementVersion?: MeasurementVersion
|
|
stopProducts?: QuotaSourceOption[]
|
|
planId: string
|
|
planNo: string
|
|
licensePlate?: string
|
|
canSaveConfiguration?: boolean
|
|
configuration: Partial<QuotaConfiguration>
|
|
sourceOptions: QuotaSourceOption[]
|
|
usages: Record<string, any>[]
|
|
}
|
|
export interface QuotaContext {
|
|
sourceFingerprint: string
|
|
sources: {
|
|
source_stop_type: string
|
|
source_stop_product_id: string
|
|
quantity: string
|
|
unit: string
|
|
snapshot: { stops: Record<string, any>[]; vehicle?: string; trailer?: string }
|
|
}[]
|
|
}
|
|
export interface QuotaPreview extends Partial<QuotaContext> {
|
|
status: string
|
|
reasons?: string[]
|
|
diagnostics?: {
|
|
certType: CertType
|
|
sourceIds: string[]
|
|
unit: string
|
|
demand: string
|
|
availableUpperBound: string | null
|
|
gapLowerBound: string | null
|
|
reason: string
|
|
}[]
|
|
purchaseAllocations?: Record<string, any>[]
|
|
transportAllocations?: Record<string, any>[]
|
|
}
|
|
export const planQuotaApi = {
|
|
get: (id: string) => request.get(`${base}/plans/${id}/usage`).then(pickBxData<PlanQuota>),
|
|
context: (id: string, body: QuotaConfiguration & QuotaPreviewVehicle) =>
|
|
request.post(`${base}/plans/${id}/context`, body).then(pickBxData<QuotaContext>),
|
|
preview: (id: string, body: QuotaConfiguration & QuotaPreviewVehicle) =>
|
|
request.post(`${base}/plans/${id}/preview`, body).then(pickBxData<QuotaPreview>),
|
|
save: (id: string, body: QuotaConfiguration) =>
|
|
request.put(`${base}/plans/${id}/configuration`, body).then(pickBxData<QuotaConfiguration>)
|
|
}
|
|
|
|
export interface QuotaPreviewVehicle {
|
|
targetLicensePlate?: string
|
|
targetTrailerNo?: string | null
|
|
}
|
|
|
|
export interface CertTotalAdjustment {
|
|
operationId: string
|
|
totalQty: string
|
|
reason: string
|
|
sourceNote: string
|
|
evidenceUrls: string
|
|
}
|
|
export const quotaAdjustmentApi = {
|
|
logs: (kind: CertType, id: string) =>
|
|
request.get(`${base}/${kind}/${id}/logs`).then(pickBxData<Record<string, any>[]>),
|
|
total: (kind: CertType, id: string, body: CertTotalAdjustment) =>
|
|
request.post(`${base}/${kind}/${id}/total-adjust`, body)
|
|
}
|
|
|
|
export type QuotaQuantity = string | number
|
|
export interface CertificateContractBinding {
|
|
editLocked?: boolean
|
|
id?: string
|
|
contractId: string
|
|
linkStatus?: 'active' | 'inactive'
|
|
products: ScopeProduct[]
|
|
scopeRevision?: number
|
|
remark?: string | null
|
|
orderNum?: number | null
|
|
}
|
|
export interface StatisticsPage<T> {
|
|
list: T[]
|
|
total: number
|
|
page: number
|
|
pageSize: number
|
|
}
|
|
export interface ContractStatisticsSummary extends ContractOption {
|
|
certCount: number
|
|
}
|
|
export interface CertificateStatisticsSummary {
|
|
id: string
|
|
certId: string
|
|
certType: CertType
|
|
certNo: string
|
|
productNameOnCert: string
|
|
quantityUnit: string
|
|
totalQty: QuotaQuantity
|
|
reservedQty: QuotaQuantity
|
|
usedQty: QuotaQuantity
|
|
availableQty: QuotaQuantity
|
|
status: string
|
|
validFrom: string | null
|
|
validTo: string | null
|
|
}
|
|
export interface StatisticsProduct {
|
|
id: string
|
|
contractProductId: string
|
|
productId: string
|
|
productName: string
|
|
model: string | null
|
|
unit: string | null
|
|
bindingId: string | null
|
|
scopeId: string | null
|
|
scopeStatus: string | null
|
|
linkStatus: string | null
|
|
reservedQty: QuotaQuantity
|
|
usedQty: QuotaQuantity
|
|
}
|
|
export interface StatisticsBinding {
|
|
bindingId: string | null
|
|
linkStatus: string | null
|
|
scopeRevision: number | null
|
|
contractReservedQty: QuotaQuantity
|
|
contractUsedQty: QuotaQuantity
|
|
products: StatisticsProduct[]
|
|
}
|
|
export interface ContractStatistics extends ContractOption {
|
|
certificates: (CertificateStatisticsSummary & StatisticsBinding & { productNames: string[] })[]
|
|
}
|
|
export interface StatisticsUsageItem {
|
|
partyName?: string | null
|
|
address?: string | null
|
|
id: string
|
|
usageId: string
|
|
certType: CertType
|
|
certId: string
|
|
planId: string
|
|
planNo: string
|
|
stopType: string
|
|
stopId: string
|
|
stopProductId: string
|
|
planProductId: string
|
|
contractId: string
|
|
contractProductId: string
|
|
productName: string
|
|
quantityUnit: string
|
|
reservedQty: QuotaQuantity
|
|
usedQty: QuotaQuantity
|
|
itemStatus: string
|
|
}
|
|
export interface CertificateStatistics extends CertificateStatisticsSummary {
|
|
contracts: (ContractOption & StatisticsBinding)[]
|
|
usageItems: StatisticsUsageItem[]
|
|
openingUsedQty: QuotaQuantity
|
|
unattributedUsedQty: QuotaQuantity
|
|
}
|
|
export interface StatisticsQuery {
|
|
contractNo?: string
|
|
certNo?: string
|
|
keyword?: string
|
|
page?: number
|
|
pageSize?: number
|
|
certType?: CertType
|
|
}
|
|
export const quotaStatisticsApi = {
|
|
contracts: (params: StatisticsQuery) =>
|
|
request
|
|
.get(`${base}/statistics/contracts`, { params })
|
|
.then(pickBxData<StatisticsPage<ContractStatisticsSummary>>),
|
|
certificates: (params: StatisticsQuery) =>
|
|
request
|
|
.get(`${base}/statistics/certificates`, { params })
|
|
.then(pickBxData<StatisticsPage<CertificateStatisticsSummary>>),
|
|
contract: (id: string) =>
|
|
request.get(`${base}/contracts/${id}/statistics`).then(pickBxData<ContractStatistics>),
|
|
certificate: (kind: CertType, id: string) =>
|
|
request.get(`${base}/${kind}/${id}/statistics`).then(pickBxData<CertificateStatistics>)
|
|
}
|