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

目前能识别的增值税发票代码包含以下几类:增值税专用发票增值税普通发票(纸质非卷票)增值税普通发票(卷票)增值税电子普通发票。在类库中,增值税代码验证相关的代码均在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. python学习——sys.argv

    sys.argv[]:用于获取命令行参数,sys.argv[0]即所运行的代码自身的文件路径,因此真正的其他参数是从1开始 sys.argv[1]:表示第一个参数 sys.argv[1][2:]:表示 ...

  2. 7-将本地的javaweb项目部署到Linux服务器的一般操作

    一.基本流程介绍: 1.安装tomcat;2.安装mysql;3.将本地的javaweb导出成.war文件,传到服务器的tomcat/webapps/下面4.将数据库文件导出成.sql文件,传到服务器 ...

  3. vue-cli启动本地服务,局域网下通过ip访问不到的原因

    1.问题描述: 新开发了一个vue-cli项目,想通过手机查看效果,发现访问不到,ip地址和端口号都没错但是手机访问不到,在本机电脑浏览器输入ip端口号一样访问不到,只能通过localhost:808 ...

  4. iOS.NSString.pitfall-in-using-nsstring

    1. NSString的使用 在CodeReview中, 发现类似以下代码, 表示深深受伤了: NSString* fString = [NSString stringWithFormat:@&quo ...

  5. laravel使用$errors提取错误信息

    1.控制器 2.模板

  6. WinScp获取一个文件

    CD /d C:\Program Files (x86)\WinSCPWinSCP.exe /console /command "option batch continue" &q ...

  7. How to Set Up an Rsync Daemon on Your Linux Server

    Introduction This tutorial will take you through setting up an rsync daemon on your Linux server. Yo ...

  8. Debian use sudo

    刚安装好的Debian默认还没有sudo功能.1.安装sudo# apt-get install sudo2.编辑 /etc/sudoers ,添加如下行# visudoroot ALL=(ALL:A ...

  9. windows10 装linux子系统

    http://blog.csdn.net/Yuxin_Liu/article/details/52347898 试了一下,下载太慢,就没继续用,可以用实验楼这个网来玩玩linux

  10. 2018.09.21 codeforces1051D. Bicolorings(线性dp)

    传送门 sb线性DP. f[i][j][0/1/2/3]f[i][j][0/1/2/3]f[i][j][0/1/2/3]表示前i列j个连通块且第i列状态为00/01/10/11时的方案总数. 这个显然 ...