invoice_validation_example.md 13.4 KB

发票验证功能使用示例

场景说明

本示例展示了发票验证功能的工作方式,包括成功场景和失败场景。

成功场景

场景1:所有经销商信息一致

输入数据:

  • 订单编号:ORDER001
  • 订单中的经销商编码:DEALER001
  • 出库单编号:DELIVERY001
  • 出库单中的经销商编码:DEALER001
  • 发票中的经销商编码:DEALER001

验证结果: ✅ 通过验证,继续执行发票创建/修改流程

代码示例:

// 订单信息
OrderMain order = new OrderMain();
order.setOrderNo("ORDER001");
order.setDealerCode("DEALER001");

// 出库单信息
DeliveryMain delivery = new DeliveryMain();
delivery.setDeliveryNo("DELIVERY001");
delivery.setDealerCode("DEALER001");

// 发票请求
InvoiceAddReq addReq = new InvoiceAddReq();
addReq.setOrderNo("ORDER001");
addReq.setDeliveryNo("DELIVERY001");
addReq.setDealerCode("DEALER001");

// 验证通过,继续执行
invoiceMainService.addInvoice(addReq);

场景2:不提供出库单号

输入数据:

  • 订单编号:ORDER001
  • 订单中的经销商编码:DEALER001
  • 发票中的经销商编码:DEALER001
  • 出库单编号:null(不提供)

验证结果: ✅ 通过验证,只验证订单经销商一致性

代码示例:

// 发票请求(不提供出库单号)
InvoiceAddReq addReq = new InvoiceAddReq();
addReq.setOrderNo("ORDER001");
addReq.setDeliveryNo(null); // 不提供出库单号
addReq.setDealerCode("DEALER001");

// 验证通过,只验证订单经销商一致性
invoiceMainService.addInvoice(addReq);

失败场景

场景3:发票与订单经销商不一致

输入数据:

  • 订单编号:ORDER001
  • 订单中的经销商编码:DEALER001
  • 发票中的经销商编码:DEALER002

验证结果: ❌ 验证失败,抛出 InvoiceValidationException

异常信息:

发票中的经销商编码[DEALER002]与关联订单[ORDER001]中的经销商编码[DEALER001]不一致

代码示例:

// 发票请求(经销商编码不匹配)
InvoiceAddReq addReq = new InvoiceAddReq();
addReq.setOrderNo("ORDER001");
addReq.setDealerCode("DEALER002"); // 不匹配的经销商编码

// 验证失败,抛出异常
try {
    invoiceMainService.addInvoice(addReq);
} catch (InvoiceValidationException e) {
    System.out.println("验证失败:" + e.getMessage());
    // 输出:验证失败:发票中的经销商编码[DEALER002]与关联订单[ORDER001]中的经销商编码[DEALER001]不一致
}

场景4:发票与出库单经销商不一致

输入数据:

  • 订单编号:ORDER001
  • 订单中的经销商编码:DEALER001
  • 出库单编号:DELIVERY001
  • 出库单中的经销商编码:DEALER001
  • 发票中的经销商编码:DEALER002

验证结果: ❌ 验证失败,抛出 InvoiceValidationException

异常信息:

发票中的经销商编码[DEALER002]与关联出库单[DELIVERY001]中的经销商编码[DEALER001]不一致

代码示例:

// 发票请求(出库单经销商编码不匹配)
InvoiceAddReq addReq = new InvoiceAddReq();
addReq.setOrderNo("ORDER001");
addReq.setDeliveryNo("DELIVERY001");
addReq.setDealerCode("DEALER002"); // 不匹配的经销商编码

// 验证失败,抛出异常
try {
    invoiceMainService.addInvoice(addReq);
} catch (InvoiceValidationException e) {
    System.out.println("验证失败:" + e.getMessage());
    // 输出:验证失败:发票中的经销商编码[DEALER002]与关联出库单[DELIVERY001]中的经销商编码[DEALER001]不一致
}

场景5:关联订单不存在

输入数据:

  • 订单编号:ORDER999(不存在的订单)
  • 发票中的经销商编码:DEALER001

验证结果: ❌ 验证失败,抛出 IllegalArgumentException

异常信息:

关联的订单不存在

代码示例:

// 发票请求(关联不存在的订单)
InvoiceAddReq addReq = new InvoiceAddReq();
addReq.setOrderNo("ORDER999"); // 不存在的订单
addReq.setDealerCode("DEALER001");

// 验证失败,抛出异常
try {
    invoiceMainService.addInvoice(addReq);
} catch (IllegalArgumentException e) {
    System.out.println("验证失败:" + e.getMessage());
    // 输出:验证失败:关联的订单不存在
}

场景6:关联出库单不存在

输入数据:

  • 订单编号:ORDER001
  • 订单中的经销商编码:DEALER001
  • 出库单编号:DELIVERY999(不存在的出库单)
  • 发票中的经销商编码:DEALER001

验证结果: ❌ 验证失败,抛出 IllegalArgumentException

异常信息:

关联的出库单不存在

代码示例:

// 发票请求(关联不存在的出库单)
InvoiceAddReq addReq = new InvoiceAddReq();
addReq.setOrderNo("ORDER001");
addReq.setDeliveryNo("DELIVERY999"); // 不存在的出库单
addReq.setDealerCode("DEALER001");

// 验证失败,抛出异常
try {
    invoiceMainService.addInvoice(addReq);
} catch (IllegalArgumentException e) {
    System.out.println("验证失败:" + e.getMessage());
    // 输出:验证失败:关联的出库单不存在
}

API调用示例

新增发票

请求:

POST /api/invoice/add
Content-Type: application/json

{
    "invoiceNo": "INVOICE001",
    "orderNo": "ORDER001",
    "deliveryNo": "DELIVERY001",
    "dealerCode": "DEALER002",  // 与订单和出库单中的经销商不一致
    "dealerName": "测试经销商",
    "totalAmount": 1000.00,
    "invoiceDate": "2024-01-15",
    "invoiceItems": [
        {
            "productCode": "PRODUCT001",
            "invoiceQty": 10,
            "amountNoTax": 1000.00
        }
    ]
}

响应(失败):

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
    "code": 400,
    "message": "发票中的经销商编码[DEALER002]与关联订单[ORDER001]中的经销商编码[DEALER001]不一致",
    "timestamp": "2024-01-15T10:00:00"
}

修改发票

请求:

PUT /api/invoice/update
Content-Type: application/json

{
    "invoiceId": 1,
    "invoiceNo": "INVOICE001",
    "orderNo": "ORDER001",
    "deliveryNo": "DELIVERY001",
    "dealerCode": "DEALER001",  // 与订单和出库单中的经销商一致
    "dealerName": "测试经销商",
    "totalAmount": 1000.00,
    "invoiceDate": "2024-01-15",
    "invoiceItems": [
        {
            "productCode": "PRODUCT001",
            "invoiceQty": 10,
            "amountNoTax": 1000.00
        }
    ]
}

响应(成功):

HTTP/1.1 200 OK
Content-Type: application/json

{
    "code": 200,
    "message": "发票修改成功",
    "data": true
}

前端处理示例

JavaScript错误处理

// 新增发票
async function addInvoice(invoiceData) {
    try {
        const response = await fetch('/api/invoice/add', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(invoiceData)
        });

        if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.message);
        }

        const result = await response.json();
        console.log('发票创建成功', result);

    } catch (error) {
        if (error.message.includes('发票中的经销商编码') && error.message.includes('与关联订单') && error.message.includes('不一致')) {
            // 处理订单经销商不匹配错误
            alert('错误:发票中的经销商与关联订单中的经销商不一致,请检查经销商信息');
        } else if (error.message.includes('发票中的经销商编码') && error.message.includes('与关联出库单') && error.message.includes('不一致')) {
            // 处理出库单经销商不匹配错误
            alert('错误:发票中的经销商与关联出库单中的经销商不一致,请检查经销商信息');
        } else if (error.message.includes('关联的订单不存在')) {
            // 处理订单不存在错误
            alert('错误:关联的订单不存在,请检查订单编号');
        } else if (error.message.includes('关联的出库单不存在')) {
            // 处理出库单不存在错误
            alert('错误:关联的出库单不存在,请检查出库单编号');
        } else {
            // 处理其他错误
            alert('发票创建失败:' + error.message);
        }
    }
}

最佳实践

1. 数据录入时验证

在用户录入发票信息时,可以实时验证经销商信息:

// 当订单编号改变时,自动填充经销商信息
async function onOrderNoChange(orderNo) {
    if (orderNo) {
        try {
            const order = await getOrderByNo(orderNo);
            if (order) {
                // 自动填充经销商信息
                document.getElementById('dealerCode').value = order.dealerCode;
                document.getElementById('dealerName').value = order.dealerName;
            }
        } catch (error) {
            console.error('获取订单信息失败', error);
        }
    }
}

// 当出库单编号改变时,验证经销商信息
async function onDeliveryNoChange(deliveryNo) {
    if (deliveryNo) {
        try {
            const delivery = await getDeliveryByNo(deliveryNo);
            if (delivery) {
                const currentDealerCode = document.getElementById('dealerCode').value;
                if (currentDealerCode && currentDealerCode !== delivery.dealerCode) {
                    alert('警告:出库单中的经销商与当前选择的经销商不一致');
                }
            }
        } catch (error) {
            console.error('获取出库单信息失败', error);
        }
    }
}

2. 表单验证

在提交前进行客户端验证:

function validateInvoiceForm() {
    const orderNo = document.getElementById('orderNo').value;
    const deliveryNo = document.getElementById('deliveryNo').value;
    const dealerCode = document.getElementById('dealerCode').value;

    if (orderNo && dealerCode) {
        // 可以调用API验证经销商一致性
        return validateDealerConsistency(orderNo, deliveryNo, dealerCode);
    }

    return true;
}

3. 错误提示优化

提供更友好的错误提示:

function handleInvoiceValidationError(error) {
    const errorMessage = error.message;

    if (errorMessage.includes('发票中的经销商编码') && errorMessage.includes('与关联订单') && errorMessage.includes('不一致')) {
        return {
            type: 'warning',
            title: '订单经销商信息不匹配',
            message: '发票中的经销商与关联订单中的经销商不一致,请确认经销商信息是否正确',
            action: '请检查并修正经销商编码,或选择正确的订单'
        };
    } else if (errorMessage.includes('发票中的经销商编码') && errorMessage.includes('与关联出库单') && errorMessage.includes('不一致')) {
        return {
            type: 'warning',
            title: '出库单经销商信息不匹配',
            message: '发票中的经销商与关联出库单中的经销商不一致,请确认经销商信息是否正确',
            action: '请检查并修正经销商编码,或选择正确的出库单'
        };
    } else if (errorMessage.includes('关联的订单不存在')) {
        return {
            type: 'error',
            title: '订单不存在',
            message: '关联的订单不存在,请检查订单编号是否正确',
            action: '请检查订单编号或选择其他订单'
        };
    } else if (errorMessage.includes('关联的出库单不存在')) {
        return {
            type: 'error',
            title: '出库单不存在',
            message: '关联的出库单不存在,请检查出库单编号是否正确',
            action: '请检查出库单编号或选择其他出库单'
        };
    }

    return {
        type: 'error',
        title: '验证失败',
        message: errorMessage
    };
}

4. 数据联动

实现订单、出库单、发票之间的数据联动:

// 根据订单自动填充相关信息
async function loadOrderInfo(orderNo) {
    try {
        const order = await getOrderByNo(orderNo);
        if (order) {
            // 填充经销商信息
            document.getElementById('dealerCode').value = order.dealerCode;
            document.getElementById('dealerName').value = order.dealerName;

            // 加载相关的出库单列表
            const deliveries = await getDeliveriesByOrderNo(orderNo);
            updateDeliveryOptions(deliveries);
        }
    } catch (error) {
        console.error('加载订单信息失败', error);
    }
}

// 根据出库单自动填充相关信息
async function loadDeliveryInfo(deliveryNo) {
    try {
        const delivery = await getDeliveryByNo(deliveryNo);
        if (delivery) {
            // 验证经销商信息是否一致
            const currentDealerCode = document.getElementById('dealerCode').value;
            if (currentDealerCode && currentDealerCode !== delivery.dealerCode) {
                showWarning('出库单中的经销商与当前选择的经销商不一致');
            }
        }
    } catch (error) {
        console.error('加载出库单信息失败', error);
    }
}