机动车合格证手机扫码开票实现方式

news/2025/1/31 6:53:55/

为方便公司开票人员准确快速的开票,通过两周的研究,码出此工具。
(试用下载地址https://download.csdn.net/download/super_farmers/12264989)
在此写下实现过程:

在这里插入图片描述
在这里插入图片描述
整个软件分为:
1.扫描二维码
手机扫描二维码使用DroidCam软件实现,DroidCam可以把手机做为二维码采集设备(由于机动车二维码的复杂性,手机摄像头像素越高越好);
首先在安装DroidCam软件的电脑端:
在这里插入图片描述
DroidCam可以使用wifi/usb两种方式连接手机,本文中使用wifi连接方式(方便);
接着安装手机端:
在这里插入图片描述
手机端安装好以后可以在电脑端输入手机端显示的IP地址,测试连接是否正常
在程序中使用下面代码读取摄像头列表会得到:DroidCam字串开头的四个设备,除编号为3的设备,其它均可使用。

    QList<QCameraInfo> cameras = QCameraInfo::availableCameras();foreach (const QCameraInfo &cameraInfo, cameras) {ui->Camera_List->addItem(cameraInfo.description());}
获取到设备以后,显示视频及截图代码:
QList<QCameraInfo> cameras = QCameraInfo::availableCameras();foreach (const QCameraInfo &cameraInfo, cameras) {if (cameraInfo.description() == ui->Camera_List->currentText()){// 摄像头实例m_Camera = new QCamera(cameraInfo);// 模式、视频采集器m_Camera->setCaptureMode(QCamera::CaptureStillImage);m_Camera->setViewfinder(m_ViewFinder);// 开始获取视频m_Camera->start();// 定时器定时截图 m_ImageCapture->capture(QDir::currentPath() + "/code");m_Timer->start(2000);// 截图m_ImageCapture = new QCameraImageCapture(m_Camera);m_ImageCapture->setCaptureDestination(QCameraImageCapture::CaptureToFile);// 由slot_imageSaved槽处理截取的图像connect(m_ImageCapture, SIGNAL(imageSaved(int,QString)), this, SLOT(slot_imageSaved(int,QString)));}}
至此扫码功能实现完成。

2.识别二维码
Qt识别二维码有QZxing库(),经测试,这个库对机动车二维码无能为力,最终选择一个商业二维码识别控件PsyQrDcd这个要注册授权的,不然识别出只有"AAAAA…"(http://www.psytec.co.jp/),这个识别率很高,速度也非常快。在这一步由于使用第三方库,没什么好说的,使用函数DecodePictureFile设置二维码文件路径,GetDecodeData函数读取结果。
3.数据解密
这一步是本软件最核心的一步,因防伪等各种原因,二维码读取到的内容为des加密后base64编码的一串文本。解码不了数据,一切白搭。这里可以调用大神做的接口,没有解密能力的可以参考(https://vehcode.scznnet.cn:449/QRcodeDoc.html#15614)。调用接口需要编译了openssl的Qt版本,因为接口是https协议的,我之前编译的Qt没有openssl,重新编译了下QT(好崩溃,需要静态编译的同学编译openssl一定不要加–debug选项,不然你得重编译);数据解密后得到64个字段,开具发票只用到其中10个字段(生产企业名称|车辆类型|车辆型号|产地|合格证号|发动机号码|车辆识别号VIN|吨位|限乘人数|车辆名称),其中车辆类型对应开票系统中产品分类名称,用此名称从产品分类名称中获取分类编码,这个分类编译在生成发票的时候要用。
4.生成发票
依照《增值税发票税控开票软件数据接口规范3.0》中描述的机动车发票导入接口生成XML文档,模板如下:

<?xml version="1.0" encoding="gbk"?>
<business><body><djh>djh</djh>                    	//单据号(30字符)<bmb_bbh>bmb_bbh</bmb_bbh>     		//编码表版本号(20个字符)<fpdm>fpdm</fpdm>                	//发票代码(10个字符)<fphm>fphm</fphm>                	//发票号码(8个字符)<gfdwmc>gfdwmc</gfdwmc>         	//购方单位名称(72个字符)<sfzhm>sfzhm</sfzhm>            	//身份证号码/组织机构代码(22个字符)<gfdwsbh>gfdwsbh</gfdwsbh>     		//购方单位识别号(20个字符)<cllx>cllx</cllx>                	//车辆类型(40个字符)<cpxh>cpxh</cpxh>                	//厂牌型号(60个字符)<cd>cd</cd>                       	//产地(32个字符)<hgzh>hgzs</hgzh>                	//合格证书(50个字符)<jkzmsh>jkzmsh</jkzmsh>         	//进口证明书号(36个字符)<sjdh>sjdh</sjdh>                	//商检单号(32个字符)<fdjhm>fdjhm</fdjhm>            	//发动机号码(60个字符)<clsbdh>clsbdh</clsbdh>         	//车辆识别代号(23个字符)<scqymc>scqymc</scqymc>         	//生产企业名称(80个字符)<jshj>jshj</jshj>                	//价税合计<dh>dh</dh>                       	//电话(40个字符)<zh>zh</zh>                       	//账号(40个字符)<dz>dz</dz>                       	//地址(80个字符)<khyh>khyh</khyh>                	//开户银行(80个字符)<zzssl>zzssl</zzssl>            	//增值税税率(实际税率)<zzsse>zzsse</zzsse>            	//增值税税额<bhsj>bhsj</bhsj>                	//不含税价<dw>dw</dw>                       	//吨位(8个字符)<xcrs>xcrs</xcrs>                	//限乘人数(12个字符)<spbm> spbm</spbm>					//商品编码(19个字符)<zxbm>zxbm </zxbm>					//自行编码(20个字符)<yhzcbs>yhzcbs</yhzcbs>				//优惠政策标识(1个字符)  0:不使用,1:使用<lslbs>lslbs</lslbs> 				//税率标识空(1个字符):非零税率,0:出口退税,1:免税,2:不征收,3普通零税率<zzstsgl>zzstsgl</zzstsgl>			//增值税特殊管理(50个字符)</body>
</business>
单据号可自定义;编码版本为税收分类编码的版本号,可以在最新税收分类编码文档中获取,发票代码,发票号码留空,增值税税率(13% 要填入 0.13)
这里有个小写金额转大写的功能,,自行实现耗时一下午(开始想的太简单了)只实现 了最大千亿的转换(应该够了吧),代码奉上:
QString Invoice::AmountsConverted(double amount)
{QStringList cnNumber = {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};// 拆分整数与小数部分QString strAmount, strInteger, strDecimal;  // 金额字符串, 整数部分, 小数部分strAmount.setNum(amount, 'f', 2);strInteger = strAmount.split('.').at(0);strDecimal = strAmount.split('.').at(1);QStringList strlInteger, strlDecimal;strlInteger = strInteger.split("");strlInteger.removeFirst(); strlInteger.removeLast();    //移除首尾空值strlDecimal = strDecimal.split("");strlDecimal.removeFirst(); strlDecimal.removeLast();    //移除首尾空值// 构建汉字列表QStringList strlCnInteger, strlCnDecimal;foreach (QString str, strlInteger) {strlCnInteger << cnNumber.at(str.toInt());}foreach (QString str, strlDecimal) {strlCnDecimal << cnNumber.at(str.toInt());}// 整数部分添加单位int nCount = strlInteger.size() - 1;     // 总共多少位bool yflag = false, sflag = false, bflag = false, qflag = false, wflag = false;int nStep = 0;for(int i = nCount; i >= 0; i--){switch (nStep++){case 0:         // 元if (strlInteger.at(i) == "0") strlCnInteger.removeAt(i);elseyflag = true;break;case 1:         // 拾if (strlInteger.at(i) == "0"){if (!yflag)strlCnInteger.removeAt(i);}else if (strlInteger.at(i) != "0")strlCnInteger.insert(i + 1, "拾"), sflag = true;break;case 2:         // 百if (strlInteger.at(i) == "0"){if (!yflag && !sflag)strlCnInteger.removeAt(i);if (yflag && !sflag)strlCnInteger.removeAt(i);}else if (strlInteger.at(i) != "0")strlCnInteger.insert(i + 1, "佰"), bflag = true;break;case 3:         // 仟if (strlInteger.at(i) == "0"){if (!yflag && !sflag && !bflag)strlCnInteger.removeAt(i);if ((yflag || sflag) && !bflag)strlCnInteger.removeAt(i);}else if (strlInteger.at(i) != "0")strlCnInteger.insert(i + 1, "仟"), qflag = true;break;case 4:         // 万if (!wflag){if (strlInteger.at(i) == "0"){if (!qflag)strlCnInteger.removeAt(i);strlCnInteger.insert(i, "万");yflag = false;wflag = true;}elsestrlCnInteger.insert(i + 1, "万"), yflag = true, wflag = true;}else        // 亿{if (strlInteger.at(i) == "0"){strlCnInteger.removeAt(i);strlCnInteger.insert(i, "亿");yflag = false;}elsestrlCnInteger.insert(i + 1, "亿"), yflag = true;}nStep = 1; sflag = bflag = qflag = false;break;}}// 小数部分添加单位// 分if (strlDecimal.at(1) == "0"){strlCnDecimal.removeAt(1);}else{strlCnDecimal.insert(2, "分");}// 角if (strlDecimal.at(0) == "0"){if (strlDecimal.at(1) == "0")strlCnDecimal.removeAt(0);}else{strlCnDecimal.insert(1, "角");}QString result = strlCnInteger.join("") + "圆" + strlCnDecimal.join("") + "整";return result;
}

5.开票导入:
这里没什么说的,生成发票XML后,在开票界面导入就成。

X.openssl des加解密的学习:
在openssl中以evp模式使用des等加解密方便快捷,但一定要注意数据类型的转换,简要代码如下:

// 生成key及iv  len为返回key的长度,
int len = EVP_BytesToKey(/*方式*/, /*hex*/, NULL, /*密钥*/, /*密钥长度*/, /*强度*/, key, iv);
// ctx上下文结构申请内存 (在构造函数中)
m_evp_ctx = EVP_CIPHER_CTX_new();// 加解密过程// 释放dtx上下文结构内存 (在析构函数中)
EVP_CIPHER_CTX_free(m_evp_ctx);
// 加密
QString Common::des_EnCrypto(QString Text)
{QByteArray bText = Text.toUtf8();unsigned char *inText = (unsigned char*)bText.data();int inTextLen = bText.length();unsigned char *outText = (unsigned char*)malloc(inTextLen + 8);int outLen, outFinalLent, outTotalLen;EVP_CIPHER_CTX_reset(m_evp_ctx);int ret = EVP_EncryptInit(m_evp_ctx, EVP_des_cbc(), m_des_key, m_des_iv);if (ret != 1){EVP_CIPHER_CTX_reset(m_evp_ctx);return QString();}ret = EVP_EncryptUpdate(m_evp_ctx, outText, &outLen, inText, inTextLen);if (ret != 1){EVP_CIPHER_CTX_reset(m_evp_ctx);return QString();}else{EVP_EncryptFinal(m_evp_ctx, outText + outLen, &outFinalLent);outTotalLen = outLen + outFinalLent;}EVP_CIPHER_CTX_reset(m_evp_ctx);return QString::fromUtf8(QByteArray((char*)outText, outTotalLen).toHex());
}
// 解密
QString Common::des_DeCrypto(QString Text)
{QByteArray bText = QByteArray::fromHex(Text.toUtf8());unsigned char *inText = (unsigned char*)bText.data();int inTextLen = bText.length();unsigned char *outText = (unsigned char*)malloc(inTextLen + 8);int outLen, outFinalLent, outTotalLen;EVP_CIPHER_CTX_reset(m_evp_ctx);int ret = EVP_DecryptInit(m_evp_ctx, EVP_des_cbc(), m_des_key, m_des_iv);if (ret != 1){EVP_CIPHER_CTX_reset(m_evp_ctx);return QString();}ret = EVP_DecryptUpdate(m_evp_ctx, outText, &outLen, inText, inTextLen);if (ret != 1){EVP_CIPHER_CTX_reset(m_evp_ctx);return QString();}else{EVP_DecryptFinal(m_evp_ctx, outText + outLen, &outFinalLent);outTotalLen = outLen + outFinalLent;}EVP_CIPHER_CTX_reset(m_evp_ctx);return QString::fromUtf8((char*)outText, outTotalLen);
}

http://www.ppmy.cn/news/508433.html

相关文章

全国增值税发票查验平台验证码识别

2020/6/13 更新&#xff1a;看了评论区&#xff0c;发现一篇有价值的博客&#xff1a;https://blog.csdn.net/kerlomz/article/details/105974823 该博客主要提供了一种高效利用数据集的方法&#xff0c;鉴于作者写得过于晦涩&#xff0c;我这里再整理一下&#xff1a; 核心思…

全国多地推行电子证照:电子签章助力政务服务“零跑腿”

近期&#xff0c;全国多地先后推行电子证照&#xff0c;着手构建电子证照数据共享平台。通过将电子签章系统与现有行政审批、证照管理流程结合&#xff0c;助力证照办理效率&#xff0c;进一步推进“互联网政务服务”纵深发展…… 一、电子证照成“互联网政务服务”加速器 自2…

使用 shell 脚本自动申请进京证 (六环外)

问题背景 外地车辆进入北京&#xff0c;需要办理《进京证》&#xff0c;不办理证件驶入后会被执法设备抓拍&#xff0c;一次罚 100 扣 1 分&#xff0c;目前唯一的线上办理通道是下载《北京交警》App&#xff0c;注册后添加车辆&#xff0c;就可以为自己的爱车随时随地办理进京…

应届生改派、派遣证、报到证、户口接收函(进京函)--人社部渠道

文章目录 一些概念提交材料重点注意进京函→报到证户口迁移证 审核流程 京外生源落户北京步骤&#xff1a;申请派遣→提交材料→等待审核 一些概念 应届生改派&#xff1a;应届生要就业&#xff0c;原单位&#xff08;学校&#xff09;的人事关系重新派到新的用人单位。 其中…

关于单位申请进京指标

网上查企业如何申请进京指标&#xff0c;这方面资料很少&#xff0c;找了两个多小时&#xff0c;终于大致摸到门道了&#xff0c;下一步可以安排刘总管尝试申请下了。 1、北京各区县人力资源和社会保障局大致会在每年的10月下旬&#xff08;有时会在11月&#xff09;&#xff0…

计算机准考证要打印多大的纸,税务证准考证打印纸张大小要求多大的?

临近税务师考试&#xff0c;下一步税务师考生应打印准考证了&#xff0c;每年在打印税务师准考证时&#xff0c;都有不少考生遇到各种问题&#xff0c;比如税务证准考证打印纸张大小要求多大的&#xff0c;税务师准考证打印具体时间&#xff0c;这些是我们必须知道的。 税务师准…

中国移动如何开具并下载打印电子发票?

首先打开中国移动官网: 浏览器搜索"中国移动",第一个就是 点进去后,先选择你的手机号所属省份,它会根据你的ip地址自动选择,如果你的手机号不是当前省份的,就需要重新选择: 比如,我的号码是江苏南京的,我就切换到江苏南京: 切换过来后,点击左上方"登录":…

进京证范围

限行区域为5环及5环内&#xff0c;京牌和外牌限行的区别有2点;1、限行时京牌可以上5环&#xff0c;外牌不能上5环&#xff1b;2、外牌在工作日早7点至9点、17点至20点不能上5环及5环以里&#xff1b; 再有&#xff0c;外牌进6环就需办进京证&#xff01;在六环行驶不用办进京证…