Files
OMS/app/invoice/lib/check.php
2026-01-04 19:08:31 +08:00

435 lines
18 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Copyright 2012-2026 ShopeX (https://www.shopex.cn)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 发票状态检查类
*/
class invoice_check
{
//检查是否可以新建发票
//老创建方法使用的check 已作废
public function checkCreate($order_id)
{
if(!$order_id){
return false;
}
//取最新发票记录一条,如存在未作废状态(即未开票或者已开票)的记录 则不能新增发票记录
$mdlInOrder = app::get('invoice')->model('order');
$arr_filter = array(
'order_id' => $order_id,
'is_status|in' => array(0,1),
);
$rs_invoice = $mdlInOrder->getList('*', $arr_filter, 0, 1, 'id DESC');
if($rs_invoice){
return false;
}else{
return true;
}
}
/**
* 校验当前订单是否可以新建发票 ------作废
* @Author: xueding
* @Vsersion: 2023/3/4 下午3:02
* @param $order_id
* @return bool
*/
public function checkOrderCreate($order_id)
{
if(!$order_id){
return false;
}
$ordersMdl = app::get('ome')->model('orders');
$arr_filter = array(
'order_id' => $order_id,
);
$rs_order = $ordersMdl->db_dump($arr_filter, 'pay_status,ship_status,payed');
if (empty($rs_order)) {
$ordersMdl = app::get('archive')->model('orders');
$rs_order = $ordersMdl->db_dump($arr_filter, 'pay_status,ship_status,payed');
}
if($rs_order && in_array($rs_order['pay_status'],['1','4']) && $rs_order['ship_status'] != '4' && $rs_order['payed'] > 0){
return true;
}else{
return false;
}
}
//检查是否可以作废/冲红发票,并获取发票信息 ----------方法作废
public function checkCancel($order_id,$id=0)
{
if(!$order_id){
return false;
}
//取相关订单所有发票记录,有且只有一条是未作废状态
$mdlInOrder = app::get('invoice')->model('order');
$arr_filter = array(
'order_id' => $order_id,
'is_status|in' => array(0,1),
);
if ($id) $arr_filter['id'] = $id;
$rs_invoice = $mdlInOrder->getList('*', $arr_filter);
if(count($rs_invoice) == 1){
#如果是电子发票,还需要再去获取一些扩展信息,打开票接口的时候要用这些扩展信息
if($rs_invoice[0]['mode'] == '1'){
$get_channel_info = app::get('invoice')->model('channel')->get_channel_info($rs_invoice[0]['shop_id']);
// #当店铺没有电子发票渠道配置直接false;
// if(empty($get_channel_info))return false;
// 冗余
$rs_invoice[0]['channel_node_id'] = $get_channel_info['node_id'];
$rs_invoice[0]['channel_node_type'] = $get_channel_info['node_type'];
$rs_invoice[0]['channel_golden_tax_version'] = $get_channel_info['golden_tax_version'];
$rs_invoice[0]['channel_id'] = $get_channel_info['channel_id'];
$rs_invoice[0]['channel_type'] = $get_channel_info['channel_type'];
$rs_invoice[0]['channel_extend_data'] = isset($get_channel_info['channel_extend_data']) ? $get_channel_info['channel_extend_data'] : '';
$rs_invoice[0]['skpdata'] = isset($get_channel_info['skpdata']) ? $get_channel_info['skpdata'] : '';#税控盘信息
$rs_invoice[0]['kpddm'] = isset($get_channel_info['kpddm']) ? $get_channel_info['kpddm'] : '';#开票点编码
$rs_invoice[0]['eqpttype'] = isset($get_channel_info['eqpttype']) ? $get_channel_info['eqpttype'] : '';#设备类型
}
return $rs_invoice[0];
}else{
return false;
}
}
//检查是否可以开票,并获取开票信息
public function checkBilling($arrBilling, &$error_msg=null)
{
$mdlInOrder = app::get('invoice')->model('order');
$id = $arrBilling["id"];
$order_id = $arrBilling["order_id"];
if(!$id || !$order_id){
$error_msg = '无效的操作';
return false;
}
//取本张发票记录外的此订单是否存在未开票/已开票(即未作废)的发票记录 存在则不能开票,一个订单可以新建相似
$arr_filter = array(
"order_id" => $order_id,
"is_status|in" => array("0","1"), #0=>'未开票',1=>'已开票'
"id|noequal" => $id,#本次开票记录以外,该订单其他开票记录
);
$rs_info = $mdlInOrder->getList('*', $arr_filter, 0, 1);
$_self_info = $mdlInOrder->getList('*',array("id"=>$id,'is_status'=>'0'));#状态必须为未开票
if($rs_info || empty($_self_info)){
$error_msg = '没有可开票的订单';
return false;
}
$rs_invoice = $_self_info[0];
#如果是电子发票,还需要再去获取一些扩展信息,打开票接口的时候要用这些扩展信息
if($rs_invoice['mode'] == '1'){
$get_channel_info = app::get('invoice')->model('channel')->get_channel_info($rs_invoice['shop_id']);
//当店铺没有电子发票渠道配置直接false;
if(empty($get_channel_info)){
$error_msg = '没有电子发票渠道配置';
return false;
}
// 渠道没有节点id,直接false;
if(!$get_channel_info['node_id']){
$error_msg = '开票渠道未绑定';
return false;
}
$rs_invoice['channel_node_id'] = $get_channel_info['node_id'];
$rs_invoice['channel_node_type'] = $get_channel_info['node_type'];
$rs_invoice['channel_golden_tax_version'] = $get_channel_info['golden_tax_version'];
$rs_invoice['channel_id'] = $get_channel_info['channel_id'];
$rs_invoice['channel_type'] = $get_channel_info['channel_type'];
$rs_invoice['channel_extend_data'] = isset($get_channel_info['channel_extend_data'])?$get_channel_info['channel_extend_data']:'';
$rs_invoice['skpdata'] = isset($get_channel_info['skpdata'])?$get_channel_info['skpdata']:'';#税控盘信息
$rs_invoice['kpddm'] = isset($get_channel_info['kpddm'])?$get_channel_info['kpddm']:'';#开票点编码
$rs_invoice['eqpttype'] = isset($get_channel_info['eqpttype'])?$get_channel_info['eqpttype']:'';#设备类型
$rs_invoice['einvoice_operating_conditions'] = $get_channel_info['einvoice_operating_conditions'];
}
return $rs_invoice;
}
//从明细如手 检查是否可以开电子发票(开蓝/冲红)
public function checkEinvoiceCreate($rs_invoice, $billing_type=1, &$error_msg=null)
{
if(!$rs_invoice["id"]){
$error_msg = '无效操作。';
return false;
}
$mdlInOrderElIt = app::get('invoice')->model('order_electronic_items');
$rs_item = $mdlInOrderElIt->dump(array("id"=>$rs_invoice["id"],"billing_type"=>$billing_type));
if(empty($rs_item)){
//没有相关明细记录 可以开票
return true;
}
if(!$rs_item["invoice_code"] && $rs_item["serial_no"]){
//之前点击开票没有成功或者开票中的状态 之前已生产开票流水号 可以开票
return true;
}
//有相关的明细记录 并且已经callback开票成功 不可以开票
$error_msg = '不能重复开票';
return false;
}
//检查是否可以编辑
public function checkEdit($id){
if(!$id){
return false;
}
//此发票记录 开票状态 必须是未开票
$mdlInOrder = app::get('invoice')->model('order');
$rs_info = $mdlInOrder->dump(array("id"=>$id));
if(intval($rs_info["is_status"]) == 0 && ($rs_info['sync'] == '0' || $rs_info['sync'] == '2')){
return true;
}else{
return false;
}
}
//检查开电子发票打接口的必要参数是否缺失
public function checkDoEinvoice($rs_invoice){
$arr_hint = array();
if(!floatval($rs_invoice["amount"])){
$arr_hint[] = "开票金额必须大于0";
}
if(!$rs_invoice["channel_type"]){
$arr_hint[] = "店铺对应的开票渠道不存在";
}
$title = $rs_invoice["tax_company"]?$rs_invoice["tax_company"]:$rs_invoice["title"];
if(!$title){
$arr_hint[] = "发票抬头不能为空";
}
return array(
"arr_hint" => $arr_hint,
"mode" => $rs_invoice["mode"],
);
}
/**
* 校验是否可以进行合并发票创建
* @Author: xueding
* @Vsersion: 2023/6/2 下午5:30
* @param $id 1,2,3
* @return bool[]
*/
public function checkMergeInvoice($id)
{
//check
if(empty($id)){
return [false,'无效的id字段值'];
}
$sql = "SELECT o.id,o.order_bn FROM sdb_invoice_order AS o LEFT JOIN sdb_invoice_order_items AS oi on oi.id=o.id WHERE oi.original_id in (".$id.") AND o.is_status != '2' group by o.id";
$res = kernel::database()->select($sql);
if (count($res) > 0) {
return [false,'创建失败已存在合并开票信息并且未开票或已开票'];
}
return [true];
}
/**
* 检查是否可以新建发票
* @Author: xueding
* @Vsersion: 2023/6/5 下午3:25
* @param $of_id
* @return array|bool|bool[]
*/
public function checkInvoiceCreate($of_id,$type = '')
{
if(!$of_id){
return false;
}
if ($type == 'add_merge_invoice') {
return [true];
}
$sql = "SELECT o.id,o.order_bn FROM sdb_invoice_order AS o LEFT JOIN sdb_invoice_order_items AS oi on oi.id=o.id WHERE oi.of_id in (".implode(',',array_unique($of_id)).") AND o.is_status != '2' group by o.id";
$res = kernel::database()->select($sql);
if (count($res) > 0) {
return [false,'有未开票或者已开票状态的发票'];
}
return [true];
}
public function checkAddSame($of_id)
{
if(!$of_id){
return false;
}
$sql = "SELECT id FROM sdb_invoice_order_front WHERE id in (".implode(',',array_unique($of_id)).") AND status != 'close' ";
$res = kernel::database()->select($sql);
if (count($res) == 0) {
return [false,'没有可开票信息'];
}
return [true];
}
/**
* 开票校验
* @Author: xueding
* @Vsersion: 2023/6/7 下午4:17
* @param $arrBilling
* @param null $error_msg
* @return bool|mixed
*/
public function checkMakeInvoice($arrBilling, &$error_msg=null)
{
$invoiceMdl = app::get('invoice')->model('order');
$invoiceItemsMdl = app::get('invoice')->model('order_items');
$frontMdl = app::get('invoice')->model('order_front');
$frontItemsMdl = app::get('invoice')->model('order_front_items');
$id = $arrBilling["id"];
if(!$id){
$error_msg = '无效的操作';
return false;
}
//取本张发票记录外的此订单是否存在未开票/已开票(即未作废)的发票记录 存在则不能开票,一个订单可以新建相似
$rs_invoice = $invoiceMdl->db_dump(['id' => $id, 'is_status' => '0']);
if (empty($rs_invoice)) {
$error_msg = '当前发票状态不可开票';
return false;
}
//明细取of_id查询是否有其他未开的票
$items = $invoiceItemsMdl->getList('of_id,quantity,of_item_id', ['id' => $rs_invoice['id'],'is_delete'=>'false']);
$of_id = array_unique(array_column($items, 'of_id'));
$invoiceItems = array_column($items,null,'of_item_id');
$itemsList = $invoiceItemsMdl->getList('id', ['id|noequal' => $rs_invoice['id'], 'of_id' => $of_id]);
$otherIds = array_unique(array_column($itemsList, 'id'));
if ($invoiceMdl->getList('id', ['id' => $otherIds, 'is_status' => ['0', '1']])) {
$error_msg = '该订单存在多张未开票或已开票信息不能开票';
return false;
}
//开票校验源数据
$mainList = $frontMdl->getList('id,amount,status',['id'=>$of_id]);
$frontItemsList = $frontItemsMdl->getList('of_id,id as of_item_id,quantity',['of_id'=>$of_id,'is_delete'=>'false','filter_sql'=>' (quantity - reship_num) > 0 ']);
$ofItems = array_column($frontItemsList,null,'of_item_id');
if (!$mainList || !$frontItemsList) {
$error_msg = '开票失败源数据为空不能开票';
return false;
}
// foreach ($mainList as $key => $val) {
// if ($val['status'] != 'finish') {
// $error_msg = '开票失败源单据未签收不能开票';
// return false;
// }
// }
if (count($invoiceItems) != count($ofItems)) {
$error_msg = '开票明细与源数据明细不一致';
return false;
}
foreach ($items as $key => $val) {
if (isset($ofItems[$val['of_item_id']]) && $val['quantity'] !=$ofItems[$val['of_item_id']]['quantity']) {
$error_msg = '开票明细数量与源数据明细数量不一致';
return false;
}
}
$amount = array_sum(array_column($mainList,'amount'));
if ($rs_invoice['amount'] > $amount) {
$error_msg = '开票失败金额异常,开票金额:'.$rs_invoice['amount'] .',原始金额:'.$amount;
return false;
}
#如果是电子发票,还需要再去获取一些扩展信息,打开票接口的时候要用这些扩展信息
if($rs_invoice['mode'] == '1'){
$get_channel_info = app::get('invoice')->model('channel')->get_channel_info($rs_invoice['shop_id']);
//当店铺没有电子发票渠道配置直接false;
if(empty($get_channel_info)){
$error_msg = '没有电子发票渠道配置';
return false;
}
$rs_invoice['channel_node_id'] = $get_channel_info['node_id'];
$rs_invoice['channel_node_type'] = $get_channel_info['node_type'];
$rs_invoice['channel_golden_tax_version'] = $get_channel_info['golden_tax_version'];
$rs_invoice['channel_id'] = $get_channel_info['channel_id'];
$rs_invoice['channel_type'] = $get_channel_info['channel_type'];
$rs_invoice['channel_extend_data'] = isset($get_channel_info['channel_extend_data'])?$get_channel_info['channel_extend_data']:'';
$rs_invoice['skpdata'] = isset($get_channel_info['skpdata'])?$get_channel_info['skpdata']:'';#税控盘信息
$rs_invoice['kpddm'] = isset($get_channel_info['kpddm'])?$get_channel_info['kpddm']:'';#开票点编码
$rs_invoice['eqpttype'] = isset($get_channel_info['eqpttype'])?$get_channel_info['eqpttype']:'';#设备类型
$rs_invoice['einvoice_operating_conditions'] = $get_channel_info['einvoice_operating_conditions'];
}
unset($rs_invoice['itemsdf']);
return $rs_invoice;
}
/**
* 校验作废发票
* @Author: xueding
* @Vsersion: 2023/6/8 上午11:12
* @param int $id
* @param null $error_msg
* @return bool|mixed
*/
public function checkInvoiceCancel($id=0, &$error_msg=null)
{
//check
if(empty($id)){
$error_msg = 'id字段为空,无法查询';
return false;
}
$sql = "SELECT o.* FROM sdb_invoice_order AS o LEFT JOIN sdb_invoice_order_items AS oi on oi.id=o.id WHERE o.id = '" . $id . "' AND o.is_status != '2' GROUP BY o.id ";
$rs_invoice = kernel::database()->select($sql);
if (empty($rs_invoice)) {
$error_msg = '当前发票状态不可作废';
return false;
}
if(count($rs_invoice) == 1){
#如果是电子发票,还需要再去获取一些扩展信息,打开票接口的时候要用这些扩展信息
if($rs_invoice[0]['mode'] == '1'){
$get_channel_info = app::get('invoice')->model('channel')->get_channel_info($rs_invoice[0]['shop_id']);
$rs_invoice[0]['channel_node_id'] = $get_channel_info['node_id'];
$rs_invoice[0]['channel_node_type'] = $get_channel_info['node_type'];
$rs_invoice[0]['channel_golden_tax_version'] = $get_channel_info['golden_tax_version'];
$rs_invoice[0]['channel_id'] = $get_channel_info['channel_id'];
$rs_invoice[0]['channel_type'] = $get_channel_info['channel_type'];
$rs_invoice[0]['channel_extend_data'] = isset($get_channel_info['channel_extend_data'])?$get_channel_info['channel_extend_data']:'';
$rs_invoice[0]['skpdata'] = isset($get_channel_info['skpdata'])?$get_channel_info['skpdata']:'';#税控盘信息
$rs_invoice[0]['kpddm'] = isset($get_channel_info['kpddm'])?$get_channel_info['kpddm']:'';#开票点编码
$rs_invoice[0]['eqpttype'] = isset($get_channel_info['eqpttype'])?$get_channel_info['eqpttype']:'';#设备类型
}
return $rs_invoice[0];
}else{
$error_msg = '当前发票状态不可作废';
return false;
}
}
}