同大陆身份证验证一样,该部分是按照国家增值税发票代码的定制规则,进行发票代码验证,如果需要查验发票信息是否正确,应该通过第三方接口(大约一毛钱查验一次),或者直接上国家税务总局全国增值税发票查验平台进行查验。

目前能识别的增值税发票代码包含以下几类:增值税专用发票增值税普通发票(纸质非卷票)增值税普通发票(卷票)增值税电子普通发票。在类库中,增值税代码验证相关的代码均在NumberValidators.Invoices下,其包含接口定义以及具体实现。

IVATCodeValidator(增值税代码识别接口)定义如下:

    /// <summary>
/// 增值税发票代码验证接口
/// </summary>
public interface IVATCodeValidator <out TResult>: IValidator<TResult>
where TResult : VATCodeValidationResult, new()
{
/// <summary>
/// 用于验证的字典数据
/// </summary>
IValidationDictionary<int, string> Dictionary { get; set; }
/// <summary>
/// 生成增值税发票代码
/// </summary>
/// <param name="areaNumber">行政区划</param>
/// <param name="year">年份</param>
/// <param name="batch">批次</param>
/// <param name="kind">要生成的发票类型</param>
/// <returns></returns>
string GenerateVATCode(int areaNumber, ushort year, ushort batch, VATKind kind);
/// <summary>
/// 发票代码验证
/// </summary>
/// <param name="vatCode">待验证的发票代码</param>
/// <param name="kind">要验证的发票类型,不指定则传null</param>
/// <param name="minYear">允许的最小年份(注:2012年1月1日营改增开始上海试点)</param>
/// <returns></returns>
TResult Validate(string vatCode, VATKind? kind = null, ushort minYear = 2012);
}

增值税发票代码验证定义了两种验证结果

VATCodeValidationResult这是默认验证结果,其定义如下:

    /// <summary>
/// 增值税发票代码验证结果
/// </summary>
public class VATCodeValidationResult : ValidationResult
{
/// <summary>
/// 行政区划代码
/// </summary>
public int AreaNumber { get; internal set; }
/// <summary>
/// 行政区域名称
/// </summary>
public string AreaName { get; internal set; }
/// <summary>
/// 发票类型
/// </summary>
public VATKind? Category { get; internal set; }
/// <summary>
/// 印刷年份
/// </summary>
public int Year { get; internal set; }
/// <summary>
/// 印刷批次
/// </summary>
public int Batch { get; internal set; }
/// <summary>
/// 发票联次,仅10位长度和12位长度折叠票发票才有
/// </summary>
public int DuplicateNumber { get; internal set; }
}

VATCode10ValidationResult是在VATCodeValidationResult的基础上,额外定义了发票金额版本,其定义如下:

    /// <summary>
/// 增值税发票和普通(纸质)专有的验证结果
/// </summary>
public class VATCode10ValidationResult : VATCodeValidationResult
{
/// <summary>
/// 发票金额版本号,仅10位长度发票才有
/// </summary>
public AmountVersion AmountVersion { get; internal set; }
}

VATCode12ValidationResult是在VATCodeValidationResult的基础上,额外定义了增值税电子发票的细分类型,其定义如下:

    /// <summary>
/// 除增值税专项发票外的验证结果
/// </summary>
public class VATCode12ValidationResult : VATCodeValidationResult
{
/// <summary>
/// 增值税电子发票细分类型
/// </summary>
public ElectronicVATKind? ElectronicVATKind { get; set; }
}

可根据IsValid来判断验证是否成功,如果验证失败,Errors 属性则包含了验证失败的原因,具体的错误原因列表如下

        /// <summary>
/// 发票代码为空
/// </summary>
public const string Empty = "发票代码为空";
/// <summary>
/// 错误的发票代码
/// </summary>
public const string Error = "错误的发票代码";
/// <summary>
/// 发票年份超出允许的年份范围
/// </summary>
public const string YearOutOfRange = "发票年份超出允许的年份范围{0} ~ {1}";
/// <summary>
/// 发票发行区域识别失败
/// </summary>
public const string InvalidArea = "发票发行区域识别失败";
/// <summary>
/// 无效的发票类别
/// </summary>
public const string InvalidKind = "无效的发票类别";
/// <summary>
/// 发票类别错误,无法生成发票代码
/// </summary>
public const string GenerateWrongKind = "发票类别错误,无法生成发票代码";
/// <summary>
/// 无效实现
/// </summary>
public const string InvalidImplement = "未能找到或无效的 {0} 位发票代码实现";
/// <summary>
/// 长度不符
/// </summary>
public const string LengthOutOfRange = "发票代码非 {0} 位";

因为目前类库中已经完整收集了所有发票代码中支持的行政区划编号(可在航信官网上查看都有哪些区域存在税务局),所以暂时不再需要自行传递Dictionary来进行支持区域的修正。

目前IVATCodeValidator包含VATCode10Validator以及VATCode12Validator两种具体实现

  • VATCode10Validator 对应长度为10的发票代码,包含增值税专用发票、增值税普通发票
  • VATCode12Validator 对应长度为12的发票代码,包含增值税普通发票、增值税普通发票(卷票)、增值税电子普通发票
  • VATCodeValidatorHelper 为静态类,用于辅助验证,其内部简单的封装了按发票代码长度调用对应的IVATCodeValidator实现

使用例子如下

            Console.WriteLine("***增值税发票***");
var vat10Validator = new VATCode10Validator();
var vat12Validator = new VATCode12Validator();
Console.WriteLine("随机的增值税发票:" + vat10Validator.GenerateRandomNumber());
Console.WriteLine("生成指定的增值税专用发票:" + vat10Validator.GenerateVATCode(3700, 2017, 1, Invoices.VATKind.Special));
Console.WriteLine("生成指定的10位增值税普通发票:" + vat10Validator.GenerateVATCode(1100, 2017, 2, Invoices.VATKind.Plain));
Console.WriteLine("生成指定的12位增值税普通发票:" + vat12Validator.GenerateVATCode(1100, 2018, 6, Invoices.VATKind.Plain));
Console.WriteLine("随机的增值税电子/卷票/普票:" + vat12Validator.GenerateRandomNumber());
string[] vatArr = { "031001600311", "3100153130", "011001800304" };
foreach (var vat in vatArr)
{
var valid = VATCodeValidatorHelper.Validate(vat, minYear: 2012);
Console.WriteLine("{0}验证结果:{1} 类型{2} 行政区划名称({3}) 验证结果类型:{4}", vat, valid.IsValid, valid.Category, valid.AreaName, valid);
}

PS:目前1.0版本中VATCode12Validator未支持12位的增值税普通发票以及收费公路通行费增值税电子发票,如果需要支持,需从git上下载代码后自行生成dll

【NumberValidators】增值税发票代码验证的更多相关文章

  1. odoo开发笔记 -- 官方模块一览表

    模块名称 技术名称 作者 电子发票管理 account OpenERP SA 会计与财务 account_accountant OpenERP SA 合同管理 account_analytic_ana ...

  2. 【NumberValidators】大陆身份证验证

    需要说明的是这里的大陆身份证识别并不是公安局联网的识别,而是按国标GB 11643进行的验证,所以其验证结果只能说符合国标规范,但不能保证该身份证一定真实存在,如果你实际需求是希望身份证一定真实存在, ...

  3. 【NumberValidators】工商营业执照号码和统一社会信用代码验证

    从本质上讲,工商营业执照号码和统一社会信用代码是两套完全不一样的编码规则,识别结果也仅有行政区划部分为两者共有,但因为这两种编码同时存在的原因,所以如果需要在系统中唯一标志一家企业时,还是可以通过工商 ...

  4. 【NumberValidators】类库介绍

    NumberValidators是一个用于验证中国大陆证件.号码是否符合国家标准的类库,因为该类库在昨日已经正式发布1.0.0版本至nuget,所以在此介绍下该类库的具体功能. NumberValid ...

  5. 【探索】无形验证码 —— PoW 算力验证

    先来思考一个问题:如何写一个能消耗对方时间的程序? 消耗时间还不简单,休眠一下就可以了: Sleep(1000) 这确实消耗了时间,但并没有消耗 CPU.如果对方开了变速齿轮,这瞬间就能完成. 不过要 ...

  6. C# 中参数验证方式的演变

    一般在写方法的时候,第一步就是进行参数验证,这也体现了编码者的细心和缜密,但是在很多时候这个过程很枯燥和乏味,比如在拿到一个API设计文档的时候,通常会规定类型参数是否允许为空,如果是字符可能有长度限 ...

  7. Yii1.1的验证规则

    在Yii1.1的数据验证是由CValidator完成,在CValidator中提供了各种基本的验证规则 <?php public static $builtInValidators=array( ...

  8. 【WCF】使用“用户名/密码”验证的合理方法

    我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”.以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然 ...

  9. PHP验证用户登录例子-学习笔记

    1.基本流程: 2.UML类图: 3.PHP代码: 3.1 index.php <?php /** * Created by PhpStorm. * User: andy * Date: 16- ...

随机推荐

  1. codeblocks 更换颜色主题

    关闭codeblocks,下载主题文件(colour_themes.conf).在关闭codeblocks的情况下,linux下的~/.config/codeblocks/下有个conf文件,将其备份 ...

  2. [leetcode]158. Read N Characters Given Read4 II - Call multiple times 用Read4读取N个字符2 - 调用多次

    The API: int read4(char *buf) reads 4 characters at a time from a file. The return value is the actu ...

  3. Spring框架的特点

    1. 为什么要学习Spring的框架 * 方便解耦,简化开发 * Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理 * AOP编程的支持 * Spring提供面向切面 ...

  4. 【SQL模板】四.插入/更新 列模板TSQL

    ---Name: 插入/更新 列模板.sql ---Purpose: 用于更新 数据库中 列 的脚本模板 ---Author: xx ---Time: 2015-12-18 10:26:06 ---R ...

  5. 删除SVN版本控制目录

    @echo On @Rem 删除SVN版本控制目录 @PROMPT [Com] @for /r . %%a in (.) do @if exist "%%a\.svn" rd /s ...

  6. 教你用PS修复老照片

    原图素材虽然很旧,不过人物部分并没有怎么损坏,只是有一些色块和杂色.修复的工程相对来说也少很多.只需要给人物磨好皮,然后把暗调和高光部分调出来即可.原图     一.打开原图素材,按Ctrl + J ...

  7. CentOs7中的网卡配置工具

    CentOs7中的网卡配置工具 摘自:https://blog.51cto.com/13572810/2087991 misslaziness1人评论2715人阅读2018-03-17 22:09:1 ...

  8. python 线程和进程概述

    计算机中执行任务的最小单元:线程 IO操作利用CPU GIL,全局解释器锁 IO密集型: 多线程(不用CPU) 计算机密集型(用CPU) 进程和线程的目的:提高执行效率 1.单进程单线程,主进程.主线 ...

  9. 动态渲染的input怎么取消记忆功能

    方法1 :自定义去除记忆功能属性: $('#index_table_filter > label > input[type="search"]').attr('auto ...

  10. SPSS-多重响应-频率和交叉表案例分析(问卷调查分析)

    在10.1休假前,希望跟大家讨论一下SPSS-多重响应--频率和交叉表分析,希望大家能够多提点提点 在云南电信网上营业厅做了一个关于“客户不使用电信3g业务的原因有哪些的问卷调查,问题所示: 这份问卷 ...