【NumberValidators】增值税发票代码验证

原文:【NumberValidators】增值税发票代码验证

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

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

原文地址:https://www.cnblogs.com/lonelyxmas/p/10713616.html

时间: 2024-08-03 12:07:14

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

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

原文:[NumberValidators]工商营业执照号码和统一社会信用代码验证 从本质上讲,工商营业执照号码和统一社会信用代码是两套完全不一样的编码规则,识别结果也仅有行政区划部分为两者共有,但因为这两种编码同时存在的原因,所以如果需要在系统中唯一标志一家企业时,还是可以通过工商营业执照号码或统一社会信用代码来进行识别. 工商营业执照号码长度按工商规定为15位,统一社会信用代码按国家规定是18位,你可以在此类库的验证基础上,通过其它网站(比如企查查)来进一步查验企业是否真实存在. 在Numbe

【NumberValidators】大陆身份证验证

原文:[NumberValidators]大陆身份证验证 需要说明的是这里的大陆身份证识别并不是公安局联网的识别,而是按国标GB 11643进行的验证,所以其验证结果只能说符合国标规范,但不能保证该身份证一定真实存在,如果你实际需求是希望身份证一定真实存在,那么你可以在通过此类库初步验证后,再调用第三方(或牛逼的可以直连公安,毕竟所有的第三方其数据来源必定是公安局)以降低调用成本(公安调用一次两块钱,还不是有钱就能调用!!!) 转到正题,在NumberValidators中,大陆身份证相关的代码

增值税

对商品生产和流通中各环节的新增价值或商品附加值,如果是一般纳税人销项减去进项税率17%,如果是小规模税率是3%.实行价外税,也就是由消费者负担,有增值才征税没增值不征税. 增值税是以商品(含应税劳务)在流转过程中产生的增值额作为计税依据而征收的一种流转税.从计税原理上说,增值税是对商品生产.流通.劳务服务中多个环节的新增价值或商品的附加值征收的一种流转税.实行价外税,也就是由消费者负担,有增值才征税没增值不征税.增值税是对销售货物或者提供加工.修理修配劳务以及进口货物的单位和个人就其实现的增值额

ASP.NET MVC 使用Remote特性实现远程属性验证

RemoteAttribute是asp.net mvc 的一个验证特性,它位于System.Web.Mvc命名空间 下面通过例子来说明 很多系统中都有会员这个功能,会员在前台注册时,用户名不能与现有的用户名重复,还要求输入手机号码去注册,同时手机号码也需要验证是否重复,下面是实体类 /// <summary> /// 会员 /// </summary> public class Member { public int Id { get; set; } [Required(Error

IE 弹出提示:由于无法验证发布者,所以Windows 已经阻止此软件

由于无法验证发布者,所以Windows 已经阻止此软件 按如下步骤:1.打开Internet Explorer---菜单栏点“工具”---Internet选项--安全---自定义级别---安全设置---“ActiveX控件和插件下”的第5个“下载未签名的ActiveX控件”选择“提示”---确定!刷新您要安装的页面即可(因为使用的软件没有通过微软的徽标认证,在SP2中默认是不允许安装这样的程序的,解决方法为开始-控制面板-系统-硬件.其中在驱动程序项里有有个“驱动程序签名”的选项,点开后.选第一

Oracle基础学习2--Oracle登录与三种验证机制

首先,Oracle安装完毕有三个默认用户 ?  Sys:数据库对象的拥有者.权限最高.password在安装的时候(口令管理)能够改变 ?  System:数据库管理员,password为manager ?  Scott:一个普通用户,password为tiger 再看连接Oracle的三种验证机制 ?  操作系统验证(具体解释见以下) ?  password文件验证 ?  数据库验证 注:前两者适用于系统用户,比方:Sys.System等:最后一个适用于普通用户.比方:Scott. 再看Ora

SpringMVC中文件上传的客户端验证

SpringMVC中文件上传的客户端验证 客户端验证主要思想:在jsp页面中利用javascript进行对文件的判断,完成验证后允许上传 验证步骤:1.文件名称 2.获取文件的后缀名称 3.判断哪些文件类型允许上传 4.判断文件大小 5.满足条件后跳转后台实现上传 前台界面(验证上传文件是否格式满足要求): <body> <h2>文件上传</h2> <form action="upload01" method="post" 

PHP.48-TP框架商城应用实例-后台23-权限管理-权限验证

权限验证 1.登录控制器 2.通过tp验证码类生成验证码图片 3.在管理员模型增加登录验证规则 4.后台中所有的控制器必须先登录才能访问 思路:在访问任何一个控制器之前都判断一个session即可,=>增加一个父控制器验证Session 让所有后台的控制器[除了Login控制器之外的]都继承自这个控制器 5.在管理员访问后台的任何一个页面之前先到数据库中查看当前管理员所在的角色是否有权限访问这个页面 在权限模型中增加此检查方法,在父类登录控制器中调用 6.后台左侧只显示当前管理员有权限访问的按钮

在Laravel中使用Middleware进行身份验证

新建一个中间件: 方法写在handle中 判断用户是否登录而且是否是管理员,不是的话返回到主页 新建判断是否为管理员的方法 在kernel定义一个中间件,key是admin 注册群组路由:prefix是路由前缀,访问路由会自动在前面加上路由前缀:middleware是key值,会去验证中间件 1在数据库中是管理员 成功 附: 注册单个路由的中间件: Route::get('admin/profile', ['middleware' => 'auth', function () { // }]);