SCTMES_V5/mes_in_sct/app/action/SetMaterialInputByItemCode.php
2025-06-14 18:55:09 +08:00

310 lines
16 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
namespace app\action;
use libs\listener\Event;
use libs\db\Db;
use Exception;
// 物料投入
class SetMaterialInputByItemCode
{
public function execute($post)
{
return $this->do_set_material_input_by_item_code($post);
}
/*
装配段设备物料投料出现以下场景:
一、一个或多个原材料+裸电芯半成品 => 单个成品电池
例子:铝塑膜+焊接裸电芯 => 包装成品电池
接口api示例
{
"action": "set_material_input_by_item_code",
"param": {
"batch": "batch1",
"workingsubclass": "BZ",
"device_code": "BZ-1",
"in": [{
"item_code":"tongbo",
"item_batch": "fengezi-1",
"item_quantity":"345.34",
"item_id": "-1"
}]
}
}
*/
/*
* 通过batch->flow->model->bom->workingsubclass->BomRelationship对应的bom结构
* 1.获取请求体设备编码参数
* 2.判断工序单元、设备编码是存在?根据设备编码查询批次工序日志表,判断该设备是否已经存在生产中物料信息
* 3.获取生产批次关联工艺流程工艺流程关联型号型号关联bombom关联bom关系bom关系关联物料信息
* 4.通过以上关联信息查询物料编码
* 4.1.获取bom的物料编码
* 4.2.获取param_in结构的物料编码
* 5.检查上传param参数中in结构是否缺少物料
* 6.剔除上传param参数中in结构多余物料信息获取最终需要处理param_in
* 7.判断原材料或半成品是否已经投入批次工序日志缓存表,不允许重复再投,只能先把上一次投入物料结束,才能允许再投入;
* 8.拼接存储结构上传数据到批次工序日志缓存表原材料新增数据半成品更改半成品状态为0
* 9.半成品更改半成品状态为0表示进行设备投料
* 10.原材料数组数据写入缓存表
*/
private function do_set_material_input_by_item_code($post)
{
// 验证数据
$param = $post['param'];
try{
if(empty($param["workingsubclass"])){
throw new Exception("缺少workingsubclass参数");
}
if(empty($param["device_code"])){
throw new Exception("缺少device_code参数");
}
if(empty($param["in"]) || count($param["in"]) < 0){
throw new Exception("缺少in参数");
}
//1.获取请求体设备编码参数
$workingsubclass = $param["workingsubclass"];
$batch = $param["batch"];
$device_code = $param["device_code"];
$param_in = $param["in"];
// 缓存wip的item_id,为后续通过item_id获取batch
$wip_item_id = '';
// 判断in结构参数是否上传
foreach($param_in as $key=>$values){
if(empty($values["item_code"])){
throw new Exception("上传的in结构内".($key+1)."个item_code参数缺少数据");
}
if(empty($values["item_batch"])){
throw new Exception("上传的in结构内".($key+1)."个item_batch参数缺少数据");
}
if(!isset($values["item_quantity"]) && $values["item_quantity"]==""){
throw new Exception("上传的in结构内".($key+1)."个item_quantity参数缺少数据");
}
if(empty($values["item_id"])){
throw new Exception("上传的in结构内".($key+1)."个item_id参数缺少数据");
}
}
// 判断in结构内item_code、item_id是否有重复值同时忽略键值对[['key'=> 'item_id','value'=> '-1']
// 指定需要校验的键名
$check_keys = ['item_code','item_id'];
// 忽略条件,可能存在多个原材料投入所以需要忽略item=-1
$ignore_key_value_pairs = [
['key' => 'item_id', 'value' => '-1'],
];
$duplicates = findDuplicates($param_in, $check_keys, $ignore_key_value_pairs);
if (!empty($duplicates)) {
$duplicates_str = '上传的in结构内';
foreach($duplicates as $key=>$value){
$duplicates_str .= '参数'.$key.'存在重复值【'.implode("",$value).'】,';
}
throw new Exception($duplicates_str);
}
//2.判断工序单元、设备编码是存在?根据设备编码查询批次工序日志表,判断该设备是否已经存在生产中物料信息
$sql = "SELECT id FROM hf_mes_process_workingsubclass WHERE code='".$workingsubclass."'";
$ret = Db::fetch($sql);
if(empty($ret)){
throw new Exception("工序单元[$workingsubclass]不存在");
}
$sql = "SELECT id FROM hf_mes_device WHERE code='".$device_code."'";
$ret = Db::fetch($sql);
if(empty($ret)){
throw new Exception("设备编码[$device_code]不存在");
}
//判断是否存在已经上料的物料
$param_in_item_code = array_column($param_in,'item_code');
$sql = "SELECT item_code FROM hf_mes_bkv_batch_process_log WHERE item_code IN ('". implode("','",$param_in_item_code)."') AND status=0";
$ret = Db::fetchAll($sql);
if($ret){
throw new Exception('工序单元['.$workingsubclass.']下设备编码['.$device_code.']已经存在投入['. implode(',',array_column($ret,'item_code')).']物料生产批次信息');
}
//3.获取生产批次关联工艺流程工艺流程关联型号型号关联bombom关联bom关系bom关系关联物料信息
$subbatch = '';
$process_code = '';
$sql = "SELECT b.subbatch,c.process FROM hf_mes_production_planning_management_batch a
INNER JOIN hf_mes_production_planning_management_subbatch b ON a.id = b.batch_id
INNER JOIN hf_mes_technology_flow c ON b.flow_id = c.id
WHERE batch='".$batch."' ORDER BY b.id DESC LIMIT 1";
$ret = Db::fetch($sql);
if(empty($ret)){
throw new Exception("批次[".$batch."]不存在子批次信息,请检查批次是否已创建");
}
$subbatch = $ret['subbatch'];
// 拿到工序编码
foreach(json_decode($ret['process'],true) as $val){
if($val[4] == $workingsubclass){
$process_code = $val[2];
}
}
//4.通过以上关联信息查询物料编码
$sql = "SELECT e.code as bom_source_code,d.bom_source_id,e.name as bom_source_name,d.bom_id,f.code as bom_source_category_code FROM hf_mes_production_planning_management_batch a
INNER JOIN hf_mes_technology_flow b ON a.flow_id = b.id
INNER JOIN hf_mes_product_bom c ON b.product_model_id = c.product_model_id
INNER JOIN hf_mes_product_bom_relationship d ON c.id = d.bom_id
INNER JOIN hf_mes_product_bom_source e ON d.bom_source_id = e.id
INNER JOIN hf_mes_product_bom_source_category f ON e.bom_source_category_id = f.id
WHERE a.batch='".$batch."' AND d.in_workingsubclass='".$workingsubclass."'";
$ret = Db::fetchAll($sql);
if(empty($ret)){
throw new Exception('批次'.$batch.'下的工序单元'.$workingsubclass.'bom关系不存在in结构物料编码');
}
//4.1获取bom的物料编码
$bom_relationship_array = $ret;
$bom_relationship_item_code = array_column($ret,'bom_source_code');
//4.2获取param_in结构的物料编码
$param_in_item_code = array_column($param_in, 'item_code');
$missing_material = [];
foreach($param_in_item_code as $key=>$val){
if(!in_array($val,$bom_relationship_item_code) ){
$missing_material[]=$val;
}
}
if(count($missing_material)>0){
throw new Exception("上传param中in结构的item_code物料[".implode(',',$missing_material)."]不在本生产批次[$batch]的[$workingsubclass]工序中投料");
}
$uuid = [];
$wip_uuid = [];
$pid = [];
// 6、剔除上传param参数中in结构多余物料信息获取最终需要处理param_in
foreach($param_in as $key=>$value){
if(!in_array($value['item_code'],$bom_relationship_item_code)){
unset($param_in[$key]);
}else{
// 7、判断原材料或半成品是否已经投入批次工序日志缓存表不允许重复再投只能先把上一次投入物料结束才能允许再投入
// 如果是某一物料追加再投,用其他接口进行处理
#物料虚拟码当item_id=-1时代表原材料当item_id!=-1代表半成品wip
if($value['item_id'] == '-1'){
// 判断原材料是否已投入但还未结束,如果是则报警不给投入
$sql = "SELECT id FROM hf_mes_bkv_batch_process_log WHERE workingsubclass='".$workingsubclass."' AND item_device_code='".$device_code."' AND item_code='".$value['item_code']."' AND status=0 ORDER BY id DESC LIMIT 1";
$ret = Db::fetch($sql);
if(!empty($ret)){
throw new Exception("工序单元[$workingsubclass]下设备[$device_code]已有正在生产中的物料[".$value['item_code']."],如需投料请先结束生产中的物料");
}
}else{
// 判断半成品是否存在,且状态是未结束;如果不存在或状态是结束,则报警不给投入
$sql = "SELECT id,uuid,status FROM hf_mes_bkv_batch_process_log WHERE item_id='".$value['item_id']."' ORDER BY id DESC LIMIT 1";
$ret = Db::fetch($sql);
if(empty($ret)){
throw new Exception("物料[".$value['item_code']."]不存在半成品信息[".$value['item_id']."],请检查半成品信息是否存在批次工序日志缓存表");
}
if($ret['status'] != '-1' && $ret['status'] != '-2'){
throw new Exception("物料[".$value['item_code']."]对应半成品信息[".$value['item_id']."已投料或已生产完成,不允许重复投料,请检查物料状态信息");
}
$uuid = array_merge($uuid, json_decode($ret["uuid"]));
$wip_uuid[$value['item_id']] = $ret["uuid"];
$pid[$value['item_id']] = $ret["pid"];
}
// 把bom_source_id、bom_id、bom_source_category_code加进param为后续拼接insert sql做准备
foreach ($bom_relationship_array as $k => $v) {
if($v['bom_source_code'] == $value['item_code']){
$param_in[$key]['bom_source_id'] = $v['bom_source_id'];
$param_in[$key]['bom_source_name'] = $v['bom_source_name'];
$param_in[$key]['bom_id'] = $v['bom_id'];
$param_in[$key]['bom_source_category_code'] = $v['bom_source_category_code'];
}
}
// 冻结解冻列表设置拦截工序调用该接口就会反馈给class和classname到设备
if ($value['item_id'] != '-1') {
$freeze_data = Event::emit('FreezeInterceptionEvent.handle', [
'battery_id' => $v['item_id'],
'workingsubclass' => $ret['next_workingsubclass'],
'ropes_time' => date("Y-m-d H:i:s"),
]);
$h = $k + 1;
if (!empty($freeze_data)) {
// 检查 status 是否为 5 和 workingsubclass为ALLPROCESS
if ($freeze_data['status'] == 5 && $freeze_data['workingsubclass'] = 'ALLPROCESS') {
throw new Exception("(!!!紧急冻结全工序!!!)第[{$h}]位置半成品物料{$value['item_id']}在工序[{$workingsubclass}]已被标记[{$freeze_data['classname']}]冻结,需要设备排出或人工取出!!!");
// 检查 status 是否为 1
} elseif ($freeze_data['status'] == 1) {
throw new Exception("(!!!请先解冻再复投!!!)第[{$h}]位置半成品物料{$value['item_id']}在工序[{$workingsubclass}]已被标记[{$freeze_data['classname']}]冻结,需要设备排出或人工取出!!!");
}
// 默认冻结报错逻辑
throw new Exception("第[{$h}]位置半成品物料{$value['item_id']}在工序[{$workingsubclass}]已被标记[{$freeze_data['classname']}]冻结,需要设备排出或人工取出!!!");
}
}
}
}
// 如果存在半成品uuid则用半成品uuid否则默认为[]
$uuid = json_encode(array_unique($uuid));
$finish_time = date("Y-m-d H:i:s");
}catch(Exception $e){
throw new Exception($e->getMessage());
}
// 开启事务
Db::beginTrans();
try {
// 8、拼接存储结构上传数据到批次工序日志缓存表原材料新增数据半成品更改半成品状态为0
$material_insert_sql_head = 'INSERT INTO "hf_mes_bkv_batch_process_log" (item_id, batch, subbatch, workingsubclass, process_code, device_code, item_device_code, status, start_time, finish_time, bom_source_id, item_batch, item_quantity, pid, uuid, bom_id, item_code,item_name, lot, tray) VALUES' ;
$material_insert_sql_val = [];
foreach($param_in as $key=>$value){
$item_id = $value['item_id'];
if($value['item_id'] == '-1'){
// 生成MES内部item_id
$item_id = generate_material_item_id($workingsubclass, $value['bom_source_category_code'], $value['item_code']);
}
$material_insert_sql_val[] = sprintf(
"('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s','%s')",
$item_id,
$batch,
$subbatch,
$workingsubclass,
$process_code,
$device_code,
$device_code,
0,
$finish_time,
$finish_time,
$value['bom_source_id'],
$value['item_batch'],
$value['item_quantity'],
$value['item_id'] == '-1' ? -1 : $pid[$value['item_id']],
$value['item_id'] == '-1' ? $uuid : $wip_uuid[$value['item_id']],
$value['bom_id'],
$value['item_code'],
$value['bom_source_name'],
strtotime(date('Y-m-d H:i:s')),
'-1'
);
}
//10.原材料数组数据写入缓存表
$sql = $material_insert_sql_head . implode(',', $material_insert_sql_val);
$row = Db::query($sql);
if ($row === NULL) {
throw new Exception("原材料新增批次工序日志缓存表失败");
}
} catch (Exception $e) {
Db::rollBackTrans();
throw new Exception($e->getMessage());
}
Db::commitTrans();
return '';
}
}