#pragma warning (disable: 4996) 
/**
===============================================================================
Copyright (c) 2012-2024 CONNOR Ltd.
Unpublished - All Rights Reserved
===============================================================================
File description:


该类的主要功能：

===============================================================================
DATE           Name             Description of Change

2024-01-16	   何贞泉			modify  新增总工时字段，修改传递仓库逻辑，修改材料用料取值方法

===============================================================================
*/
#include "connor_erp_mes.h"
#include "tinyxml.h"
#include "tinystr.h"
#include "ics/ics.h"
#include "libxl.h"
#include <tccore/aom_prop.h>
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental\filesystem>

#define ITK_err 919031
#define TYPE_REV "ItemRevision"

#define PREF_ERP_WL "Connor_ERP_wl"
#define PREF_ERP_WL_GI "Connor_ERP_wl_gi"
#define PREF_ERP_WL_PLI "Connor_ERP_wl_pli"

#define PREF_ERP_BOM "Connor_ERP_BOM"
#define PREF_ERP_BOM_PARENT "Connor_ERP_BOM_parent"
#define PREF_ERP_BOM_CHILD "Connor_ERP_BOM_child"
#define PREF_ERP_BOM_PARENT_ZZ "Connor_ERP_BOM_parent_zizhi"
#define PREF_ERP_BOM_CHILD_ZZ "Connor_ERP_BOM_child_zizhi"
#define QFgxtouliao_type "QFgxtouliao_type"
#define QF_JYXtype "QF_JYXtype"
#define QF_MES_TYPE "QF_MES_TYPE"

#define PREF_MES_WL "Connor_MES_wl"
#define PREF_MES_WL_DATA "Connor_MES_wl_data"

#define PREF_MES_BOM "Connor_MES_BOP"
#define PREF_MES_BOM_PARENT "Connor_MES_BOP_parent"
#define PREF_MES_BOM_CHILD1 "Connor_MES_BOP_opstaiton_detail"
#define PREF_MES_BOM_CHILD2 "Connor_MES_BOP_opresource_detail"
#define PREF_MES_BOM_CHILD3 "Connor_MES_BOP_opsup_detail"
#define PREF_MES_BOM_CHILD4 "Connor_MES_BOP_material_detail"
#define  IMAN_MASTER_FORM_REV "IMAN_master_form_rev"
#define  IMAN_MANIFESTATION "IMAN_manifestation"
#define  IMAN_RENDERING "IMAN_Rendering"
#define  IMAN_SPECIFICATION "IMAN_specification"
#define  PDF_REFERENCE "PDF_Reference"
#define  JTPART "JTPART"
#define  MESTATIONREVISION "MEStationRevision"
#define  OBJECT_NAME "object_name"
#define  BL_REVISION "bl_revision"
#define  QF8SFWW "QF8sfww"
#define  ITEM_ID "item_id"
#define  QF8CLYL "qf8clyl"
#define  QF8GXBM "qf8gxbm"
#define  QF8LAIYUAN "qf8laiyuan"
#define  ZIZHI "自制"
#define  XUSHE "虚设"
#define  CK1001 "1001"
#define  CK1002 "1002"
#define  CK1003 "1003"
#define  WAIGOU "外购"
#define  QF8JGGS "qf8jggs"
#define  QF8ZBGS "qf8zbgs"
#define  QF8ZGS "qf8zgs"
#define  QF8ZGJ "qf8zgj"
#define  QF8ZXBM "qf8gxbm"
#define  OBJECT_NAME "object_name"
#define  OBJECT_TYPE "object_type"
#define  BL_LINE_OBJECT "bl_line_object"
#define  STRUCTURE_REVISIONS "structure_revisions"
#define QF8GXREVISION "QF8gxRevision"
#define QF8GYREVISION "QF8gyRevision"

#define  QF8BOPRELATION "QF8BOPrelation"
#define  STANDARD_MACHINE_HOUR "STANDARD_MACHINE_HOUR"
#define  DYNAMIC_LEAD_TIME_M "DYNAMIC_LEAD_TIME_M"
#define  CE2_CUR_LEVEL "CE2_CUR_LEVEL"
#define  INBOUND_WAREHOUSE_CODE "INBOUND_WAREHOUSE_CODE"
#define  OUTBOUND_WAREHOUSE_CODE "OUTBOUND_WAREHOUSE_CODE"

#define PREF_FTP_PATH "QF_PLM_MES_FILE"
#include <codecvt>
#include "crossStub.h"
#include "IntegrationEntrySoapBinding.nsmap"
#include "plugin.h"
#include "md5.h"
#include "crossH.h"

#include <stdlib.h> 
#include <stdio.h>
#include <windows.h>
#include <memory.h>
#include <sstream>

#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;

using namespace libxl;
string ftp_dir;
string local_dir;

string str2md5(string str) {
	return MD5(str).toStr();
}

struct ProductInfo {
	std::string wl_id;        // 产品编号
	std::string wl_rev_id;   // 产品版本
	std::string qf8gxbm;             // 操作编号
	std::string SUBOPSEQUENCE;    // 子操作序列
};


typedef struct {
	bool isDisplayValue;
	string type;
	string erp;
	//string child_line_type;
	string prop_loc;
	string prop_name;
	string sub_prop_name;
	string text_value;
	string splitter;
	int split_index;
	map<string, string> replace_map;
} ERP_Config;

bool findUnitPrice(const std::vector<std::string>& pref_unitPrice, const char* id, const char* name, double& unitPrice) {
	std::string idStr(id);
	std::string nameStr(name);

	for (const auto& entry : pref_unitPrice) {
		size_t idPos = entry.find(idStr);
		size_t namePos = entry.find(nameStr);

		if (idPos != std::string::npos && namePos != std::string::npos && idPos < namePos) {
			size_t unitPricePos = entry.find('-', namePos + nameStr.length());
			if (unitPricePos != std::string::npos) {
				std::string unitPriceStr = entry.substr(unitPricePos + 1);

				std::stringstream ss(unitPriceStr);
				if (ss >> unitPrice) {
					return true;
				}
			}
		}
	}

	return false; // Not found or conversion failed
}



void removeLogsFile() {
	fs::path filePath("D:\\logs.txt");
	if (fs::exists(filePath)) {
		fs::remove(filePath);
		std::cout << "文件已删除: " << filePath << std::endl;
	}
	else {
		std::cout << "文件不存在: " << filePath << std::endl;
	}
}

// 获取当前时间的字符串表示
std::string getCurrentTime() {
	std::time_t now = std::time(nullptr);
	std::tm* now_tm = std::localtime(&now);
	char buffer[20];
	std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", now_tm);
	return std::string(buffer);
}

// 写入日志信息到文件
void writeLog(const std::string& message) {
	std::ofstream logFile("D:\\logs.txt", std::ios_base::app); // 以追加模式打开文件
	if (logFile.is_open()) {
		std::string logEntry = "[" + getCurrentTime() + "] " + message + "\n";
		logFile << logEntry;
		logFile.close();
	}
	else {
		std::cerr << "无法打开日志文件" << std::endl;
	}
}

int get_child_line(tag_t pline, string cline_types, vector<tag_t>& cline) {
	int ifail = ITK_ok, child_cnt = 0;
	tag_t* child_lines = NULL;
	bool is_exclude = false;
	vector<string> types;
	char* object_type = NULL, * object_string = NULL;
	if (cline_types.find('!') == 0) {
		is_exclude = true;
		Split(cline_types.substr(1), ";", types);
	}
	else {
		Split(cline_types, ";", types);
	}
	if (types.size() == 0 || pline == NULLTAG) {
		goto end;
	}
	ITKCALL(ifail = BOM_line_ask_child_lines(pline, &child_cnt, &child_lines));
	ITKCALL(ifail = AOM_ask_value_string(pline, "object_string", &object_string));
	//WriteLog(debug, "提取子行[%d]：%s 类型[%s]",child_cnt, object_string, cline_types.c_str());
	if (child_cnt == 0) {
		goto end;
	}
	for (int i = 0; i < child_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		tag_t cline_tag = child_lines[i], citem_tag = NULLTAG;
		ITKCALL(ifail = AOM_ask_value_tag(cline_tag, "bl_item", &citem_tag));
		if (ifail != ITK_ok) {
			continue;
		}
		ITKCALL(ifail = AOM_ask_value_string(citem_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(citem_tag, &object_type));
		//WriteLog(debug, "%d. %s 类型[%s]", i+1, object_string, object_type);
		if (inArray(types, object_type)) {
			if (!is_exclude) {
				cline.push_back(cline_tag);
			}
		}
		else {
			if (is_exclude) {
				cline.push_back(cline_tag);
			}
		}
	}
	//WriteLog(debug, "提取子行完成");
end:
	DOFREE(object_string);
	DOFREE(object_type);
	DOFREE(child_lines);
	return ifail;
}
int set_xml_value(TiXmlElement* node, const char* value) {
	TiXmlText* txt = new TiXmlText(G2U(value));
	node->LinkEndChild(txt);
	return ITK_ok;
}

/*
*获取当前时间
*/
void get_current_time(char* date_time)
{
	time_t the_time;
	struct tm* time_ptr;
	char* time_format = "%Y-%m-%d-%H-%M-%S";

	the_time = time((time_t*)0);
	time_ptr = localtime(&the_time);
	strftime(date_time, 128, time_format, time_ptr);
}
void WcharToString(string& szDst, const wchar_t* wchar)
{
	DWORD dwNum = WideCharToMultiByte(CP_OEMCP, NULL, wchar, -1, NULL, 0, NULL, FALSE);
	char* psText;
	psText = new char[dwNum];
	WideCharToMultiByte(CP_OEMCP, NULL, wchar, -1, psText, dwNum, NULL, FALSE);
	szDst = psText;
	delete[]psText;
}
//不要忘记在使用完wchar_t*后delete[]释放内存
wchar_t* StringToWchar(const string& pKey)
{
	const char* pCStrKey = pKey.c_str();
	//第一次调用返回转换后的字符串长度，用于确认为wchar_t*开辟多大的内存空间
	int pSize = MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, NULL, 0);
	wchar_t* pWCStrKey = new wchar_t[pSize];
	//第二次调用将单字节字符串转换成双字节字符串
	MultiByteToWideChar(CP_OEMCP, 0, pCStrKey, strlen(pCStrKey) + 1, pWCStrKey, pSize);
	return pWCStrKey;
}
std::string checkColumns(const std::string& F, const std::string& G) {
	bool isFEmpty = F.empty(); // 检查 F 列是否为空
	bool isGEmpty = G.empty(); // 检查 G 列是否为空

	if (!isFEmpty && !isGEmpty) {
		return "0"; // F 列和 G 列均不为空
	}
	else if (!isFEmpty && isGEmpty) {
		return "1"; // F 列不为空，G 列为空
	}
	else if (isFEmpty && !isGEmpty) {
		return "2"; // F 列为空，G 列不为空
	}
	else {
		return "3"; // F 列和 G 列均为空
	}
}
wstring StringToWString(const string s)
{
	setlocale(LC_ALL, "chs");
	const char* _Source = s.c_str();
	size_t _Dsize = s.size() + 1;
	wchar_t* _Dest = new wchar_t[_Dsize];
	wmemset(_Dest, 0, _Dsize);
	mbstowcs(_Dest, _Source, _Dsize);
	wstring result = _Dest;
	delete[]_Dest;
	setlocale(LC_ALL, "C");
	return result;
}

/*
*读取excel
*/
void readExcel(TiXmlElement* qcitem_detail, int& seqNum, tag_t excelTag, string ext, map<string, string>& QF_JYXtype_map, const ProductInfo& product) {
	cout << "开始读取excel" << endl;
	char current_date_time_str[128 + 1] = { "\0" };
	get_current_time(current_date_time_str);
	string time_now = current_date_time_str;
	int ifail = 0;
	//获取系统temp目录
	char* temp_dir = getenv("temp");//获得缓存路径
	string output_file_pdf1_temp;
	output_file_pdf1_temp.append(temp_dir)
		.append("\\jyxxb")
		.append(time_now.c_str())
		.append(".xlsx");
	//cout << "文件路径----------" << time_now << endl;
	if (access(output_file_pdf1_temp.c_str(), 0) == 0) {//存在

		remove(output_file_pdf1_temp.c_str());
	}
	ifail = AE_export_named_ref(excelTag, "excel", output_file_pdf1_temp.c_str());

	cout << "开始读取表格" << endl;
	
	Book* book = nullptr;
	if (strcmp(ext.c_str(), "xlsx") == 0)
	{
		book = xlCreateXMLBook();
	}
	book->setKey(L"Halil Kural", L"windows-2421220b07c2e10a6eb96768a2p7r6gc");//如果购买了该库，则设置相应的key，若没有购买，则不用这行
	if (book->load(StringToWString(output_file_pdf1_temp).c_str()))
	{
		Sheet* sheet0 = book->getSheet(0);//获得第1个sheet页
		if (sheet0) {
			cout << "开始读取数据页面" << endl;
			int firstRow = sheet0->firstRow();
			int lastRow = sheet0->lastRow();
			int firstCol = sheet0->firstCol();
			int lastCol = sheet0->lastCol();
			for (int row = 3; row < lastRow; row++)
			{
				string seq = "";//序号A
				string category = "";//类别C
				string type = "";//类型D
				string Standard_value = "";//数据内容E
				string max_value = "";//辅助内容1F
				string min_value = "";//辅助内容2G
				string QcDescription = "";//辅助内容3H
				string Reason = "";//辅助内容3H
				string SpecLineType = "";
				string Unit = "";
				string USLCSLSL = "";

				for (int c = firstCol; c < lastCol; c++)
				{
					if (c == 1) {
						continue;
					}
					if (c > 7) {
						break;
					}

					double		db = 0;
					const wchar_t* wstr;
					char* s = NULL;
					CellType t = sheet0->cellType(row, c);
					string value = "";
					switch (t)
					{
					case libxl::CELLTYPE_EMPTY:
						break;
					case libxl::CELLTYPE_NUMBER:
						db = sheet0->readNum(row, c);
						std::cout << db << "\n";
						char str[20];

						sprintf(str, "%f", db);  // 使用%f占位符将double转
						if (c == 0) {
							seq = str;
						}
						else if (c == 2) {
							category = str;
						}
						else if (c == 3) {
							type = str;
						}
						else if (c == 4) {
							Standard_value = str;
						}
						else if (c == 5) {
							max_value = str;
						}
						else if (c == 6) {
							min_value = str;
						}
						else if (c == 7) {
							QcDescription = str;
						}

						break;
					case libxl::CELLTYPE_STRING:
						wstr = sheet0->readStr(row, c);
						if (wstr != NULL) {
							WcharToString(value, wstr);
							std::cout << value << "\n";
						}
						if (c == 0) {
							seq = value;
						}
						else if (c == 2) {
							category = value;
						}
						else if (c == 3) {
							type = value;
						}
						else if (c == 4) {
							Standard_value = value;
						}
						else if (c == 5) {
							max_value = value;
						}
						else if (c == 6) {
							min_value = value;
						}
						else if (c == 7) {
							QcDescription = value;
						}
						break;
						/*	case libxl::CELLTYPE_BOOLEAN:
								break;
							case libxl::CELLTYPE_BLANK:
								break;
							case libxl::CELLTYPE_ERROR:
								break;*/
					default:
						break;
					}
				}
				cout << "seq" << seq << endl;

				if (tc_strcmp(seq.c_str(), "") == 0) {
					cout << "空序号，跳过" << endl;
					continue;
				}

				string Checktype;//检验类别
				string Qcitem_no = seq;//检验项目编号
				string Qcitem_name = category + type;//检验项目名称

				auto it = QF_JYXtype_map.find(Qcitem_name);
				if (it != QF_JYXtype_map.end()) {
					Checktype = it->second;
				}
				else {
					WriteLog(debug, "品管项目名称 ：【%s】在首选项【%s】中未找到对应值", Qcitem_name.c_str(), QF_JYXtype);
					continue;
				}
				//if (tc_strcmp("计量", Checktype.c_str()) == 0) {
				if (tc_strcmp("1", Checktype.c_str()) == 0) {
					Reason = "1";
					Unit = "mm";
					double 	standardValueDouble = 0;
					logical l1 = true;
					logical l2 = true;
					logical l3 = true;
					try
					{
						standardValueDouble = atof(Standard_value.c_str());
					}
					catch (const std::exception&)
					{
						l1 = false;
					}
					double maxValueDouble = 0;
					try
					{
						maxValueDouble = atof(max_value.c_str());
					}
					catch (const std::exception&)
					{
						l2 = false;
					}
					double minValueDouble = 0;
					try
					{
						minValueDouble = atof(min_value.c_str());
					}
					catch (const std::exception&)
					{
						l3 = false;
					}
					char standardValueStr[20] = "";
					char maxValueStr[20] = "";
					char minValueStr[20] = "";
					if (l1 == true) {
						if (max_value.empty() && min_value.empty()) {
							sprintf(standardValueStr, "%.2f", standardValueDouble);
						}
						else {
							maxValueDouble = standardValueDouble + maxValueDouble;
							minValueDouble = standardValueDouble + minValueDouble;
							sprintf(standardValueStr, "%.2f~%.2f", minValueDouble, maxValueDouble);
						}
						WriteLog(debug, "USLCSLSL ==>%s", standardValueStr);
						USLCSLSL = standardValueStr;
					}
					SpecLineType=checkColumns(max_value, min_value);

				}
				//else 	if (tc_strcmp("计数", Checktype.c_str()) == 0) {
				else 	if (tc_strcmp("0", Checktype.c_str()) == 0) {
					USLCSLSL = "";
				}
				std::cout << "序号A (seq): " << seq << std::endl;
				std::cout << "类别C (category): " << category << std::endl;
				std::cout << "类型D (type): " << type << std::endl;
				std::cout << "数据内容E (Standard_value): " << Standard_value << std::endl;
				std::cout << "辅助内容1F (max_value): " << max_value << std::endl;
				std::cout << "辅助内容2G (min_value): " << min_value << std::endl;
				std::cout << "辅助内容3H (QcDescription): " << QcDescription << std::endl;
				std::cout << "辅助内容3H (Reason): " << Reason << std::endl;
				std::cout << "SpecLineType: " << SpecLineType << std::endl;
				std::cout << "Unit: " << Unit << std::endl;
				std::cout << "USLCSLSL: " << USLCSLSL << std::endl;
				cout << "开始输出内容" << endl;
				//char numStr[25];
				//itoa(seqNum, numStr, 10);
				TiXmlElement* childGxRow = new TiXmlElement("row");
				childGxRow->SetAttribute("seq", seqNum);
				qcitem_detail->LinkEndChild(childGxRow);

				TiXmlElement* seqField = new TiXmlElement("field");
				seqField->SetAttribute("name", "op_no");
				seqField->SetAttribute("type", "string");
				set_xml_value(seqField, product.qf8gxbm.c_str());
				childGxRow->LinkEndChild(seqField);
				TiXmlElement* QCOBJECTTYPE_Field = new TiXmlElement("field");
				QCOBJECTTYPE_Field->SetAttribute("name", "QCOBJECTTYPE");
				QCOBJECTTYPE_Field->SetAttribute("type", "string");
				set_xml_value(QCOBJECTTYPE_Field, "1");
				childGxRow->LinkEndChild(QCOBJECTTYPE_Field);
				TiXmlElement* QCOBJECTNO_Field = new TiXmlElement("field");
				QCOBJECTNO_Field->SetAttribute("name", "QCOBJECTNO");
				QCOBJECTNO_Field->SetAttribute("type", "string");
				set_xml_value(QCOBJECTNO_Field, product.wl_id.c_str());
				childGxRow->LinkEndChild(QCOBJECTNO_Field);
				TiXmlElement* ItemType_Field = new TiXmlElement("field");
				ItemType_Field->SetAttribute("name", "ItemType");
				ItemType_Field->SetAttribute("type", "numeric");
				set_xml_value(ItemType_Field, "1");
				childGxRow->LinkEndChild(ItemType_Field);
				string EXECUTEORDER_str=to_string(row - 2);
				TiXmlElement* EXECUTEORDERField = new TiXmlElement("field");
				EXECUTEORDERField->SetAttribute("name", "EXECUTEORDER");
				EXECUTEORDERField->SetAttribute("type", "string");
				set_xml_value(EXECUTEORDERField, EXECUTEORDER_str.c_str());
				childGxRow->LinkEndChild(EXECUTEORDERField);
				TiXmlElement* QCItemNo_Field = new TiXmlElement("field");
				QCItemNo_Field->SetAttribute("name", "QCItemNo");
				QCItemNo_Field->SetAttribute("type", "string");
				set_xml_value(QCItemNo_Field, Qcitem_no.c_str());
				childGxRow->LinkEndChild(QCItemNo_Field);
				TiXmlElement* QCItemName_Field = new TiXmlElement("field");
				QCItemName_Field->SetAttribute("name", "QCItemName");
				QCItemName_Field->SetAttribute("type", "string");
				set_xml_value(QCItemName_Field, Qcitem_name.c_str());
				childGxRow->LinkEndChild(QCItemName_Field);
				TiXmlElement* QCITEMTYPE_Field = new TiXmlElement("field");
				QCITEMTYPE_Field->SetAttribute("name", "QCITEMTYPE");
				QCITEMTYPE_Field->SetAttribute("type", "string");
				set_xml_value(QCITEMTYPE_Field, Checktype.c_str());
				childGxRow->LinkEndChild(QCITEMTYPE_Field);
				TiXmlElement* USLCSLSL_Field = new TiXmlElement("field");
				USLCSLSL_Field->SetAttribute("name", "USLCSLSL");
				USLCSLSL_Field->SetAttribute("type", "string");
				set_xml_value(USLCSLSL_Field, USLCSLSL.c_str());
				childGxRow->LinkEndChild(USLCSLSL_Field);
				TiXmlElement* Reason_Field = new TiXmlElement("field");
				Reason_Field->SetAttribute("name", "Reason");
				Reason_Field->SetAttribute("type", "string");
				set_xml_value(Reason_Field, Reason.c_str());
				childGxRow->LinkEndChild(Reason_Field);
				TiXmlElement* PlanNo_Field = new TiXmlElement("field");
				PlanNo_Field->SetAttribute("name", "PlanNo");
				PlanNo_Field->SetAttribute("type", "string");
				set_xml_value(PlanNo_Field, "GDCY-3");
				childGxRow->LinkEndChild(PlanNo_Field);
				TiXmlElement* Description_Field = new TiXmlElement("field");
				Description_Field->SetAttribute("name", "Description");
				Description_Field->SetAttribute("type", "string");
				set_xml_value(Description_Field, QcDescription.c_str());
				childGxRow->LinkEndChild(Description_Field);
				TiXmlElement* SampleSize_Field = new TiXmlElement("field");
				SampleSize_Field->SetAttribute("name", "SampleSize");
				SampleSize_Field->SetAttribute("type", "string");
				set_xml_value(SampleSize_Field, "10");
				childGxRow->LinkEndChild(SampleSize_Field);
				TiXmlElement* ChartType_Field = new TiXmlElement("field");
				ChartType_Field->SetAttribute("name", "ChartType");
				ChartType_Field->SetAttribute("type", "string");
				set_xml_value(ChartType_Field, "0");
				childGxRow->LinkEndChild(ChartType_Field);
				TiXmlElement* LimitLotNum_Field = new TiXmlElement("field");
				LimitLotNum_Field->SetAttribute("name", "LimitLotNum");
				LimitLotNum_Field->SetAttribute("type", "string");
				set_xml_value(LimitLotNum_Field, "30");
				childGxRow->LinkEndChild(LimitLotNum_Field);
				TiXmlElement* SpecLineType_Field = new TiXmlElement("field");
				SpecLineType_Field->SetAttribute("name", "SpecLineType");
				SpecLineType_Field->SetAttribute("type", "string");
				set_xml_value(SpecLineType_Field, SpecLineType.c_str());
				childGxRow->LinkEndChild(SpecLineType_Field);
				TiXmlElement* InspectionTools_Field = new TiXmlElement("field");
				InspectionTools_Field->SetAttribute("name", "InspectionTools");
				InspectionTools_Field->SetAttribute("type", "string");
				set_xml_value(InspectionTools_Field, "");
				childGxRow->LinkEndChild(InspectionTools_Field);
				TiXmlElement* Unit_Field = new TiXmlElement("field");
				Unit_Field->SetAttribute("name", "Unit");
				Unit_Field->SetAttribute("type", "string");
				set_xml_value(Unit_Field, Unit.c_str());
				childGxRow->LinkEndChild(Unit_Field);
				TiXmlElement* OnlineFactorNo_Field = new TiXmlElement("field");
				OnlineFactorNo_Field->SetAttribute("name", "OnlineFactorNo");
				OnlineFactorNo_Field->SetAttribute("type", "string");
				set_xml_value(OnlineFactorNo_Field, "OP");
				childGxRow->LinkEndChild(OnlineFactorNo_Field);
				seqNum = seqNum + 1;
			}

		}
	}
	book->release();//释放对象！！！！
	cout << "关闭excel" << endl;
}
int getPropertyStringValue(tag_t objectTag, const char* propName, string subPropName, string& propValue, bool isDisplayValue, string splitter, int split_index) {
	int ifail = ITK_ok;
	// 属性值得到的类型
	writeLog("属性值得到的类型:");
	writeLog(propName);
	PROP_value_type_t propertyType;
	char* propertyTypeName = NULL,
		* cValue = NULL,
		* tempValue1 = NULL,
		* tempValue2 = NULL,
		* tempValue3 = NULL;

	if (objectTag == NULLTAG || tc_strlen(propName) < 1) {
		return ifail;
	}
	if (strcmp("ICS", propName) == 0) {// 获取分类ID
		tag_t ico_tag = NULLTAG, cls_tag = NULLTAG;
		ITKCALL(ifail = ICS_ask_classification_object(objectTag, &ico_tag));
		if (ico_tag != NULLTAG) {
			ITKCALL(ifail = ICS_ask_class_of_classification_obj(ico_tag, &cls_tag));
			if (cls_tag != NULLTAG) {
				char* cls_id = NULL, * cls_name = NULL;
				ITKCALL(ifail = ICS_ask_id_name(cls_tag, &cls_id, &cls_name));
				if (ifail == ITK_ok) {
					propValue.append(cls_id);
				}
				DOFREE(cls_id);
				DOFREE(cls_name);
			}
		}
	}
	else if (strcmp("ICSORTYPE", propName) == 0) {// 获取分类ID 如果无分类则类型名称
		tag_t ico_tag = NULLTAG, cls_tag = NULLTAG;
		bool has_ico = false;
		ITKCALL(ifail = ICS_ask_classification_object(objectTag, &ico_tag));
		if (ico_tag != NULLTAG) {
			ITKCALL(ifail = ICS_ask_class_of_classification_obj(ico_tag, &cls_tag));
			if (cls_tag != NULLTAG) {
				char* cls_id = NULL, * cls_name = NULL;
				ITKCALL(ifail = ICS_ask_id_name(cls_tag, &cls_id, &cls_name));
				if (ifail == ITK_ok) {
					propValue.append(cls_id);
					has_ico = true;
				}
				DOFREE(cls_id);
				DOFREE(cls_name);
			}
		}
		if (!has_ico) {
			char* object_type = NULL;
			ITKCALL(ifail = WSOM_ask_object_type2(objectTag, &object_type));
			if (ifail == ITK_ok) {
				propValue.append(object_type);
			}
			DOFREE(object_type);
		}
	}
	else if (strcmp("ICSNAME", propName) == 0) {// 获取分类名称
		tag_t ico_tag = NULLTAG, cls_tag = NULLTAG;
		ITKCALL(ifail = ICS_ask_classification_object(objectTag, &ico_tag));
		if (ico_tag != NULLTAG) {
			ITKCALL(ifail = ICS_ask_class_of_classification_obj(ico_tag, &cls_tag));
			if (cls_tag != NULLTAG) {
				char* cls_id = NULL, * cls_name = NULL;
				ITKCALL(ifail = ICS_ask_id_name(cls_tag, &cls_id, &cls_name));
				if (ifail == ITK_ok) {
					propValue.append(cls_name);
				}
				DOFREE(cls_id);
				DOFREE(cls_name);
			}
		}
	}
	else if (strcmp("UID", propName) == 0) { // 获取UID
		char* uid = NULL;
		ITK__convert_tag_to_uid(objectTag, &uid);
		propValue.append(uid);
		DOFREE(uid);
	}
	else if (strcmp("GCBH", propName) == 0) { // 工厂编号
		int child_cnt = 0;
		tag_t* child_tags = NULL;
		char* object_type = NULL, * object_string = NULL;
		bool addSp = false;
		ITKCALL(ifail = AOM_ask_value_string(objectTag, "object_string", &object_string));
		ITKCALL(ifail = AOM_ask_value_tags(objectTag, "IMAN_MEWorkArea", &child_cnt, &child_tags));
		WriteLog(debug, "Check WorkArea: %s [%d]", object_string, child_cnt);
		for (int i = 0; i < child_cnt; i++) {
			DOFREE(object_string);
			DOFREE(object_type);
			tag_t child_tag = child_tags[i];
			ITKCALL(ifail = AOM_ask_value_string(child_tag, "object_string", &object_string));
			ITKCALL(ifail = WSOM_ask_object_type2(child_tag, &object_type));
			WriteLog(debug, ">> WorkArea: %s [%s]", object_string, object_type);
			if (strcmp("MEPlant", object_type) == 0) {
				//if (strcmp("QF8gc", object_type) == 0) {
				tag_t rev_tag = NULLTAG;
				int form_cnt = 0;
				tag_t* form_tags = NULL;
				ITKCALL(ifail = ITEM_ask_latest_rev(child_tag, &rev_tag));
				if (rev_tag == NULLTAG) {
					WriteLog(debug, ">> No Latest Rev");
					continue;
				}
				ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
				if (form_cnt > 0) {
					char* value = NULL;
					tag_t form_tag = form_tags[0];
					ITKCALL(ifail = AOM_ask_value_string(form_tag, "qf8gcbh", &value));
					if (ifail == ITK_ok) {
						WriteLog(debug, "qf8gcbh = [%s]", value);
						if (addSp) {
							propValue.append(";");
						}
						else {
							addSp = true;
						}
						propValue.append(value);
					}
					DOFREE(value);
				}
				DOFREE(form_tags);
			}
			else if (strcmp("MEPlantRevision", object_type) == 0) {
				//if (strcmp("QF8gc", object_type) == 0) {
				tag_t rev_tag = child_tag;
				int form_cnt = 0;
				tag_t* form_tags = NULL;
				ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
				if (form_cnt > 0) {
					char* value = NULL;
					tag_t form_tag = form_tags[0];
					ITKCALL(ifail = AOM_ask_value_string(form_tag, "qf8gcbh", &value));
					if (ifail == ITK_ok) {
						WriteLog(debug, "qf8gcbh = [%s]", value);
						if (addSp) {
							propValue.append(";");
						}
						else {
							addSp = true;
						}
						propValue.append(value);
					}
					DOFREE(value);
				}
				DOFREE(form_tags);
			}
		}
		DOFREE(object_string);
		DOFREE(object_type);
		DOFREE(child_tags);
	}
	else if (strcmp("GWBM", propName) == 0) { // 工位编码
		int child_cnt = 0;
		char* object_type = NULL, * object_string = NULL;
		bool addSp = false;
		vector<tag_t> targetline_tags;
		get_child_line(objectTag, "MEStation", targetline_tags);
		child_cnt = targetline_tags.size();
		ITKCALL(ifail = AOM_ask_value_string(objectTag, "object_string", &object_string));
		WriteLog(debug, "Check MEStation: %s [%d]", object_string, child_cnt);
		for (int i = 0; i < child_cnt; i++) {
			DOFREE(object_string);
			DOFREE(object_type);
			tag_t child_line_tag = targetline_tags[i];
			tag_t rev_tag = NULLTAG;
			int form_cnt = 0;
			tag_t* form_tags = NULL;
			ITKCALL(ifail = AOM_ask_value_string(child_line_tag, "object_string", &object_string));
			WriteLog(debug, ">> MEStation: %s", object_string);
			ITKCALL(ifail = AOM_ask_value_tag(child_line_tag, "bl_revision", &rev_tag));
			if (rev_tag == NULLTAG) {
				WriteLog(debug, ">> revision not found");
				continue;
			}
			ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
			if (form_cnt > 0) {
				char* value = NULL;
				tag_t form_tag = form_tags[0];
				ITKCALL(ifail = AOM_ask_value_string(form_tag, "qf8gwbm", &value));
				if (ifail == ITK_ok) {
					WriteLog(debug, "qf8gwbm = [%s]", value);
					if (addSp) {
						propValue.append(";");
					}
					else {
						addSp = true;
					}
					propValue.append(value);
				}
				DOFREE(value);
			}
			DOFREE(form_tags);
		}
		DOFREE(object_string);
		DOFREE(object_type);
		targetline_tags.clear();
	}
	else if (strcmp("GWID", propName) == 0) { // 工位ID
		int child_cnt = 0;
		char* object_type = NULL, * object_string = NULL;
		bool addSp = false;
		vector<tag_t> targetline_tags;
		get_child_line(objectTag, "MEStation", targetline_tags);
		child_cnt = targetline_tags.size();
		ITKCALL(ifail = AOM_ask_value_string(objectTag, "object_string", &object_string));
		WriteLog(debug, "Check MEStation: %s [%d]", object_string, child_cnt);
		for (int i = 0; i < child_cnt; i++) {
			DOFREE(object_string);
			DOFREE(object_type);
			tag_t child_line_tag = targetline_tags[i];
			tag_t item_tag = NULLTAG;
			char* item_id = NULL;
			ITKCALL(ifail = AOM_ask_value_string(child_line_tag, "object_string", &object_string));
			WriteLog(debug, ">> MEStation: %s", object_string);
			ITKCALL(ifail = AOM_ask_value_tag(child_line_tag, "bl_item", &item_tag));
			if (item_tag == NULLTAG) {
				WriteLog(debug, ">> item not found");
				continue;
			}
			ITKCALL(ifail = AOM_ask_value_string(item_tag, "item_id", &item_id));
			if (ifail == ITK_ok) {
				WriteLog(debug, "object_name = [%s]", item_id);
				if (addSp) {
					propValue.append(";");
				}
				else {
					addSp = true;
				}
				propValue.append(item_id);
			}
			DOFREE(item_id);
		}
		DOFREE(object_string);
		DOFREE(object_type);
		targetline_tags.clear();
	}
	else if (strcmp("GWMC", propName) == 0) { // 工位名称
		int child_cnt = 0;
		char* object_type = NULL, * object_string = NULL;
		bool addSp = false;
		vector<tag_t> targetline_tags;
		get_child_line(objectTag, "MEStation", targetline_tags);
		child_cnt = targetline_tags.size();
		ITKCALL(ifail = AOM_ask_value_string(objectTag, "object_string", &object_string));
		WriteLog(debug, "Check MEStation: %s [%d]", object_string, child_cnt);
		for (int i = 0; i < child_cnt; i++) {
			DOFREE(object_string);
			DOFREE(object_type);
			tag_t child_line_tag = targetline_tags[i];
			tag_t item_tag = NULLTAG;
			char* object_name = NULL;
			ITKCALL(ifail = AOM_ask_value_string(child_line_tag, "object_string", &object_string));
			WriteLog(debug, ">> MEStation: %s", object_string);
			ITKCALL(ifail = AOM_ask_value_tag(child_line_tag, "bl_item", &item_tag));
			if (item_tag == NULLTAG) {
				WriteLog(debug, ">> item not found");
				continue;
			}
			ITKCALL(ifail = AOM_ask_value_string(item_tag, "object_name", &object_name));
			if (ifail == ITK_ok) {
				WriteLog(debug, "object_name = [%s]", object_name);
				if (addSp) {
					propValue.append(";");
				}
				else {
					addSp = true;
				}
				propValue.append(object_name);
			}
			DOFREE(object_name);
		}
		DOFREE(object_string);
		DOFREE(object_type);
		targetline_tags.clear();
	}
	else {
		// 判断输入的属性名是什么类型的值
		ITKCALL(ifail = AOM_ask_value_type(objectTag, propName, &propertyType, &propertyTypeName));
		if (ifail != ITK_ok) {
			return ifail;
		}
		switch (propertyType) {
		case PROP_string:
			if (isDisplayValue) {
				ITKCALL(ifail = AOM_UIF_ask_value(objectTag, propName, &cValue));
			}
			else {
				writeLog("==> 当前获取属性");
				writeLog(propName);
				// 图号&型号规格&材料 ==> 三个字段合并后传递，特殊处理
				if (strcmp(propName, "qf8xhgg") == 0) {
					writeLog("----获取图号&型号规格&材料result");
					ITKCALL(ifail = AOM_ask_value_string(objectTag, "qf8th", &tempValue1));
					ITKCALL(ifail = AOM_ask_value_string(objectTag, propName, &tempValue2));
					ITKCALL(ifail = AOM_ask_value_string(objectTag, "qf8cl", &tempValue3));
					propValue.append(tempValue1);
					propValue.append(" ");
					propValue.append(tempValue2);
					propValue.append(" ");
					propValue.append(tempValue3);

					//size_t len1 = strlen(tempValue1);
					//size_t len2 = strlen(tempValue2);
					//size_t len3 = strlen(tempValue3);
					//size_t totalLen = len1 + len2 + len3 + 1; // +1 for the null terminator

					//char* result = (char*)malloc(totalLen);
					//if (result == NULL) {
					//	return NULL;
					//}

					//strcpy(result, tempValue1);
					//strcat(result, tempValue2);
					//strcat(result, tempValue3);
					//writeLog(result);
				}
				else {
					writeLog("---cValue");
					ITKCALL(ifail = AOM_ask_value_string(objectTag, propName, &cValue));
					propValue.append(cValue);
					writeLog(cValue);
				}
			}
			break;
		case PROP_date:
			date_t date_value;
			ITKCALL(ifail = AOM_ask_value_date(objectTag, propName, &date_value));
			ITKCALL(ifail = DATE_date_to_string(date_value, "%Y%m%d%H%M%S000", &cValue));
			propValue.append(cValue);
			break;
		case PROP_typed_reference://last_mod_user
		case PROP_typed_relation:
		case PROP_untyped_relation:
		case PROP_untyped_reference:
		{
			int attCnt = 0;
			tag_t* attTags = NULL;
			const char* subPropNameC = subPropName.size() <= 0 ? "object_string" : subPropName.c_str();
			WriteLog(debug, "引用属性：%s-%s", propName, subPropNameC);
			ITKCALL(ifail = AOM_ask_value_tags(objectTag, propName, &attCnt, &attTags));
			for (int i = 0; i < attCnt; i++) {
				string subPropValue;
				ifail = getPropertyStringValue(attTags[i], subPropNameC, "", subPropValue, isDisplayValue, splitter, -1);
				if (ifail != ITK_ok) {
					goto end;
				}
				if (subPropValue.size() > 0) {
					if (propValue.size() > 0) {
						propValue.append(";");
					}
					propValue.append(subPropValue);
				}
			}
			DOFREE(attTags);
		}
		break;
		default:
			ITKCALL(ifail = AOM_UIF_ask_value(objectTag, propName, &cValue));
			propValue.append(cValue);
			break;
		}
	}
	if (splitter.size() > 0 && split_index >= 0 && propValue.size() > 0) {
		vector<string> vec;
		Split(propValue, splitter, vec);
		if (split_index < vec.size()) {
			propValue.assign(vec[split_index]);
		}
		else {
			propValue.assign("");
		}
		vec.clear();
	}
end:
	DOFREE(propertyTypeName);
	DOFREE(cValue);
	return ifail;
}

int getAllTargetRevs(tag_t task_tag, vector<tag_t>& target_tags, vector<string>& target_types) {
	int ifail = ITK_ok, att_cnt = 0;
	tag_t rootTask_tag = NULLTAG, rev_type = NULLTAG;
	tag_t* attachments = NULL;
	char* objectstring = NULL, * objecttype = NULL;
	ITKCALL(ifail = TCTYPE_ask_type(TYPE_REV, &rev_type));
	if (rev_type == NULLTAG) {
		EMH_store_error_s2(EMH_severity_error, ITK_err, ("未找到版本类型"), TYPE_REV);
		return ITK_err;
	}
	ITKCALL(ifail = EPM_ask_root_task(task_tag, &rootTask_tag));
	ITKCALL(ifail = EPM_ask_attachments(rootTask_tag, EPM_target_attachment, &att_cnt, &attachments));
	if (att_cnt == 0) {
		return ITK_ok;
	}
	for (int i = 0; i < att_cnt; i++) {
		tag_t attachment = attachments[i];
		tag_t type = NULLTAG;
		bool isForm = false;
		DOFREE(objectstring);
		DOFREE(objecttype);
		ITKCALL(ifail = TCTYPE_ask_object_type(attachment, &type));
		ITKCALL(ifail = AOM_ask_value_string(attachment, "object_string", &objectstring));
		if (ifail != ITK_ok) {
			continue;
		}
		ITKCALL(ifail = WSOM_ask_object_type2(attachment, &objecttype));
		WriteLog(debug, "%d. 传递对象： %s [%s]", i + 1, objectstring, objecttype);
		ITKCALL(ifail = TCTYPE_is_type_of(type, rev_type, &isForm));
		if (!isForm) {
			WriteLog(debug, ">> 非版本类型");
			continue;
		}
		if (target_types.size() > 0) {
			if (!inArray(target_types, objecttype)) {
				WriteLog(debug, ">> 非目标类型");
				continue;
			}
		}
		target_tags.push_back(attachment);
	}
	DOFREE(attachments);
	DOFREE(objecttype);
	DOFREE(objectstring);
	return ifail;
}

// 假设传进去的值是 ITME_CODE=string=item.item_id
// 或者LIFECYCLE_CODE=string=form.qf8cpjd:sz=LC01;lc=LC03;ts=LC05
int parseErpConfigs(vector<string> pref_values, int start_index, vector<ERP_Config>& erp_configs) {
	for (int i = start_index; i < pref_values.size(); i++) {
		string prop_config;
		// "sz=LC01","lc=LC03","ts=LC05"
		vector<string> replace_configs;
		// string
		// string
		string type;
		// ITEM_CODE 
		// LIFECYCLE_CODE
		string erp;
		string child_line_type;
		// ITEM
		// FORM
		string prop_loc;
		// item_id
		// qf8cpjd
		string prop_name;
		string sub_prop_name;
		string text_value;
		string splitter;
		int split_index = -1;
		bool isDisplay = false;
		prop_config.assign(pref_values[i]);
		//	"sz" -> "LC01"
		//	"lc" -> "LC03"
		//	"ts" -> "LC05"
		map<string, string> replace_map;
		int ind = prop_config.find_first_of(':');
		if (ind != string::npos) {
			Split(prop_config.substr(ind + 1), ";", replace_configs);
			prop_config = prop_config.substr(0, ind);
		}
		ind = prop_config.find('=');
		if (ind != string::npos) {
			erp = prop_config.substr(0, ind);
			prop_config = prop_config.substr(ind + 1);
		}
		ind = prop_config.find('=');
		if (ind != string::npos) {
			type = prop_config.substr(0, ind);
			prop_config = prop_config.substr(ind + 1);
		}
		ind = prop_config.find('=');
		if (ind != string::npos) {
			child_line_type = prop_config.substr(0, ind);
			prop_config = prop_config.substr(ind + 1);
		}
		ind = prop_config.find_first_of('.');
		if (ind != string::npos) {
			prop_loc = prop_config.substr(0, ind);
			prop_name = prop_config.substr(ind + 1);
			transform(prop_loc.begin(), prop_loc.end(), prop_loc.begin(), ::toupper);
		}
		else {
			text_value = prop_config;
		}
		ind = prop_name.find_first_of('.');
		if (ind != string::npos) {
			sub_prop_name = prop_name.substr(ind + 1);
			prop_name = prop_name.substr(0, ind);
		}
		if (erp.size() > 0 && strcmp("$", erp.substr(0, 1).c_str()) == 0) {
			isDisplay = true;
			erp = erp.substr(1);
		}
		ind = erp.find("@@");
		if (ind != string::npos) {
			string split_config = erp.substr(ind + 2);
			erp = erp.substr(0, ind);
			ind = split_config.find("@@");
			if (ind != string::npos) {
				splitter = split_config.substr(0, ind);
				split_index = atoi(split_config.substr(ind + 2).c_str());
			}
		}
		if (erp.size() == 0) {
			WriteLog(debug, "无效配置：%s", pref_values[i].c_str());
			continue;
		}
		for (int j = 0; j < replace_configs.size(); j++) {
			string replace_config = replace_configs[j];
			ind = replace_config.find_first_of('=');
			if (ind == string::npos) {
				WriteLog(debug, "无效替换配置：%s", replace_config.c_str());
				continue;
			}
			string replace_from = replace_config.substr(0, ind);
			string replace_to = replace_config.substr(ind + 1);
			if (replace_map.count(replace_from) > 0) {
				WriteLog(debug, "重复替换配置：%s", replace_config.c_str());
				continue;
			}
			replace_map.insert(make_pair(replace_from, replace_to));
		}
		ERP_Config erp_config;
		erp_config.isDisplayValue = isDisplay;
		erp_config.type = type;
		erp_config.erp = erp;
		// erp_config.child_line_type = child_line_type;
		erp_config.prop_loc = prop_loc;
		erp_config.prop_name = prop_name;
		erp_config.sub_prop_name = sub_prop_name;
		erp_config.text_value = text_value;
		erp_config.replace_map = replace_map;
		erp_config.splitter = splitter;
		erp_config.split_index = split_index;
		erp_configs.push_back(erp_config);
	}
	return ITK_ok;
}



int read_data(tag_t bomline_tag, tag_t rev_tag, tag_t parent_bomline_tag, tag_t parent_rev_tag, vector<ERP_Config> erp_configs, TiXmlElement* p) {
	int ifail = ITK_ok, att_cnt = 0;
	// 版本对应的对象，版本表单对象，
	tag_t item_tag = NULLTAG, form_tag = NULLTAG, * form_tags = NULL;
	string combined_prop_value;
	if (rev_tag == NULLTAG) {
		if (bomline_tag != NULLTAG) {
			ITKCALL(ifail = AOM_ask_value_tag(bomline_tag, "bl_revision", &rev_tag));
		}
	}
	if (parent_rev_tag == NULLTAG) {
		if (parent_bomline_tag != NULLTAG) {
			ITKCALL(ifail = AOM_ask_value_tag(parent_bomline_tag, "bl_revision", &parent_rev_tag));
		}
	}
	if (rev_tag == NULLTAG || erp_configs.size() == 0) {
		return ifail;
	}
	ITKCALL(ITEM_ask_item_of_rev(rev_tag, &item_tag));
	ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &att_cnt, &form_tags));
	if (att_cnt > 0) {
		form_tag = form_tags[0];
		DOFREE(form_tags);
	}
	for (int i = 0; i < erp_configs.size(); i++) {
		ERP_Config config = erp_configs[i];
		writeLog("===>当前对象类型: " + std::string(config.prop_loc.c_str()));
		string prop_value;
		bool combine = false;
		if (i < erp_configs.size() - 1) {
			combine = config.erp == erp_configs[i + 1].erp;
		}
		if (config.prop_loc.size() == 0) {
			prop_value = config.text_value;
		}
		else {
			if (tc_strcmp("ITEM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("REV", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("FORM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(form_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("BOM", config.prop_loc.c_str()) == 0) {
				if (bomline_tag == NULLTAG) {
					continue;
				}
				ifail = getPropertyStringValue(bomline_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			// 2、Connor_ERP_BOM_child、Connor_ERP_BOM_child_zizhi首选项中如何配置可以获取BOM父项的信息，比如ID；
			else if (tc_strcmp("PREV", config.prop_loc.c_str()) == 0) {
				if (parent_rev_tag == NULLTAG) {
					continue;
				}
				ifail = getPropertyStringValue(parent_rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("PBOM", config.prop_loc.c_str()) == 0) {
				tag_t parent_bomline = NULLTAG;
				if (parent_bomline_tag == NULLTAG) {
					continue;
				}
				ifail = getPropertyStringValue(parent_bomline_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "未知属性提取位置，请检查首选项配置", config.prop_loc.c_str());
				return ITK_err;
			}
		}
		if (prop_value.size() == 0) {
			if (config.replace_map.count("default") > 0) {
				prop_value = config.replace_map["default"];
			}
		}
		else {
			if (config.replace_map.count(prop_value) > 0) {
				prop_value = config.replace_map[prop_value];
			}
			else if (config.replace_map.count("*") > 0) {
				prop_value = config.replace_map["*"];
			}
		}
		if (combine) {
			combined_prop_value.append(prop_value);
			WriteLog(debug, "[%s][%s] = [%s]", config.erp.c_str(), config.type.c_str(), prop_value.c_str());
			continue;
		}
		combined_prop_value.append(prop_value);
		WriteLog(debug, "[%s][%s] = [%s]", config.erp.c_str(), config.type.c_str(), combined_prop_value.c_str());
		TiXmlElement* field = new TiXmlElement("field");
		field->SetAttribute("name", config.erp.c_str());
		field->SetAttribute("type", config.type.c_str());
		set_xml_value(field, combined_prop_value.c_str());
		p->LinkEndChild(field);
		combined_prop_value.clear();
	}
	return ifail;
}

int read_data2(tag_t bomline_tag, tag_t rev_tag, tag_t parent_bomline_tag, tag_t parent_rev_tag, vector<ERP_Config> erp_configs, TiXmlElement* p) {
	int ifail = ITK_ok, att_cnt = 0;
	// 版本对应的对象，版本表单对象，
	tag_t item_tag = NULLTAG, form_tag = NULLTAG, * form_tags = NULL;
	string combined_prop_value;
	if (rev_tag == NULLTAG) {
		if (bomline_tag != NULLTAG) {
			ITKCALL(ifail = AOM_ask_value_tag(bomline_tag, "bl_revision", &rev_tag));
		}
	}
	if (parent_rev_tag == NULLTAG) {
		if (parent_bomline_tag != NULLTAG) {
			ITKCALL(ifail = AOM_ask_value_tag(parent_bomline_tag, "bl_revision", &parent_rev_tag));
		}
	}
	if (rev_tag == NULLTAG || erp_configs.size() == 0) {
		return ifail;
	}
	ITKCALL(ITEM_ask_item_of_rev(rev_tag, &item_tag));
	ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &att_cnt, &form_tags));
	if (att_cnt > 0) {
		form_tag = form_tags[0];
		DOFREE(form_tags);
	}
	for (int i = 0; i < erp_configs.size(); i++) {
		ERP_Config config = erp_configs[i];
		writeLog("===>当前对象类型: "+ std::string(config.prop_loc.c_str()));
		string prop_value;
		bool combine = false;
		if (i < erp_configs.size() - 1) {
			combine = config.erp == erp_configs[i + 1].erp;
		}
		if (config.prop_loc.size() == 0) {
			prop_value = config.text_value;
		}
		else {
			if (tc_strcmp("ITEM", config.prop_loc.c_str()) == 0) {
				//ifail = getPropertyStringValue(item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				printf("item:config.prop_name:%s\n", config.prop_name.c_str());
				if (config.prop_name == "ITEM_CODE") { // 特殊处理 ITEM_CODE
					ifail = getPropertyStringValue(item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
					prop_value += "-M"; // 在 ITEM_CODE 后面添加 "-M"
				}
				else {
					ifail = getPropertyStringValue(item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				}
			}
			else if (tc_strcmp("REV", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("FORM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(form_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("BOM", config.prop_loc.c_str()) == 0) {
				if (bomline_tag == NULLTAG) {
					continue;
				}
				ifail = getPropertyStringValue(bomline_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			// 2、Connor_ERP_BOM_child、Connor_ERP_BOM_child_zizhi首选项中如何配置可以获取BOM父项的信息，比如ID；
			else if (tc_strcmp("PREV", config.prop_loc.c_str()) == 0) {
				if (parent_rev_tag == NULLTAG) {
					continue;
				}
				ifail = getPropertyStringValue(parent_rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("PBOM", config.prop_loc.c_str()) == 0) {
				tag_t parent_bomline = NULLTAG;
				if (parent_bomline_tag == NULLTAG) {
					continue;
				}
				ifail = getPropertyStringValue(parent_bomline_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "未知属性提取位置，请检查首选项配置", config.prop_loc.c_str());
				return ITK_err;
			}
		}
		if (prop_value.size() == 0) {
			if (config.replace_map.count("default") > 0) {
				prop_value = config.replace_map["default"];
			}
		}
		else {
			if (config.replace_map.count(prop_value) > 0) {
				prop_value = config.replace_map[prop_value];
			}
			else if (config.replace_map.count("*") > 0) {
				prop_value = config.replace_map["*"];
			}
		}
		if (combine) {
			combined_prop_value.append(prop_value);
			WriteLog(debug, "[%s][%s] = [%s]", config.erp.c_str(), config.type.c_str(), prop_value.c_str());
			continue;
		}
		combined_prop_value.append(prop_value);
		WriteLog(debug, "[%s][%s] = [%s]", config.erp.c_str(), config.type.c_str(), combined_prop_value.c_str());
		TiXmlElement* field = new TiXmlElement("field");
		field->SetAttribute("name", config.erp.c_str());
		field->SetAttribute("type", config.type.c_str());
		set_xml_value(field, combined_prop_value.c_str());
		p->LinkEndChild(field);
		combined_prop_value.clear();
	}
	return ifail;
}


int read_data_sp(tag_t bomline_tag, tag_t rev_tag, tag_t raw_material_rev_tag, vector<ERP_Config> erp_configs, TiXmlElement* p, map<string, string> overwrite) {
	int ifail = ITK_ok, att_cnt = 0;
	tag_t item_tag = NULLTAG, form_tag = NULLTAG, * form_tags = NULL;
	tag_t r_item_tag = NULLTAG, r_form_tag = NULLTAG;
	string combined_prop_value;
	if (rev_tag == NULLTAG) {
		if (bomline_tag != NULLTAG) {
			ITKCALL(ifail = AOM_ask_value_tag(bomline_tag, "bl_revision", &rev_tag));
		}
	}
	if (erp_configs.size() == 0) {
		return ifail;
	}
	if (rev_tag != NULLTAG) {
		ITKCALL(ITEM_ask_item_of_rev(rev_tag, &item_tag));
		ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &att_cnt, &form_tags));
		if (att_cnt > 0) {
			form_tag = form_tags[0];
			DOFREE(form_tags);
		}
	}
	if (raw_material_rev_tag != NULLTAG) {
		DOFREE(form_tags);
		ITKCALL(ITEM_ask_item_of_rev(raw_material_rev_tag, &r_item_tag));
		ITKCALL(AOM_ask_value_tags(raw_material_rev_tag, "IMAN_master_form_rev", &att_cnt, &form_tags));
		if (att_cnt > 0) {
			r_form_tag = form_tags[0];
			DOFREE(form_tags);
		}
	}
	for (int i = 0; i < erp_configs.size(); i++) {
		ERP_Config config = erp_configs[i];
		string prop_value;
		bool combine = false;
		if (i < erp_configs.size() - 1) {
			combine = (config.erp == erp_configs[i + 1].erp);
		}
		if (overwrite.count(config.erp) > 0) {
			prop_value = overwrite[config.erp];
		}
		else if (config.prop_loc.size() == 0) {
			prop_value = config.text_value;
		}
		else {
			if (tc_strcmp("ITEM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("REV", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("FORM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(form_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("BOM", config.prop_loc.c_str()) == 0) {
				if (bomline_tag != NULLTAG) {
					ifail = getPropertyStringValue(bomline_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				}
			}
			else if (tc_strcmp("RAWITEM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(r_item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("RAWREV", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(raw_material_rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else if (tc_strcmp("RAWFORM", config.prop_loc.c_str()) == 0) {
				ifail = getPropertyStringValue(r_form_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
			}
			else {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "未知属性提取位置，请检查首选项配置", config.prop_loc.c_str());
				return ITK_err;
			}
			/*vector<tag_t> target_item_tags, target_rev_tags, target_form_tags, targetline_tags;
			if (config.child_line_type.size() > 0 && bomline_tag != NULLTAG) {
				get_child_line(bomline_tag, config.child_line_type, targetline_tags);
				for (int k = 0; k < targetline_tags.size(); k++) {
					tag_t cline_tag = targetline_tags[k], target_item_tag = NULLTAG, target_rev_tag = NULLTAG, target_form_tag = NULLTAG;
					ITKCALL(ifail = AOM_ask_value_tag(cline_tag, "bl_revision", &target_rev_tag));
					ITKCALL(ITEM_ask_item_of_rev(target_rev_tag, &target_item_tag));
					ITKCALL(AOM_ask_value_tags(target_rev_tag, "IMAN_master_form_rev", &att_cnt, &form_tags));
					if (att_cnt > 0) {
						target_form_tag = form_tags[0];
						DOFREE(form_tags);
					}
					target_item_tags.push_back(target_item_tag);
					target_rev_tags.push_back(target_rev_tag);
					target_form_tags.push_back(target_form_tag);
				}
			}
			else {
				target_item_tags.push_back(item_tag);
				target_rev_tags.push_back(rev_tag);
				target_form_tags.push_back(form_tag);
				targetline_tags.push_back(bomline_tag);
			}
			for (int k = 0; k < targetline_tags.size(); k++) {
				tag_t target_item_tag = target_item_tags[k];
				tag_t target_rev_tag = target_rev_tags[k];
				tag_t target_form_tag = target_form_tags[k];
				tag_t targetline_tag = targetline_tags[k];
				if (k != 0) {
					prop_value.append(";");
				}
				if (tc_strcmp("ITEM", config.prop_loc.c_str()) == 0) {
					ifail = getPropertyStringValue(target_item_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				}
				else if (tc_strcmp("REV", config.prop_loc.c_str()) == 0) {
					ifail = getPropertyStringValue(target_rev_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				}
				else if (tc_strcmp("FORM", config.prop_loc.c_str()) == 0) {
					ifail = getPropertyStringValue(target_form_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				}
				else if (tc_strcmp("BOM", config.prop_loc.c_str()) == 0) {
					if (bomline_tag == NULLTAG) {
						continue;
					}
					ifail = getPropertyStringValue(targetline_tag, config.prop_name.c_str(), config.sub_prop_name, prop_value, config.isDisplayValue, config.splitter, config.split_index);
				}
				else {
					EMH_store_error_s2(EMH_severity_error, ITK_err, "未知属性提取位置，请检查首选项配置", config.prop_loc.c_str());
					return ITK_err;
				}
			}
			target_item_tags.clear();
			target_rev_tags.clear();
			target_form_tags.clear();
			targetline_tags.clear();*/
		}
		if (prop_value.size() == 0) {
			if (config.replace_map.count("default") > 0) {
				prop_value = config.replace_map["default"];
			}
		}
		else {
			if (config.replace_map.count(prop_value) > 0) {
				prop_value = config.replace_map[prop_value];
			}
			else if (config.replace_map.count("*") > 0) {
				prop_value = config.replace_map["*"];
			}
		}
		if (combine) {
			combined_prop_value.append(prop_value);
			WriteLog(debug, "[%s][%s] = [%s]", config.erp.c_str(), config.type.c_str(), prop_value.c_str());
			continue;
		}
		combined_prop_value.append(prop_value);
		WriteLog(debug, "[%s][%s] = [%s]", config.erp.c_str(), config.type.c_str(), combined_prop_value.c_str());
		TiXmlElement* field = new TiXmlElement("field");
		field->SetAttribute("name", config.erp.c_str());
		field->SetAttribute("type", config.type.c_str());
		set_xml_value(field, combined_prop_value.c_str());
		p->LinkEndChild(field);
		combined_prop_value.clear();
	}
	return ifail;
}

int ask_zizhi(tag_t rev_tag, string& value) {
	int ifail = ITK_ok, form_cnt = 0;
	char* object_string = NULL, * prop_value = NULL;
	tag_t form_tag = NULLTAG, * form_tags = NULL;
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, "object_string", &object_string));
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
	if (form_cnt == 0) {
		goto end;
	}
	form_tag = form_tags[0];
	ITKCALL(ifail = AOM_UIF_ask_value(form_tag, "qf8laiyuan", &prop_value));
	if (ifail != ITK_ok) {
		goto end;
	}
	WriteLog(debug, "检查自制件 [%s][%s]", object_string, prop_value);
	value.assign(prop_value);
end:
	DOFREE(form_tags);
	DOFREE(object_string);
	DOFREE(prop_value);
	return ifail;
}

// 底层自制件展开
int write_zizhi(tag_t rev_tag, vector<ERP_Config> zz_parent_configs, vector<ERP_Config> zz_child_configs, int& bomSeq, TiXmlElement* bom_data) {
	int ifail = ITK_ok, form_cnt = 0, child_cnt = 0;
	char* object_string = NULL, * object_type = NULL, * prop_value = NULL;
	tag_t* form_tags = NULL, form_tag = NULLTAG, * child_tags = NULL, relation_tag = NULLTAG;
	vector<tag_t> child_revs;
	string clyl;
	ITKCALL(ifail = WSOM_ask_object_type2(rev_tag, &object_type));
	if (strcmp("QF8PartRevision", object_type) != 0) {
		goto end;
	}
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, "object_string", &object_string));
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
	if (form_cnt == 0) {
		goto end;
	}
	form_tag = form_tags[0];
	ITKCALL(ifail = AOM_UIF_ask_value(form_tag, "qf8laiyuan", &prop_value));
	if (ifail != ITK_ok) {
		goto end;
	}
	WriteLog(debug, "检查自制件 [%s][%s]", object_string, prop_value);
	if (strcmp("自制", prop_value) != 0) {
		goto end;
	}
	ITKCALL(ifail = GRM_find_relation_type("QF8SCXXrelation", &relation_tag));
	if (relation_tag == NULLTAG) {
		EMH_store_error_s1(EMH_severity_error, ITK_err, "未定义关系：QF8SCXXrelation");
		ifail = ITK_err;
		goto end;
	}
	ITKCALL(ifail = GRM_list_secondary_objects_only(rev_tag, relation_tag, &child_cnt, &child_tags));
	WriteLog(debug, ">> 找到子对象数量：%d", child_cnt);
	if (child_cnt == 0) {
		goto end;
	}
	for (int i = 0; i < child_cnt; i++) {
		tag_t child_tag = child_tags[i];
		DOFREE(object_string);
		DOFREE(object_type);
		ITKCALL(ifail = WSOM_ask_object_type2(child_tag, &object_type));
		ITKCALL(ifail = AOM_ask_value_string(child_tag, "object_string", &object_string));
		WriteLog(debug, ">> %d. [%s][%s]", i + 1, object_string, object_type);
		if (strcmp("QF8RawMaterialsRevision", object_type) == 0) {
			child_revs.push_back(child_tag);
		}
		else if (strcmp("QF8SCXXBDRevision", object_type) == 0) {
			//char* prop_value = NULL;
			//ITKCALL(ifail = AOM_UIF_ask_value(child_tag, "qf8clyl", &prop_value));
			//if (ifail == ITK_ok) {
			//	WriteLog(debug, "  >> [qf8clyl] = [%s]", prop_value);
			//	clyl.assign(prop_value);
			//}
			//DOFREE(prop_value);

			//TODO 20240116材料用量由原来的从对象上获取改成从版本表单上获取，最新版本？
			char* prop_value = NULL;
			int form_cnt = 0;
			tag_t* formTag = NULL;
			AOM_ask_value_tags(child_tag, IMAN_MASTER_FORM_REV, &form_cnt, &formTag);
			ITKCALL(ifail = AOM_UIF_ask_value(formTag[0], QF8CLYL, &prop_value));

			if (ifail == ITK_ok) {
				WriteLog(debug, "  >> [%s] = [%s]", QF8CLYL, prop_value);
				clyl.assign(prop_value);
			}
			DOFREE(prop_value);
			DOFREE(formTag);
		}
	}
	child_cnt = child_revs.size();
	WriteLog(debug, ">> 找到原材料数量：%d", child_cnt);
	if (child_cnt == 0) {
		goto end;
	}
	// 填写BOM数据
	{
		TiXmlElement* data_row = new TiXmlElement("row");
		data_row->SetAttribute("seq", bomSeq);
		bomSeq += 1;
		bom_data->LinkEndChild(data_row);
		read_data(NULLTAG, rev_tag, NULLTAG, NULLTAG, zz_parent_configs, data_row);
		TiXmlElement* detail = new TiXmlElement("detail");
		detail->SetAttribute("name", "BOM_D");
		data_row->LinkEndChild(detail);
		for (int i = 0; i < child_cnt; i++) {
			TiXmlElement* row = new TiXmlElement("row");
			row->SetAttribute("seq", i + 1);
			detail->LinkEndChild(row);
			read_data(NULLTAG, child_revs[i], NULLTAG, rev_tag, zz_child_configs, row);
			// 材料用量
			{
				TiXmlElement* field = new TiXmlElement("field");
				field->SetAttribute("name", "QTY_PER");
				field->SetAttribute("type", "numeric");
				set_xml_value(field, clyl.c_str());
				row->LinkEndChild(field);
			}
		}
	}
end:
	child_revs.clear();
	DOFREE(object_string);
	DOFREE(object_type);
	DOFREE(prop_value);
	DOFREE(form_tags);
	DOFREE(child_tags);
	return ifail;
}


int cycle_bom(tag_t bomline_tag, vector<ERP_Config> parent_configs, vector<ERP_Config> child_configs,
	vector<ERP_Config> zz_parent_configs, vector<ERP_Config> zz_child_configs,
	vector<ERP_Config> pli_configs, vector<ERP_Config> gi_configs,
	TiXmlElement* bom_data, TiXmlElement* wl_pli, TiXmlElement* wl_gi,
	int& bomSeq, int& wlSeq, vector<string>& added_lines) {
	int ifail = ITK_ok, child_cnt = 0, att_cnt = 0;
	tag_t* child_lines = NULL;
	tag_t rev_tag = NULLTAG;
	char* rev_uid = NULL;
	string zizhi;
	if (bomline_tag == NULLTAG || parent_configs.size() == 0 || child_configs.size() == 0) {
		return ifail;
	}
	ITKCALL(ifail = AOM_ask_value_tag(bomline_tag, "bl_revision", &rev_tag));
	if (rev_tag == NULLTAG) {
		return ifail;
	}
	ITK__convert_tag_to_uid(rev_tag, &rev_uid);
	if (inArray(added_lines, rev_uid)) {
		WriteLog(debug, ">> 取消重复添加BOM - %s", rev_uid);
		return ifail;
	}
	added_lines.push_back(rev_uid);
	DOFREE(rev_uid);
	// 20220328 BOM传递时需要判读每层物料的自制外购属性（表单属性qf8laiyuan），如果是非自制，则BOM传递到此层停止；
	ifail = ask_zizhi(rev_tag, zizhi);
	if (ifail != ITK_ok || zizhi != "自制") {
		WriteLog(debug, ">> 跳过BOM行");
		return ifail;
	}
	//填写物料数据
	{
		TiXmlElement* row_gi = new TiXmlElement("row");
		row_gi->SetAttribute("seq", wlSeq);
		wl_gi->LinkEndChild(row_gi);
		read_data(bomline_tag, NULLTAG, NULLTAG, NULLTAG, gi_configs, row_gi);
		TiXmlElement* row_pli = new TiXmlElement("row");
		row_pli->SetAttribute("seq", wlSeq);
		wl_pli->LinkEndChild(row_pli);
		read_data(bomline_tag, NULLTAG, NULLTAG, NULLTAG, pli_configs, row_pli);
		wlSeq += 1;
	}
	ITKCALL(ifail = BOM_line_ask_child_lines(bomline_tag, &child_cnt, &child_lines));
	// 底层自制件炸开
	if (child_cnt == 0) {
		write_zizhi(rev_tag, zz_parent_configs, zz_child_configs, bomSeq, bom_data);
	}
	if (child_cnt == 0) {
		DOFREE(child_lines);
		return ifail;
	}
	// 遍历bom
	for (int i = 0; i < child_cnt; i++) {
		cycle_bom(child_lines[i], parent_configs, child_configs, zz_parent_configs, zz_child_configs, pli_configs, gi_configs, bom_data, wl_pli, wl_gi, bomSeq, wlSeq, added_lines);
	}
	// 填写BOM数据
	{
		TiXmlElement* data_row = new TiXmlElement("row");
		data_row->SetAttribute("seq", bomSeq);
		bomSeq += 1;
		bom_data->LinkEndChild(data_row);
		read_data(bomline_tag, NULLTAG, NULLTAG, NULLTAG, parent_configs, data_row);
		TiXmlElement* detail = new TiXmlElement("detail");
		detail->SetAttribute("name", "BOM_D");
		data_row->LinkEndChild(detail);
		for (int i = 0; i < child_cnt; i++) {
			TiXmlElement* row = new TiXmlElement("row");
			row->SetAttribute("seq", i + 1);
			detail->LinkEndChild(row);
			read_data(child_lines[i], NULLTAG, bomline_tag, rev_tag, child_configs, row);
		}
	}

	DOFREE(child_lines);
	return ifail;
}

int read_bom_data(tag_t rev_tag, vector<ERP_Config> parent_configs, vector<ERP_Config> child_configs,
	vector<ERP_Config> zz_parent_configs, vector<ERP_Config> zz_child_configs,
	vector<ERP_Config> pli_configs, vector<ERP_Config> gi_configs, TiXmlElement* bom_data, TiXmlElement* wl_pli, TiXmlElement* wl_gi, int& bomSeq, int& wlSeq, vector<string>& added_lines) {
	int ifail = ITK_ok;
	tag_t bom_view = NULLTAG, bvr_tag = NULLTAG, bom_window = NULLTAG, topBomLine = NULLTAG;
	char* objectstring = NULL;
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, "object_string", &objectstring));
	WriteLog(debug, "读取BOM - %s", objectstring);
	ITKCALL(ifail = GetBomView(rev_tag, "view", &bom_view, &bvr_tag, debug));
	if (bom_view != NULLTAG) {
		ITKCALL(ifail = BOM_create_window(&bom_window));
		ITKCALL(ifail = BOM_set_window_top_line_bvr(bom_window, bvr_tag, &topBomLine));
		ifail = cycle_bom(topBomLine, parent_configs, child_configs, zz_parent_configs, zz_child_configs, pli_configs, gi_configs, bom_data, wl_pli, wl_gi, bomSeq, wlSeq, added_lines);
		ITKCALL(BOM_close_window(bom_window));
	}
	else {
		WriteLog(debug, "没有找到BOM视图");
		char* rev_uid = NULL;
		ITK__convert_tag_to_uid(rev_tag, &rev_uid);
		if (!inArray(added_lines, rev_uid)) {
			added_lines.push_back(rev_uid);
			write_zizhi(rev_tag, zz_parent_configs, zz_child_configs, bomSeq, bom_data);
		}
		else {
			WriteLog(debug, ">> 取消重复添加自制件 - %s", rev_uid);
		}
		DOFREE(rev_uid);
		// return ITK_err;
	}
	DOFREE(objectstring);
	return ifail;
}

int add_bop_dataset(string& name_prefix, tag_t dataset_tag, TiXmlElement* detail, const char* type, int& sop_index, const char* ref_name) {
	int ifail = ITK_ok;
	char* object_name = NULL, * uid = NULL, * origin_file_name = NULL;
	tag_t spec_dataset_rev = NULLTAG, * ref_objects = NULL, ref_object = NULLTAG;
	AE_reference_type_t my_ref_type;

	ITKCALL(ifail = AOM_ask_value_string(dataset_tag, "object_name", &object_name));
	ITK__convert_tag_to_uid(dataset_tag, &uid);
	WriteLog(debug, "添加数据集[%s] 类型[%s]", object_name, type);
	string ds_dir;
	ds_dir.assign(local_dir).append(uid).append("\\");
	WriteLog(debug, "========1======");

	if (std::experimental::filesystem::is_directory(ds_dir)) {

		WriteLog(debug, "=========2=====");
		std::experimental::filesystem::remove_all(ds_dir);

		WriteLog(debug, "======3========");
	}
	if (!std::experimental::filesystem::is_directory(ds_dir)) {
		WriteLog(debug, "=======4=======");
		if (!std::experimental::filesystem::create_directories(ds_dir)) {
			WriteLog(debug, "新建目录失败：%s", ds_dir.c_str());
			ifail = ITK_err;
			EMH_store_error_s2(EMH_severity_error, ITK_err, "创建数据集目录失败", ds_dir.c_str());
			goto end;
		}
	}
	ITKCALL(ifail = AE_ask_dataset_latest_rev(dataset_tag, &spec_dataset_rev));
	ITKCALL(ifail = AE_ask_dataset_named_ref2(spec_dataset_rev, ref_name, &my_ref_type, &ref_object));
	{
		DOFREE(origin_file_name);
		string temp_file, ftp_file;
		//tag_t ref_object = ref_objects[i];
		WriteLog(debug, "=========6=====");
		ITKCALL(ifail = IMF_ask_original_file_name2(ref_object, &origin_file_name));
		WriteLog(debug, "======7========");
		if (ifail != ITK_ok) {
			goto end;
		}
		temp_file.assign(ds_dir).append(name_prefix).append(origin_file_name);
		ftp_file.assign(ftp_dir).append(uid).append("\\").append(name_prefix).append(origin_file_name);

		WriteLog(debug, "=======8=======");
		ITKCALL(ifail = IMF_export_file(ref_object, temp_file.c_str()));

		WriteLog(debug, "========9======");
		if (ifail != ITK_ok) {
			goto end;
		}
		WriteLog(debug, "导出文件完成：%s", (temp_file.c_str()));
		// 填写xml
		{
			TiXmlElement* row = new TiXmlElement("row");
			row->SetAttribute("seq", sop_index++);
			detail->LinkEndChild(row);
			TiXmlElement* field1 = new TiXmlElement("field");
			field1->SetAttribute("name", "type");
			field1->SetAttribute("type", "string");
			row->LinkEndChild(field1);
			set_xml_value(field1, type);
			TiXmlElement* field2 = new TiXmlElement("field");
			field2->SetAttribute("name", "sop_no");
			field2->SetAttribute("type", "string");
			row->LinkEndChild(field2);
			set_xml_value(field2, object_name);
			TiXmlElement* field3 = new TiXmlElement("field");
			field3->SetAttribute("name", "sop_file");
			field3->SetAttribute("type", "string");
			set_xml_value(field3, ftp_file.c_str());
			row->LinkEndChild(field3);
		}
	}
end:
	DOFREE(origin_file_name);
	DOFREE(ref_objects);
	DOFREE(uid);
	DOFREE(object_name);
	return ifail;
}

int add_bop_dataset_xml(string& name_prefix, tag_t dataset_tag, TiXmlElement* detail, const char* type, int& sop_index, const char* ref_name, const ProductInfo& product) {
	int ifail = ITK_ok;
	char* object_name = NULL, * uid = NULL, * origin_file_name = NULL;
	tag_t spec_dataset_rev = NULLTAG, * ref_objects = NULL, ref_object = NULLTAG;
	AE_reference_type_t my_ref_type;

	ITKCALL(ifail = AOM_ask_value_string(dataset_tag, "object_name", &object_name));
	ITK__convert_tag_to_uid(dataset_tag, &uid);
	WriteLog(debug, "添加数据集[%s] 类型[%s]", object_name, type);
	string ds_dir;
	ds_dir.assign(local_dir).append(uid).append("\\");
	WriteLog(debug, "========1======");

	if (std::experimental::filesystem::is_directory(ds_dir)) {

		WriteLog(debug, "=========2=====");
		std::experimental::filesystem::remove_all(ds_dir);

		WriteLog(debug, "======3========");
	}
	if (!std::experimental::filesystem::is_directory(ds_dir)) {
		WriteLog(debug, "=======4=======");
		if (!std::experimental::filesystem::create_directories(ds_dir)) {
			WriteLog(debug, "新建目录失败：%s", ds_dir.c_str());
			ifail = ITK_err;
			EMH_store_error_s2(EMH_severity_error, ITK_err, "创建数据集目录失败", ds_dir.c_str());
			goto end;
		}
	}
	ITKCALL(ifail = AE_ask_dataset_latest_rev(dataset_tag, &spec_dataset_rev));
	ITKCALL(ifail = AE_ask_dataset_named_ref2(spec_dataset_rev, ref_name, &my_ref_type, &ref_object));
	{
		DOFREE(origin_file_name);
		string temp_file, ftp_file;
		//tag_t ref_object = ref_objects[i];
		WriteLog(debug, "=========6=====");
		ITKCALL(ifail = IMF_ask_original_file_name2(ref_object, &origin_file_name));
		WriteLog(debug, "======7========");
		if (ifail != ITK_ok) {
			goto end;
		}
		temp_file.assign(ds_dir).append(name_prefix).append(origin_file_name);
		ftp_file.assign(ftp_dir).append(uid).append("\\").append(name_prefix).append(origin_file_name);

		WriteLog(debug, "=======8=======");
		ITKCALL(ifail = IMF_export_file(ref_object, temp_file.c_str()));

		WriteLog(debug, "========9======");
		if (ifail != ITK_ok) {
			goto end;
		}
		WriteLog(debug, "导出文件完成：%s", (temp_file.c_str()));
		// 填写xml
		{
			TiXmlElement* row = new TiXmlElement("row");
			row->SetAttribute("seq", sop_index);
			detail->LinkEndChild(row);
			TiXmlElement* field1 = new TiXmlElement("field");
			field1->SetAttribute("name", "type");
			field1->SetAttribute("type", "string");
			set_xml_value(field1, type);
			row->LinkEndChild(field1);
			TiXmlElement* field2 = new TiXmlElement("field");
			field2->SetAttribute("name", "sop_no");
			field2->SetAttribute("type", "string");
			set_xml_value(field2, origin_file_name);
			row->LinkEndChild(field2);
			TiXmlElement* field3 = new TiXmlElement("field");
			field3->SetAttribute("name", "sop_file");
			field3->SetAttribute("type", "string");
			set_xml_value(field3, ftp_file.c_str());
			row->LinkEndChild(field3);
			TiXmlElement* field4 = new TiXmlElement("field");
			field4->SetAttribute("name", "op_no");
			field4->SetAttribute("type", "string");
			set_xml_value(field4, product.qf8gxbm.c_str());
			row->LinkEndChild(field4);
			TiXmlElement* field5 = new TiXmlElement("field");
			field5->SetAttribute("name", "supop_no");
			field5->SetAttribute("type", "string");
			set_xml_value(field5, "");
			row->LinkEndChild(field5);
			TiXmlElement* field6 = new TiXmlElement("field");
			field6->SetAttribute("name", "op_seq");
			field6->SetAttribute("type", "string");
			set_xml_value(field6, to_string(sop_index++).c_str());
			row->LinkEndChild(field6);
		}
	}
end:
	DOFREE(origin_file_name);
	DOFREE(ref_objects);
	DOFREE(uid);
	DOFREE(object_name);
	return ifail;
}

int add_bop_datasets_xml(tag_t rev_tag, TiXmlElement* SOP_detail,  int& sop_index, TiXmlElement* qcitem_detail, int& qcitem_index , const ProductInfo& product, map<string, string>& QF_JYXtype_map,bool isWL) {
	int ifail = ITK_ok;
	int ds_cnt = 0, metarget_cnt = 0;
	tag_t* ds_tags = NULL, * metarget_tags = NULL;
	char* object_string = NULL, * object_type = NULL;
	string name_prefix;
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, ITEM_ID, &object_string));
	//name_prefix.assign(object_string);
	name_prefix.append(product.wl_id).append("_").append(product.wl_rev_id).append("_");
	if (!isWL) {
		if (!product.qf8gxbm.empty()) {
			name_prefix.append(product.qf8gxbm).append("_");
		}
		name_prefix.append(product.SUBOPSEQUENCE).append("_");
	}

	WriteLog(debug, "文件名称添加前缀：%s", name_prefix.c_str());
	if (!isWL) {
		ITKCALL(ifail = AOM_ask_value_tags(rev_tag, IMAN_SPECIFICATION, &ds_cnt, &ds_tags));
		WriteLog(debug, "数据集数量：%d", ds_cnt);
		for (int i = 0; i < ds_cnt; i++) {
			DOFREE(object_string);
			DOFREE(object_type);
			tag_t ds_tag = ds_tags[i];
			ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
			ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
			WriteLog(debug, ">> %d. %s [%s] ", i + 1, object_string, object_type);
			if (tc_strcmp("MSExcelX", object_type) == 0) {
				readExcel(qcitem_detail, qcitem_index, ds_tag, "xlsx", QF_JYXtype_map, product);
			}
			//string ref_name = strcmp("PDF", object_type) == 0 ? PDF_REFERENCE : (strcmp("直接模型", object_type) == 0 ? JTPART : (strcmp("HTML", object_type) == 0 ? "HTML" : ""));
			//WriteLog(debug, ">> %d. %s [%s] ref_name: [%s]", i + 1, object_string, object_type, ref_name.c_str());
			//if (ref_name.size() == 0)
			//	continue;
			//string type;
			//if (tc_strcmp(ref_name.c_str(), "HTML") == 0) {
			//	type.assign("0");
			//}
			//else if (tc_strcmp(ref_name.c_str(), PDF_REFERENCE) == 0  || tc_strcmp(ref_name.c_str(), JTPART) == 0) {
			//	type.assign("1");
			//}
			//ifail = add_bop_dataset_xml(name_prefix, ds_tag, SOP_detail, type.c_str(), sop_index, ref_name.c_str(), product);
			if (ifail != ITK_ok) {
				goto end;
			}
		}
		DOFREE(ds_tags);
	}

	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, IMAN_MANIFESTATION, &ds_cnt, &ds_tags));
	for (int i = 0; i < ds_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		tag_t ds_tag = ds_tags[i];
		ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
		string ref_name = strcmp("PDF", object_type) == 0 ? PDF_REFERENCE : (strcmp("HTML", object_type) == 0 ? "HTML" : "");
		WriteLog(debug, ">> %d. %s [%s] ref_name: [%s]", i + 1, object_string, object_type, ref_name.c_str());
		if (ref_name.size() == 0)
			continue;
		string type;
		if (tc_strcmp(ref_name.c_str(), "HTML") == 0) {
			type.assign("1");
		}
		else if (tc_strcmp(ref_name.c_str(), PDF_REFERENCE) == 0) {
			type.assign("0");
		}
		ifail = add_bop_dataset_xml(name_prefix, ds_tag, SOP_detail, type.c_str(), sop_index, ref_name.c_str(), product);
		if (ifail != ITK_ok) {
			goto end;
		}
	}
	DOFREE(ds_tags);
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, IMAN_RENDERING, &ds_cnt, &ds_tags));
	for (int i = 0; i < ds_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		tag_t ds_tag = ds_tags[i];
		ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
		string ref_name = strcmp("DirectModel", object_type) == 0 ? JTPART : "";
		WriteLog(debug, ">> %d. %s [%s] ref_name: [%s]", i + 1, object_string, object_type, ref_name.c_str());
		if (ref_name.size() == 0)
			continue;
		string type;
		if (tc_strcmp(ref_name.c_str(), JTPART) == 0) {
			type.assign("1");
		}
		ifail = add_bop_dataset_xml(name_prefix, ds_tag, SOP_detail, type.c_str(), sop_index, ref_name.c_str(), product);
		if (ifail != ITK_ok) {
			goto end;
		}
	}

end:
	DOFREE(object_string);
	DOFREE(object_type);
	DOFREE(ds_tags);
	DOFREE(metarget_tags);
	return ifail;
}





/*
*对于装配，下载PDF类型的数据集
*rev_tag：版本
*detail：xml
*sop_index：序号，递增
*/
int add_bop_datasets_with11(tag_t rev_tag, TiXmlElement* detail, int& sop_index) {
	int ifail = ITK_ok;
	int ds_cnt = 0, metarget_cnt = 0;
	tag_t* ds_tags = NULL, * metarget_tags = NULL;
	char* object_string = NULL, * object_type = NULL;
	string name_prefix;
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, ITEM_ID, &object_string));
	name_prefix.assign(object_string);
	name_prefix.append("_");
	WriteLog(debug, "文件名称添加前缀：%s", name_prefix.c_str());
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, IMAN_SPECIFICATION, &ds_cnt, &ds_tags));
	WriteLog(debug, "数据集数量：%d", ds_cnt);
	for (int i = 0; i < ds_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		tag_t ds_tag = ds_tags[i];
		ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
		WriteLog(debug, ">> %d. %s [%s]", i + 1, object_string, object_type);
		if (strcmp("PDF", object_type) == 0) {
			ifail = add_bop_dataset(name_prefix, ds_tag, detail, "3", sop_index, PDF_REFERENCE);
			if (ifail != ITK_ok) {
				goto end;
			}
		}
	}
end:
	DOFREE(object_string);
	DOFREE(object_type);
	DOFREE(ds_tags);
	DOFREE(metarget_tags);
	return ifail;
}
/*
*对于焊接或机加下载PDF类型的数据集
*rev_tag：版本
*detail：xml
*sop_index：序号，递增
*/
int add_bop_datasets_with12(tag_t rev_tag, TiXmlElement* detail, int& sop_index) {
	int ifail = ITK_ok;
	int ds_cnt = 0, metarget_cnt = 0;
	tag_t* ds_tags = NULL, * metarget_tags = NULL;
	char* object_string = NULL, * object_type = NULL;
	string name_prefix;
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, "item_id", &object_string));
	name_prefix.assign(object_string);
	name_prefix.append("_");
	WriteLog(debug, "文件名称添加前缀：%s", name_prefix.c_str());
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, IMAN_SPECIFICATION, &ds_cnt, &ds_tags));
	WriteLog(debug, "数据集数量：%d", ds_cnt);
	for (int i = 0; i < ds_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		tag_t ds_tag = ds_tags[i];
		ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
		WriteLog(debug, ">> %d. %s [%s]", i + 1, object_string, object_type);
		if (strcmp("DirectModel", object_type) == 0 || strcmp("直接模型", object_type) == 0) {
			ifail = add_bop_dataset(name_prefix, ds_tag, detail, "1", sop_index, JTPART);
			if (ifail != ITK_ok) {
				goto end;
			}
		}


	}
end:
	DOFREE(object_string);
	DOFREE(object_type);
	DOFREE(ds_tags);
	DOFREE(metarget_tags);
	return ifail;
}


int add_bop_datasets(tag_t gy_line, TiXmlElement* detail, int& sop_index) {
	int ifail = ITK_ok;
	int ds_cnt = 0, metarget_cnt = 0;
	tag_t* ds_tags = NULL, rev_tag = NULLTAG, * metarget_tags = NULL;
	char* object_string = NULL, * object_type = NULL;
	string name_prefix;
	ITKCALL(ifail = AOM_ask_value_tag(gy_line, "bl_revision", &rev_tag));
	if (ifail != ITK_ok) {
		goto end;
	}
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, "item_id", &object_string));
	name_prefix.assign(object_string);
	name_prefix.append("_");
	WriteLog(debug, "文件名称添加前缀：%s", name_prefix.c_str());
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, "IMAN_specification", &ds_cnt, &ds_tags));
	WriteLog(debug, "数据集数量：%d", ds_cnt);
	for (int i = 0; i < ds_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		tag_t ds_tag = ds_tags[i];
		ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
		WriteLog(debug, ">> %d. %s [%s]", i + 1, object_string, object_type);
		if (strcmp("UGMASTER", object_type) == 0) {
			ifail = add_bop_dataset(name_prefix, ds_tag, detail, "1", sop_index, "UGPART");
			if (ifail != ITK_ok) {
				goto end;
			}
		}
		else if (strcmp("HTML", object_type) == 0) {
			ifail = add_bop_dataset(name_prefix, ds_tag, detail, "2", sop_index, "HTML");
			if (ifail != ITK_ok) {
				goto end;
			}
		}
	}
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, "IMAN_METarget", &metarget_cnt, &metarget_tags));
	WriteLog(debug, "制造目标数量：%d", metarget_cnt);
	for (int i = 0; i < metarget_cnt; i++) {
		DOFREE(object_string);
		DOFREE(object_type);
		DOFREE(ds_tags);
		tag_t metarget_tag = metarget_tags[i];
		ITKCALL(ifail = AOM_ask_value_string(metarget_tag, "object_string", &object_string));
		ITKCALL(ifail = WSOM_ask_object_type2(metarget_tag, &object_type));
		WriteLog(debug, ">> %d. %s [%s]", i + 1, object_string, object_type);
		ITKCALL(ifail = AOM_ask_value_tags(metarget_tag, "IMAN_Rendering", &ds_cnt, &ds_tags));
		WriteLog(debug, ">> 数据集数量：%d", ds_cnt);
		for (int j = 0; j < ds_cnt; j++) {
			DOFREE(object_string);
			DOFREE(object_type);
			tag_t ds_tag = ds_tags[j];
			ITKCALL(ifail = AOM_ask_value_string(ds_tag, "object_string", &object_string));
			ITKCALL(ifail = WSOM_ask_object_type2(ds_tag, &object_type));
			WriteLog(debug, ">> %d.%d %s [%s]", i + 1, j + 1, object_string, object_type);
			if (strcmp("DirectModel", object_type) == 0 || strcmp("直接模型", object_type) == 0) {
				ifail = add_bop_dataset(name_prefix, ds_tag, detail, "1", sop_index, "JTPART");
				if (ifail != ITK_ok) {
					goto end;
				}
			}
		}
	}
end:
	DOFREE(object_string);
	DOFREE(object_type);
	DOFREE(ds_tags);
	DOFREE(metarget_tags);
	return ifail;
}

int add_material_detail_xml(tag_t gx_line, TiXmlElement* detail, vector<ERP_Config> child_configs, map<string, string> overwrite, vector<string>& QFgxtouliao_type_prefs,int * material_detail_cnt) {
	WriteLog(debug, ">> 开始material_detail资源信息");
	int ifail = ITK_ok, child_cnt = 0;
	tag_t* child_lines = NULL;
	char* object_name = NULL;
	ITKCALL(ifail = AOM_ask_value_string(gx_line,"bl_rev_object_name", &object_name));
	ITKCALL(ifail = BOM_line_ask_child_lines(gx_line, &child_cnt, &child_lines));
	int break_outer = 0; // 标志变量
	for (size_t i = 0; i < child_cnt; i++)
	{
		char* object_type = NULL;
		tag_t child_tag = child_lines[i];
		tag_t child_rev = NULLTAG;
		ITKCALL(ifail = AOM_ask_value_tag(child_tag, "bl_revision", &child_rev));
		ITKCALL(ifail = WSOM_ask_object_type2(child_rev, &object_type));
		for (const auto& str : QFgxtouliao_type_prefs) {
			if (tc_strcmp(object_type,str.c_str())==0) {
				tag_t rev_tag = NULLTAG, form_tag = NULLTAG, * form_tags = NULL;
				int att_cnt = 0;
				
				ITKCALL(ifail = AOM_ask_value_tag(gx_line, "bl_revision", &rev_tag));
				if (rev_tag != NULLTAG) {
					ITKCALL(AOM_ask_value_tags(rev_tag, "IMAN_master_form_rev", &att_cnt, &form_tags));
				}
				if (att_cnt <= 0) continue;
				if (tc_strcmp(object_name, "下料")==0) {
					char* qf8zl = NULL;
					ITKCALL(ifail = AOM_ask_value_string(form_tags[0], "qf8zl", &qf8zl));
					overwrite.insert(make_pair("STDQTY", tc_strlen(qf8zl) > 0 ? qf8zl : ""));
					DOFREE(qf8zl);
					
				}
				//char* child_name = NULL;
				//ITKCALL(ifail = AOM_ask_value_string(child_tag, "object_name", &child_name));
				//char* qf8xhgg = NULL;
				//ITKCALL(ifail = AOM_UIF_ask_value(form_tags[0], "qf8xhgg", &qf8xhgg));
				//
				//WriteLog(debug, ">>  %s [%s]", child_name, qf8xhgg);
				//overwrite.insert(make_pair("MATERIALNAME", tc_strlen(child_name) > 0 ? child_name : ""));
				//overwrite.insert(make_pair("MATERIALSPEC", tc_strlen(qf8xhgg) > 0 ? qf8xhgg : ""));
				TiXmlElement* row = new TiXmlElement("row");
				*material_detail_cnt += 1;
				row->SetAttribute("seq", *material_detail_cnt);
				detail->LinkEndChild(row);
				read_data_sp(child_tag, NULLTAG, NULLTAG, child_configs, row, overwrite);
				break_outer = 1;
				DOFREE(form_tags);
				//DOFREE(child_name);
				//DOFREE(qf8xhgg);
				break;
			}
		}
		if (break_outer) {
			break; // 跳出外层循环
		}
		DOFREE(object_type);
	}
	WriteLog(debug, ">> 结束material_detail资源信息");
	DOFREE(object_name);
	return ifail;

}

int add_bop_sublines(tag_t gx_line, tag_t raw_material_rev_tag, TiXmlElement* detail, const char* title, const char* subline_types, int& index, vector<ERP_Config> child_configs, map<string, string> overwrite) {
	int ifail = ITK_ok;
	char* object_string = NULL;
	vector<tag_t> resource_lines;
	get_child_line(gx_line, subline_types, resource_lines);
	int line_cnt = resource_lines.size();
	WriteLog(debug, "%s数量：%d", title, line_cnt);
	for (int j = 0; j < line_cnt; j++) {
		DOFREE(object_string);
		tag_t reource_line = resource_lines[j];
		ITKCALL(ifail = AOM_ask_value_string(reource_line, "object_string", &object_string));
		WriteLog(debug, ">> %d. 添加%s [%s]", j + 1, title, object_string);
		TiXmlElement* row = new TiXmlElement("row");
		row->SetAttribute("seq", index++);
		detail->LinkEndChild(row);
		read_data_sp(reource_line, NULLTAG, raw_material_rev_tag, child_configs, row, overwrite);
	}
end:
	resource_lines.clear();
	return ifail;
}
/*
*输入键，从map集合中找到值
*mapX：map集合
*element：map的键
*/
vector<tag_t> findElement(map<tag_t, vector<tag_t>> mapX, tag_t element) {
	map<tag_t, vector<tag_t>>::iterator it;
	it = mapX.find(element);
	vector<tag_t> vec;
	if (it != mapX.end()) {
		return  it->second;
	}
	else {
		return vec;
	}
}
/*
*输入键，从map集合中找到值
*mapX：map集合
*element：map的键
*/
tag_t findElement(map<tag_t, tag_t> mapX, tag_t element) {
	map<tag_t, tag_t>::iterator it;
	it = mapX.find(element);
	tag_t value = NULL;
	if (it != mapX.end()) {
		return  it->second;
	}
	else {
		return value;
	}


}

/*
*通过输入元素来判断该集合是否存在改元素
*v:集合
*element:元素
*返回1表示不包含该元素
*返回0表示包含该元素
*/
int hasElement(vector<tag_t> v, tag_t element) {
	//vector<string>::iterator it = find(v.begin(), v.end(), element);
	//if (it != v.end()) {
	//	return 0;//包含
	//}
	//else {
	//	return 1;//不包含
	//
	//}
	if (count(v.begin(), v.end(), element))
	{
		//cout << "Element"<< element<<" found" << endl;
		return 0;//包含
	}
	else
	{
		//cout << "Element "<< element<<"NOT found" << endl;
		return 1;//不包含
	}

}
/*
*遍历循环整个工艺BOM，把其中的工艺，工序，工位加到集合中
*topBomLinev:bomline
*gyVec:集合
*/
void loopGy(tag_t topBomLine, vector<tag_t>& gyVec) {
	int ifail = 0;
	int wlCnt = 0;
	tag_t* wlLines = NULL;
	ITKCALL(ifail = BOM_line_ask_child_lines(topBomLine, &wlCnt, &wlLines));
	if (wlCnt > 0) {
		for (int n = 0; n < wlCnt; n++) {
			tag_t childWlRev = NULL;
			ITKCALL(ifail = AOM_ask_value_tag(wlLines[n], BL_REVISION, &childWlRev));
			char* childWlType = NULL;
			AOM_ask_value_string(childWlRev, OBJECT_TYPE, &childWlType);
			if (tc_strcmp(childWlType, QF8GYREVISION) == 0 || tc_strcmp(childWlType, QF8GXREVISION) == 0 || tc_strcmp(childWlType, MESTATIONREVISION) == 0) {
				int	exist = hasElement(gyVec, childWlRev);
				if (exist == 1) {
					gyVec.push_back(childWlRev);
				}
			}
			DOFREE(childWlType);
			loopGy(wlLines[n], gyVec);
		}
	}
	DOFREE(wlLines);
}
/*
*遍历循环整个物料BOM，把其中的虚设加到集合中
*topBomLinev:bomline
*gyVec:集合
*/
void loopWl(tag_t topBomLine, vector<tag_t>& wlVec, vector<tag_t>& gyVec) {
	int ifail = 0;
	int wlCnt = 0;
	tag_t* wlLines = NULL;
	ITKCALL(ifail = BOM_line_ask_child_lines(topBomLine, &wlCnt, &wlLines));
	for (int i = 0; i < wlCnt; i++) {
		tag_t childWl = wlLines[i];
		tag_t childRev = NULL;
		ITKCALL(ifail = AOM_ask_value_tag(childWl, BL_REVISION, &childRev));
		tag_t* form_tags = NULL;
		int form_cnt = 0;
		ITKCALL(AOM_ask_value_tags(childRev, IMAN_MASTER_FORM_REV, &form_cnt, &form_tags));
		if (form_cnt > 0) {
			char* value = NULL;
			tag_t form_tag = form_tags[0];
			ITKCALL(ifail = AOM_UIF_ask_value(form_tag, QF8LAIYUAN, &value));
			if (ifail == ITK_ok) {
				WriteLog(debug, ">> qf8laiyuan = [%s]", value);
				if (strcmp("虚设", value) == 0) {
					int	exist = hasElement(wlVec, childRev);
					if (exist == 1) {
						wlVec.push_back(childRev);
					}
					//获取BOP关系下的工艺对象
					int BOPcnt = 0;
					tag_t* BOPtags = NULL;
					ITKCALL(AOM_ask_value_tags(childRev, QF8BOPRELATION, &BOPcnt, &BOPtags));
					char* gyType = NULL;
					for (int j = 0; j < BOPcnt; j++) {
						tag_t gyTag = BOPtags[j];
						AOM_ask_value_string(gyTag, OBJECT_TYPE, &gyType);
						if (tc_strcmp(gyType, QF8GYREVISION) == 0) {
							exist = hasElement(gyVec, gyTag);
							if (exist == 1) {

								gyVec.push_back(gyTag);
							}
							int bom_num = 0;
							tag_t* bom_tags = NULLTAG;
							AOM_ask_value_tags(gyTag, STRUCTURE_REVISIONS, &bom_num, &bom_tags);
							if (bom_num > 0)
							{
								tag_t only_bom = bom_tags[0];
								tag_t bom_window_tag = NULLTAG, top_bom_line_tag = NULLTAG, rev_tag = NULLTAG, item_tag = NULLTAG;
								ITKCALL(ifail = BOM_create_window(&bom_window_tag));
								ITKCALL(ifail = BOM_set_window_top_line_bvr(bom_window_tag, only_bom, &top_bom_line_tag));
								//循环方法：遍历所有层级，把工艺上的工序和工位加到集合中
								loopGy(top_bom_line_tag, gyVec);
								ITKCALL(ifail = BOM_close_window(bom_window_tag));//有开必有关
							}
							DOFREE(bom_tags);
						}
					}
					DOFREE(BOPtags);
					DOFREE(gyType);
				}
			}
			DOFREE(value);
		}
		DOFREE(form_tags);
		//TODO 循环遍历所有层级
		loopWl(childWl, wlVec, gyVec);
	}
	DOFREE(wlLines);

}


/*
*为每个工位下的工序集合添加检验信息
*/
void addInspectionInformationWithVec(TiXmlElement* row, vector<tag_t> gxVec) {
	TiXmlElement* qcitem_detail = new TiXmlElement("detail");
	qcitem_detail->SetAttribute("name", "qcitem_detail");
	row->LinkEndChild(qcitem_detail);
	int seq = 0;
	for (int n = 0; n < gxVec.size(); n++) {
		tag_t gxRev = gxVec[n];
		int attachCnt = 0;
		tag_t* attachTags = NULL;
		char* gxId = NULL;
		char* gxRevisionId = NULL;
		AOM_ask_value_string(gxRev, "item_id", &gxId);
		AOM_ask_value_string(gxRev, "item_revision_id", &gxRevisionId);
		string excelName = "";
		excelName = excelName.append(gxId).append("_").append(gxRevisionId).append("_JYX.xlsx");
		cout << "excelName" << excelName << endl;
		AOM_ask_value_tags(gxRev, IMAN_SPECIFICATION, &attachCnt, &attachTags);
		for (int x = 0; x < attachCnt; x++) {
			tag_t excelTag = attachTags[x];
			char* excelType = NULL;
			AOM_ask_value_string(excelTag, "object_type", &excelType);
			cout << "excelType" << excelType << endl;

			if (tc_strcmp("MSExcelX", excelType) == 0) {
				char* excelNameChar = NULL;
				AOM_ask_value_string(excelTag, "object_name", &excelNameChar);
				cout << "excelNameChar" << excelNameChar << endl;

				if (tc_strcmp(excelName.c_str(), excelNameChar) == 0) {
					//添加检验信息
					cout << "找到excel" << endl;
					//readExcel(qcitem_detail, seq, excelTag, "xlsx");
				}
			}
			DOFREE(excelType);
		}
		DOFREE(attachTags);
		DOFREE(gxId);
		DOFREE(gxRevisionId);

	}


}
/*
*为单个工序添加检验信息
*/
void addInspectionInformationWithGxLine(TiXmlElement* row, tag_t  gxLine) {
	tag_t rev_tag = NULL;
	int ifail = 0;
	if (gxLine != NULLTAG) {
		ITKCALL(ifail = AOM_ask_value_tag(gxLine, BL_REVISION, &rev_tag));
	}

	TiXmlElement* qcitem_detail = new TiXmlElement("detail");
	qcitem_detail->SetAttribute("name", "qcitem_detail");
	row->LinkEndChild(qcitem_detail);
	int seq = 0;
	tag_t gxRev = rev_tag;
	int attachCnt = 0;
	tag_t* attachTags = NULL;
	char* gxId = NULL;
	char* gxRevisionId = NULL;
	AOM_ask_value_string(gxRev, "item_id", &gxId);
	AOM_ask_value_string(gxRev, "item_revision_id", &gxRevisionId);
	string excelName = "";
	excelName = excelName.append(gxId).append("_").append(gxRevisionId).append("_JYX.xlsx");
	AOM_ask_value_tags(gxRev, "IMAN_specification", &attachCnt, &attachTags);
	for (int x = 0; x < attachCnt; x++) {
		tag_t excelTag = attachTags[x];
		char* excelType = NULL;
		AOM_ask_value_string(excelTag, "object_type", &excelType);
		if (tc_strcmp("MSExcelX", excelType) == 0) {
			char* excelNameChar = NULL;
			AOM_ask_value_string(excelTag, "object_name", &excelNameChar);
			if (tc_strcmp(excelName.c_str(), excelNameChar) == 0) {
				//添加检验信息
				//readExcel(qcitem_detail, seq, excelTag, "xlsx");
			}
		}
		DOFREE(excelType);
	}
	DOFREE(attachTags);
	DOFREE(gxId);
	DOFREE(gxRevisionId);
}



int cycle_bop(tag_t bomline_tag, tag_t raw_material_rev_tag, vector<ERP_Config> parent_configs, vector<ERP_Config> child_configs1, vector<ERP_Config> child_configs2, vector<ERP_Config> child_configs3, vector<ERP_Config> child_configs4, TiXmlElement* bom_data, int& bomSeq, vector<string>& added_lines, tag_t wlRev
	, vector<string>& QFgxtouliao_type_prefs, map<string, string>& QF_JYXtype_map, vector<string>& QF_MES_TYPE_prefs) {
	int ifail = ITK_ok, child_cnt = 0, att_cnt = 0;

	tag_t rev_tag = NULLTAG;
	char* rev_uid = NULL, * object_string = NULL;
	vector<tag_t> meop_lines;
	if (bomline_tag == NULLTAG || parent_configs.size() == 0 || child_configs1.size() == 0 || child_configs2.size() == 0 || child_configs3.size() == 0 || child_configs4.size() == 0) {
		return ifail;
	}
	ITKCALL(ifail = AOM_ask_value_tag(bomline_tag, bomAttr_lineObjectTag, &rev_tag));
	//ITKCALL(ifail = AOM_ask_value_tag(bomline_tag, "bl_revision", &rev_tag));
	if (rev_tag == NULLTAG) {
		return ifail;
	}
	ITK__convert_tag_to_uid(rev_tag, &rev_uid);
	if (inArray(added_lines, rev_uid)) {
		return ifail;
	}
	added_lines.push_back(rev_uid);
	DOFREE(rev_uid);
	vector<tag_t> MEProcessRevision_Vec;



	// 获取所有工序行
	{
		tag_t* gx_lines = NULL;
		ITKCALL(ifail = BOM_line_ask_child_lines(bomline_tag, &child_cnt, &gx_lines));
		for (int k = 0; k < child_cnt; k++) {
			DOFREE(object_string);
			tag_t gx_line = gx_lines[k], gx_rev = NULLTAG;
			logical is_gy = false;
			ITKCALL(ifail = AOM_ask_value_string(gx_line, "object_string", &object_string));
			// WriteLog(debug, ">> %d. 添加工序 [%s]", k + 1, object_string);
			// 过滤非工序行
			{
				ITKCALL(ifail = AOM_ask_value_tag(gx_line, "bl_revision", &gx_rev));
				if (gx_rev == NULLTAG) {
					WriteLog(debug, ">> 获取bom行版本失败[%s]", object_string);
				}
				else {
					tag_t rev_type = NULLTAG;
					ITKCALL(ifail = TCTYPE_ask_object_type(gx_rev, &rev_type));
					ITKCALL(ifail = TCTYPE_is_type_of_as_str(rev_type, "MEProcessRevision", &is_gy));
				}
			}
			if (is_gy) {
				MEProcessRevision_Vec.push_back(gx_line);
				int child_cnt2 = 0;
				tag_t* gx_lines2 = NULL;
				ITKCALL(ifail = BOM_line_ask_child_lines(gx_line, &child_cnt2, &gx_lines2));
				WriteLog(debug, ">> 展开中间层[%s] -> [%d]", object_string, child_cnt2);
				for (int j = 0; j < child_cnt2; j++) {
					meop_lines.push_back(gx_lines2[j]);
				}
				DOFREE(gx_lines2);
			}
			else {
				meop_lines.push_back(gx_line);
			}
		}
		DOFREE(gx_lines);
	}
	child_cnt = meop_lines.size();
	WriteLog(debug, "第二层BOM行数量 [%d]", child_cnt);
	if (child_cnt == 0) {
		goto end;
	}
	{
		map<string, string> overwrite1;
		int material_detail_cnt = 0;
		// 填写工艺数据
		TiXmlElement* data_row = new TiXmlElement("row");
		data_row->SetAttribute("seq", bomSeq);
		bomSeq += 1;
		bom_data->LinkEndChild(data_row);
		int reource_index = 1, sup_index = 1, material_index = 1, sop_index = 1, qcitem_index = 1;
		read_data_sp(bomline_tag, NULLTAG, raw_material_rev_tag, parent_configs, data_row, overwrite1);
		TiXmlElement* gx_detail = new TiXmlElement("detail");
		gx_detail->SetAttribute("name", "opstaiton_detail");
		TiXmlElement* resource_detail = new TiXmlElement("detail");
		resource_detail->SetAttribute("name", "opresource_detail");
		TiXmlElement* sup_detail = new TiXmlElement("detail");
		sup_detail->SetAttribute("name", "opsup_detail");
		TiXmlElement* material_detail = new TiXmlElement("detail");
		material_detail->SetAttribute("name", "material_detail");
		TiXmlElement* sop_detail = new TiXmlElement("detail");
		sop_detail->SetAttribute("name", "sop_detail");
		TiXmlElement* qcitem_detail = new TiXmlElement("detail");
		qcitem_detail->SetAttribute("name", "qcitem_detail");
		data_row->LinkEndChild(gx_detail);
		data_row->LinkEndChild(resource_detail);
		data_row->LinkEndChild(sup_detail);
		data_row->LinkEndChild(material_detail);
		data_row->LinkEndChild(sop_detail);
		data_row->LinkEndChild(qcitem_detail);
		// 填写工序数据
		//TODO 20240123 需要区分装配或者其他情况

		char* wl_type = NULL;
		bool isPrefOtherType = false;
		ITKCALL(ifail = WSOM_ask_object_type2(wlRev, &wl_type));
		//11 10
		if (tc_strcmp(wl_type, "QF8AssemblyRevision") == 0 || tc_strcmp(wl_type, "QF8ProductRevision") == 0) {
			isPrefOtherType = false;
			//找gy
		}
		//12 13
		else if (find(QF_MES_TYPE_prefs.begin(), QF_MES_TYPE_prefs.end(), wl_type) != QF_MES_TYPE_prefs.end()) {
			isPrefOtherType = true;
			//找gx
		}
		
		if (!isPrefOtherType) {
			
			int gy_count = MEProcessRevision_Vec.size();
			for (int k = 0; k < gy_count; k++) {
				DOFREE(object_string);
				tag_t gy_line = MEProcessRevision_Vec[k], gy_rev = NULLTAG;
				ITKCALL(ifail = AOM_ask_value_string(gy_line, "object_string", &object_string));
				ITKCALL(ifail = AOM_ask_value_tag(gy_line, BL_REVISION, &gy_rev));

				WriteLog(debug, ">> %d. 添加工艺 [%s]", k + 1, object_string);
				map<string, string> overwrite;
				string op_no, next_station, op_no_new;

				char* object_name = NULL;
				ITKCALL(ifail = AOM_ask_value_string(gy_line, "bl_item_object_name", &object_name));
				op_no.assign(object_name);
				DOFREE(object_name);

				if (k == gy_count - 1) {
					next_station.assign("END");
				}
				else {

					tag_t next_gx_rev = NULLTAG;
					ITKCALL(ifail = AOM_ask_value_tag(MEProcessRevision_Vec[k + 1], BL_REVISION, &next_gx_rev));
					char* prop_value = NULL;
					int form_cnt = 0;
					tag_t* formTag = NULL;
					AOM_ask_value_tags(next_gx_rev, IMAN_MASTER_FORM_REV, &form_cnt, &formTag);
					ITKCALL(ifail = AOM_UIF_ask_value(formTag[0], QF8GXBM, &prop_value));

					if (ifail == ITK_ok) {
						WriteLog(debug, "  >> [%s] = [%s]", QF8GXBM, prop_value);
						next_station.assign(prop_value);
					}
					DOFREE(prop_value);
					DOFREE(formTag);

				}
				//20240318 焊接和机加 op_no和next_station设的是版本表单属性qf8gxbm
				char* prop_value = NULL;
				int form_cnt = 0;
				tag_t* formTag = NULL;
				AOM_ask_value_tags(gy_rev, IMAN_MASTER_FORM_REV, &form_cnt, &formTag);
				ITKCALL(ifail = AOM_UIF_ask_value(formTag[0], QF8GXBM, &prop_value));

				if (ifail == ITK_ok) {
					WriteLog(debug, "  >> [%s] = [%s]", QF8GXBM, prop_value);
					op_no_new.assign(prop_value);
				}
				DOFREE(prop_value);
				DOFREE(formTag);

				overwrite.insert(make_pair("op_no", op_no_new));//20240318 焊接和机加 op_no设的是版本表单属性qf8gxbm
				overwrite.insert(make_pair("op_name", op_no));//20240123 新增字段op_name,值为当前工序的名称
				overwrite.insert(make_pair("opno", op_no_new));
				overwrite.insert(make_pair("opname", op_no));
				overwrite.insert(make_pair("next_station", next_station));
				// 工序
				{
					// 添加第一行
					if (k == 0) {
						map<string, string> overwrite0;
						TiXmlElement* row = new TiXmlElement("row");
						row->SetAttribute("seq", "1");
						gx_detail->LinkEndChild(row);
						overwrite0.insert(make_pair("op_no", "START"));
						overwrite0.insert(make_pair("opno", "START"));
						overwrite0.insert(make_pair("prepare_time", "0"));//20240315当为start时，准备工时和加工工时设为0
						overwrite0.insert(make_pair("product_time", "0"));
						//overwrite.insert(make_pair("op_name", "开始"));//20240123 新增字段op_name,值为当前工序的名称
						overwrite0.insert(make_pair("next_station", op_no_new));//20240318 焊接和机加 op_no和next_station设的是版本表单属性qf8gxbm
						overwrite0.insert(make_pair("ratio", "1"));//2025-01-16 增加默认值
						read_data_sp(NULLTAG, NULLTAG, NULLTAG, child_configs1, row, overwrite0);
					}
					TiXmlElement* row = new TiXmlElement("row");
					row->SetAttribute("seq", k + 2);
					gx_detail->LinkEndChild(row);
					read_data_sp(gy_line, NULLTAG, raw_material_rev_tag, child_configs1, row, overwrite);

				}
				// 资源
				{
					add_bop_sublines(gy_line, raw_material_rev_tag, resource_detail, "工艺资源", "QF8ProcessRES", reource_index, child_configs2, overwrite);
				}
				// 辅料
				{
					add_bop_sublines(gy_line, raw_material_rev_tag, sup_detail, "工艺辅料", "QF8Ingredients", sup_index, child_configs3, overwrite);
				}
				add_material_detail_xml(gy_line, material_detail, child_configs4, overwrite, QFgxtouliao_type_prefs, &material_detail_cnt);
				overwrite.clear();
			}
		}
		else {
			for (int k = 0; k < child_cnt; k++) {
				DOFREE(object_string);
				tag_t gx_line = meop_lines[k], gx_rev = NULLTAG;
				ITKCALL(ifail = AOM_ask_value_string(gx_line, "object_string", &object_string));
				ITKCALL(ifail = AOM_ask_value_tag(gx_line, BL_REVISION, &gx_rev));

				WriteLog(debug, ">> %d. 添加工序 [%s]", k + 1, object_string);
				map<string, string> overwrite;
				string op_no, next_station, op_no_new;
				if (k == -1) {
					op_no.assign("START");
				}
				else {
					char* object_name = NULL;
					ITKCALL(ifail = AOM_ask_value_string(gx_line, "bl_item_object_name", &object_name));
					op_no.assign(object_name);
					DOFREE(object_name);
				}
				if (k == child_cnt - 1) {
					next_station.assign("END");
				}
				else {
					/*char* object_name = NULL;
					ITKCALL(ifail = AOM_ask_value_string(meop_lines[k + 1], "bl_item_object_name", &object_name));
					next_station.assign(object_name);
					DOFREE(object_name);*/
					//20240319修改
					tag_t next_gx_rev = NULLTAG;
					ITKCALL(ifail = AOM_ask_value_tag(meop_lines[k + 1], BL_REVISION, &next_gx_rev));
					char* prop_value = NULL;
					int form_cnt = 0;
					tag_t* formTag = NULL;
					AOM_ask_value_tags(next_gx_rev, IMAN_MASTER_FORM_REV, &form_cnt, &formTag);
					ITKCALL(ifail = AOM_UIF_ask_value(formTag[0], QF8GXBM, &prop_value));

					if (ifail == ITK_ok) {
						WriteLog(debug, "  >> [%s] = [%s]", QF8GXBM, prop_value);
						next_station.assign(prop_value);
					}
					DOFREE(prop_value);
					DOFREE(formTag);

				}
				//20240318 焊接和机加 op_no和next_station设的是版本表单属性qf8gxbm
				char* prop_value = NULL;
				int form_cnt = 0;
				tag_t* formTag = NULL;
				AOM_ask_value_tags(gx_rev, IMAN_MASTER_FORM_REV, &form_cnt, &formTag);
				ITKCALL(ifail = AOM_UIF_ask_value(formTag[0], QF8GXBM, &prop_value));

				if (ifail == ITK_ok) {
					WriteLog(debug, "  >> [%s] = [%s]", QF8GXBM, prop_value);
					op_no_new.assign(prop_value);
				}
				DOFREE(prop_value);
				DOFREE(formTag);


				overwrite.insert(make_pair("op_no", op_no_new));//20240318 焊接和机加 op_no设的是版本表单属性qf8gxbm
				overwrite.insert(make_pair("op_name", op_no));//20240123 新增字段op_name,值为当前工序的名称
				overwrite.insert(make_pair("opno", op_no_new));
				overwrite.insert(make_pair("opname", op_no));
				overwrite.insert(make_pair("next_station", next_station));
				// 工序
				{
					// 添加第一行
					if (k == 0) {
						map<string, string> overwrite0;
						TiXmlElement* row = new TiXmlElement("row");
						row->SetAttribute("seq", "1");
						gx_detail->LinkEndChild(row);
						overwrite0.insert(make_pair("op_no", "START"));
						overwrite0.insert(make_pair("opno", "START"));
						overwrite0.insert(make_pair("prepare_time", "0"));//20240315当为start时，准备工时和加工工时设为0
						overwrite0.insert(make_pair("product_time", "0"));
						//overwrite.insert(make_pair("op_name", "开始"));//20240123 新增字段op_name,值为当前工序的名称
						overwrite0.insert(make_pair("next_station", op_no_new));//20240318 焊接和机加 op_no和next_station设的是版本表单属性qf8gxbm
						overwrite0.insert(make_pair("ratio", "1"));//2025-01-16 增加默认值
						read_data_sp(NULLTAG, NULLTAG, NULLTAG, child_configs1, row, overwrite0);
					}
					TiXmlElement* row = new TiXmlElement("row");
					row->SetAttribute("seq", k + 2);
					gx_detail->LinkEndChild(row);
					read_data_sp(gx_line, NULLTAG, raw_material_rev_tag, child_configs1, row, overwrite);

					WriteLog(debug, ">> 开始添加工序的检验信息");
					//添加检验信息
					//addInspectionInformationWithGxLine(row, gx_line);

				}
				// 资源
				{
					add_bop_sublines(gx_line, raw_material_rev_tag, resource_detail, "工艺资源", "QF8ProcessRES", reource_index, child_configs2, overwrite);
				}
				// 辅料
				{
					add_bop_sublines(gx_line, raw_material_rev_tag, sup_detail, "工艺辅料", "QF8Ingredients", sup_index, child_configs3, overwrite);
				}
				// 物料
				/*{
				add_bop_sublines(gx_line, raw_material_rev_tag, material_detail, "物料", "!QF8ProcessRES;QF8Ingredients", material_index, child_configs4, overwrite);
				}*/
				add_material_detail_xml(gx_line, material_detail, child_configs4, overwrite, QFgxtouliao_type_prefs, &material_detail_cnt);
				overwrite.clear();
			}

		}
		//wlRev 流程目标对象
		
		char* wl_item_id = NULL;
		char* wl_rev_id = NULL;
		ITKCALL(ifail = WSOM_ask_object_type2(wlRev, &wl_type));
		ITKCALL(ifail = AOM_ask_value_string(wlRev,"item_id", & wl_item_id));
		ITKCALL(ifail = ITEM_ask_rev_id2(wlRev, &wl_rev_id));
		ProductInfo product = { wl_item_id, wl_rev_id, "", ""};
		ifail = add_bop_datasets_xml(wlRev, sop_detail, sop_index, qcitem_detail, qcitem_index, product, QF_JYXtype_map, true);
		//isPrefOtherType = false; 找gy isPrefOtherType = true; 找gx
		if (!isPrefOtherType) {
			int vec_size = MEProcessRevision_Vec.size();
			for (int k = 0; k < vec_size; k++) {
				char* qf8gxbm = NULL;
				int formCnt = 0;
				tag_t* formTags = NULL;
				tag_t gy_line = MEProcessRevision_Vec[k], gy_rev = NULLTAG;
				char* gy_rev_id = NULL;
				int bomAttr_lineItemRevTag_int = 0;
				ITKCALL(BOM_line_look_up_attribute(bomAttr_lineObjectTag, &bomAttr_lineItemRevTag_int));
				ITKCALL(ifail = BOM_line_ask_attribute_tag(gy_line, bomAttr_lineItemRevTag_int, &gy_rev));
				if (gy_rev) {
					ITKCALL(ifail = AOM_ask_value_tags(gy_rev, IMAN_MASTER_FORM_REV, &formCnt, &formTags));
					ITKCALL(ifail = AOM_ask_value_string(formTags[0], QF8GXBM, &qf8gxbm));
					ITKCALL(ifail = ITEM_ask_rev_id2(gy_rev, &gy_rev_id));
					ProductInfo product = { wl_item_id, wl_rev_id, qf8gxbm, gy_rev_id };
					ifail = add_bop_datasets_xml(gy_rev, sop_detail, sop_index, qcitem_detail, qcitem_index, product, QF_JYXtype_map, false);
				}
				else {
					WriteLog(debug, "工艺版本获取失败");
				}
				DOFREE(qf8gxbm);
				DOFREE(formTags);
				DOFREE(gy_rev_id);
			}
		}
		else {
			int vec_size = meop_lines.size();
			for (int k = 0; k < vec_size; k++) {
				char* qf8gxbm = NULL;
				int formCnt = 0;
				tag_t* formTags = NULL;
				tag_t gx_line = meop_lines[k], gx_rev = NULLTAG;
				char* gx_rev_id = NULL;
				ITKCALL(ifail = AOM_ask_value_tag(gx_line, BL_REVISION, &gx_rev));
				ITKCALL(ifail = AOM_ask_value_tags(gx_rev, IMAN_MASTER_FORM_REV, &formCnt, &formTags));
				ITKCALL(ifail = AOM_ask_value_string(formTags[0], QF8GXBM, &qf8gxbm));
				ITKCALL(ifail = ITEM_ask_rev_id2(gx_rev, &gx_rev_id));
				ProductInfo product = { wl_item_id, wl_rev_id, qf8gxbm, gx_rev_id };
				ifail = add_bop_datasets_xml(gx_rev, sop_detail,  sop_index, qcitem_detail, qcitem_index, product,QF_JYXtype_map, false);
				DOFREE(qf8gxbm);
				DOFREE(formTags);
				DOFREE(gx_rev_id);
			}
		}


		DOFREE(wl_type);
		DOFREE(wl_item_id);
		DOFREE(wl_rev_id);

	}
end:
	meop_lines.clear();
	DOFREE(object_string);
	return ifail;
}

int add_map(map<string, string>& map, string key, string value) {
	int ifail = ITK_ok;
	if (key.size() > 0 && map.count(key) == 0) {
		map.insert(make_pair(key, value));
	}
	return ifail;
}

int read_bop_data(tag_t rev_tag, vector<ERP_Config> parent_configs, vector<ERP_Config> child_configs1, vector<ERP_Config> child_configs2, vector<ERP_Config> child_configs3, vector<ERP_Config> child_configs4, vector<ERP_Config> wl_data_configs, TiXmlElement* bom_data, TiXmlElement* wl_data, int& bomSeq, int& wlSeq
	, vector<string>& added_wl, vector<string>& added_lines, vector<string>& QFgxtouliao_type_prefs, map<string, string>& QF_JYXtype_map, vector<string>& QF_MES_TYPE_prefs) {
	int ifail = ITK_ok, wl_cnt = 0;
	tag_t bom_view = NULLTAG, bvr_tag = NULLTAG, bom_window = NULLTAG, topBomLine = NULLTAG, * wl_tags = NULL;
	char* objectstring = NULL, * rev_uid = NULL, * object_type = NULL;
	tag_t raw_material_rev_tag = NULLTAG;
	ITKCALL(ifail = AOM_ask_value_string(rev_tag, "object_string", &objectstring));
	//填写物料数据
	ITKCALL(ifail = AOM_ask_value_tags(rev_tag, "IMAN_METarget", &wl_cnt, &wl_tags));//TODO 20240123 在BOP关系下
	//int wlType = 0;
	//20240123 先判断工艺关联的物料是装配（11），焊接（12），机加（13）
	tag_t wlRev = NULL;
	char* objectId = NULL;
	WriteLog(debug, "读取BOP - %s", objectstring);
	logical isZiZhi = true;
	for (int i = 0; i < wl_cnt; i++) {
		//wlType = 0;
		DOFREE(objectId);
		DOFREE(objectstring);
		DOFREE(object_type);
		DOFREE(rev_uid);
		tag_t wl_tag = wl_tags[i];
		wlRev = wl_tag;
		int wl_form_num = 0;
		tag_t* wl_forms = NULL;
		char* qf8laiyuan = NULL;
		ITKCALL(ifail = AOM_ask_value_string(wl_tag, "object_string", &objectstring));
		ITKCALL(ifail = WSOM_ask_object_type2(wl_tag, &object_type));
		WriteLog(debug, "( %d / %d ) 读取物料数据：%s [%s]", i + 1, wl_cnt, objectstring, object_type);
		ITKCALL(AOM_ask_value_tags(wl_tag, "IMAN_master_form_rev", &wl_form_num, &wl_forms));
		if (wl_form_num > 0) {
			ITKCALL(ifail = AOM_UIF_ask_value(wl_forms[0], "qf8laiyuan", &qf8laiyuan));
			WriteLog(debug, "qf8laiyuan [%s]", qf8laiyuan);
		}
		if (tc_strcmp(qf8laiyuan, "自制")!=0) {
			WriteLog(debug, "物料【%s】 qf8laiyuan属性不为自制，跳过 ", objectstring);
			isZiZhi = false;
			continue;
		}
		//ITKCALL(ifail = AOM_ask_value_string(wl_tag, "item_id", &objectId));
		////获取itemid前两位
		//char firstTwo[3];
		//strncpy(firstTwo, objectId, 2);
		//firstTwo[2] = '\0';  // 添加字符串结束符
		//printf("前两位字符是：%s\n", firstTwo);
		//WriteLog(debug, "前两位字符是 - %s", firstTwo);
		//if (tc_strcmp("11", firstTwo) == 0) {
		//	wlType = 11;
		//}
		//else if (tc_strcmp("12", firstTwo) == 0) {
		//	wlType = 12;
		//}
		//else if (tc_strcmp("13", firstTwo) == 0) {
		//	wlType = 13;
		//}
		//WriteLog(debug, "前两位字符是  %d", wlType);


		ITK__convert_tag_to_uid(wl_tag, &rev_uid);
		if (inArray(added_wl, rev_uid)) {
			WriteLog(debug, ">> 取消重复添加物料");
			continue;
		}
		added_wl.push_back(rev_uid);
		TiXmlElement* row_gi = new TiXmlElement("row");
		row_gi->SetAttribute("seq", wlSeq);
		wl_data->LinkEndChild(row_gi);
		read_data(NULLTAG, wl_tag, NULLTAG, NULLTAG, wl_data_configs, row_gi);

		tag_t relation_tag = NULLTAG;
		ITKCALL(ifail = GRM_find_relation_type("QF8SCXXrelation", &relation_tag));
		if (relation_tag == NULLTAG) {
			EMH_store_error_s1(EMH_severity_error, ITK_err, "未定义关系：QF8SCXXrelation");
			ifail = ITK_err;
			goto end;
		}
		//2025-01-16 新增字段USER_DEFINED01 取值伪文件夹QF8SCXXrelation底下的版本对象(根据首选项QFgxtouliao_type找到第一个版本对象)的属性object_name+版本表单属性合并。
		int child_cnt = 0;
		tag_t* child_tags = NULL;
		ITKCALL(ifail = GRM_list_secondary_objects_only(wl_tag, relation_tag, &child_cnt, &child_tags));
		WriteLog(debug, ">> 找到QF8SCXXrelation子对象数量：%d", child_cnt);
		int break_outer = 0; // 标志变量
		string USER_DEFINED01;
		for (int k = 0; k < child_cnt; k++) {
			tag_t child_tag = child_tags[k];
			ITKCALL(ifail = AOM_ask_value_string(child_tag, "object_name", &objectstring));
			ITKCALL(ifail = WSOM_ask_object_type2(child_tag, &object_type));
			WriteLog(debug, ">> %d. %s [%s]", k + 1, objectstring, object_type);
			for (const auto& str : QFgxtouliao_type_prefs) {
				if (str == object_type) {
					USER_DEFINED01.append(objectstring);
					tag_t* form_tags = NULL;
					int form_cnt = 0;
					char* qf8xhgg = NULL;
					ITKCALL(AOM_ask_value_tags(child_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
					if (form_cnt > 0) {
						ITKCALL(ifail = AOM_UIF_ask_value(form_tags[0], "qf8xhgg", &qf8xhgg));
						if ( tc_strlen(qf8xhgg) > 0) {
							USER_DEFINED01.append(" ").append(qf8xhgg);
						}
					}
					break_outer = 1;
					break;
				}
				if (break_outer) {
					break; // 跳出外层循环
				}
			}
		}
		TiXmlElement* field = new TiXmlElement("field");
		field->SetAttribute("name", "item_raw_spec");
		field->SetAttribute("type", "string");
		set_xml_value(field, USER_DEFINED01.c_str());
		row_gi->LinkEndChild(field);

		wlSeq += 1;
		// 如果工艺的关联（IMAN_METarget）目标类型为QF8PartRevision且表单属性qf8laiyuan为自制，则获取其QF8SCXXrelation关系下QF8RawMaterialsRevision类型对象的信息
		if (strcmp("QF8PartRevision", object_type) == 0) {
			tag_t* form_tags = NULL;
			int form_cnt = 0;
			ITKCALL(AOM_ask_value_tags(wl_tag, "IMAN_master_form_rev", &form_cnt, &form_tags));
			if (form_cnt > 0) {
				char* value = NULL;
				tag_t form_tag = form_tags[0];
				ITKCALL(ifail = AOM_UIF_ask_value(form_tag, "qf8laiyuan", &value));
				if (ifail == ITK_ok) {
					WriteLog(debug, ">> qf8laiyuan = [%s]", value);
					if (strcmp("自制", value) == 0) {
						for (int k = 0; k < child_cnt; k++) {
							DOFREE(objectstring);
							DOFREE(object_type);
							tag_t child_tag = child_tags[k];
							ITKCALL(ifail = AOM_ask_value_string(child_tag, "object_string", &objectstring));
							ITKCALL(ifail = WSOM_ask_object_type2(child_tag, &object_type));
							//WriteLog(debug, ">> %d. %s [%s]", k + 1, objectstring, object_type);
							if (strcmp("QF8RawMaterialsRevision", object_type) == 0) {
								raw_material_rev_tag = child_tag;
								break;
							}
						}
						
					}
				}
				DOFREE(value);
			}
			DOFREE(form_tags);
		}
		DOFREE(child_tags);
	}
	if (!isZiZhi) {
		goto end;
	}

	
	ITKCALL(ifail = GetBomView(rev_tag, "view", &bom_view, &bvr_tag, debug));
	if (bom_view != NULLTAG) {
		ITKCALL(ifail = BOM_create_window(&bom_window));
		ITKCALL(ifail = BOM_set_window_top_line_bvr(bom_window, bvr_tag, &topBomLine));
		ifail = cycle_bop(topBomLine, raw_material_rev_tag, parent_configs, child_configs1, child_configs2, child_configs3, child_configs4, bom_data, bomSeq, added_lines, wlRev, QFgxtouliao_type_prefs, QF_JYXtype_map, QF_MES_TYPE_prefs);
		ITKCALL(BOM_close_window(bom_window));
	}
	else {
		WriteLog(debug, "没有找到BOM视图");
		// return ITK_err;
	}
end:
	DOFREE(rev_uid);
	DOFREE(wl_tags);
	DOFREE(objectstring);
	DOFREE(object_type);
	return ifail;
}

int getChildNodes(TiXmlNode* parent, const char* node_name, vector<TiXmlNode*>& children) {
	TiXmlNode* cNode = NULL;
	for (cNode = parent->FirstChildElement(); cNode; cNode = cNode->NextSiblingElement()) {
		const char* cNodeName = cNode->ToElement()->Value();
		if (strcmp(node_name, cNodeName) != 0) {
			continue;
		}
		children.push_back(cNode);
	}
	return ITK_ok;
}

int parseResponse(string resp) {
	int ifail = ITK_ok;
	//string resp;
	//resp.assign(U2G(respUTF.c_str()));
	WriteLog(debug, "\n收到返回数据：%s", resp.c_str());
	TiXmlDocument* doc = new TiXmlDocument();
	doc->Parse(resp.c_str(), 0, TIXML_DEFAULT_ENCODING);
	TiXmlElement* response = doc->RootElement();
	if (!response) {
		WriteLog(debug, "未找到根节点");
		EMH_store_error_s1(EMH_severity_error, ITK_err, "无法解析ERP接口返回信息");
		return ITK_err;
	}
	vector<TiXmlNode*> payloads, params, data_responses, datainfos;
	getChildNodes(response, "payload", payloads);
	for (TiXmlNode* payload : payloads)
	{
		getChildNodes(payload, "param", params);
	}
	for (TiXmlNode* param : params)
	{
		getChildNodes(param, "data_response", data_responses);
	}
	for (TiXmlNode* data_response : data_responses)
	{
		getChildNodes(data_response, "datainfo", datainfos);
	}
	if (datainfos.size() <= 0) {
		WriteLog(debug, "未找到datainfo节点");
		EMH_store_error_s1(EMH_severity_error, ITK_err, "无法解析ERP接口返回信息");
		return ITK_err;
	}
	// WriteLog(debug, "XXXXXXXXXXXXXXXX");
	for (TiXmlNode* datainfo : datainfos)
	{
		string status;
		string message;
		TiXmlNode* cNode = NULL;
		for (cNode = datainfo->FirstChildElement(); cNode; cNode = cNode->NextSiblingElement()) {
			const char* cNodeName = cNode->ToElement()->Value();
			if (strcmp("parameter", cNodeName) != 0) {
				continue;
			}
			const char* key = cNode->ToElement()->Attribute("key");
			const char* text = cNode->ToElement()->GetText();
			if (key == NULL || text == NULL) {
				continue;
			}
			if (strcmp("Status", key) == 0) {
				status.assign(text);
			}
			else if (strcmp("Message", key) == 0) {
				message.assign(U2G(text));
			}
		}
		WriteLog(debug, "status [%s] message [%s]", status.c_str(), message.c_str());
		if (strcmp("0", status.c_str()) != 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "ERP接口返回错误信息", message.size() > 0 ? message.c_str() : "无错误描述");
			ifail = ITK_err;
		}
	}
	return ifail;
}

int parseXMLResponse(FILE* LOGFILE,string resp) {
	int ifail = ITK_ok;
	//string resp;
	//resp.assign(U2G(respUTF.c_str()));
	TiXmlDocument* doc = new TiXmlDocument();
	doc->Parse(resp.c_str(), 0, TIXML_DEFAULT_ENCODING);
	TiXmlElement* response = doc->RootElement();
	if (!response) {
		WriteToFile(LOGFILE, "无法解析ERP接口返回信息,未找到报文根节点");
		EMH_store_error_s1(EMH_severity_error, ITK_err, "无法解析ERP接口返回信息");
		return ITK_err;
	}
	vector<TiXmlNode*> payloads, params, data_responses, execution;
	getChildNodes(response, "payload", payloads);
	for (TiXmlNode* payload : payloads)
	{
		getChildNodes(payload, "param", params);
	}
	for (TiXmlNode* param : params)
	{
		getChildNodes(param, "data_response", data_responses);
	}
	for (TiXmlNode* data_response : data_responses)
	{
		getChildNodes(data_response, "execution", execution);
	}
	if (execution.size() <= 0) {
		WriteToFile(LOGFILE, "无法解析ERP接口返回信息,未找到execution节点");
		EMH_store_error_s1(EMH_severity_error, ITK_err, "无法解析ERP接口返回信息");
		return ITK_err;
	}
	// WriteLog(debug, "XXXXXXXXXXXXXXXX");
	for (TiXmlNode* execNode : execution)
	{
		TiXmlElement* execElement = execNode->ToElement();
		if (execElement) {
			// 获取 execution 元素的属性
			const char* status = execElement->Attribute("status");
			const char* description = execElement->Attribute("description");
			// 打印获取到的属性
			char formattedString[512];  // 用于存储格式化后的字符串

			// 使用 snprintf 格式化字符串，确保不会超过缓冲区大小
			snprintf(formattedString, sizeof(formattedString), "status [%s] message [%s]", status, description);
			WriteToFile(LOGFILE, formattedString);
			if (tc_strcmp("0", status) != 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "ERP接口返回错误信息", tc_strlen(description) > 0 ? description : "无错误描述");
				ifail = ITK_err;
			}
			else {
				WriteToFile(LOGFILE, "发送成功");
			}
		}
	}


	return ifail;
}

/*
*循环遍历计算总工时
*top_bom_line_tag 循环遍历的层级对象
*totalGs 总工时
*/
int Loop_BOM_count_totalGs(tag_t top_bom_line_tag, char* localType, double& totalGs, double& totalGj, vector<string> pref_unitPrice)
{
	tag_t rev_tag; int ifail = 0;
	int child_count = 0; tag_t* child_tags = NULLTAG;
	ITKCALL(ifail = BOM_line_ask_all_child_lines(top_bom_line_tag, &child_count, &child_tags));
	int form_nums = 0;
	tag_t* form_tags = NULLTAG;
	for (int n = 0; n < child_count; n++)
	{
		tag_t temRev = NULL; tag_t tempItem = NULL; tag_t lstRev = NULL;
		ITKCALL(ifail = AOM_ask_value_tag(child_tags[n], BL_LINE_OBJECT, &temRev));
		char* object_type = NULL;
		AOM_ask_value_string(temRev, OBJECT_TYPE, &object_type);
		if ((tc_strcmp(localType, "QF8ProductRevision") == 0 || tc_strcmp(localType, "QF8AssemblyRevision") == 0) || tc_strcmp(object_type, QF8GXREVISION) == 0) {
			int formCnt = 0;
			tag_t* formTags = NULL;
			char* zbgs = NULL;
			char* jggs = NULL;
			char* gxCode = NULL;
			char* gxName = NULL;
			double unitPrice = 0;
			double zbgsDouble = 0;
			double jggsDouble = 0;
			AOM_ask_value_tags(temRev, IMAN_MASTER_FORM_REV, &formCnt, &formTags);
			AOM_ask_value_string(formTags[0], QF8ZBGS, &zbgs);
			AOM_ask_value_string(formTags[0], QF8JGGS, &jggs);
			AOM_ask_value_string(formTags[0], QF8GXBM, &gxCode);
			AOM_ask_value_string(temRev, OBJECT_NAME, &gxName);
			try
			{
				zbgsDouble = atof(zbgs);
			}
			catch (const std::exception&)
			{
			}
			try
			{
				jggsDouble = atof(jggs);
			}
			catch (const std::exception&)
			{
			}
			totalGs = totalGs + zbgsDouble + jggsDouble;
			if (findUnitPrice(pref_unitPrice, gxCode, gxName, unitPrice)) {
				writeLog("当前工价、单价："+ std::to_string(totalGj) +" || " + std::to_string(unitPrice));
				totalGj = totalGj + (((zbgsDouble + jggsDouble) / 60) * unitPrice);
			}
			else {
				writeLog("Not found or conversion failed");
			}
			writeLog("此时工时-工价: " + std::to_string(totalGs)+ "-" + std::to_string(totalGj));
			DOFREE(formTags);
			DOFREE(zbgs);
			DOFREE(jggs);
			DOFREE(gxCode);
			DOFREE(gxName);
		}
		else if ((tc_strcmp(localType, "QF8WeldAssemblyRevision") == 0 || tc_strcmp(localType, "QF8PartRevision") == 0) && tc_strcmp(object_type, QF8GYREVISION) == 0) {
			Loop_BOM_count_totalGs(child_tags[n], localType, totalGs, totalGj, pref_unitPrice);
		}
		DOFREE(object_type);
	}
	//清空指针
	DOFREE(form_tags);
	DOFREE(child_tags);
	return 0;
}
/*
*double转string，并且去除后面跟随的0
*/
void Double2String(char* str, int len, double value) {
	len = snprintf(str, len, "%0.9lf", value);//实现小数点后9位的转换
	for (int i = len - 1; i >= 0; i--) {
		if (str[i] == '\0') continue;

		if (i == 0) return;
		//小数点退出
		if (str[i] == '.') {
			return;
		}

		if (str[i] != '0') return;

		//如果小数点后一位也是零，这个0就不能去掉
		if (str[i] == '0' && str[i - 1] != '0') {
			if (str[i - 1] == '.') {
				return;
			}
			str[i] = '\0';
			//return;
		}
		//把零消除掉
		if (str[i] == '0') {
			str[i] = '\0';
		}
	}
}

char* doubleToCharPtr(double value) {
	std::stringstream ss;
	ss << value;
	std::string str = ss.str();
	char* result = new char[str.length() + 1];
	strcpy(result, str.c_str());
	return result;
}

/*
*计算总工时
*revTag 循环遍历的层级对象
*fieldX xml节点
*/
void addZgs(tag_t revTag, TiXmlElement* fieldX, vector<string> pref_unitPrice) {
	int BOPcnt = 0;
	tag_t* BOPtags = NULL;
	char* localType = NULL;
	char* objectStr = NULL;
	ITKCALL(AOM_ask_value_string(revTag, "object_type", &localType));
	ITKCALL(AOM_ask_value_string(revTag, "object_string", &objectStr));
	ITKCALL(AOM_ask_value_tags(revTag, QF8BOPRELATION, &BOPcnt, &BOPtags));
	int formCnt = 0;
	tag_t* formTags = NULL;
	char* gxType = NULL;
	char* gyType = NULL;
	char* zbgs = NULL;
	char* jggs = NULL;
	double zbgsDouble = 0;
	int ifail = 0;
	double totalGs = 0;
	double jggsDouble = 0;
	double totalGj = 0;

	writeLog("***开始计算当前对象工时工价："+ std::string(objectStr)+" || " + std::string(localType));
	DOFREE(objectStr);
	//修改功能QF8ProductRevision、QF8AssemblyRevision为两层结构2024/12/30
	for (int j = 0; j < BOPcnt; j++) {
		zbgsDouble = 0;
		jggsDouble = 0;
		// gyTag ==> 第一层工艺对象
		tag_t gyTag = BOPtags[j];
		AOM_ask_value_string(gyTag, OBJECT_TYPE, &gyType);
		if (tc_strcmp(gyType, QF8GYREVISION) == 0) {
			int bom_num = 0;
			// bom_tags ==> 第二层gyBOM对象
			tag_t* bom_tags = NULLTAG;
			AOM_ask_value_tags(gyTag, STRUCTURE_REVISIONS, &bom_num, &bom_tags);
			if (bom_num > 0)
			{
				tag_t only_bom = bom_tags[0];
				tag_t bom_window_tag = NULLTAG, top_bom_line_tag = NULLTAG, rev_tag = NULLTAG, item_tag = NULLTAG;
				ITKCALL(ifail = BOM_create_window(&bom_window_tag));
				ITKCALL(ifail = BOM_set_window_top_line_bvr(bom_window_tag, only_bom, &top_bom_line_tag));
				//循环方法
				Loop_BOM_count_totalGs(top_bom_line_tag, localType, totalGs, totalGj, pref_unitPrice);
				ITKCALL(ifail = BOM_close_window(bom_window_tag));//有开必有关
			}
			DOFREE(bom_tags);
		}
	}


	//设置到版本表单上
	writeLog("end:总工时(分钟):"+ std::to_string(totalGs));
	totalGs = totalGs / 480;//20240129 四舍五入保留一位小数(PLM总工时/60/8)=ERP固定人时
	//totalGs = (int)(10 * totalGs + 0.5);
	//totalGs = totalGs / 10;

	// 处理 totalGs 的值
	if (totalGs < 0.1) {
		totalGs = 0.1;
	}
	else {
		totalGs = round(totalGs * 10) / 10; // 四舍五入保留一位小数
	}

	cout << "总工时（天）" << totalGs << endl;
	writeLog("end:总工时(天):" + std::to_string(totalGs));
	char zgsStr[20];
	char* zgjStr = doubleToCharPtr(totalGj);
	Double2String(zgsStr, 20, totalGs);
	//sprintf(zgsStr, "%f", totalGs);
	printf("Double2String = %s\n", zgsStr);
	//string zgsString = to_string(totalGs);
	cout << "总工时（天）去0后" << zgsStr << endl;
	writeLog("end:总工时（天）去0后:");
    writeLog(zgsStr);
	AOM_ask_value_tags(revTag, IMAN_MASTER_FORM_REV, &formCnt, &formTags);
	POM_AM__set_application_bypass(true);
	ITKCALL(AOM_load(formTags[0]));
	ITKCALL(AOM_lock(formTags[0]));
	ITKCALL(AOM_set_value_string(formTags[0], QF8ZGS, zgsStr));
	ITKCALL(AOM_set_value_string(formTags[0], QF8ZGJ, zgjStr));
	POM_AM__set_application_bypass(false);
	AOM_save(formTags[0]);
	AOM_unlock(formTags[0]);
	//2025/1/7 取消工时工价的接口版本
	//TiXmlElement* zgsField = new TiXmlElement("field");
	//zgsField->SetAttribute("name", DYNAMIC_LEAD_TIME_M);//传递到标准机时上
	//zgsField->SetAttribute("type", "string");
	////20240307临时取消传递总工时
	//set_xml_value(zgsField, zgsStr);
	//TiXmlElement* zgjField = new TiXmlElement("field");
	//zgjField->SetAttribute("name", CE2_CUR_LEVEL);//传递到标准机时上
	//zgjField->SetAttribute("type", "string");
	//set_xml_value(zgjField, zgjStr);
	//fieldX->LinkEndChild(zgsField);
	//fieldX->LinkEndChild(zgjField);
	DOFREE(BOPtags);
	DOFREE(formTags);
	DOFREE(zbgs);
	DOFREE(jggs);
	DOFREE(gxType);
}
/*
*添加仓库字段
*revTag 物料对象
*fieldX xml节点
*/

void addCk(tag_t revTag, TiXmlElement* fieldX) {
	WriteLog(debug, "添加仓库字段");
	tag_t ico_tag = NULLTAG, cls_tag = NULLTAG;
	long classId = 0;//分类id
	string laiyuan = "";//来源
	int ifail = 0;
	ITKCALL(ifail = ICS_ask_classification_object(revTag, &ico_tag));
	if (ico_tag != NULLTAG) {
		ITKCALL(ifail = ICS_ask_class_of_classification_obj(ico_tag, &cls_tag));
		if (cls_tag != NULLTAG) {
			char* cls_id = NULL, * cls_name = NULL;
			ITKCALL(ifail = ICS_ask_id_name(cls_tag, &cls_id, &cls_name));
			if (ifail == ITK_ok) {
				classId = atoll(cls_id);
			}
			DOFREE(cls_id);
			DOFREE(cls_name);
		}
	}
	//获取版本表单上的来源qf8laiyuan
	int formCnt = 0; tag_t* formTag = NULL;
	AOM_ask_value_tags(revTag, IMAN_MASTER_FORM_REV, &formCnt, &formTag);
	char* qf8laiyuan = NULL;
	AOM_UIF_ask_value(formTag[0], QF8LAIYUAN, &qf8laiyuan);
	laiyuan = qf8laiyuan;
	DOFREE(formTag);
	DOFREE(qf8laiyuan);
	//分情况讨论仓库
	string ck = "";//仓库
	WriteLog(debug, "=来源==%s==", laiyuan);
	//20240328 新增默认仓库设置：分类ID=11、12、13 物料来源=虚设，传1002
	/*
	if (classId == 11 || classId == 12) {
		if (tc_strcmp(laiyuan.c_str(), ZIZHI) == 0 || tc_strcmp(laiyuan.c_str(), WAIGOU) == 0||  tc_strcmp(laiyuan.c_str(), XUSHE) == 0) {
			ck = CK1002;
		}
	}
	else if (classId == 13) {
		if (tc_strcmp(laiyuan.c_str(), ZIZHI) == 0 || tc_strcmp(laiyuan.c_str(), XUSHE) == 0) {
			ck = CK1002;
		}
		else if (tc_strcmp(laiyuan.c_str(), WAIGOU) == 0) {
			ck = CK1001;
		}
	}
	else if (classId >= 100101 && classId <= 100999) {
		if (tc_strcmp(laiyuan.c_str(), ZIZHI) == 0 || tc_strcmp(laiyuan.c_str(), WAIGOU) == 0) {
			ck = CK1003;
		}
	}
	else if ((classId >= 1501 && classId <= 2599) || (classId >= 150101 && classId <= 259999)) {
		if (tc_strcmp(laiyuan.c_str(), WAIGOU) == 0) {
			ck = CK1001;
		}
	}
	*/
	// 2024/12/30 仓库修改
	if (tc_strcmp(laiyuan.c_str(), WAIGOU) == 0 || (classId == 13 && tc_strcmp(laiyuan.c_str(), WAIGOU) == 0)) {
		ck = CK1001;
	}
	else {
		if (classId == 10) {
			ck = CK1003;
		}
		else if (classId == 11 || classId == 12 || (classId == 13 && tc_strcmp(laiyuan.c_str(), ZIZHI) == 0)) {
			ck = CK1002;
		}
		else if ((classId >= 1501 && classId <= 2599) || (classId >= 150101 && classId <= 259999)) {
			ck = CK1001;
		}
	}
	TiXmlElement* field = new TiXmlElement("field");
	field->SetAttribute("name", INBOUND_WAREHOUSE_CODE);
	field->SetAttribute("type", "string");
	set_xml_value(field, ck.c_str());
	TiXmlElement* field2 = new TiXmlElement("field");
	field2->SetAttribute("name", OUTBOUND_WAREHOUSE_CODE);
	field2->SetAttribute("type", "string");
	set_xml_value(field2, ck.c_str());
	fieldX->LinkEndChild(field);
	fieldX->LinkEndChild(field2);
}

int QF_ERP_wl(EPM_action_message_t msg) {
	int ifail = ITK_ok, arg_cnt = 0, task_count = 0;
	char* arg = NULL, * argflag = NULL, * argvalue = NULL, * propvalue = NULL, * objectstring = NULL, * task_name = NULL, * task_type = NULL;
	char* user_id = NULL, * user_name = NULL;
	tag_t task_tag = NULLTAG, rootTask_tag = NULLTAG, user_tag = NULLTAG;
	tag_t* task_tags = NULL;
	bool bypass = false;

	vector<tag_t> target_tags;
	char* log_file_path;
	FILE* LOG;
	vector<ERP_Config> gi_configs, pli_configs;
	vector<string> pref_values, target_types, pref_unitPrice;

	/**
	* 0=endpoint
	* 1=host prod
	* 2=host ver
	* 3=host ip
	* 4=host lang
	* 5=host acct
	* 6=service prod
	* 7=service name
	* 8=service ip
	* 9=param product name
	* 10=param template name
	* 可选11=host id
	* 可选12=service id
	*/
	string end_point, host_prod, host_ver, host_ip, host_lang, host_acct;
	string service_prod, service_name, service_ip;
	string param_productname, param_template_name, param_import_data;

	arg_cnt = TC_number_of_arguments(msg.arguments);
	if (arg_cnt > 0) {
		for (int i = 0; i < arg_cnt; i++) {
			arg = TC_next_argument(msg.arguments);
			ITKCALL(ifail = ITK_ask_argument_named_value(arg, &argflag, &argvalue));
			if (strcmp(argflag, "bypass") == 0) {
				bypass = true;
			}
			else if (strcmp(argflag, "include_type") == 0) {
				Split(argvalue, ";", target_types);
			}
			MEM_free(argflag);
			MEM_free(argvalue);
		}
	}
	ITKCALL(POM_get_user(&user_name, &user_tag));
	ITKCALL(SA_ask_user_identifier2(user_tag, &user_id));
	if (debug) {
		initUserDir(user_id);
		CreateUserFile(debug, "QF_ERP_wl", user_id, &log_file_path, &LOG);
		printf("日志文件路径：%s\n", log_file_path);
	}
	DOFREE(user_id);
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "开始执行：QF_ERP_wl");
	WriteLog(debug, "=============================================================");

	removeLogsFile();
	WriteToFile(LOG, "START");
	task_tag = msg.task;
	if (task_tag == NULLTAG) {
		goto end;
	}
	if (bypass) {
		POM_AM__set_application_bypass(true);
	}
	ifail = getAllTargetRevs(task_tag, target_tags, target_types);
	if (target_tags.size() == 0) {
		WriteToFile(LOG, "TARGET NOT FOUND");
		WriteLog(debug, "未找到需要传递的对象");
		goto end;
	}
	//解析首选项
	{
		vector<string> gi_prefs, pli_prefs;
		ifail = getPrefStrings(PREF_ERP_WL, TC_preference_site, pref_values);
		if (pref_values.size() < 11) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_WL_GI, TC_preference_site, gi_prefs);
		parseErpConfigs(gi_prefs, 0, gi_configs);
		if (gi_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL_GI);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_WL_PLI, TC_preference_site, pli_prefs);
		parseErpConfigs(pli_prefs, 0, pli_configs);
		if (pli_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL_PLI);
			ifail = ITK_err;
			goto end;
		}

		// 2024/12/30 新增工价首选项
		writeLog("获取首选项");
		char** tempValues;
		int tempNum = 0;
		ITKCALL(ifail = PREF_ask_char_values("QF8GJB", &tempNum, &tempValues));
		if (ifail == ITK_ok) {
			for (int i = 0; i < tempNum; i++)
			{
				pref_unitPrice.push_back(tempValues[i]);
				writeLog(tempValues[i]);
			}
		}
		DOFREE(tempValues);
	}
	// 构造xml
	{
		char date_string[20];
		struct tm* p;
		time_t now;
		time(&now);
		p = localtime(&now);
		memset(date_string, 0, sizeof(date_string));
		sprintf(date_string, "%4d%02d%02d%02d%02d%02d%03d", 1900 + p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, getCurrentMilliSeconds());

		TiXmlDocument* doc = new TiXmlDocument();

		TiXmlElement* host = new TiXmlElement("host");
		TiXmlElement* service = new TiXmlElement("service");
		host->SetAttribute("prod", pref_values[1].c_str());
		host->SetAttribute("ver", pref_values[2].c_str());
		host->SetAttribute("ip", pref_values[3].c_str());
		host->SetAttribute("id", pref_values.size() > 11 ? pref_values[11].c_str() : "");
		host->SetAttribute("lang", pref_values[4].c_str());
		host->SetAttribute("timestamp", date_string);
		host->SetAttribute("acct", pref_values[5].c_str());
		service->SetAttribute("prod", pref_values[6].c_str());
		service->SetAttribute("name", pref_values[7].c_str());
		service->SetAttribute("ip", pref_values[8].c_str());
		service->SetAttribute("id", pref_values.size() > 12 ? pref_values[12].c_str() : "");

		TiXmlElement* request = new TiXmlElement("request");
		request->SetAttribute("type", "sync");
		string host_service_str;
		TiXmlPrinter host_printer, service_printer;
		host->Accept(&host_printer);
		host_service_str.append(trim(host_printer.CStr()));
		service->Accept(&service_printer);
		host_service_str.append(trim(service_printer.CStr()));

		//unsigned char buffer[2048];
		//memcpy(buffer, host_service_str.c_str(), host_service_str.size());
		WriteLog(debug, "Get MD5 From \"%s\"", host_service_str.c_str());
		request->SetAttribute("key", str2md5(host_service_str).c_str());

		doc->LinkEndChild(request);
		request->LinkEndChild(host);
		request->LinkEndChild(service);
		TiXmlElement* payload = new TiXmlElement("payload");
		request->LinkEndChild(payload);
		TiXmlElement* param = new TiXmlElement("param");
		param->SetAttribute("key", "std_data");
		param->SetAttribute("type", "xml");
		payload->LinkEndChild(param);
		TiXmlElement* data_request = new TiXmlElement("data_request");
		param->LinkEndChild(data_request);
		TiXmlElement* datainfo = new TiXmlElement("datainfo");
		data_request->LinkEndChild(datainfo);
		TiXmlElement* param1 = new TiXmlElement("parameter");
		param1->SetAttribute("key", "productName");
		param1->SetAttribute("type", "string");
		set_xml_value(param1, pref_values[9].c_str());
		datainfo->LinkEndChild(param1);
		TiXmlElement* param2 = new TiXmlElement("parameter");
		param2->SetAttribute("key", "templateName");
		param2->SetAttribute("type", "string");
		set_xml_value(param2, pref_values[10].c_str());
		datainfo->LinkEndChild(param2);
		TiXmlElement* param3 = new TiXmlElement("parameter");
		param3->SetAttribute("key", "importData");
		param3->SetAttribute("type", "data");
		datainfo->LinkEndChild(param3);
		TiXmlElement* data = new TiXmlElement("data");
		data->SetAttribute("name", "ITEM");
		param3->LinkEndChild(data);
		TiXmlElement* row = new TiXmlElement("row");
		row->SetAttribute("seq", 1);
		data->LinkEndChild(row);
		TiXmlElement* detail_gi = new TiXmlElement("detail");
		detail_gi->SetAttribute("name", "GI");
		row->LinkEndChild(detail_gi);


		for (int i = 0; i < target_tags.size(); i++) {
			TiXmlElement* row_gi = new TiXmlElement("row");
			row_gi->SetAttribute("seq", i + 1);
			detail_gi->LinkEndChild(row_gi);
			ifail = read_data(NULLTAG, target_tags[i], NULLTAG, NULLTAG, gi_configs, row_gi);

			// 特殊处理设置默认值2024/12/30
			// 取消默认的版本
			/*TiXmlElement* defaultField = new TiXmlElement("field");
			defaultField->SetAttribute("name", "BATCH_QUANTITY_M");
			defaultField->SetAttribute("type", "string");
			set_xml_value(defaultField, "1");
			row_gi->LinkEndChild(defaultField);*/

			if (ifail != ITK_ok) {
				goto end;
			}
		}

		TiXmlElement* detail_pli = new TiXmlElement("detail");
		detail_pli->SetAttribute("name", "PLI");
		row->LinkEndChild(detail_pli);
		for (int i = 0; i < target_tags.size(); i++) {
			TiXmlElement* row_pli = new TiXmlElement("row");
			row_pli->SetAttribute("seq", i + 1);
			detail_pli->LinkEndChild(row_pli);
			//	调用 read_data 函数，从 target_tags[i] 中读取数据，并将数据存储在 row_pli 元素中。
			ifail = read_data(NULLTAG, target_tags[i], NULLTAG, NULLTAG, pli_configs, row_pli);
			if (ifail != ITK_ok) {
				goto end;
			}
			//TODO 20240118 pli新增字段总工时
			writeLog("开始给pli添加总工时");
			addZgs(target_tags[i], row_pli, pref_unitPrice);
			//TODO 20240116 对于仓库字段做特殊处理
            writeLog("给pli添加仓库字段做特殊处理");
			addCk(target_tags[i], row_pli);
		}
		writeLog("PLI之后");
		TiXmlPrinter doc_printer;
		doc->Accept(&doc_printer);
		if (LOG) {
			WriteToFile(LOG, "发送请求");
			WriteToFile(LOG, doc_printer.CStr());
		}
		soap* soap = soap_new();
		soap_init(soap);
		soap_register_plugin(soap, plugin);
		soap_set_mode(soap, SOAP_C_UTFSTRING);
		_WS1__invokeSrv* soapReq = new _WS1__invokeSrv();
		soapReq->soap_default(soap);
		soapReq->in0.assign(doc_printer.CStr());
		_WS1__invokeSrvResponse response;
		ifail = soap_call___WS1__invokeSrv(soap, pref_values[0].c_str(), NULL, soapReq, response);
		WriteToFile(LOG, "REQUEST SENT");
		if (ifail == SOAP_OK) {
			if (LOG) {
				WriteToFile(LOG, "RETURN DATA");
				WriteToFile(LOG, response.invokeSrvReturn.c_str());
			}

			writeLog("RETURN DATA");
			writeLog(response.invokeSrvReturn.c_str());
			ifail = parseResponse(response.invokeSrvReturn);
		}
		else {
			WriteLog(debug, "调用接口异常，%d", ifail);
			soap_print_fault(soap, stderr);
			ifail = ITK_err;
			EMH_store_error_s1(EMH_severity_error, ITK_err, "调用接口失败");
		}
		soap_destroy(soap);
		soap_done(soap);
		//ifail = parseResponse("<response> <code>019</code> <message>平台返回同步结果</message> <reqid>99299d70-ce98-43d3-890b-0c2331f04ca7</reqid> <srvver>2.0</srvver> <srvcode>000</srvcode> <payload> <param key=\"std_data\" type=\"xml\"> <data_response> <execution> <status code=\"0\" description=\"\" sql_code=\"0\"/> </execution> <datainfo> <parameter key=\"Status\" type=\"string\">1</parameter> <parameter key=\"Message\" type=\"string\">失败信息</parameter> </datainfo> </data_response> </param> </payload> </response>");
	}

end:
	POM_AM__set_application_bypass(false);
	WriteToFile(LOG, "FINISH");
	target_tags.clear();
	if (LOG)
	{
		fclose(LOG);
		LOG = NULL;
	}
	target_types.clear();
	gi_configs.clear();
	pli_configs.clear();
	pref_values.clear();
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "执行结束：QF_ERP_wl");
	WriteLog(debug, "=============================================================");
	return ifail;
}

int QF_ERP_wl_M(EPM_action_message_t msg) {
	int ifail = ITK_ok, arg_cnt = 0, task_count = 0;
	char* arg = NULL, * argflag = NULL, * argvalue = NULL, * propvalue = NULL, * objectstring = NULL, * task_name = NULL, * task_type = NULL;
	char* user_id = NULL, * user_name = NULL;
	tag_t task_tag = NULLTAG, rootTask_tag = NULLTAG, user_tag = NULLTAG;
	tag_t* task_tags = NULL;
	bool bypass = false;

	vector<tag_t> target_tags;
	char* log_file_path;
	FILE* LOG;
	vector<ERP_Config> gi_configs, pli_configs;
	vector<string> pref_values, target_types, pref_unitPrice;

	/**
	* 0=endpoint
	* 1=host prod
	* 2=host ver
	* 3=host ip
	* 4=host lang
	* 5=host acct
	* 6=service prod
	* 7=service name
	* 8=service ip
	* 9=param product name
	* 10=param template name
	* 可选11=host id
	* 可选12=service id
	*/
	string end_point, host_prod, host_ver, host_ip, host_lang, host_acct;
	string service_prod, service_name, service_ip;
	string param_productname, param_template_name, param_import_data;

	arg_cnt = TC_number_of_arguments(msg.arguments);
	if (arg_cnt > 0) {
		for (int i = 0; i < arg_cnt; i++) {
			arg = TC_next_argument(msg.arguments);
			ITKCALL(ifail = ITK_ask_argument_named_value(arg, &argflag, &argvalue));
			if (strcmp(argflag, "bypass") == 0) {
				bypass = true;
			}
			else if (strcmp(argflag, "include_type") == 0) {
				Split(argvalue, ";", target_types);
			}
			MEM_free(argflag);
			MEM_free(argvalue);
		}
	}
	ITKCALL(POM_get_user(&user_name, &user_tag));
	ITKCALL(SA_ask_user_identifier2(user_tag, &user_id));
	if (debug) {
		initUserDir(user_id);
		CreateUserFile(debug, "QF_ERP_wl_M", user_id, &log_file_path, &LOG);
		printf("日志文件路径：%s\n", log_file_path);
	}
	DOFREE(user_id);
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "开始执行：QF_ERP_wl_M");
	WriteLog(debug, "=============================================================");

	removeLogsFile();
	WriteToFile(LOG, "START");
	task_tag = msg.task;
	if (task_tag == NULLTAG) {
		goto end;
	}
	if (bypass) {
		POM_AM__set_application_bypass(true);
	}
	ifail = getAllTargetRevs(task_tag, target_tags, target_types);
	if (target_tags.size() == 0) {
		WriteToFile(LOG, "TARGET NOT FOUND");
		WriteLog(debug, "未找到需要传递的对象");
		goto end;
	}
	//解析首选项
	{
		vector<string> gi_prefs, pli_prefs;
		ifail = getPrefStrings(PREF_ERP_WL, TC_preference_site, pref_values);
		if (pref_values.size() < 11) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_WL_GI, TC_preference_site, gi_prefs);
		parseErpConfigs(gi_prefs, 0, gi_configs);
		if (gi_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL_GI);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_WL_PLI, TC_preference_site, pli_prefs);
		parseErpConfigs(pli_prefs, 0, pli_configs);
		if (pli_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL_PLI);
			ifail = ITK_err;
			goto end;
		}

		// 2024/12/30 新增工价首选项
		writeLog("获取首选项");
		char** tempValues;
		int tempNum = 0;
		ITKCALL(ifail = PREF_ask_char_values("QF8GJB", &tempNum, &tempValues));
		if (ifail == ITK_ok) {
			for (int i = 0; i < tempNum; i++)
			{
				pref_unitPrice.push_back(tempValues[i]);
				writeLog(tempValues[i]);
			}
		}
		DOFREE(tempValues);
	}
	// 构造xml
	{
		char date_string[20];
		struct tm* p;
		time_t now;
		time(&now);
		p = localtime(&now);
		memset(date_string, 0, sizeof(date_string));
		sprintf(date_string, "%4d%02d%02d%02d%02d%02d%03d", 1900 + p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, getCurrentMilliSeconds());

		TiXmlDocument* doc = new TiXmlDocument();

		TiXmlElement* host = new TiXmlElement("host");
		TiXmlElement* service = new TiXmlElement("service");
		host->SetAttribute("prod", pref_values[1].c_str());
		host->SetAttribute("ver", pref_values[2].c_str());
		host->SetAttribute("ip", pref_values[3].c_str());
		host->SetAttribute("id", pref_values.size() > 11 ? pref_values[11].c_str() : "");
		host->SetAttribute("lang", pref_values[4].c_str());
		host->SetAttribute("timestamp", date_string);
		host->SetAttribute("acct", pref_values[5].c_str());
		service->SetAttribute("prod", pref_values[6].c_str());
		service->SetAttribute("name", pref_values[7].c_str());
		service->SetAttribute("ip", pref_values[8].c_str());
		service->SetAttribute("id", pref_values.size() > 12 ? pref_values[12].c_str() : "");

		TiXmlElement* request = new TiXmlElement("request");
		request->SetAttribute("type", "sync");
		string host_service_str;
		TiXmlPrinter host_printer, service_printer;
		host->Accept(&host_printer);
		host_service_str.append(trim(host_printer.CStr()));
		service->Accept(&service_printer);
		host_service_str.append(trim(service_printer.CStr()));

		//unsigned char buffer[2048];
		//memcpy(buffer, host_service_str.c_str(), host_service_str.size());
		WriteLog(debug, "Get MD5 From \"%s\"", host_service_str.c_str());
		request->SetAttribute("key", str2md5(host_service_str).c_str());

		doc->LinkEndChild(request);
		request->LinkEndChild(host);
		request->LinkEndChild(service);
		TiXmlElement* payload = new TiXmlElement("payload");
		request->LinkEndChild(payload);
		TiXmlElement* param = new TiXmlElement("param");
		param->SetAttribute("key", "std_data");
		param->SetAttribute("type", "xml");
		payload->LinkEndChild(param);
		TiXmlElement* data_request = new TiXmlElement("data_request");
		param->LinkEndChild(data_request);
		TiXmlElement* datainfo = new TiXmlElement("datainfo");
		data_request->LinkEndChild(datainfo);
		TiXmlElement* param1 = new TiXmlElement("parameter");
		param1->SetAttribute("key", "productName");
		param1->SetAttribute("type", "string");
		set_xml_value(param1, pref_values[9].c_str());
		datainfo->LinkEndChild(param1);
		TiXmlElement* param2 = new TiXmlElement("parameter");
		param2->SetAttribute("key", "templateName");
		param2->SetAttribute("type", "string");
		set_xml_value(param2, pref_values[10].c_str());
		datainfo->LinkEndChild(param2);
		TiXmlElement* param3 = new TiXmlElement("parameter");
		param3->SetAttribute("key", "importData");
		param3->SetAttribute("type", "data");
		datainfo->LinkEndChild(param3);
		TiXmlElement* data = new TiXmlElement("data");
		data->SetAttribute("name", "ITEM");
		param3->LinkEndChild(data);
		TiXmlElement* row = new TiXmlElement("row");
		row->SetAttribute("seq", 1);
		data->LinkEndChild(row);
		TiXmlElement* detail_gi = new TiXmlElement("detail");
		detail_gi->SetAttribute("name", "GI");
		row->LinkEndChild(detail_gi);


		for (int i = 0; i < target_tags.size(); i++) {
			TiXmlElement* row_gi = new TiXmlElement("row");
			row_gi->SetAttribute("seq", i + 1);
			detail_gi->LinkEndChild(row_gi);
			ifail = read_data2(NULLTAG, target_tags[i], NULLTAG, NULLTAG, gi_configs, row_gi);

			// 特殊处理设置默认值2024/12/30
			// 取消默认的版本
			/*TiXmlElement* defaultField = new TiXmlElement("field");
			defaultField->SetAttribute("name", "BATCH_QUANTITY_M");
			defaultField->SetAttribute("type", "string");
			set_xml_value(defaultField, "1");
			row_gi->LinkEndChild(defaultField);*/

			if (ifail != ITK_ok) {
				goto end;
			}
		}

		TiXmlElement* detail_pli = new TiXmlElement("detail");
		detail_pli->SetAttribute("name", "PLI");
		row->LinkEndChild(detail_pli);
		for (int i = 0; i < target_tags.size(); i++) {
			TiXmlElement* row_pli = new TiXmlElement("row");
			row_pli->SetAttribute("seq", i + 1);
			detail_pli->LinkEndChild(row_pli);
			//	调用 read_data 函数，从 target_tags[i] 中读取数据，并将数据存储在 row_pli 元素中。
			ifail = read_data2(NULLTAG, target_tags[i], NULLTAG, NULLTAG, pli_configs, row_pli);
			if (ifail != ITK_ok) {
				goto end;
			}
			//TODO 20240118 pli新增字段总工时
			writeLog("开始给pli添加总工时");
			addZgs(target_tags[i], row_pli, pref_unitPrice);
			//TODO 20240116 对于仓库字段做特殊处理
			writeLog("给pli添加仓库字段做特殊处理");
			addCk(target_tags[i], row_pli);
		}
		writeLog("PLI之后");
		TiXmlPrinter doc_printer;
		doc->Accept(&doc_printer);
		if (LOG) {
			WriteToFile(LOG, "发送请求");
			WriteToFile(LOG, doc_printer.CStr());
		}
		soap* soap = soap_new();
		soap_init(soap);
		soap_register_plugin(soap, plugin);
		soap_set_mode(soap, SOAP_C_UTFSTRING);
		_WS1__invokeSrv* soapReq = new _WS1__invokeSrv();
		soapReq->soap_default(soap);
		soapReq->in0.assign(doc_printer.CStr());
		_WS1__invokeSrvResponse response;
		ifail = soap_call___WS1__invokeSrv(soap, pref_values[0].c_str(), NULL, soapReq, response);
		WriteToFile(LOG, "REQUEST SENT");
		if (ifail == SOAP_OK) {
			if (LOG) {
				WriteToFile(LOG, "RETURN DATA");
				WriteToFile(LOG, response.invokeSrvReturn.c_str());
			}

			writeLog("RETURN DATA");
			writeLog(response.invokeSrvReturn.c_str());
			ifail = parseResponse(response.invokeSrvReturn);
		}
		else {
			WriteLog(debug, "调用接口异常，%d", ifail);
			soap_print_fault(soap, stderr);
			ifail = ITK_err;
			EMH_store_error_s1(EMH_severity_error, ITK_err, "调用接口失败");
		}
		soap_destroy(soap);
		soap_done(soap);
		//ifail = parseResponse("<response> <code>019</code> <message>平台返回同步结果</message> <reqid>99299d70-ce98-43d3-890b-0c2331f04ca7</reqid> <srvver>2.0</srvver> <srvcode>000</srvcode> <payload> <param key=\"std_data\" type=\"xml\"> <data_response> <execution> <status code=\"0\" description=\"\" sql_code=\"0\"/> </execution> <datainfo> <parameter key=\"Status\" type=\"string\">1</parameter> <parameter key=\"Message\" type=\"string\">失败信息</parameter> </datainfo> </data_response> </param> </payload> </response>");
	}

end:
	POM_AM__set_application_bypass(false);
	WriteToFile(LOG, "FINISH");
	target_tags.clear();
	if (LOG)
	{
		fclose(LOG);
		LOG = NULL;
	}
	target_types.clear();
	gi_configs.clear();
	pli_configs.clear();
	pref_values.clear();
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "执行结束：QF_ERP_wl");
	WriteLog(debug, "=============================================================");
	return ifail;
}



int QF_ERP_BOM(EPM_action_message_t msg) {
	int ifail = ITK_ok, arg_cnt = 0, task_count = 0;
	char* arg = NULL, * argflag = NULL, * argvalue = NULL, * propvalue = NULL, * objectstring = NULL, * task_name = NULL, * task_type = NULL;
	char* user_id = NULL, * user_name = NULL;
	tag_t task_tag = NULLTAG, rootTask_tag = NULLTAG, user_tag = NULLTAG;
	tag_t* task_tags = NULL;
	bool bypass = false;
	bool has_err = false;
	vector<tag_t> target_tags;
	char* log_file_path;
	FILE* LOG;
	vector<ERP_Config> parent_configs, child_configs, zz_parent_configs, zz_child_configs, gi_configs, pli_configs;
	vector<string> target_types;
	vector<string> wl_pref_values, bom_pref_values;
	TiXmlDocument* bom_doc = new TiXmlDocument();
	TiXmlDocument* wl_doc = new TiXmlDocument();
	TiXmlElement* bom_data, * wl_pli, * wl_gi;
	/**
	* 0=endpoint
	* 1=host prod
	* 2=host ver
	* 3=host ip
	* 4=host lang
	* 5=host acct
	* 6=service prod
	* 7=service name
	* 8=service ip
	* 9=param product name
	* 10=param template name
	* 可选11=host id
	* 可选12=service id
	*/
	string end_point, host_prod, host_ver, host_ip, host_lang, host_acct;
	string service_prod, service_name, service_ip;
	string param_productname, param_template_name, param_import_data;
	vector<string> added_lines;
	arg_cnt = TC_number_of_arguments(msg.arguments);
	if (arg_cnt > 0) {
		for (int i = 0; i < arg_cnt; i++) {
			arg = TC_next_argument(msg.arguments);
			ITKCALL(ifail = ITK_ask_argument_named_value(arg, &argflag, &argvalue));
			if (strcmp(argflag, "bypass") == 0) {
				bypass = true;
			}
			else if (strcmp(argflag, "include_type") == 0) {
				Split(argvalue, ";", target_types);
			}
			MEM_free(argflag);
			MEM_free(argvalue);
		}
	}
	ITKCALL(POM_get_user(&user_name, &user_tag));
	ITKCALL(SA_ask_user_identifier2(user_tag, &user_id));
	if (debug) {
		initUserDir(user_id);
		CreateUserFile(debug, "QF_ERP_BOM", user_id, &log_file_path, &LOG);
		printf("日志文件路径：%s\n", log_file_path);
	}
	DOFREE(user_id);
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "开始执行：QF_ERP_BOM");
	WriteLog(debug, "=============================================================");

	WriteToFile(LOG, "START");
	task_tag = msg.task;
	if (task_tag == NULLTAG) {
		goto end;
	}
	if (bypass) {
		POM_AM__set_application_bypass(true);
	}
	ifail = getAllTargetRevs(task_tag, target_tags, target_types);
	if (target_tags.size() == 0) {
		WriteToFile(LOG, "TARGET NOT FOUND");
		WriteLog(debug, "未找到需要传递的对象");
		goto end;
	}
	//解析物料首选项
	{
		vector<string> gi_prefs, pli_prefs;
		ifail = getPrefStrings(PREF_ERP_WL, TC_preference_site, wl_pref_values);
		if (wl_pref_values.size() < 11) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_WL_GI, TC_preference_site, gi_prefs);
		parseErpConfigs(gi_prefs, 0, gi_configs);
		if (gi_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL_GI);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_WL_PLI, TC_preference_site, pli_prefs);
		parseErpConfigs(pli_prefs, 0, pli_configs);
		if (pli_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_WL_PLI);
			ifail = ITK_err;
			goto end;
		}
	}
	//解析BOM首选项
	{
		vector<string> parent_prefs, child_prefs, zz_parent_prefs, zz_child_prefs;
		ifail = getPrefStrings(PREF_ERP_BOM, TC_preference_site, bom_pref_values);
		if (bom_pref_values.size() < 11) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_BOM);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_BOM_PARENT, TC_preference_site, parent_prefs);
		parseErpConfigs(parent_prefs, 0, parent_configs);
		if (parent_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_BOM_PARENT);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_BOM_CHILD, TC_preference_site, child_prefs);
		parseErpConfigs(child_prefs, 0, child_configs);
		if (child_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_BOM_CHILD);
			ifail = ITK_err;
			goto end;
		}

		ifail = getPrefStrings(PREF_ERP_BOM_PARENT_ZZ, TC_preference_site, zz_parent_prefs);
		parseErpConfigs(zz_parent_prefs, 0, zz_parent_configs);
		if (zz_parent_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_BOM_PARENT_ZZ);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_ERP_BOM_CHILD_ZZ, TC_preference_site, zz_child_prefs);
		parseErpConfigs(zz_child_prefs, 0, zz_child_configs);
		if (zz_child_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_ERP_BOM_CHILD_ZZ);
			ifail = ITK_err;
			goto end;
		}
	}
	// 构造物料xml
	{
		char date_string[20];
		struct tm* p;
		time_t now;
		time(&now);
		p = localtime(&now);
		memset(date_string, 0, sizeof(date_string));
		sprintf(date_string, "%4d%02d%02d%02d%02d%02d%03d", 1900 + p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, getCurrentMilliSeconds());

		TiXmlElement* host = new TiXmlElement("host");
		TiXmlElement* service = new TiXmlElement("service");
		host->SetAttribute("prod", wl_pref_values[1].c_str());
		host->SetAttribute("ver", wl_pref_values[2].c_str());
		host->SetAttribute("ip", wl_pref_values[3].c_str());
		host->SetAttribute("id", wl_pref_values.size() > 11 ? wl_pref_values[11].c_str() : "");
		host->SetAttribute("lang", wl_pref_values[4].c_str());
		host->SetAttribute("timestamp", date_string);
		host->SetAttribute("acct", wl_pref_values[5].c_str());
		service->SetAttribute("prod", wl_pref_values[6].c_str());
		service->SetAttribute("name", wl_pref_values[7].c_str());
		service->SetAttribute("ip", wl_pref_values[8].c_str());
		service->SetAttribute("id", wl_pref_values.size() > 12 ? wl_pref_values[12].c_str() : "");

		TiXmlElement* request = new TiXmlElement("request");
		request->SetAttribute("type", "sync");
		string host_service_str;
		TiXmlPrinter host_printer, service_printer;
		host->Accept(&host_printer);
		host_service_str.append(trim(host_printer.CStr()));
		service->Accept(&service_printer);
		host_service_str.append(trim(service_printer.CStr()));

		//unsigned char buffer[2048];
		//memcpy(buffer, host_service_str.c_str(), host_service_str.size());
		WriteLog(debug, "Get MD5 From \"%s\"", host_service_str.c_str());
		request->SetAttribute("key", str2md5(host_service_str).c_str());

		wl_doc->LinkEndChild(request);
		request->LinkEndChild(host);
		request->LinkEndChild(service);
		TiXmlElement* payload = new TiXmlElement("payload");
		request->LinkEndChild(payload);
		TiXmlElement* param = new TiXmlElement("param");
		param->SetAttribute("key", "std_data");
		param->SetAttribute("type", "xml");
		payload->LinkEndChild(param);
		TiXmlElement* data_request = new TiXmlElement("data_request");
		param->LinkEndChild(data_request);
		TiXmlElement* datainfo = new TiXmlElement("datainfo");
		data_request->LinkEndChild(datainfo);
		TiXmlElement* param1 = new TiXmlElement("parameter");
		param1->SetAttribute("key", "productName");
		param1->SetAttribute("type", "string");
		set_xml_value(param1, wl_pref_values[9].c_str());
		datainfo->LinkEndChild(param1);
		TiXmlElement* param2 = new TiXmlElement("parameter");
		param2->SetAttribute("key", "templateName");
		param2->SetAttribute("type", "string");
		set_xml_value(param2, wl_pref_values[10].c_str());
		datainfo->LinkEndChild(param2);
		TiXmlElement* param3 = new TiXmlElement("parameter");
		param3->SetAttribute("key", "importData");
		param3->SetAttribute("type", "data");
		datainfo->LinkEndChild(param3);
		TiXmlElement* data = new TiXmlElement("data");
		data->SetAttribute("name", "ITEM");
		param3->LinkEndChild(data);
		TiXmlElement* row = new TiXmlElement("row");
		row->SetAttribute("seq", 1);
		data->LinkEndChild(row);
		wl_gi = new TiXmlElement("detail");
		wl_gi->SetAttribute("name", "GI");
		row->LinkEndChild(wl_gi);
		/*for (int i = 0; i < target_tags.size(); i++) {
			TiXmlElement* row_gi = new TiXmlElement("row");
			row_gi->SetAttribute("seq", i + 1);
			detail_gi->LinkEndChild(row_gi);
			ifail = read_data(NULLTAG, target_tags[i], gi_configs, row_gi);
			if (ifail != ITK_ok) {
				goto end;
			}
		}*/
		wl_pli = new TiXmlElement("detail");
		wl_pli->SetAttribute("name", "PLI");
		row->LinkEndChild(wl_pli);
		/*for (int i = 0; i < target_tags.size(); i++) {
			TiXmlElement* row_pli = new TiXmlElement("row");
			row_pli->SetAttribute("seq", i + 1);
			detail_pli->LinkEndChild(row_pli);
			ifail = read_data(NULLTAG, target_tags[i], pli_configs, row_pli);
			if (ifail != ITK_ok) {
				goto end;
			}
		}*/
	}

	// 构造BOMxml
	{
		char date_string[20];
		struct tm* p;
		time_t now;
		time(&now);
		p = localtime(&now);
		memset(date_string, 0, sizeof(date_string));
		sprintf(date_string, "%4d%02d%02d%02d%02d%02d%03d", 1900 + p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, getCurrentMilliSeconds());



		TiXmlElement* host = new TiXmlElement("host");
		TiXmlElement* service = new TiXmlElement("service");
		host->SetAttribute("prod", bom_pref_values[1].c_str());
		host->SetAttribute("ver", bom_pref_values[2].c_str());
		host->SetAttribute("ip", bom_pref_values[3].c_str());
		host->SetAttribute("id", bom_pref_values.size() > 11 ? bom_pref_values[11].c_str() : "");
		host->SetAttribute("lang", bom_pref_values[4].c_str());
		host->SetAttribute("timestamp", date_string);
		host->SetAttribute("acct", bom_pref_values[5].c_str());
		service->SetAttribute("prod", bom_pref_values[6].c_str());
		service->SetAttribute("name", bom_pref_values[7].c_str());
		service->SetAttribute("ip", bom_pref_values[8].c_str());
		service->SetAttribute("id", bom_pref_values.size() > 12 ? bom_pref_values[12].c_str() : "");

		TiXmlElement* request = new TiXmlElement("request");
		request->SetAttribute("type", "sync");
		string host_service_str;
		TiXmlPrinter host_printer, service_printer;

		host->Accept(&host_printer);
		host_service_str.append(trim(host_printer.CStr()));

		service->Accept(&service_printer);
		host_service_str.append(trim(service_printer.CStr()));

		//unsigned char buffer[2048];
		//memcpy(buffer, host_service_str.c_str(), host_service_str.size());
		WriteLog(debug, "Get MD5 From \"%s\"", host_service_str.c_str());
		request->SetAttribute("key", str2md5(host_service_str).c_str());


		bom_doc->LinkEndChild(request);
		request->LinkEndChild(host);
		request->LinkEndChild(service);
		TiXmlElement* payload = new TiXmlElement("payload");
		request->LinkEndChild(payload);
		TiXmlElement* param = new TiXmlElement("param");
		param->SetAttribute("key", "std_data");
		param->SetAttribute("type", "xml");
		payload->LinkEndChild(param);
		TiXmlElement* data_request = new TiXmlElement("data_request");
		param->LinkEndChild(data_request);
		TiXmlElement* datainfo = new TiXmlElement("datainfo");
		data_request->LinkEndChild(datainfo);
		TiXmlElement* param1 = new TiXmlElement("parameter");
		param1->SetAttribute("key", "productName");
		param1->SetAttribute("type", "string");
		set_xml_value(param1, bom_pref_values[9].c_str());
		datainfo->LinkEndChild(param1);
		TiXmlElement* param2 = new TiXmlElement("parameter");
		param2->SetAttribute("key", "templateName");
		param2->SetAttribute("type", "string");
		set_xml_value(param2, bom_pref_values[10].c_str());
		datainfo->LinkEndChild(param2);
		TiXmlElement* param3 = new TiXmlElement("parameter");
		param3->SetAttribute("key", "importData");
		param3->SetAttribute("type", "data");
		datainfo->LinkEndChild(param3);
		bom_data = new TiXmlElement("data");
		bom_data->SetAttribute("name", "BOM");
		param3->LinkEndChild(bom_data);

	}
	//读取数据
	{
		int bomSeq = 1, wlSeq = 1;
		for (int i = 0; i < target_tags.size(); i++) {
			ifail = read_bom_data(target_tags[i], parent_configs, child_configs, zz_parent_configs, zz_child_configs, pli_configs, gi_configs, bom_data, wl_pli, wl_gi, bomSeq, wlSeq, added_lines);
			if (ifail != ITK_ok) {
				goto end;
			}
		}
	}

	{
		TiXmlPrinter bom_doc_printer;
		bom_doc->Accept(&bom_doc_printer);

		TiXmlPrinter wl_doc_printer;
		wl_doc->Accept(&wl_doc_printer);

		soap* soap = soap_new();
		soap_init(soap);
		soap_register_plugin(soap, plugin);
		soap_set_mode(soap, SOAP_C_UTFSTRING);


		//发送BOM
		{
			if (LOG) {
				WriteToFile(LOG, "SEND BOM");
				WriteToFile(LOG, bom_doc_printer.CStr());
			}
			_WS1__invokeSrv* soapReq = new _WS1__invokeSrv();
			soapReq->soap_default(soap);
			soapReq->in0.assign(bom_doc_printer.CStr());
			_WS1__invokeSrvResponse response;
			WriteToFile(LOG, "...");
			ifail = soap_call___WS1__invokeSrv(soap, bom_pref_values[0].c_str(), NULL, soapReq, response);
			WriteToFile(LOG, "REQUEST SENT");
			if (ifail == SOAP_OK) {
				if (LOG) {
					WriteToFile(LOG, "RETURN DATA");
					WriteToFile(LOG, response.invokeSrvReturn.c_str());
				}
				ifail = parseResponse(response.invokeSrvReturn);
			}
			else {
				WriteLog(debug, "调用接口异常，%d", ifail);
				soap_print_fault(soap, stderr);
				ifail = ITK_err;
				EMH_store_error_s1(EMH_severity_error, ITK_err, "调用接口失败");
			}
			if (ifail != ITK_ok) {
				has_err = true;
			}
		}
		//发送物料
		/*{
			if (LOG) {
				WriteToFile(LOG, "SEND WL");
				WriteToFile(LOG, wl_doc_printer.CStr());
			}
			_WS1__invokeSrv* soapReq = new _WS1__invokeSrv();
			soapReq->soap_default(soap);
			soapReq->in0.assign(wl_doc_printer.CStr());
			_WS1__invokeSrvResponse response;
			WriteToFile(LOG, "...");
			ifail = soap_call___WS1__invokeSrv(soap, wl_pref_values[0].c_str(), NULL, soapReq, response);
			WriteToFile(LOG, "REQUEST SENT");
			if (ifail == SOAP_OK) {
				if (LOG) {
					WriteToFile(LOG, "RETURN DATA");
					WriteToFile(LOG, response.invokeSrvReturn.c_str());
				}
				ifail = parseResponse(response.invokeSrvReturn);
			}
			else {
				WriteLog(debug, "调用接口异常，%d", ifail);
				soap_print_fault(soap, stderr);
				ifail = ITK_err;
				EMH_store_error_s1(EMH_severity_error, ITK_err, "调用接口失败");
			}
			if (ifail != ITK_ok) {
				has_err = true;
			}
		}*/

		soap_destroy(soap);
		soap_done(soap);
		//ifail = parseResponse("<response> <code>019</code> <message>平台返回同步结果</message> <reqid>99299d70-ce98-43d3-890b-0c2331f04ca7</reqid> <srvver>2.0</srvver> <srvcode>000</srvcode> <payload> <param key=\"std_data\" type=\"xml\"> <data_response> <execution> <status code=\"0\" description=\"\" sql_code=\"0\"/> </execution> <datainfo> <parameter key=\"Status\" type=\"string\">1</parameter> <parameter key=\"Message\" type=\"string\">失败信息</parameter> </datainfo> </data_response> </param> </payload> </response>");
	}

end:
	POM_AM__set_application_bypass(false);
	WriteToFile(LOG, "FINISH");
	target_tags.clear();
	if (LOG)
	{
		fclose(LOG);
		LOG = NULL;
	}
	if (has_err) {
		ifail = ITK_err;
	}
	target_types.clear();
	parent_configs.clear();
	child_configs.clear();
	zz_parent_configs.clear();
	zz_child_configs.clear();
	gi_configs.clear();
	pli_configs.clear();
	bom_pref_values.clear();
	wl_pref_values.clear();
	added_lines.clear();
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "执行结束：QF_ERP_BOM");
	WriteLog(debug, "=============================================================");
	return ifail;
}

int getPrefStringsToMap(const char* preference, TC_preference_search_scope_t scope, char splitter, map<string, string>& pref_map) {
	int ifail = ITK_ok, i = 0, num = 0;
	char** values;
	ITKCALL(ifail = PREF_ask_char_values_at_location(preference, scope, &num, &values));
	if (ifail != ITK_ok) {
		return ifail;
	}
	for (i = 0; i < num; i++) {
		vector<string> split;
		Split(values[i], splitter, split);
		int size = split.size();
		if (size == 2) {
			pref_map[split[0]] = split[1];
		}
	}
	DOFREE(values);
	return ifail;
}


int QF_ERP_BOP(EPM_action_message_t msg) {
	int ifail = ITK_ok, arg_cnt = 0, task_count = 0;
	char* arg = NULL, * argflag = NULL, * argvalue = NULL, * propvalue = NULL, * objectstring = NULL, * task_name = NULL, * task_type = NULL;
	char* user_id = NULL, * user_name = NULL;
	tag_t task_tag = NULLTAG, rootTask_tag = NULLTAG, user_tag = NULLTAG;
	tag_t* task_tags = NULL;
	bool bypass = false;
	bool has_err = false;
	vector<tag_t> target_tags;
	char* log_file_path;
	FILE* LOG;
	vector<ERP_Config> parent_configs, child_configs1, child_configs2, child_configs3, child_configs4, wl_data_configs;
	vector<string> wl_pref_values, bom_pref_values, target_types, QFgxtouliao_type_prefs, QF_MES_TYPE_prefs;
	map<string, string> QF_JYXtype_map;
	TiXmlDocument* bom_doc = new TiXmlDocument();
	TiXmlDocument* wl_doc = new TiXmlDocument();
	TiXmlElement* bom_data, * wl_data;
	vector<string> added_lines;
	string end_point, host_prod, host_ver, host_ip, host_lang, host_acct;
	string service_prod, service_name, service_ip;
	string param_productname, param_template_name, param_import_data;

	arg_cnt = TC_number_of_arguments(msg.arguments);
	if (arg_cnt > 0) {
		for (int i = 0; i < arg_cnt; i++) {
			arg = TC_next_argument(msg.arguments);
			ITKCALL(ifail = ITK_ask_argument_named_value(arg, &argflag, &argvalue));
			if (strcmp(argflag, "bypass") == 0) {
				bypass = true;
			}
			else if (strcmp(argflag, "include_type") == 0) {
				Split(argvalue, ";", target_types);
			}
			MEM_free(argflag);
			MEM_free(argvalue);
		}
	}
	ITKCALL(POM_get_user(&user_name, &user_tag));
	ITKCALL(SA_ask_user_identifier2(user_tag, &user_id));
	if (debug) {
		initUserDir(user_id);
		CreateUserFile(debug, "QF_ERP_BOP", user_id, &log_file_path, &LOG);
		printf("日志文件路径：%s\n", log_file_path);
	}
	DOFREE(user_id);
	WriteLog(debug, "=============================================================");
	WriteLog(debug, "开始执行：QF_ERP_BOP");
	WriteLog(debug, "=============================================================");

	WriteToFile(LOG, "START");
	task_tag = msg.task;
	if (task_tag == NULLTAG) {
		goto end;
	}
	if (bypass) {
		POM_AM__set_application_bypass(true);
	}
	ifail = getAllTargetRevs(task_tag, target_tags, target_types);
	if (target_tags.size() == 0) {
		WriteToFile(LOG, "TARGET NOT FOUND");
		WriteLog(debug, "未找到需要传递的对象");
		goto end;
	}
	// 文件首选项
	ftp_dir.clear();
	local_dir.clear();
	{
		vector<string> data_prefs;
		ifail = getPrefStrings(PREF_FTP_PATH, TC_preference_site, data_prefs);
		if (data_prefs.size() == 2) {
			ftp_dir.assign(data_prefs[0]);
			local_dir.assign(data_prefs[1]);
		}
		data_prefs.clear();
	}
	WriteLog(debug, "ftp路径：%s", ftp_dir.c_str());
	WriteLog(debug, "本地路径：%s", local_dir.c_str());
	if (ftp_dir.size() == 0 || local_dir.size() == 0) {
		EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_FTP_PATH);
		ifail = ITK_err;
		goto end;
	}
	if (!std::experimental::filesystem::is_directory(local_dir)) {
		WriteLog(debug, "新建本地目录");
		if (std::experimental::filesystem::create_directories(local_dir)) {
			WriteLog(debug, "新建完成");
		}
		else {
			WriteLog(debug, "新建失败");
			EMH_store_error_s2(EMH_severity_error, ITK_err, "创建本地目录失败", local_dir.c_str());
			ifail = ITK_err;
			goto end;
		}
	}
	//解析物料首选项
	{
		vector<string> data_prefs;
		ifail = getPrefStrings(PREF_MES_WL, TC_preference_site, wl_pref_values);
		if (wl_pref_values.size() != 13) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_WL);
			ifail = ITK_err;
			goto end;
		}
		ifail = getPrefStrings(PREF_MES_WL_DATA, TC_preference_site, data_prefs);
		parseErpConfigs(data_prefs, 0, wl_data_configs);
		if (wl_data_configs.size() == 0) {
			EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_WL_DATA);
			ifail = ITK_err;
			goto end;
		}
		data_prefs.clear();
	}
	//解析BOM首选项
	{
			vector<string> parent_prefs;
			ifail = getPrefStrings(PREF_MES_BOM, TC_preference_site, bom_pref_values);
			if (bom_pref_values.size() != 10) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_BOM);
				ifail = ITK_err;
				goto end;
			}
			ifail = getPrefStrings(PREF_MES_BOM_PARENT, TC_preference_site, parent_prefs);
			parseErpConfigs(parent_prefs, 0, parent_configs);
			if (parent_configs.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_BOM_PARENT);
				ifail = ITK_err;
				goto end;
			}
		{
			vector<string> child_prefs;
			ifail = getPrefStrings(PREF_MES_BOM_CHILD1, TC_preference_site, child_prefs);
			parseErpConfigs(child_prefs, 0, child_configs1);
			if (child_configs1.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_BOM_CHILD1);
				ifail = ITK_err;
				goto end;
			}
			child_prefs.clear();
		}
		{
			vector<string> child_prefs;
			ifail = getPrefStrings(PREF_MES_BOM_CHILD2, TC_preference_site, child_prefs);
			parseErpConfigs(child_prefs, 0, child_configs2);
			if (child_configs2.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_BOM_CHILD2);
				ifail = ITK_err;
				goto end;
			}
			child_prefs.clear();
		}
		{
			vector<string> child_prefs;
			ifail = getPrefStrings(PREF_MES_BOM_CHILD3, TC_preference_site, child_prefs);
			parseErpConfigs(child_prefs, 0, child_configs3);
			if (child_configs3.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_BOM_CHILD3);
				ifail = ITK_err;
				goto end;
			}
			child_prefs.clear();
		}
		{
			vector<string> child_prefs;
			ifail = getPrefStrings(PREF_MES_BOM_CHILD4, TC_preference_site, child_prefs);
			parseErpConfigs(child_prefs, 0, child_configs4);
			if (child_configs4.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", PREF_MES_BOM_CHILD4);
				ifail = ITK_err;
				goto end;
			}
			child_prefs.clear();
		}
		{
			ifail = getPrefStrings(QFgxtouliao_type, TC_preference_site, QFgxtouliao_type_prefs);
			if (QFgxtouliao_type_prefs.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", QFgxtouliao_type);
				ifail = ITK_err;
				goto end;
			}
		}
		{
			ifail = getPrefStringsToMap(QF_JYXtype, TC_preference_site, '=', QF_JYXtype_map);
			if (QF_JYXtype_map.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", QF_JYXtype);
				ifail = ITK_err;
				goto end;
			}
		}
		{
			ifail = getPrefStrings(QF_MES_TYPE, TC_preference_site, QF_MES_TYPE_prefs);
			if (QF_MES_TYPE_prefs.size() == 0) {
				EMH_store_error_s2(EMH_severity_error, ITK_err, "首选项配置错误", QF_MES_TYPE);
				ifail = ITK_err;
				goto end;
			}
		}

			parent_prefs.clear();
	}
		// 构造物料xml
		{
			char date_string[20];
			struct tm* p;
			time_t now;
			time(&now);
			p = localtime(&now);
			memset(date_string, 0, sizeof(date_string));
			sprintf(date_string, "%4d%02d%02d%02d%02d%02d%03d", 1900 + p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, getCurrentMilliSeconds());

			TiXmlElement* host = new TiXmlElement("host");
			TiXmlElement* service = new TiXmlElement("service");
			TiXmlElement* datakey = new TiXmlElement("datakey");
			host->SetAttribute("prod", wl_pref_values[1].c_str());
			host->SetAttribute("ver", wl_pref_values[2].c_str());
			host->SetAttribute("ip", wl_pref_values[3].c_str());
			host->SetAttribute("id", wl_pref_values[4].c_str());
			host->SetAttribute("acct", wl_pref_values[5].c_str());
			host->SetAttribute("timestamp", date_string);
			service->SetAttribute("prod", wl_pref_values[6].c_str());
			service->SetAttribute("ip", wl_pref_values[7].c_str());
			service->SetAttribute("id", wl_pref_values[8].c_str());
			service->SetAttribute("name", wl_pref_values[9].c_str());
			{
				TiXmlElement* key = new TiXmlElement("key");
				key->SetAttribute("name", "EntId");
				set_xml_value(key, wl_pref_values[10].c_str());
				datakey->LinkEndChild(key);
			}
			{
				TiXmlElement* key = new TiXmlElement("key");
				key->SetAttribute("name", "CompanyId");
				set_xml_value(key, wl_pref_values[11].c_str());
				datakey->LinkEndChild(key);
			}
			{
				TiXmlElement* key = new TiXmlElement("key");
				key->SetAttribute("name", "item_no");
				set_xml_value(key, wl_pref_values[12].c_str());
				datakey->LinkEndChild(key);
			}

			TiXmlElement* request = new TiXmlElement("request");
			request->SetAttribute("type", "sync");
			string host_service_str;
			TiXmlPrinter host_printer, service_printer;
			host->Accept(&host_printer);
			host_service_str.append(trim(host_printer.CStr()));
			service->Accept(&service_printer);
			host_service_str.append(trim(service_printer.CStr()));

			//unsigned char buffer[2048];
			//memcpy(buffer, host_service_str.c_str(), host_service_str.size());
			WriteLog(debug, "Get MD5 From \"%s\"", host_service_str.c_str());
			request->SetAttribute("key", str2md5(host_service_str).c_str());

			wl_doc->LinkEndChild(request);
			request->LinkEndChild(host);
			request->LinkEndChild(service);
			request->LinkEndChild(datakey);


			TiXmlElement* payload = new TiXmlElement("payload");
			request->LinkEndChild(payload);
			TiXmlElement* param = new TiXmlElement("param");
			param->SetAttribute("key", "std_data");
			param->SetAttribute("type", "xml");
			payload->LinkEndChild(param);
			TiXmlElement* data_request = new TiXmlElement("data_request");
			param->LinkEndChild(data_request);
			TiXmlElement* datainfo = new TiXmlElement("datainfo");
			data_request->LinkEndChild(datainfo);
			TiXmlElement* param3 = new TiXmlElement("parameter");
			param3->SetAttribute("key", "item_data");
			param3->SetAttribute("type", "data");
			datainfo->LinkEndChild(param3);
			wl_data = new TiXmlElement("data");
			wl_data->SetAttribute("name", "ITEM");
			param3->LinkEndChild(wl_data);
		}

		// 构造BOMxml
		{
			char date_string[20];
			struct tm* p;
			time_t now;
			time(&now);
			p = localtime(&now);
			memset(date_string, 0, sizeof(date_string));
			sprintf(date_string, "%4d%02d%02d%02d%02d%02d%03d", 1900 + p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, getCurrentMilliSeconds());



			TiXmlElement* host = new TiXmlElement("host");
			TiXmlElement* service = new TiXmlElement("service");
			host->SetAttribute("prod", bom_pref_values[1].c_str());
			host->SetAttribute("ver", bom_pref_values[2].c_str());
			host->SetAttribute("ip", bom_pref_values[3].c_str());
			host->SetAttribute("id", bom_pref_values[4].c_str());
			host->SetAttribute("acct", bom_pref_values[5].c_str());
			host->SetAttribute("timestamp", date_string);
			service->SetAttribute("prod", bom_pref_values[6].c_str());
			service->SetAttribute("ip", bom_pref_values[7].c_str());
			service->SetAttribute("id", bom_pref_values[8].c_str());
			service->SetAttribute("name", bom_pref_values[9].c_str());

			TiXmlElement* request = new TiXmlElement("request");
			request->SetAttribute("type", "sync");
			string host_service_str;
			TiXmlPrinter host_printer, service_printer;

			host->Accept(&host_printer);
			host_service_str.append(trim(host_printer.CStr()));

			service->Accept(&service_printer);
			host_service_str.append(trim(service_printer.CStr()));

			//unsigned char buffer[2048];
			//memcpy(buffer, host_service_str.c_str(), host_service_str.size());
			WriteLog(debug, "Get MD5 From \"%s\"", host_service_str.c_str());
			request->SetAttribute("key", str2md5(host_service_str).c_str());


			bom_doc->LinkEndChild(request);
			request->LinkEndChild(host);
			request->LinkEndChild(service);
			TiXmlElement* payload = new TiXmlElement("payload");
			request->LinkEndChild(payload);
			TiXmlElement* param = new TiXmlElement("param");
			param->SetAttribute("key", "std_data");
			param->SetAttribute("type", "xml");
			payload->LinkEndChild(param);
			TiXmlElement* data_request = new TiXmlElement("data_request");
			param->LinkEndChild(data_request);
			TiXmlElement* datainfo = new TiXmlElement("datainfo");
			data_request->LinkEndChild(datainfo);
			TiXmlElement* param3 = new TiXmlElement("parameter");
			param3->SetAttribute("key", "process_data");
			param3->SetAttribute("type", "data");
			datainfo->LinkEndChild(param3);
			bom_data = new TiXmlElement("data");
			bom_data->SetAttribute("name", "process");
			param3->LinkEndChild(bom_data);

		}
		//读取数据
		{
			vector<string> added_wl;
			int bomSeq = 1, wlSeq = 1;
			for (int i = 0; i < target_tags.size(); i++) {
				char* object_type = NULL;
				AOM_ask_value_string(target_tags[i], "object_type", &object_type);
				if (strcmp(object_type, "QF8gyRevision") != 0) {
					DOFREE(object_type);
					continue;
				}
				DOFREE(object_type);
				ifail = read_bop_data(target_tags[i], parent_configs, child_configs1, child_configs2, child_configs3, child_configs4,
					wl_data_configs, bom_data, wl_data, bomSeq, wlSeq, added_wl, added_lines, QFgxtouliao_type_prefs, QF_JYXtype_map, QF_MES_TYPE_prefs);
				if (ifail != ITK_ok) {
					goto end;
				}
			}
		}

		{
			TiXmlPrinter bom_doc_printer;
			bom_doc->Accept(&bom_doc_printer);

			TiXmlPrinter wl_doc_printer;
			wl_doc->Accept(&wl_doc_printer);

			soap* soap = soap_new();
			soap_init(soap);
			soap_register_plugin(soap, plugin);
			soap_set_mode(soap, SOAP_C_UTFSTRING);

			//发送物料
			{
				if (LOG) {
					WriteToFile(LOG, "SEND WL");
					WriteToFile(LOG, wl_doc_printer.CStr());
				}
				_WS1__invokeSrv* soapReq = new _WS1__invokeSrv();
				soapReq->soap_default(soap);
				soapReq->in0.assign(wl_doc_printer.CStr());
				_WS1__invokeSrvResponse response;
				WriteToFile(LOG, "...");
				ifail = soap_call___WS1__invokeSrv(soap, wl_pref_values[0].c_str(), NULL, soapReq, response);
				WriteToFile(LOG, "REQUEST SENT");
				if (ifail == SOAP_OK) {
					if (LOG) {
						WriteToFile(LOG, "RETURN DATA");
						WriteToFile(LOG, response.invokeSrvReturn.c_str());
					}
					ifail = parseResponse(response.invokeSrvReturn);
				}
				else {
					WriteLog(debug, "调用接口异常，%d", ifail);
					soap_print_fault(soap, stderr);
					ifail = ITK_err;
					EMH_store_error_s1(EMH_severity_error, ITK_err, "调用接口失败");
				}
				if (ifail != ITK_ok) {
					has_err = true;
				}
			}

			//发送BOM
			{
				if (LOG) {
					WriteToFile(LOG, "SEND BOM");
					WriteToFile(LOG, bom_doc_printer.CStr());
				}
				_WS1__invokeSrv* soapReq = new _WS1__invokeSrv();
				soapReq->soap_default(soap);
				soapReq->in0.assign(bom_doc_printer.CStr());
				_WS1__invokeSrvResponse response;
				WriteToFile(LOG, "...");
				ifail = soap_call___WS1__invokeSrv(soap, bom_pref_values[0].c_str(), NULL, soapReq, response);
				WriteToFile(LOG, "REQUEST SENT");
				if (ifail == SOAP_OK) {
					if (LOG) {
						WriteToFile(LOG, "RETURN DATA");
						WriteToFile(LOG, response.invokeSrvReturn.c_str());
					}
					ifail = parseResponse(response.invokeSrvReturn);
				}
				else {
					WriteLog(debug, "调用接口异常，%d", ifail);
					soap_print_fault(soap, stderr);
					ifail = ITK_err;
					EMH_store_error_s1(EMH_severity_error, ITK_err, "调用接口失败");
				}
				if (ifail != ITK_ok) {
					has_err = true;
				}
			}


			soap_destroy(soap);
			soap_done(soap);
			//ifail = parseResponse("<response> <code>019</code> <message>平台返回同步结果</message> <reqid>99299d70-ce98-43d3-890b-0c2331f04ca7</reqid> <srvver>2.0</srvver> <srvcode>000</srvcode> <payload> <param key=\"std_data\" type=\"xml\"> <data_response> <execution> <status code=\"0\" description=\"\" sql_code=\"0\"/> </execution> <datainfo> <parameter key=\"Status\" type=\"string\">1</parameter> <parameter key=\"Message\" type=\"string\">失败信息</parameter> </datainfo> </data_response> </param> </payload> </response>");
		}

	end:
		POM_AM__set_application_bypass(false);
		WriteToFile(LOG, "FINISH");
		target_tags.clear();
		if (LOG)
		{
			fclose(LOG);
			LOG = NULL;
		}
		if (has_err) {
			ifail = ITK_err;
		}
		target_types.clear();
		parent_configs.clear();
		child_configs1.clear();
		child_configs2.clear();
		child_configs3.clear();
		child_configs4.clear();
		wl_data_configs.clear();
		bom_pref_values.clear();
		wl_pref_values.clear();
		added_lines.clear();
		WriteLog(debug, "=============================================================");
		WriteLog(debug, "执行结束：QF_ERP_BOP");
		WriteLog(debug, "=============================================================");
		return ifail;
}

