feat(round-15): 跨境多币种发票税额/线索自动化阶段跃升/名片私密访问锁/芯片激光标刻排版 (P1/P2功能达成)

这个提交包含在:
wikiboss
2026-09-21 02:15:08 +08:00
父节点 45f5f74805
当前提交 e6d06f762c
共有 8 个文件被更改,包括 928 次插入 和 0 次删除

查看文件

@@ -0,0 +1,175 @@
import { Provide } from '@midwayjs/core';
import { BaseService } from '@cool-midway/core';
export type SupportedCurrency = 'USD' | 'AED' | 'EUR' | 'GBP' | 'JPY' | 'CNY';
/**
* 相对 USD 的基准出海商业结算汇率表
*/
const USD_RATES: Record<SupportedCurrency, number> = {
USD: 1.0,
AED: 3.6725, // 阿联酋迪拉姆与美元挂钩固定汇率
EUR: 0.9259, // 1 EUR = 1.08 USD -> 1 USD = 0.9259 EUR
GBP: 0.7874, // 1 GBP = 1.27 USD
JPY: 155.50,
CNY: 7.2350,
};
const CURRENCY_SYMBOLS: Record<SupportedCurrency, string> = {
USD: '$',
AED: 'AED ',
EUR: '€',
GBP: '£',
JPY: '¥',
CNY: '¥',
};
export interface TaxCalculationParams {
amount: number;
currency: SupportedCurrency;
country: string; // ISO 2位国家码 (如 AE, DE, US, GB)
isB2b?: boolean;
vatNumber?: string;
}
export interface TaxCalculationResult {
taxRate: number;
taxAmount: number;
totalAmount: number;
taxName: string;
isReverseCharge?: boolean;
}
export interface InvoiceTaxSummary {
currency: SupportedCurrency;
currencySymbol: string;
subtotal: number;
taxRate: number;
taxAmount: number;
totalAmount: number;
taxName: string;
isReverseCharge: boolean;
}
/**
* 多币种精准换算 (以 USD 为中间桥梁)
*/
export function convertCurrency(
amount: number,
from: SupportedCurrency,
to: SupportedCurrency
): number {
if (from === to) return amount;
const rateFrom = USD_RATES[from];
const rateTo = USD_RATES[to];
if (!rateFrom || !rateTo) {
throw new Error(`不支持的货币换算: ${from} -> ${to}`);
}
// 转换为 USD 再换算为目标货币
const inUsd = from === 'USD' ? amount : amount / (from === 'EUR' ? 1.08 : rateFrom);
let converted = 0;
if (to === 'AED') {
converted = inUsd * 3.6725;
} else if (to === 'USD') {
converted = from === 'EUR' ? amount * 1.08 : inUsd;
} else {
converted = inUsd * rateTo;
}
return Number(converted.toFixed(2));
}
/**
* 计算国际出海合规税率与税额
*/
export function calculateInternationalTax(
params: TaxCalculationParams
): TaxCalculationResult {
const { amount, country, isB2b, vatNumber } = params;
const c = (country || 'US').toUpperCase();
// 1. 阿联酋 (AE): 统一 5% VAT
if (c === 'AE') {
const taxRate = 0.05;
const taxAmount = Number((amount * taxRate).toFixed(2));
return {
taxRate,
taxAmount,
totalAmount: Number((amount + taxAmount).toFixed(2)),
taxName: 'UAE VAT',
};
}
// 2. 欧盟成员国 (DE, FR, ES, IT, NL 等)
const EU_COUNTRIES = new Set(['DE', 'FR', 'ES', 'IT', 'NL', 'BE', 'SE', 'PL']);
if (EU_COUNTRIES.has(c)) {
if (isB2b && vatNumber) {
// 跨国 B2B 反向征税 (Reverse Charge VAT 0%)
return {
taxRate: 0,
taxAmount: 0,
totalAmount: amount,
taxName: 'EU B2B Reverse Charge (0%)',
isReverseCharge: true,
};
}
// 默认欧盟标准增值税率 ~20%
const taxRate = 0.20;
const taxAmount = Number((amount * taxRate).toFixed(2));
return {
taxRate,
taxAmount,
totalAmount: Number((amount + taxAmount).toFixed(2)),
taxName: 'EU Standard VAT',
};
}
// 3. 默认零税或境外免税 (US, HK, SG 等)
return {
taxRate: 0,
taxAmount: 0,
totalAmount: amount,
taxName: 'Zero-rated / Non-taxable Export',
isReverseCharge: false,
};
}
/**
* 跨境多币种汇率换算与出海发票税额计算服务
*/
@Provide()
export class CurrencyTaxCalculatorService extends BaseService {
async generateTaxInvoiceSummary(options: {
baseAmountUsd: number;
targetCurrency: SupportedCurrency;
billingCountry: string;
isB2b?: boolean;
vatNumber?: string;
}): Promise<InvoiceTaxSummary> {
const { baseAmountUsd, targetCurrency, billingCountry, isB2b, vatNumber } = options;
const subtotal = convertCurrency(baseAmountUsd, 'USD', targetCurrency);
const tax = calculateInternationalTax({
amount: subtotal,
currency: targetCurrency,
country: billingCountry,
isB2b,
vatNumber,
});
return {
currency: targetCurrency,
currencySymbol: CURRENCY_SYMBOLS[targetCurrency] || `${targetCurrency} `,
subtotal,
taxRate: tax.taxRate,
taxAmount: tax.taxAmount,
totalAmount: tax.totalAmount,
taxName: tax.taxName,
isReverseCharge: !!tax.isReverseCharge,
};
}
}