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

目前能识别的增值税发票代码包含以下几类:增值税专用发票增值税普通发票(纸质非卷票)增值税普通发票(卷票)增值税电子普通发票。在类库中,增值税代码验证相关的代码均在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. java实现24点游戏代码

    import java.util.Arrays;import java.util.Scanner; public class Test07 {    public static void main(S ...

  2. oracle 使用正则表达式获取字符串中包含的数字

    select REGEXP_REPLACE('字符串中包含的数字123,提取后就是123', '[^0-9]', '') from dual;

  3. Java 架构师

    “学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书的价值远超 ...

  4. PHP下ajax跨域的解决方案之CORS

    由于安全的限制(同源策略,javascript只能访问同域名下的内容),如果需要进行跨域操作,那就免不了要进行跨域.   CORS(跨域资源共享,Cross-Origin Resource Shari ...

  5. discuz回贴通知插件实现-发送邮件

    通过discuz的sendmail()来发送邮件 //引入发送邮件的函数文件 include libfile('function/mail'); //设置收件人地址,标题,内容发送邮件 sendmai ...

  6. 配置Spring框架编写XML的提示

    1. 步骤一:先复制, http://www.springframework.org/schema/beans/spring-beans.xsd 2. 步骤二:搜索XML Catalog,点击Add按 ...

  7. cookies,sessionStorage 和 localStorage 的区别

    请描述一下 cookies,sessionStorage 和 localStorage 的区别? sessionStorage 和 localStorage 是HTML5 Web Storage AP ...

  8. script标签的type="test/html"时

    们可以在<script>片断中定义一个被JS调用的代码,但代码又不在页面上显示,这时,我们可以使用下面的方法: 1 <script id="commentTemplate& ...

  9. Git 初始状操作指引

    You have an empty repository To get started you will need to run these commands in your terminal. Ne ...

  10. 2018.09.15 hdu3018Ant Trip(欧拉路)

    传送门 显然答案等于各个连通分量的笔画数之和. 因此我们dfs每个连通分量计算对答案的贡献. 对于一个连通分量,如果本来就有欧拉回路那么只需要一笔. 否则需要寄点数/2那么多笔才能画完. 知道这个结论 ...