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 { 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), get: (kind: CertType, id: string) => request.get(`${base}/${kind}/${id}`).then(pickBxData), save: (kind: CertType, body: CertBody, id?: string) => (id ? request.put(`${base}/${kind}/${id}`, body) : request.post(`${base}/${kind}`, body)).then( pickBxData ), 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), verify: (kind: CertType, id: string, body: { operationId: string; reason: string }) => request.post(`${base}/${kind}/${id}/verify`, body).then(pickBxData), recognize: (kind: CertType, attachmentUrl: string) => request.post(`${base}/${kind}/recognize`, { attachmentUrl }, { timeout: 90000 }).then( pickBxData<{ candidates: Partial invalidFields: Record requiresConfirmation: boolean }> ), contracts: (keyword = '') => request.get(`${base}/contracts`, { params: { keyword } }).then(pickBxData), products: (id: string) => request.get(`${base}/contracts/${id}/products`).then(pickBxData), bindings: (id: string) => request.get(`${base}/contracts/${id}/bindings`).then(pickBxData), saveBindings: (id: string, bindings: CertBinding[]) => request.put(`${base}/contracts/${id}/bindings`, { bindings }).then(pickBxData), certificateContracts: (kind: CertType, id: string) => request.get(`${base}/${kind}/${id}/contracts`).then(pickBxData) } 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), saveStops: ( id: string, items: Pick[], contractId?: string ) => request .patch(`${legalBase}/stops/${id}`, { items }, { params: { contractId } }) .then(pickBxData), list: (params: { keyword?: string; status?: string }) => request.get(legalBase, { params }).then(pickBxData), get: (id: string) => request.get(`${legalBase}/${id}`).then(pickBxData), save: (id: string, products: { id: string; legalUseCertUrl?: string | null }[]) => request.patch(`${legalBase}/${id}`, { products }).then(pickBxData) } 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 sourceOptions: QuotaSourceOption[] usages: Record[] } export interface QuotaContext { sourceFingerprint: string sources: { source_stop_type: string source_stop_product_id: string quantity: string unit: string snapshot: { stops: Record[]; vehicle?: string; trailer?: string } }[] } export interface QuotaPreview extends Partial { status: string reasons?: string[] diagnostics?: { certType: CertType sourceIds: string[] unit: string demand: string availableUpperBound: string | null gapLowerBound: string | null reason: string }[] purchaseAllocations?: Record[] transportAllocations?: Record[] } export const planQuotaApi = { get: (id: string) => request.get(`${base}/plans/${id}/usage`).then(pickBxData), context: (id: string, body: QuotaConfiguration & QuotaPreviewVehicle) => request.post(`${base}/plans/${id}/context`, body).then(pickBxData), preview: (id: string, body: QuotaConfiguration & QuotaPreviewVehicle) => request.post(`${base}/plans/${id}/preview`, body).then(pickBxData), save: (id: string, body: QuotaConfiguration) => request.put(`${base}/plans/${id}/configuration`, body).then(pickBxData) } 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[]>), 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 { 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>), certificates: (params: StatisticsQuery) => request .get(`${base}/statistics/certificates`, { params }) .then(pickBxData>), contract: (id: string) => request.get(`${base}/contracts/${id}/statistics`).then(pickBxData), certificate: (kind: CertType, id: string) => request.get(`${base}/${kind}/${id}/statistics`).then(pickBxData) }