Files
OMS/app/console/model/material/package.php
chenping 61783b7d01 1. 【新增】售后单售后原因类型支持搜索
2. 【新增】手工创建订单折扣可输入正数

3. 【优化】盘点申请单确认

4. 【修复】采购退货单模拟出库失败问题

5. 【新增】订单金额客户实付与结算金额

6. 【优化】仓库发货统计报表物料名称显示

7. 【优化】自有仓储虚拟发货逻辑

8. 【修复】基础物料分类管理问题
2026-04-01 11:59:17 +08:00

488 lines
20 KiB
PHP
Raw 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 console_mdl_material_package extends dbeav_model {
private $templateColumn = array(
'加工单名称' => 'mp_name',
'仓库编码' => 'branch_bn',
'单据备注' => 'memo',
'MOVEMENT CODE' => 'movement_code',
'礼盒物料编码' => 'bm_bn',
'数量' => 'number',
);
public function getTemplateColumn() {
return array_keys($this->templateColumn);
}
public function prepared_import_csv(){
$this->import_data =[];
$this->import_data_bm_bn =[];
$this->ioObj->cacheTime = time();
}
public function prepared_import_csv_row($row,&$title,&$tmpl,&$mark,&$newObjFlag,&$msg)
{
if(empty($row) || empty(array_filter($row))) return false;
if( $row[0] == '加工单名称' ){
$this->nums = 1;
$title = array_flip($row);
foreach($this->templateColumn as $k => $val) {
if(!isset($title[$k])) {
$msg['error'] = '请使用正确的模板';
return false;
}
}
return false;
}
$this->nums++;
if (empty($title)) {
$msg['error'] = "请使用正确的模板格式!";
return false;
}
$arrRequired = ['bm_bn','number'];
if(!$this->import_data['main']) {
$arrRequired = ['bm_bn','number','mp_name','branch_bn','movement_code'];
}
$arrData = array();
foreach($this->templateColumn as $k => $val) {
$arrData[$val] = trim($row[$title[$k]]);
if(in_array($val, $arrRequired) && empty($arrData[$val])) {
$msg['warning'][] = 'Line '.$this->nums.''.$k.'不能都为空!';
return false;
}
}
if($this->nums > 10000){
$msg['error'] = "导入的数据量过大请减少到10000条以下";
return false;
}
if(!$this->import_data['main']) {
$main = [];
$main['mp_name'] = $arrData['mp_name'];
if($main['mp_name']) {
if($this->db_dump(['mp_name'=>$main['mp_name']], 'id')) {
$msg['error'] = '加工单重复';
return false;
}
}
$branch = app::get('ome')->model('branch')->db_dump(['branch_bn'=>$arrData['branch_bn']], 'branch_id');
$main['branch_id'] = $branch['branch_id'];
$main['memo'] = $arrData['memo'];
$main['movement_code'] = $arrData['movement_code'];
$this->import_data['main'] = $main;
}
if(in_array($arrData['bm_bn'], $this->import_data_bm_bn)) {
$msg['error'] = 'Line '.$this->nums.''.$arrData['bm_bn'].' 基础物料重复';
return false;
}
$bm = app::get('material')->model('basic_material')->db_dump(['material_bn'=>$arrData['bm_bn'], 'type'=>'4'], 'bm_id,material_name');
if(empty($bm)) {
$msg['error'] = 'Line '.$this->nums.''.$arrData['bm_bn'].' 礼盒物料不存在';
return false;
}
$this->import_data_bm_bn[] = $arrData['bm_bn'];
$item = [
'bm_id' => $bm['bm_id'],
'bm_bn' => $arrData['bm_bn'],
'bm_name' => $bm['material_name'],
'number' => $arrData['number'],
];
$this->import_data['items'][] = $item;
$mark = 'contents';
return true;
}
function prepared_import_csv_obj($data,$mark,$tmpl,&$msg = ''){
return null;
}
public function finish_import_csv(){
if(empty($this->import_data)) {
return null;
}
$data = $this->import_data['main'];
$items = $this->import_data['items'];
$this->insertDataItems($data, $items, '导入');
}
public function insertDataItems($data, $items, $source) {
if(empty($data) || empty($items)) {
return [false, '数据不全'];
}
$this->db->beginTransaction();
$data['mp_bn'] = $this->gen_id();
$this->insert($data);
if(!$data['id']) {
$this->db->rollBack();
return [false, ['msg'=>'写入失败']];
}
app::get('ome')->model('operation_log')->write_log('material_package@console',$data['id'],"订单".$source."新建");
foreach ($items as $k => $value) {
$items[$k]['mp_id'] = $data['id'];
}
$itemObj = app::get('console')->model('material_package_items');
$sql = ome_func::get_insert_sql($itemObj, $items);
$this->db->exec($sql);
$items = $itemObj->getList('*', ['mp_id'=>$data['id']]);
$bmIds = array_column($items, 'bm_id');
$basicMaterialCombinationItemsObj = app::get('material')->model('basic_material_combination_items');
$bmsRows = $basicMaterialCombinationItemsObj->getList('pbm_id,bm_id,material_bn,material_name,material_num', array('pbm_id' => $bmIds), 0, -1);
$bms = [];
foreach ($bmsRows as $v) {
$bms[$v['pbm_id']][] = $v;
}
// 校验物料同步状态
list($validateRs, $validateMsg) = $this->_validateMaterialSyncStatus($data['branch_id'], $items, $bmsRows);
if (!$validateRs) {
$this->db->rollBack();
return [false, ['msg' => $validateMsg]];
}
$storeManage = kernel::single('ome_store_manage');
$storeManage->loadBranch(array('branch_id'=>$data['branch_id']));
$mpid = [];
$storeLessMsg = '';
foreach ($items as $v) {
if(empty($bms[$v['bm_id']])) {
$this->db->rollBack();
return [false, ['msg'=>'缺少明细']];
}
if($data['service_type'] == '2') {
$validNum = $storeManage->processBranchStore(
array(
'node_type' =>'getAvailableStore',
'params' =>array(
'branch_id' =>$data['branch_id'],
'product_id'=>$v['bm_id'],
),
), $err_msg);
if($v['number'] > $validNum) {
$storeLessMsg .= $v['bm_bn'].'库存不足;';
}
}
foreach ($bms[$v['bm_id']] as $vv) {
$number = bcmul($v['number'], $vv['material_num']);
$mpid[] = [
'mp_id' => $v['mp_id'],
'mpi_id' => $v['id'],
'bm_id' => $vv['bm_id'],
'bm_bn' => $vv['material_bn'],
'bm_name' => $vv['material_name'],
'number' => $number,
];
if($data['service_type'] == '1') {
$validNum = $storeManage->processBranchStore(
array(
'node_type' =>'getAvailableStore',
'params' =>array(
'branch_id' =>$data['branch_id'],
'product_id'=>$vv['bm_id'],
),
), $err_msg);
if($number > $validNum) {
$storeLessMsg .= $vv['material_bn'].'库存不足;';
}
}
}
}
if($storeLessMsg) {
$this->db->rollBack();
return [false, ['msg'=>$storeLessMsg]];
}
$itemDetailObj = app::get('console')->model('material_package_items_detail');
$sql = ome_func::get_insert_sql($itemDetailObj, $mpid);
$this->db->exec($sql);
$this->db->commit();
return [true, ['msg'=>'操作成功']];
}
public function gen_id() {
$prefix = 'MP'.date("ymd");
$sign = kernel::single('eccommon_guid')->incId('material_package', $prefix, 6);
return $sign;
}
/**
* 校验物料同步状态
* @param int $branch_id 仓库ID
* @param array $mainItems 主商品数组,包含 bm_id 和 bm_bn
* @param array $detailItems 子商品数组,包含 bm_id 和 bm_bn或 material_bn
* @return array [是否成功, 错误信息]
*/
public function _validateMaterialSyncStatus($branch_id, $mainItems, $detailItems = array()) {
// 获取 wms_id如果为空则跳过校验门店仓
$wms_id = kernel::single('ome_branch')->getWmsIdById($branch_id);
if (!$wms_id) {
return [true, ''];
}
// 收集所有商品的 bm_id 和 bm_bn
$bm_ids = array();
$bm_bn_map = array();
// 收集主商品
foreach ($mainItems as $item) {
if (!empty($item['bm_id'])) {
$bm_ids[] = $item['bm_id'];
$bm_bn_map[$item['bm_id']] = isset($item['bm_bn']) ? $item['bm_bn'] : '';
}
}
// 收集子商品
foreach ($detailItems as $item) {
if (!empty($item['bm_id'])) {
$bm_ids[] = $item['bm_id'];
// 子商品可能使用 material_bn 或 bm_bn
$bm_bn_map[$item['bm_id']] = isset($item['material_bn']) ? $item['material_bn'] : (isset($item['bm_bn']) ? $item['bm_bn'] : '');
}
}
$bm_ids = array_unique($bm_ids);
if (empty($bm_ids)) {
return [true, ''];
}
// 查询 foreign_sku 表,获取所有商品的同步状态
$foreignSkuModel = app::get('console')->model('foreign_sku');
$foreignSkuList = $foreignSkuModel->getList('inner_product_id, sync_status, inner_sku', array(
'inner_product_id|in' => $bm_ids,
'wms_id' => $wms_id
));
// 构建已查询到的商品映射bm_id => sync_status
$syncStatusMap = array();
$skuMap = array();
foreach ($foreignSkuList as $foreignSku) {
$syncStatusMap[$foreignSku['inner_product_id']] = $foreignSku['sync_status'];
$skuMap[$foreignSku['inner_product_id']] = $foreignSku['inner_sku'];
}
// 校验同步状态,收集未同步的商品信息
$unsyncItems = array();
$syncStatusText = array(
0 => '未同步',
1 => '同步失败',
2 => '同步中',
3 => '同步成功',
4 => '同步后编辑',
);
foreach ($bm_ids as $bm_id) {
if (!isset($syncStatusMap[$bm_id])) {
// 商品在 foreign_sku 表中不存在,视为未分配
$material_bn = isset($bm_bn_map[$bm_id]) && !empty($bm_bn_map[$bm_id]) ? $bm_bn_map[$bm_id] : $bm_id;
$unsyncItems[] = array(
'material_bn' => $material_bn,
'status_text' => '未分配'
);
} elseif ($syncStatusMap[$bm_id] != '3') {
// sync_status 不等于 3视为未同步成功
// inner_sku 就是 material_bn
$material_bn = isset($skuMap[$bm_id]) ? $skuMap[$bm_id] : (isset($bm_bn_map[$bm_id]) && !empty($bm_bn_map[$bm_id]) ? $bm_bn_map[$bm_id] : $bm_id);
$unsyncItems[] = array(
'material_bn' => $material_bn,
'status_text' => isset($syncStatusText[$syncStatusMap[$bm_id]]) ? $syncStatusText[$syncStatusMap[$bm_id]] : '未知状态'
);
}
}
// 如果有未同步的商品,生成错误提示信息
if (!empty($unsyncItems)) {
$errorMsg = '以下商品未同步到仓库,请先到"基础物料分配"页面同步物料:';
$itemMsgs = array();
foreach ($unsyncItems as $item) {
$itemMsgs[] = '商品编码:' . $item['material_bn'] . ',同步状态:' . $item['status_text'];
}
$errorMsg .= implode('', $itemMsgs);
return [false, $errorMsg];
}
return [true, ''];
}
public function updateDataItems($data, $items, $source) {
if(empty($data) || empty($items)) {
return [false, '数据不全'];
}
$this->db->beginTransaction();
$upData = [
'mp_name' => $data['mp_name'],
'branch_id' => $data['branch_id'],
'movement_code' => $data['movement_code'],
'memo' => $data['memo'],
];
$rs = $this->update($upData, ['id'=>$data['id']]);
app::get('ome')->model('operation_log')->write_log('material_package@console',$data['id'],"订单".$source."更新");
$itemObj = app::get('console')->model('material_package_items');
$oldItems = $itemObj->getList('*', ['mp_id'=>$data['id']]);
$bmIds = array_column($items, 'bm_id');
$oldBmIds = array_column($oldItems, 'bm_id');
$bmIds = array_merge($bmIds, $oldBmIds);
$basicMaterialCombinationItemsObj = app::get('material')->model('basic_material_combination_items');
$bmsRows = $basicMaterialCombinationItemsObj->getList('pbm_id,bm_id,material_bn,material_name,material_num', array('pbm_id' => $bmIds), 0, -1);
$bms = [];
foreach ($bmsRows as $v) {
$bms[$v['pbm_id']][$v['bm_id']] = $v;
}
// 收集需要校验的主商品(只包括保留和新增的,不包括删除的)
// $items 中包含了所有要保留和新增的主商品
$allMainItems = $items;
// 获取要保留和新增的主商品的 bm_id 列表
$keepBmIds = array_column($items, 'bm_id');
// 收集需要校验的子商品
// 从 bmsRows 过滤,只保留那些 pbm_id 在 $items 中的子商品(排除要删除的主商品的子商品)
$allDetailItems = array();
foreach ($bmsRows as $bmsRow) {
if (in_array($bmsRow['pbm_id'], $keepBmIds)) {
$allDetailItems[] = $bmsRow;
}
}
// 2. 收集保留的旧主商品对应的子商品(从 itemDetail 获取)
// 只收集那些在 $items 中存在的旧主商品对应的子商品
$itemDetailObj = app::get('console')->model('material_package_items_detail');
foreach ($oldItems as $oldItem) {
// 如果这个旧主商品在 $items 中存在,说明是保留的,需要校验其子商品
if (isset($items[$oldItem['bm_id']])) {
$oldDetailItems = $itemDetailObj->getList('bm_id, bm_bn', ['mpi_id' => $oldItem['id']]);
$allDetailItems = array_merge($allDetailItems, $oldDetailItems);
}
// 如果不在 $items 中,说明是要删除的,不需要校验
}
// 校验物料同步状态
list($validateRs, $validateMsg) = $this->_validateMaterialSyncStatus($data['branch_id'], $allMainItems, $allDetailItems);
if (!$validateRs) {
$this->db->rollBack();
return [false, ['msg' => $validateMsg]];
}
$storeManage = kernel::single('ome_store_manage');
$storeManage->loadBranch(array('branch_id'=>$data['branch_id']));
$storeLessMsg = '';
foreach ($oldItems as $value) {
if($items[$value['bm_id']]) {
$newItem = $items[$value['bm_id']];
unset($items[$value['bm_id']]);
if($newItem['number'] != $value['number']) {
$itemObj->update(['number'=>$newItem['number']], ['id'=>$value['id']]);
if($data['service_type'] == '2') {
$validNum = $storeManage->processBranchStore(
array(
'node_type' =>'getAvailableStore',
'params' =>array(
'branch_id' =>$data['branch_id'],
'product_id'=>$newItem['bm_id'],
),
), $err_msg);
if($newItem['number'] > $validNum) {
$storeLessMsg .= $newItem['bm_bn'].'库存不足;';
}
}
$itemDetail = $itemDetailObj->getList('id, bm_id, bm_bn', ['mpi_id'=>$value['id']]);
foreach ($itemDetail as $vv) {
$number = bcmul($newItem['number'], $bms[$value['bm_id']][$vv['bm_id']]['material_num']);
$itemDetailObj->update(['number' => $number], ['id'=>$vv['id']]);
if($data['service_type'] == '1') {
$validNum = $storeManage->processBranchStore(
array(
'node_type' =>'getAvailableStore',
'params' =>array(
'branch_id' =>$data['branch_id'],
'product_id'=>$vv['bm_id'],
),
), $err_msg);
if($number > $validNum) {
$storeLessMsg .= $vv['bm_bn'].'库存不足;';
}
}
}
}
} else {
$itemObj->delete(['id'=>$value['id']]);
$itemDetailObj->delete(['mpi_id'=>$value['id']]);
}
}
if($storeLessMsg) {
$this->db->rollBack();
return [false, ['msg'=>$storeLessMsg]];
}
if(empty($items)) {
$this->db->commit();
return [true, ['msg'=>'操作成功']];
}
$mpid = [];
foreach ($items as $v) {
if(empty($bms[$v['bm_id']])) {
$this->db->rollBack();
return [false, ['msg'=>'缺少明细']];
}
$v['mp_id'] = $data['id'];
$itemObj->insert($v);
if($data['service_type'] == '2') {
$validNum = $storeManage->processBranchStore(
array(
'node_type' =>'getAvailableStore',
'params' =>array(
'branch_id' =>$data['branch_id'],
'product_id'=>$v['bm_id'],
),
), $err_msg);
if($v['number'] > $validNum) {
$storeLessMsg .= $v['bm_bn'].'库存不足;';
}
}
foreach ($bms[$v['bm_id']] as $vv) {
$number = bcmul($v['number'], $vv['material_num']);
$mpid[] = [
'mp_id' => $v['mp_id'],
'mpi_id' => $v['id'],
'bm_id' => $vv['bm_id'],
'bm_bn' => $vv['material_bn'],
'bm_name' => $vv['material_name'],
'number' => $number,
];
if($data['service_type'] == '1') {
$validNum = $storeManage->processBranchStore(
array(
'node_type' =>'getAvailableStore',
'params' =>array(
'branch_id' =>$data['branch_id'],
'product_id'=>$vv['bm_id'],
),
), $err_msg);
if($number > $validNum) {
$storeLessMsg .= $vv['material_bn'].'库存不足;';
}
}
}
}
if($storeLessMsg) {
$this->db->rollBack();
return [false, ['msg'=>$storeLessMsg]];
}
$itemDetailObj = app::get('console')->model('material_package_items_detail');
$sql = ome_func::get_insert_sql($itemDetailObj, $mpid);
$this->db->exec($sql);
$this->db->commit();
return [true, ['msg'=>'操作成功']];
}
}