#include "stdafx.h"
#include <finder.h>
#include "CaplNrmmManager.h"

using namespace std;

CaplNrmmManager::CaplNrmmManager(CaplAPI *api) : m_api(api)
{
	if(m_api)
	{
		m_baseUnit = GetBasePrdUnit();
		m_shopCharact = m_api->m_charact_mgr.GetCharacteristicBN(APL_T("Цех-потребитель"));
		m_rascCharact = m_api->m_charact_mgr.GetCharacteristicBN(APL_T("Расцеховка"));	
		m_warehouseCharact = m_api->m_charact_mgr.GetCharacteristicBN(APL_T("№ Склада"));			
	}	
}

CaplNrmmManager::~CaplNrmmManager(void)
{
}

bool CaplNrmmManager::GenerateLimitByPdf( CaplInstance *nrm, CaplInstance *pdf )
{
	m_api->m_prd_mgr.LoadPrdInfo(nrm);

	// можно ли вообще что-то делать
	if(false == CanGenerateNrm(nrm, pdf))
		return false;

	// разузлуем состав конструкторской сборки на предмет материалов
	aplExtent materials, nrmItems;
	if(false == FindMaterials(pdf, materials, nrmItems))
		return false;

	// найдем и структурируем информацию о справочниках
	TStrStrMap classifiers;
	if(false == LoadClassifiers(classifiers))
		return false;

	// сохраняться будем один раз в конце
	bool bOldAutosave = m_api->m_AutoSave;
	m_api->m_AutoSave = false;
	bool bOldPrdAutosave = m_api->m_prd_mgr.m_AutoSave;
	m_api->m_prd_mgr.m_AutoSave = false;

	// для этих элементов в конце зададим доступ
	aplExtent accessItems;

	// будем создавать нормы
	CaplInstance *prd = 0;
	CString code, nrmId = m_api->m_prd_mgr.GetProductId(nrm);
	nrmId.TrimLeft(APL_T("НРМ-"));
		
	map<CString, CaplInstance*> nrmgItems;	
	map<CaplInstance*, CaplInstance*> nrmmMaterial;
	long dId = aplStartWaitDlg(APL_T("Создание объектов БД..."));	

	CString logMessage;
	for(int i=0; i<materials.Size; ++i)
	{
		logMessage.Format(APL_T("Этап 1 из 3. Создание норм.\nВыполнено на %d%%"), (i+1)*100/materials.Size);
		aplSetTextWaitDlg(dId, logMessage);

		m_api->m_data.GetAttr(materials[i], m_api->m_prd_mgr.a_pdf_code, code);
		if(code.IsEmpty() == false && code.GetLength() >= 4)
		{
			// либо два символа, либо четыре
			TStrStrMap::iterator it = classifiers.find(code.Left(2));
			if(it == classifiers.end())
				it = classifiers.find	(code.Left(4));							

			if(it != classifiers.end())
			{
				// "НРМГ"
				CaplInstance *nrmg = 0;
				map<CString, CaplInstance*>::iterator NrmgIt = nrmgItems.find(it->second) ;

				if(NrmgIt == nrmgItems.end())
				{					
					nrmg = AddChild(nrm, APL_T("НРМГ-") + nrmId + _T("-") + it->second, _T(""));
					if(nrmg)
					{
						nrmgItems[it->second] = nrmg;
						m_api->m_data.GetAttr(nrmg, m_api->m_prd_mgr.a_pdf_prd, prd);
						accessItems.Add(prd);
						accessItems.Add(nrmg);
					}									
				}
				else
				{
					nrmg = NrmgIt->second;
				}

				// "НРММ"
				if(nrmg)
				{					
					CString materialId = m_api->m_prd_mgr.GetProductId(materials[i]);
					CaplInstance *nrmm = AddChild(nrmg, APL_T("НРММ-") + nrmId + _T("-") + code, materialId);

					if(nrmm)
					{
						nrmmMaterial[nrmm] = materials[i];						

						// запомним элементы для устанвоки прав доступа в конце
						m_api->m_data.GetAttr(nrmm, m_api->m_prd_mgr.a_pdf_prd, prd);
						accessItems.Add(prd);
						accessItems.Add(nrmm);																																	
					}					
				}
			}
		}
	}

	// существующие "нрм" просто запихываем в состав как есть
	for(int i=0; i<nrmItems.Size; ++i)
	{
		double count = 1.0;
		map<CaplInstance*, CaplInstance*>::iterator nrmIt = m_nrmForAssembly.find(nrmItems[i]);

		if(nrmIt != m_nrmForAssembly.end())
			count = GetCountForCustomPdf(pdf, nrmIt->second);
				
		m_api->m_prd_mgr.CreatePdfRelation(0, nrm, nrmItems[i], _T(""), _T(""), count, m_baseUnit, false, false);		
	}

	// зададим доступ
	aplSetTextWaitDlg(dId, APL_T("Этап 2 из 3.\nУстановка прав доступа..."));
	m_api->SaveChanges();
	//SetNewItemsAccess(accessItems);

	// синхронизируем норму по составу
	SynchronizeCount(nrmmMaterial, dId, pdf);

	// завершаем наш процесс
	aplEndWaitDlg(dId);
	m_api->m_prd_mgr.m_AutoSave = bOldPrdAutosave;
	m_api->m_AutoSave = bOldAutosave;

	return m_api->SaveChanges();

	return true;
}

double CaplNrmmManager::GetCountForCustomPdf(CaplInstance *topPdf, CaplInstance *pdfToCount)
{
	map<CaplInstance*, vector<CaplInstance*>>::iterator It = m_disassembliedPdfs.find(topPdf);
	if(It == m_disassembliedPdfs.end())
		return 0;

	CaplInstance *edItem = 0;	
	vector<CaplInstance*> &vec = It->second;	
	map<CaplInstance*, vector<CaplInstance*>> relationsOrderedByEdItem;

	for(unsigned int i=0; i<vec.size(); ++i)
	{
		m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);			
		relationsOrderedByEdItem[edItem].push_back(vec[i]);		
	}

	return GetTotalPdfCount(pdfToCount, relationsOrderedByEdItem, topPdf);
}

bool CaplNrmmManager::SynchronizeCount(CaplInstance *nrmm, CaplInstance *material, CaplInstance *pdf, double koeff)
{
	if(0 == material || 0 == nrmm)
		return false;

	CaplInstance *unit = 0;
	m_api->m_data.GetAttr(material, m_api->m_prd_mgr.a_apl_pdf_unit, unit);

	// грузим из БД или нет
	bool bLoadFromDb = true;
	set<CaplInstance*>::iterator It = m_relationsWithLoadedCharacts.find(material);

	if(It == m_relationsWithLoadedCharacts.end())
	{				
		m_relationsWithLoadedCharacts.insert(material);
	}
	else
	{
		bLoadFromDb = false;
	}

	// определим склад
	CString warehouseStr;		
	aplExtent materialCharactValues;
	m_api->m_charact_mgr.FindAssociatedCharacteristic(material, materialCharactValues, false, false, false, bLoadFromDb);

	for(int i=0; i<materialCharactValues.Size; ++i)
	{
		CaplInstance *charact = 0;
		m_api->m_data.GetAttr(materialCharactValues[i], m_api->m_charact_mgr.a_apl_charact_val_charact, charact);

		if(charact == m_warehouseCharact)
		{
			CaplInstance *warehouse = 0;
			m_api->m_data.GetAttr(materialCharactValues[i], m_api->m_charact_mgr.a_apl_reference_val, warehouse);

			if(warehouse)
			{
				m_api->m_data.GetAttr(warehouse, m_api->m_appr_mgr.a_org_id, warehouseStr);			
				break;
			}
		}
	}				

	// узнаем деталировку материала: цех и количество
	map<CString, double> info;
	GetMaterialCountByShop(info, material, pdf);
	
	/************************************************************************/
	/*                                                                      */
	/************************************************************************/

	// создаем норму
	map<CString, double>::iterator it;
	for(it=info.begin(); it!=info.end(); ++it)
	{
		CString shop = it->first;
		CString amount; amount.Format(_T("%g"), it->second);				

		// создаем связь
		CaplInstance *m_relation = m_api->m_data.CreateInstance(m_api->m_prd_mgr.e_qacu_nauo);
		m_api->m_data.PutAttr(m_relation, m_api->m_prd_mgr.a_pdr_r_ting, nrmm);
		m_api->m_data.PutAttr(m_relation, m_api->m_prd_mgr.a_pdr_r_ted, material);

		m_api->m_data.PutAttr(m_relation, m_api->m_prd_mgr.a_pdr_id, warehouseStr + _T("-") + shop);
		m_api->m_data.PutAttr(m_relation, m_api->m_prd_mgr.a_apl_qacu_value, it->second * koeff);		
		m_api->m_data.PutAttr(m_relation, m_api->m_prd_mgr.a_apl_qacu_unit, unit);		
	}

	return true;
}

bool CaplNrmmManager::GetMaterialCountByShop(map<CString, double> &info, CaplInstance *material, CaplInstance *pdf)
{
	// состав уже должен быть загружен ранее
	map<CaplInstance*, vector<CaplInstance*>>::iterator It = m_disassembliedPdfs.find(pdf);
	if(It == m_disassembliedPdfs.end())
		return false;

	CaplInstance *edItem = 0;	
	vector<CaplInstance*> &vec = It->second;
	aplExtent materialRelations, structureRelations;
	map<CaplInstance*, vector<CaplInstance*>> relationsOrderedByEdItem;

	for(unsigned int i=0; i<vec.size(); ++i)
	{
		if(0 == vec[i]->GetType())
			continue;		

		if(false == TestMaterialRelation(vec[i]))
			continue;

		m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);			
		relationsOrderedByEdItem[edItem].push_back(vec[i]);

		if(edItem && edItem == material)
			materialRelations.Add(vec[i]);																							
	}

	// подгружаем атрибуты
	LoadAttrsMaterialRelations(materialRelations, false);

	// группируем связи по складу
	map<CString, vector<CaplInstance*>> groupedMaterialRelations;
	GroupRelationsByShop(materialRelations, groupedMaterialRelations);

	// узнаем итоговое количество материала
	GetTotalMaterialCount(relationsOrderedByEdItem, groupedMaterialRelations, info, pdf);

	return true;
}

void CaplNrmmManager::GetTotalMaterialCount(map<CaplInstance*, vector<CaplInstance*>> &relationsOrderedByEdItem,
											map<CString, vector<CaplInstance*>> &groupedMaterialRelations,
											map<CString, double> &info,
											CaplInstance *topPdf)
{
	map<CString, vector<CaplInstance*>>::const_iterator It;
	for(It=groupedMaterialRelations.begin(); It!=groupedMaterialRelations.end(); ++It)
	{
		const vector<CaplInstance*> &vec = It->second;
		for(unsigned int i=0; i<vec.size(); ++i)
		{
			double totalCount = 0.0;
			CaplInstance *ingItem = 0;
			m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);

			if(topPdf == ingItem)
			{
				totalCount = 1.0;
			}
			else
			{
				map<CaplInstance*, vector<CaplInstance*>>::iterator IngIt = relationsOrderedByEdItem.find(ingItem);
				if(IngIt != relationsOrderedByEdItem.end())
				{
					double ingCount = 0.0;				
					vector<CaplInstance*> &ingVec = IngIt->second;

					// узнаем количество, проходя по дереву вверх
					for(unsigned int m=0; m<ingVec.size(); ++m)
					{												
						double tempCount = 0.0;
						CaplInstance *tmpIngItem = 0;

						m_api->m_data.GetAttr(ingVec[m], m_api->m_prd_mgr.a_pdr_r_ting, tmpIngItem);
						m_api->m_data.GetAttr(ingVec[m], m_api->m_prd_mgr.a_apl_qacu_value, tempCount);								

						ingCount = (tempCount * GetTotalPdfCount(tmpIngItem, relationsOrderedByEdItem, topPdf));				
						totalCount += ingCount;
					}				
				}
			}
			
			// количество материала
			double count = 0.0;
			if(m_api->m_data.IsKindOf(vec[i], m_api->m_prd_mgr.e_make_from))
			{
				m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_make_from_value, count);
			}
			else
			{
				m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_apl_qacu_value, count);
			}

			// суммируем общее количество материала по цеху
			info[It->first] += (count * totalCount);			
		}
	}
}

double CaplNrmmManager::GetTotalPdfCount(CaplInstance *item, 
										 map<CaplInstance*, vector<CaplInstance*>> &relationsOrderedByEdItem,
										 CaplInstance *topPdf)
{
	double tempCount = 0.0;
	double totalCount = 0.0;
	CaplInstance *edItem = 0, *ingItem = 0;

	map<CaplInstance*, vector<CaplInstance*>>::iterator It = relationsOrderedByEdItem.find(item);
	if(It != relationsOrderedByEdItem.end())
	{
		vector<CaplInstance*> &vec = It->second;

		for(unsigned int i=0; i<vec.size(); ++i)
		{
			m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
			m_api->m_data.GetAttr(vec[i], m_api->m_prd_mgr.a_apl_qacu_value, tempCount);
			totalCount += (tempCount * GetTotalPdfCount(ingItem, relationsOrderedByEdItem, topPdf));
		}
	}
	else if(item == topPdf)
	{
		// это если дошли до верхушки
		totalCount = 1.0;
	}
	
	return totalCount;
}

void CaplNrmmManager::GroupRelationsByShop(aplExtent &materialRelations, map<CString, vector<CaplInstance*>> &groupedMaterialRelations)
{
	if(0 == materialRelations.Size)
		return;

	// найдем характеристики для связей
	aplExtent relCharValues;
	aplExtent ingItemsCharValues;

	{
		groupedMaterialRelations.clear();
		CaplFinder ff(DEF_SOURCE);

		if(m_shopCharact)
		{
			int objIndex = ff.CreateExt(materialRelations, true);
			int charValIndex = ff.CreateExt(m_api->m_charact_mgr.e_apl_charact_value, 0, 0, CaplFinder::grpAND);
			ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_charact, m_shopCharact, CaplFinder::cmpEqual);	
			ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_item, objIndex);
			ff.AddToResult(charValIndex);
			ff.Find(m_api->m_data, relCharValues, false, true, false, true, true);
			ff.Clear();
			aplQSortByAttr(&m_api->m_data, relCharValues, 0, relCharValues.Size-1, m_api->m_charact_mgr.a_apl_charact_val_item);
		}

		// найдем характеристики для деталей
		aplExtent ingItems;
		for(int i=0; i<materialRelations.Size; ++i)
		{
			CaplInstance *ingItem = 0;
			m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
			if(ingItem)
				ingItems.Add(ingItem);			
		}

		if(m_rascCharact)
		{
			int ingIndex = ff.CreateExt(ingItems, true);
			int charValIndex = ff.CreateExt(m_api->m_charact_mgr.e_apl_charact_value, 0, 0, CaplFinder::grpAND);
			ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_charact, m_rascCharact, CaplFinder::cmpEqual);	
			ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_item, ingIndex);
			ff.AddToResult(charValIndex);
			ff.Find(m_api->m_data, ingItemsCharValues, false, true, false, true, true);
			aplQSortByAttr(&m_api->m_data, ingItemsCharValues, 0, ingItemsCharValues.Size-1, m_api->m_charact_mgr.a_apl_charact_val_item);
		}
	}

	// сгруппируем
	for(int i=0; i<materialRelations.Size; ++i)
	{
		CaplValue value;
		bool bRelationGrouped = false;

		CaplInstance *ingItem = 0, *edItem = 0;
		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);

		if(edItem->GetId() == 58694684)
			int h = 0;

		if(ingItem && edItem)
		{
			// поищем у вышестоящего изделия хар-ку расцеховка
			value.Set(ingItem);

			int index = aplQFindInstInExtentByAttr(&m_api->m_data, ingItemsCharValues, m_api->m_charact_mgr.a_apl_charact_val_item, &value);
			if(-1 != index)
			{
				CString rasc;
				m_api->m_data.GetAttr(ingItemsCharValues[index], m_api->m_charact_mgr.a_apl_charact_val_descr_val, rasc);

				if(rasc.IsEmpty() == false)
				{
					CStringArray saGrps, saDepots;
					ParseRasc(rasc, _T(";"), _T("/"), &saDepots, &saGrps);

					if(saDepots.GetSize() > 0)
					{
						// берем последний цех, если покупное и первый во всех остальных случаях 0000849: Цеха-потребители
						int shopNum = 0;
						CString pdfSource, pdfType;

						m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_pdfwss_source, pdfSource);
						m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_apl_pdf_type, pdfType);

						if(pdfSource == _T("bought") && pdfType != _T("material"))
							shopNum = saDepots.GetSize() - 1;

						groupedMaterialRelations[saDepots[shopNum]].push_back(materialRelations[i]);
						bRelationGrouped = true;
					}
				}
			}	
		}					
		
		// поищем хар-ку "	ребитель" у связи			
		if(false == bRelationGrouped)
		{						
			// поищем хар-ку "	ребитель" у связи
			value.Set(materialRelations[i]);
			int index = aplQFindInstInExtentByAttr(&m_api->m_data, relCharValues, m_api->m_charact_mgr.a_apl_charact_val_item, &value);

			if(-1 != index)
			{
				CaplInstance *org = 0;
				m_api->m_data.GetAttr(relCharValues[index], m_api->m_charact_mgr.a_apl_reference_val, org);

				if(org != 0)
				{
					CString orgId;
					m_api->m_data.GetAttr(org, m_api->m_appr_mgr.a_org_id, orgId);

					if(orgId.IsEmpty() == false)
					{
						groupedMaterialRelations[orgId].push_back(materialRelations[i]);
						bRelationGrouped = true;
					}
				}
			}		
		}

		// если уж совсем не получилось найти цех (хотя такого быть не должно)
		if(false == bRelationGrouped)
		{
			ASSERT(FALSE);
		}
	}	
}

void CaplNrmmManager::ParseRasc(CString& csInRasc,CString csDepotSep,CString csGrpSep,
							 CStringArray* csapOutDepots,CStringArray* csapOutGrps)
{
	CString		csAll,csPart,csDep,csGrp;

	int			iLen4Del=-1;
	if(csInRasc.IsEmpty()) return;

	csAll=csInRasc;

	// убираем цеха покрытия и термообработки
	int nIndex = csAll.Find(_T('П'));
	if(nIndex != -1)
	{
		csAll = csAll.Left(nIndex - 1);
	}
	else 
	{
		nIndex = csAll.Find(_T('Т'));
		if(nIndex != -1)
		{
			csAll = csAll.Left(nIndex - 1);
		}
	}

	do 
	{
		csPart=_T("");csDep=_T("");csGrp=_T("");

		//	получаем цех (и группу, если есть)
		csPart=csAll.SpanExcluding(csDepotSep);
		//	получаем только цех
		csDep=csPart.SpanExcluding(csGrpSep);

		//	получаем группу
		if(csDep.GetLength()<csPart.GetLength())
			csGrp=csPart.Right(csPart.GetLength()-csDep.GetLength()-1);

		if(NULL!=csapOutDepots)
			csapOutDepots->Add(csDep);

		if(NULL!=csapOutGrps)
			csapOutGrps->Add(csGrp);

		iLen4Del=csPart.GetLength();
		if(csAll.GetLength()>=csPart.GetLength()+1)
			iLen4Del++;
		csAll.Delete(0,iLen4Del);
	} 
	while(_T("")!=csAll);
}

bool CaplNrmmManager::SynchronizeCount(map<CaplInstance*, CaplInstance*> &nrmmMaterial, long dId, CaplInstance *pdf)
{
	// на этот коэффициент будем домножать общее кол-во для каждого матераила
	double koeff = 1.0;
	CaplInstance *charact = m_api->m_charact_mgr.GetCharacteristicBN(APL_T("Коэффициент пересчета"));
	if(charact)
	{
		CaplFinder ff(DEF_SOURCE);
		ff.QuickFind(&m_api->m_data, true, 2, std::make_pair(m_api->m_charact_mgr.a_apl_charact_val_item, pdf),
			std::make_pair(m_api->m_charact_mgr.a_apl_charact_val_charact, charact));
		
		if(ff.m_innerExtent.Size)
			m_api->m_data.GetAttr(ff.m_innerExtent[0], m_api->m_charact_mgr.a_apl_charact_val_meas_val, koeff);
	}	

	int i= 0 ;
	CString logMessage;
	map<CaplInstance*, CaplInstance*>::iterator it;

	for(it=nrmmMaterial.begin(); it!=nrmmMaterial.end(); ++it, ++i)
	{
		logMessage.Format(APL_T("Этап 3 из 3. Синхронизация норм.\nВыполнено на %d%%"), (i+1)*100/nrmmMaterial.size());
		aplSetTextWaitDlg(dId, logMessage);

		SynchronizeCount(it->first, it->second, pdf, koeff);
	}

	return true;
}

bool CaplNrmmManager::SetNewItemsAccess(aplExtent &accessItems)
{
	if(0 == accessItems.Size)
		return false;

	CaplAdminModeProvider adminmode(&(m_api->m_data),-1, -1,DEF_SOURCE);

	CaplAttrValue	val;
	aplExtent		result;

	val.attr = m_api->m_appr_mgr.a_apl_user_name;
	val.value.Set(APL_T("Ведущие сводных НРМ"));
	m_api->m_data.NET_FindInstancesWithAttrValues(m_api->m_data.GetEntityBN(_T("apl_workgroup")),1,&val,result,false);

	if(result.Size > 0)
	{
		CString accessPattern(_T(""));

		accessPattern.Format(_T("0:RO;%d:RW"), result[0]->GetId());		
		m_api->m_data.NET_SetAccessFromPattern(&accessItems, accessPattern);
	}

	return true;	 
}

CaplInstance* CaplNrmmManager::GetBasePrdUnit()
{
	aplExtent ext;
	CaplAttrValue value;

	value.value.Set(APL_T("шт"));
	value.attr = m_api->m_charact_mgr.a_apl_unit_id;
	m_api->m_data.FindInstancesWithAttrValuesInLocalCache(m_api->m_charact_mgr.e_apl_unit, 0, 1, &value, ext, false, false, false);
	if(ext.Size==0)
	{
		ASSERT(FALSE);
		return 0;
	}

	CaplInstance *inst=ext[0];
	return inst;
}

CaplInstance* CaplNrmmManager::AddChild(CaplInstance *parent, LPCTSTR id, LPCTSTR name)
{	
	CaplInstance *prd = m_api->m_data.CreateInstance(m_api->m_prd_mgr.e_prd);
	CaplInstance *pdf = m_api->m_data.CreateInstance(m_api->m_prd_mgr.e_apl_pdf);

	m_api->m_data.PutAttr(pdf, m_api->m_prd_mgr.a_pdf_prd, prd);
	m_api->m_data.PutAttr(pdf, m_api->m_prd_mgr.a_apl_pdf_type, _T("kit"));	
	m_api->m_data.PutAttr(prd, m_api->m_prd_mgr.a_prd_id, id);
	m_api->m_data.PutAttr(prd, m_api->m_prd_mgr.a_prd_name, name);

	m_api->m_data.PutAttr(pdf, m_api->m_prd_mgr.a_apl_pdf_unit, m_baseUnit);
	m_api->m_prd_mgr.CreatePdfRelation(0, parent, pdf, _T(""), _T(""), 1.0, m_baseUnit, false, false);	

	return pdf;
}

bool CaplNrmmManager::LoadClassifiers(TStrStrMap &classifiers)
{
	// найдем первый уровень справочников "материалы" и "стандартные изделия"
	CaplFinder ff(DEF_SOURCE);
	aplExtent result;
	CaplInstance *nullInst = 0;
	long dId = aplStartWaitDlg(APL_T("Загрузка справочника..."));

	int clsSystemIndex = ff.CreateExt(m_api->m_classifier_mgr.e_apl_classifier_system, 0, 0, CaplFinder::grpOR);
	ff.AddAttr(clsSystemIndex, m_api->m_classifier_mgr.a_apl_classifier_system_id, APL_T("Стандартные изделия"), CaplFinder::cmpEqual);
	ff.AddAttr(clsSystemIndex, m_api->m_classifier_mgr.a_apl_classifier_system_id, APL_T("Материалы"), CaplFinder::cmpEqual);
	ff.AddAttr(clsSystemIndex, m_api->m_classifier_mgr.a_apl_classifier_system_id, APL_T("ПКИ114"), CaplFinder::cmpEqual);	

	int clsLvlIndex = ff.CreateExt(m_api->m_classifier_mgr.e_apl_classifier_level);
	ff.AddAttr(clsLvlIndex, m_api->m_classifier_mgr.a_apl_classifier_level_parent, nullInst, CaplFinder::cmpEqual);
	ff.AddAttr(clsLvlIndex, m_api->m_classifier_mgr.a_apl_classifier_level_system, clsSystemIndex);

	ff.AddToResult(clsLvlIndex);
	ff.Find(m_api->m_data, result, true, false, false, true, true);
	aplEndWaitDlg(dId);

	// запомним обозначения
	CString id, name;
	for(int i=0; i<result.Size; ++i)
	{
		m_api->m_data.GetAttr(result[i], m_api->m_classifier_mgr.a_apl_classifier_level_id, id);
		m_api->m_data.GetAttr(result[i], m_api->m_classifier_mgr.a_apl_classifier_level_name, name);

		if(id.IsEmpty() == false && name.IsEmpty() == false)
			classifiers[id] = name;
	}

	// оценим результат
	if(classifiers.size() == 0)
	{
		AfxMessageBox(APL_T("Не удалось найти справочник материалов и стандартных изделий."), MB_ICONERROR);
		return false;
	}

	return true;
}

bool CaplNrmmManager::CanGenerateNrm( CaplInstance *nrm, CaplInstance *pdf )
{
	if(0 == nrm || 0 == pdf) 
		return false;

	if(0 == m_api || false == m_api->m_data.IsConnected())
		return false;

	if(m_api->m_prd_mgr.GetProductId(nrm).Left(4) != APL_T("НРМ-"))
	{
		AfxMessageBox(APL_T("Указанная норма должна начинаться на \"НРМ-\"."), MB_ICONERROR);
		return false;
	}

	CString source;
	m_api->m_data.GetAttr(pdf, m_api->m_prd_mgr.a_pdfwss_source, source);
	if(0 == source.CompareNoCase(_T("bought")))
	{
		AfxMessageBox(APL_T("Указанная сборка покупная."), MB_ICONINFORMATION);
		return false;
	}

	CaplFinder ff(DEF_SOURCE);
	int index = ff.CreateExt(m_api->m_prd_mgr.e_pdr);
	ff.AddAttr(index, m_api->m_prd_mgr.a_pdr_r_ting, nrm, CaplFinder::cmpEqual);
	ff.AddToResult(index);
	ff.Find(m_api->m_data, true, false, false, false, true);
	if(ff.m_innerExtent.Size > 0)
	{
		AfxMessageBox(APL_T("Указанная норма содержит дочерние элементы."), MB_ICONERROR);
		return false;
	}

	return true;
}

CaplInstance* CaplNrmmManager::IsPreFabricated(CaplInstance *relation)
{
	CaplInstance *ingItem = 0, *edItem = 0;
	m_api->m_data.GetAttr(relation, m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
	m_api->m_data.GetAttr(relation, m_api->m_prd_mgr.a_pdr_r_ted, edItem);
	if(ingItem == 0 || edItem == 0)
		return false;

	CString source;
	m_api->m_data.GetAttr(ingItem, m_api->m_prd_mgr.a_pdfwss_source, source);

	CString type;
	m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_apl_pdf_type, type);

	if(0 == type.CompareNoCase(_T("material")) && (0 == source.CompareNoCase(_T("coproduction")) || 0 == source.CompareNoCase(_T("coproduction"))))
		return ingItem;

	return 0;
}

void CaplNrmmManager::FindExistingNrm(aplExtent &pdfs, aplExtent &nrmItems)
{
	CaplFinder ff(DEF_SOURCE);
	map<CString, CaplInstance*> items;

	int prdIndex = ff.CreateExt(m_api->m_prd_mgr.e_prd, 0, 0, CaplFinder::grpOR);
	for(int i=0; i<pdfs.Size; ++i)
	{
		CString prdId = m_api->m_prd_mgr.GetProductId(pdfs[i]);
		items[APL_T("НРМ-") + prdId] = pdfs[i];
		ff.AddAttr(prdIndex, m_api->m_prd_mgr.a_prd_id, APL_T("НРМ-") + prdId, CaplFinder::cmpEqual);
		
		if(items.size() == 1000 || i == pdfs.Size - 1)
		{
			int pdfIndex = ff.CreateExt(m_api->m_prd_mgr.e_pdf);
			ff.AddAttr(pdfIndex, m_api->m_prd_mgr.a_pdf_prd, prdIndex);

			ff.AddToResult(pdfIndex);
			ff.Find(m_api->m_data, true, false, false, true, true);
			m_api->m_prd_mgr.LoadPrdInfo(ff.m_innerExtent);

			for(int j=0; j<ff.m_innerExtent.Size; ++j)
			{
				CString prdId = m_api->m_prd_mgr.GetProductId(ff.m_innerExtent[j]);
				map<CString, CaplInstance*>::iterator it = items.find(prdId);

				if(it != items.end())
				{
					int index = pdfs.Find(it->second);
					nrmItems.Add(ff.m_innerExtent[j]);
					pdfs.Remove(index);

					m_nrmForAssembly[ff.m_innerExtent[j]] = it->second;
				}
			}

			/************************************************************************/
			/*                                                                      */
			/************************************************************************/

			items.clear();
			ff.Clear();
			prdIndex = ff.CreateExt(m_api->m_prd_mgr.e_prd, 0, 0, CaplFinder::grpOR);
		}				
	}
}

void CaplNrmmManager::FindMaterialRelations(aplExtent &parentPdfs, aplExtent &structureRelations, aplExtent &nrmItems)
{
	int val = 0;
	m_api->m_options_mgr.GetOptionValueBN(_T("КВЗ\\Использовать существующие НРМ при автогенерации"), val, 0);

	// поищем соответствующие НРМ
	if(false == (parentPdfs.Size == 1 && structureRelations.Size == 0) && val == 1)
		FindExistingNrm(parentPdfs, nrmItems);

	// для переданных pdf ищем связи, в которых они являются верхушками
	aplExtent tmpParentPdfs;	
	aplExtent tmpRelations; tmpRelations.Unique = false;

	int count = 0; 
	for(int i=0; i<parentPdfs.Size; ++i)
	{
		if(!parentPdfs[i]) 
			continue;
		if(!parentPdfs[i]->GetType())
			continue;

		++count;
		tmpParentPdfs.Add(parentPdfs[i]);

		if(((count == 1000) || (i == parentPdfs.GetSize()-1)))
		{			
			CaplFinder ff(DEF_SOURCE);
			aplExtent subResult;								

			int pdfIndex = ff.CreateExt(tmpParentPdfs, true);
			int relIndex = ff.CreateExt(m_api->m_prd_mgr.e_pdr);
			ff.AddAttr(relIndex, m_api->m_prd_mgr.a_pdr_r_ting, pdfIndex);						

			ff.AddToResult(relIndex);
			ff.Find(m_api->m_data, subResult, true, false, false, false, true);	

			tmpParentPdfs.Clear();
			count = 0;

			// подгрузим дополнительные атрибуты
			CaplLoadData ld(&m_api->m_data, DEF_SOURCE);
			for(int j=0; j<subResult.Size; ++j)
			{
				if(!subResult[j]) 
					continue;
				if(!subResult[j]->GetId())
					continue;
				if(!subResult[j]->GetType())
					continue;
				ld.AddQuery(0, subResult[j], true);
				tmpRelations.Add(subResult[j]);
			}

			if(ld.m_queries.GetSize())
			{
				int m0 = ld.AddQuery(_T('d'), 0, m_api->m_prd_mgr.e_pdr, m_api->m_prd_mgr.a_pdr_r_ting, true, true);
				int m1 = ld.AddQuery(_T('d'), 0, m_api->m_prd_mgr.e_pdr, m_api->m_prd_mgr.a_pdr_r_ted, true, true);

				ld.AddQuery(_T('d'), m0, m_api->m_prd_mgr.e_pdf, m_api->m_prd_mgr.a_pdf_prd, true, true);
				ld.AddQuery(_T('d'), m1, m_api->m_prd_mgr.e_pdf, m_api->m_prd_mgr.a_pdf_prd, true, true);

				ld.LoadEx();
			}
		}				
	}

	// найдем правила применяемости и отфильтруем по дате
	aplExtent pdfEffectivities;
	if(tmpRelations.Size > 0)
	{
		aplExtent tmpSubRelations;	

		for(int i=0; i<tmpRelations.Size; ++i)
		{
			++count;
			tmpSubRelations.Add(tmpRelations[i]);

			if(((count == 1000) || (i == tmpRelations.GetSize()-1)))
			{
				CaplFinder ff(DEF_SOURCE);
				aplExtent subResult;

				int relIndex = ff.CreateExt(tmpSubRelations, true);
				int pdfEffIndex = ff.CreateExt(m_api->m_prd_mgr.e_pd_eff);
				ff.AddAttr(pdfEffIndex, m_api->m_prd_mgr.a_pde_usage, relIndex);
				ff.AddToResult(pdfEffIndex);
				ff.Find(m_api->m_data, subResult, true, false, false, true, true);

				tmpSubRelations.Clear();
				pdfEffectivities.Append(subResult);
				count = 0;
			}
		}
	}

	aplExtent relations;
	m_api->m_prd_mgr.FilterlByDate(tmpRelations, COleDateTime::GetCurrentTime(), relations);	

	tmpRelations.Clear(); tmpRelations.Append(relations); relations.Clear();
	m_api->m_prd_mgr.FilterlBySN(tmpRelations, _T("09399999"), relations);	

	parentPdfs.Clear();

	// если нужно, то отфильтруем изделия, которые уже связаны с нормами
	int optionValue = 1;
	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("КВЗ\\Не учитывать вложенные нормы"), optionValue, 1);	

	// подготовим данные для следующего поиска
	CString type, source;
	CaplInstance *edItem = NULL, *ingItem = NULL;

	for(int i=0; i<relations.Size; ++i)
	{
		if(!relations[i]->GetType())
			continue;

		m_api->m_data.GetAttr(relations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);
		m_api->m_data.GetAttr(relations[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);		

		if(edItem && ingItem)
		{			
			// покупное дальше не разузловываем
			m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_pdfwss_source, source);
			if(0 != source.CompareNoCase(_T("bought")))
				parentPdfs.Add(edItem);			
		}		
	}

	structureRelations.Append(relations);

	if(parentPdfs.Size > 0)
		FindMaterialRelations(parentPdfs, structureRelations, nrmItems);
}

bool CaplNrmmManager::TestMaterialRelation(CaplInstance *relation)
{
	CaplInstance *edItem = 0;
	m_api->m_data.GetAttr(relation, m_api->m_prd_mgr.a_pdr_r_ted, edItem);
	if(edItem == 0)
		return false;

	CaplInstance *ingItem = 0;
	m_api->m_data.GetAttr(relation, m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
	if(ingItem == 0)
		return false;

	// проверим не является ли это копонентным материалом				
	CString type;
	m_api->m_data.GetAttr(ingItem, m_api->m_prd_mgr.a_apl_pdf_type, type);
	if(0 == type.CompareNoCase(_T("material")))
		return false;

	// может это полуфабрикат
	CString source;
	m_api->m_data.GetAttr(ingItem, m_api->m_prd_mgr.a_pdfwss_source, source);
	m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_apl_pdf_type, type);
	if((0 == source.CompareNoCase(_T("coproduction")) || 0 == source.CompareNoCase(_T("bought"))) && 0 == type.CompareNoCase(_T("material")))
		return false;

	// проверим не является ли деталь из отходов
	bool fromWaste = false;
	m_api->m_data.GetAttr(relation, m_api->m_prd_mgr.a_make_from_waste, fromWaste);
	if(true == fromWaste)
		return false;

	return true;
}

void CaplNrmmManager::SaveDisassembly(CaplInstance *pdf, aplExtent &structureRelations)
{
	m_disassembliedPdfs.insert(make_pair(pdf, vector<CaplInstance*>()));
	map<CaplInstance*, vector<CaplInstance*>>::iterator It = m_disassembliedPdfs.find(pdf);

	if(It != m_disassembliedPdfs.end())
	{
		vector<CaplInstance*> &vec = It->second;
		vec.resize(structureRelations.Size);

		for(unsigned int i=0; i<vec.size(); ++i)
			vec[i] = structureRelations[i];
	}	
}

void CaplNrmmManager::LoadAttrsMaterialRelations(aplExtent &materialRelations, bool bLoadFromDb)
{
	CaplInstance *ingItem = 0, *edItem = 0;
	CaplLoadData ld(&m_api->m_data, DEF_SOURCE);

	for(int i=0; i<materialRelations.Size; ++i)
	{
		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
		if(bLoadFromDb || ingItem && m_pdfWithLoadedAttrs.find(ingItem) == m_pdfWithLoadedAttrs.end())
		{
			if(ingItem)
			{
				ld.AddQuery(0, ingItem, true);
				m_pdfWithLoadedAttrs.insert(ingItem);
			}
		}

		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);
		if(bLoadFromDb || edItem && m_pdfWithLoadedAttrs.find(edItem) == m_pdfWithLoadedAttrs.end())
		{
			if(edItem)
			{
				ld.AddQuery(0, edItem, true);
				m_pdfWithLoadedAttrs.insert(edItem);
			}
		}
	}

	if(ld.m_queries.GetSize() > 0)
	{
		ld.AddQuery(_T('d'), 0, m_api->m_prd_mgr.e_pdf, m_api->m_prd_mgr.a_pdf_prd, true, true);
		ld.LoadEx();
	}
}

void CaplNrmmManager::LoadCharacts(aplExtent &materialRelations)
{
	long dId = aplStartWaitDlg(APL_T("Загрузка характеристик..."));

	CaplInstance *edItem = 0;
	for(int i=0; i<materialRelations.Size; ++i)
	{
		m_relationsWithLoadedCharacts.insert(materialRelations[i]);

		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);
		m_relationsWithLoadedCharacts.insert(edItem);
	}

	// найдем характеристики для связей
	CaplFinder ff(DEF_SOURCE);
	aplExtent relCharValues;
	aplExtent ingItemsCharValues;	

	if(m_shopCharact)
	{
		int objIndex = ff.CreateExt(materialRelations, true);
		int charValIndex = ff.CreateExt(m_api->m_charact_mgr.e_apl_charact_value, 0, 0, CaplFinder::grpAND);
		ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_charact, m_shopCharact, CaplFinder::cmpEqual);	
		ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_item, objIndex);
		ff.AddToResult(charValIndex);
		ff.Find(m_api->m_data, relCharValues, true, false, false, true, true);
		ff.Clear();		
	}

	// найдем характеристики для деталей
	aplExtent ingItems, edItems;
	for(int i=0; i<materialRelations.Size; ++i)
	{
		CaplInstance *ingItem = 0, *edItem = 0;
		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);
		if(ingItem)
			ingItems.Add(ingItem);			

		m_api->m_data.GetAttr(materialRelations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);
		if(edItem)
			edItems.Add(edItem);			
	}

	if(m_rascCharact)
	{
		int ingIndex = ff.CreateExt(ingItems, true);
		int charValIndex = ff.CreateExt(m_api->m_charact_mgr.e_apl_charact_value, 0, 0, CaplFinder::grpAND);
		ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_charact, m_rascCharact, CaplFinder::cmpEqual);	
		ff.AddAttr(charValIndex, m_api->m_charact_mgr.a_apl_charact_val_item, ingIndex);
		ff.AddToResult(charValIndex);
		ff.Find(m_api->m_data, ingItemsCharValues, true, false, false, true, true);		
	}

	aplExtent edItemsCharactValues;
	m_api->m_charact_mgr.FindAssociatedCharacteristic(edItems, edItemsCharactValues, false, false, false, true);
	aplEndWaitDlg(dId);
}

bool CaplNrmmManager::FindMaterials( CaplInstance *parentPdf, aplExtent &materials, aplExtent &nrmItems )
{
	aplExtent structureRelations;
	aplExtent parentPdfs; parentPdfs.Add(parentPdf);	

	// разузлуем состав и запомним, чтобы потом искать по кешу
	long dId = aplStartWaitDlg(APL_T("Поиск материалов..."));
	FindMaterialRelations(parentPdfs, structureRelations, nrmItems);	
	SaveDisassembly(parentPdf, structureRelations);
	LoadAttrsMaterialRelations(structureRelations, true);
	aplEndWaitDlg(dId);

	// отберем материалы
	CString source;
	CString type, code;	
	bool isStandard = false;
	CaplInstance *edItem = 0;
	aplExtent materialRelations;

	int count = 0;
	for(int i=0; i<structureRelations.Size; ++i)
	{
		m_api->m_data.GetAttr(structureRelations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);
		if(edItem)
		{
			CaplInstance *ingItem = 0;
			m_api->m_data.GetAttr(structureRelations[i], m_api->m_prd_mgr.a_pdr_r_ting, ingItem);

			if(-1 != m_api->m_prd_mgr.GetProductId(edItem).Find(APL_T("Пф")))
				count++;

			m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_pdf_code, code);
			m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_apl_pdf_type, type);

			if(code == _T("020204193"))
				int h = 0;

			if(code.IsEmpty() == false)
			{
				if(0 == type.CompareNoCase(_T("material")))
				{	
					if(true == TestMaterialRelation(structureRelations[i]))
					{
						materials.Add(edItem);	
						materialRelations.Add(structureRelations[i]);
					}
					else 
					{
						CaplInstance *item = IsPreFabricated(structureRelations[i]);
						if(item)
						{
							materials.Add(item);						
							materialRelations.Add(structureRelations[i]);
						}
					}
				}
				else
				{					
					m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_pdfwss_source, source);
					m_api->m_data.GetAttr(edItem, m_api->m_prd_mgr.a_apl_pdf_standard, isStandard);

					if(0 == source.CompareNoCase(_T("bought")) || 0 == source.CompareNoCase(_T("coproduction")))
					{
						materials.Add(edItem);	
						materialRelations.Add(structureRelations[i]);
					}
				}
			}
		}
	}

	// подгрузим нужные характеристики
	LoadCharacts(materialRelations);

	// оценим результат
	if(0 == materials.Size && 0 == nrmItems.Size)
	{
		AfxMessageBox(APL_T("Не найдено ни одного материала."), MB_ICONINFORMATION);
		return false;
	}

	return true;
}

bool CaplNrmmManager::DeleteLimit( CaplInstance *nrm )
{
	if(0 == nrm)
		return false;

	CString nrmId = APL_T("НРМ-");
	CString prdId = m_api->m_prd_mgr.GetProductId(nrm);

	if(prdId.IsEmpty() || prdId.GetLength() < 4 || prdId.Left(4) != nrmId)
		return false;

	aplExtent parentPdf; parentPdf.Add(nrm);
	prdId = prdId.Right(prdId.GetLength() - nrmId.GetLength());
	
	DeleteLimitRecursive(parentPdf, prdId);
	return m_api->SaveChanges();
}

bool CaplNrmmManager::DeleteLimitRecursive( aplExtent &parentPdf, const CString &pdfId )
{	
	m_api->m_prd_mgr.LoadPrdInfo(parentPdf);
	for(int i=0; i<parentPdf.Size; ++i)
	{
		CString prdId = m_api->m_prd_mgr.GetProductId(parentPdf[i]);
		if(prdId == APL_T("НРМ-") + pdfId)
			continue;

		if(prdId.Find(_T("-") + pdfId + _T("-")) == -1)
			parentPdf.Remove(i--);
	}

	// найдем связи
	CaplFinder ff(DEF_SOURCE);
	aplExtent relations;

	int relIndex = ff.CreateExt(m_api->m_prd_mgr.e_pdr);
	ff.AddAttr(relIndex, m_api->m_prd_mgr.a_pdr_r_ting, ff.CreateExt(parentPdf, true));
	ff.AddToResult(relIndex);
	ff.Find(m_api->m_data, relations, true, false, false, true, true);
	ff.Clear();

	// удалим характерестики
	aplExtent items;
	items.Append(parentPdf);
	items.Append(relations);
	int charactIndex = ff.CreateExt(m_api->m_charact_mgr.e_apl_charact_value);
	ff.AddAttr(charactIndex, m_api->m_charact_mgr.a_apl_charact_val_item, ff.CreateExt(items, true));
	ff.AddToResult(charactIndex);
	ff.Find(m_api->m_data, true, false, false, true, true);
	
	for(int i=0; i<ff.m_innerExtent.Size; ++i)
		m_api->m_data.DeleteInstance(ff.m_innerExtent[i], false);		

	// удалим изделия
	for(int i=0; i<parentPdf.Size; ++i)
	{
		if(parentPdf[i]->GetType() == 0)
			continue;

		// на последнем уровне у нас лежат изделия из справочника
		// их естественно не трогаем
		CString pdfCode;
		m_api->m_data.GetAttr(parentPdf[i], m_api->m_prd_mgr.a_pdf_code, pdfCode);
		if(pdfCode.IsEmpty() == false)
			continue;

		CaplInstance *prd = 0;
		m_api->m_data.GetAttr(parentPdf[i], m_api->m_prd_mgr.a_pdf_prd, prd);

		// удаляем сам объект и все что с ним связано
		m_api->m_data.DeleteInstance(prd, false);
		m_api->m_data.DeleteInstance(parentPdf[i], false);
	}	
	
	// узнаем следующий уровень
	parentPdf.Clear();
	for(int i=0; i<relations.Size; ++i)
	{
		CaplInstance *edItem = 0;
		m_api->m_data.GetAttr(relations[i], m_api->m_prd_mgr.a_pdr_r_ted, edItem);		
		if(edItem)
			parentPdf.Add(edItem);
	}

	// удаляем все связи
	for(int i=0; i<relations.Size; ++i)
		m_api->m_data.DeleteInstance(relations[i], false);		

	if(parentPdf.Size)
		DeleteLimitRecursive(parentPdf, pdfId);

	return true;
}
