#include "StdAfx.h"
#include "SpecGenerator.h"
#include "GenSpecDlg.h"

LPCTSTR reg_sevtion=_T("CSpecGenerator");

CSpecGenerator::CSpecGenerator(CaplAPI *api)
{
	m_api=api;
	m_pdf=0;
	m_change=0;

	m_max_position=0;
	m_bSetEmptyPosition=TRUE;
	m_bClearExistPos=TRUE;

	m_bUseSubst=TRUE;

	m_bGetFormatForPart=TRUE; // Определять форматы для деталей
	m_bSetBCHForPart=TRUE; // Если нет чертежа, то ставить 'БЧ'

	m_bBCHF_AddMat=FALSE;
	m_bUseOst4=FALSE;
	m_bUsage=FALSE;
	m_bUseAassemblyDescr4Notes=FALSE;

	m_comumn_format=0;
	m_comumn_zone=0;
	m_comumn_pos=0;
	m_comumn_id=0;
	m_comumn_name=0;
	m_comumn_kol=0;
	m_comumn_prim=0;

	LoadPlural();
	LoadOptions();
}

CSpecGenerator::~CSpecGenerator(void)
{
}
//*****************************************************************************************
bool CSpecGenerator::ShowOptionsDlg(bool b4change)
{
	CGenSpecDlg dlg;
	dlg.m_SpecGenerator=this;
	dlg.m_b4change=b4change;
	if (IDOK!=dlg.DoModal())  return false;
	return true;
}
//*****************************************************************************************
bool CSpecGenerator::SaveOptions()
{
	CWinApp *app=AfxGetApp();
	if(0==app) return false;

	app->WriteProfileInt(reg_sevtion,_T("m_bSaveAsDocument"),m_bSaveAsDocument);
	app->WriteProfileInt(reg_sevtion,_T("m_bSetNewVersionActive"),m_bSetNewVersionActive);
	
	app->WriteProfileInt(reg_sevtion,_T("m_bSetEmptyPosition"),m_bSetEmptyPosition);
	app->WriteProfileInt(reg_sevtion,_T("m_bClearExistPos"),m_bClearExistPos);
	app->WriteProfileInt(reg_sevtion,_T("m_bGetFormatForPart"),m_bGetFormatForPart);
	app->WriteProfileInt(reg_sevtion,_T("m_bSetBCHForPart"),m_bSetBCHForPart);

	app->WriteProfileInt(reg_sevtion,_T("m_bBCHF_AddMat"),m_bBCHF_AddMat);
	app->WriteProfileInt(reg_sevtion,_T("m_bUseOst4"),m_bUseOst4);
	app->WriteProfileInt(reg_sevtion,_T("m_bUsage"),m_bUsage);
	app->WriteProfileInt(reg_sevtion,_T("m_bUseSubst"),m_bUseSubst);
	app->WriteProfileInt(reg_sevtion,_T("m_bUseAassemblyDescr4Notes"),m_bUseAassemblyDescr4Notes);

	return true;
}
//*****************************************************************************************
bool CSpecGenerator::LoadOptions()
{
	CWinApp *app=AfxGetApp();
	if(0==app) return false;

	m_bSaveAsDocument=app->GetProfileInt(reg_sevtion,_T("m_bSaveAsDocument"),TRUE);
	m_bSetNewVersionActive=app->GetProfileInt(reg_sevtion,_T("m_bSetNewVersionActive"),TRUE);

	m_bSetEmptyPosition=app->GetProfileInt(reg_sevtion,_T("m_bSetEmptyPosition"),TRUE);
	m_bClearExistPos=app->GetProfileInt(reg_sevtion,_T("m_bClearExistPos"),TRUE);
	m_bGetFormatForPart=app->GetProfileInt(reg_sevtion,_T("m_bGetFormatForPart"),TRUE);
	m_bSetBCHForPart=app->GetProfileInt(reg_sevtion,_T("m_bSetBCHForPart"),TRUE);

	m_bBCHF_AddMat=app->GetProfileInt(reg_sevtion,_T("m_bBCHF_AddMat"),FALSE);
	m_bUseOst4=app->GetProfileInt(reg_sevtion,_T("m_bUseOst4"),FALSE);
	m_bUsage=app->GetProfileInt(reg_sevtion,_T("m_bUsage"),FALSE);
	m_bUseSubst=app->GetProfileInt(reg_sevtion,_T("m_bUseSubst"),TRUE);
	m_bUseAassemblyDescr4Notes=app->GetProfileInt(reg_sevtion,_T("m_bUseAassemblyDescr4Notes"),FALSE);

	return true;
}

//*****************************************************************************************
//*****************************************************************************************
//*****************************************************************************************
void MakeFirstCharUpper(CString &str) // Делает первую букву заглавной
{
	if(str==_T("")) return;
	
	TCHAR c=str[0];
	if(c>=_T('а') && c<=_T('я')) c+=_T('А')-_T('а');
	else if(c>=_T('a') && c<=_T('z')) c+=_T('Z')-_T('z');
	((LPSTR)(LPCTSTR(str)))[0]=c;
}
//*****************************************************************************************
bool CSpecGenerator::LoadPlural()
{
	int i;
	CString buf, str, str1, str2, str3, fname;
	buf=AfxGetApp()->m_pszHelpFilePath;
	i=buf.ReverseFind(_T('\\'));
	if(i>0) fname=buf.Left(i+1);
	fname+=_T("Plural.plr");

	CStdioFile file;
	if(!file.Open(fname,CFile::modeRead|CFile::shareDenyNone))
	{
		AfxMessageBox(_T("Ошибка открытия файла Plural.plr !\n\nПреобразование во множественное число невозможно!"),MB_ICONWARNING|MB_OK);
		return false;
	}

	m_plural_map.Clear();
	m_plural_supergroup_map.Clear();
	
	while(file.ReadString(buf))
	{
		if(buf==_T("")) continue;
		i=buf.Find(_T('*'));
		if(i<0)
		{
			str1=_T("В файле Plural.plr ошибка - в строке отсутстует '*'. Строка:\n\n");
			str1+=buf;
			AfxMessageBox(str1,MB_ICONWARNING|MB_OK);
			continue;
		}
		str1=buf.Left(i);

		int j=buf.Find(_T('*'),i+1);
		if(j>=0) // Есть супергруппа
		{
			str2=buf.Mid(i+1,j-(i+1));
			str3=buf.Right(buf.GetLength()-(j+1));
			MakeFirstCharUpper(str3);
			m_plural_supergroup_map.Add(str1,str3);
		}
		else str2=buf.Right(buf.GetLength()-(i+1));
	
		MakeFirstCharUpper(str2);// делаем первый смвол заглавным
		m_plural_map.Add(str1,str2);
	}

	file.Close();
	return true;
}
//*****************************************************************************************

bool CSpecGenerator::Generate(CaplInstance *pdf, bool bShowOptions, CaplInstance *change, CaplInstance **new_spec_rev)
{
	if(0!=new_spec_rev) *new_spec_rev=0; 

	m_all_rows.Clear();
	m_map_pdr_line.Clear();
	m_map_subst.Clear();
	m_map_eff.Clear();
	m_max_position=0;

	if(bShowOptions)
	{
		if(!ShowOptionsDlg()) return false;
	}

	CString sPathRep; aplGetTempPath(sPathRep);
	CString buf=AfxGetApp()->m_pszHelpFilePath;
	CString template_path=buf.Left(buf.ReverseFind(_T('\\')));

	template_path+=_T("\\Шаблон_СП_01.xml");

	CaplReport report;
	if(!report.OpenFile(template_path)) {AfxMessageBox(_T("Не найден шаблон отчета. Создание невозможно!)"),MB_ICONSTOP|MB_OK); return false;}

	// Берем параметры из шаблона
	if(report.m_pages.GetSize()<2) {AfxMessageBox(_T("Некорректный шаблон (менее 2х страниц)!!)"),MB_ICONSTOP|MB_OK); return false;}
	
	CaplReportTable *table=report.m_pages[0]->m_table;
	if(0==table) {AfxMessageBox(_T("Некорректный шаблон (нет определения твблицы)!!)"),MB_ICONSTOP|MB_OK); return false;}
		
	if(7!=table->m_columns.GetSize()) {AfxMessageBox(_T("Некорректный шаблон (кол-во столбцов не равно 7)!!)"),MB_ICONSTOP|MB_OK); return false;}

	m_comumn_format=table->m_columns[0];
	m_comumn_zone=table->m_columns[1];
	m_comumn_pos=table->m_columns[2];
	m_comumn_id=table->m_columns[3];
	m_comumn_name=table->m_columns[4];
	m_comumn_kol=table->m_columns[5];
	m_comumn_prim=table->m_columns[6];

	//m_higt_row=table->m_row_cy;

	CString str, sAplRep;//, sChngId, sDocName, sOldVer, sNewVer, sDescr,sNotes, sDocChngId,sMetod,sAutocontent, reg_num, doc_id,sCode;

	m_pdf =pdf;
	m_change=change;

	CaplInstance *prd;
	CString sPrdId, sPdfId, sPrdName, sFirstUsage;
	m_api->m_data.GetAttr(pdf, m_api->m_prd_mgr.a_pdf_id, sPdfId);
	m_api->m_data.GetAttr(pdf, m_api->m_prd_mgr.a_pdf_prd, prd);
	m_api->m_data.GetAttr(prd, m_api->m_prd_mgr.a_prd_id, sPrdId);
	m_api->m_data.GetAttr(prd, m_api->m_prd_mgr.a_prd_name, sPrdName);

	m_sMainPdвIdUpper=sPrdId;  m_sMainPdвIdUpper.MakeUpper();// Испрользуется при сортировке

	if(sPdfId!=_T("")) {sPrdId+=_T("-"); sPrdId+=sPdfId; }

	buf= sPrdId;
	buf.Replace(' ','_');
	buf.Replace('?','_');
	buf.Replace('*','_');
	buf.Replace('/','_');
	buf.Replace('\\','_');
	sAplRep = "СП" + buf + ".xmlrep";

	sPathRep+=sAplRep;


	CaplInstance *fu_pdf;
	m_api->m_data.GetAttr(pdf, m_api->m_prd_mgr.a_apl_pdf_first_usage, fu_pdf);
	if(0!=fu_pdf)
	{
		m_api->LoadItemInfo(fu_pdf);
		CaplInstance *fu_prd;
		m_api->m_data.GetAttr(fu_pdf, m_api->m_prd_mgr.a_pdf_prd, fu_prd);
		if(fu_prd)
			m_api->m_data.GetAttr(fu_prd, m_api->m_prd_mgr.a_prd_id, sFirstUsage);
		
		m_api->m_data.GetAttr(fu_pdf, m_api->m_prd_mgr.a_pdf_prd, buf);
		if(buf!="") {sFirstUsage+=_T("-");  sFirstUsage+=buf;}
	}


	//CaplReportDataRow *row;
	//CaplReportField *field;

	CString prim = "",sNewState;
	CString word, pdf1, pdf2, sType;
	CString sAttacment; // Для графы приложения

	// Здесь должна быть генерация
	CaplStrStrMap map_params;
	T_LIST_CaplReportDataRow  data_rows;

	GenerateSpDocs(pdf,data_rows);
	GenerateSpParts(pdf,data_rows);
	GenerateSpNotes(pdf,data_rows);

	if(m_all_rows.GetSize()<1)
	{
		AfxMessageBox(_T("Нет данных для вывода в спецификацию!"),MB_OK|MB_ICONWARNING);
		return false;
	}


	// Формирование финального документа

	//sPathRep=_T("C:\\Users\\Alexey\\Desktop\\СП_rep.xmlrep");  // Для отладки

	SetFileAttributes(sPathRep,FILE_ATTRIBUTE_NORMAL);
	DeleteFile(sPathRep);

	map_params.Add("id", sPrdId);
	map_params.Add("name", sPrdName);
	map_params.Add("firstusage", sFirstUsage);

	// Проставляем высоту (ибо с 02.2024 нулевая высота считается разрывом раздела)
	// Здесь надо чтобы m_cy не была равна 0. Реальная высота задается в шаблоне в теге <table>
	for( int i=0;i<m_all_rows.GetSize();i++)
	{
		CaplReportDataRow *row=m_all_rows[i];
		if(0==row->m_cy) row->m_cy=1;
	}

	if(!report.GenerateReport(&map_params, &m_all_rows,sPathRep,false,true))
	{
		AfxMessageBox(_T("Ошибка при генерации отчета!"), MB_ICONSTOP); 
		return false;
	}

	long file_size=-1;
	CFileStatus status;
	if(CFile::GetStatus(sPathRep,status)) file_size=(long)status.m_size;

	if(file_size<1) 
	{
		AfxMessageBox(_T("Файл не создан!"),MB_OK|MB_ICONSTOP);
		return false;
	}

	if(TRUE!=m_bSaveAsDocument) // Для отладки
	{
		if(IDYES==AfxMessageBox(_T("Документ создан!  Открыть для просмотра ?"),MB_ICONINFORMATION|MB_YESNO))
		{
			aplShellExecute(AfxGetMainWnd()->m_hWnd,_T("open"),sPathRep,NULL,NULL,SW_SHOW);
		}
		return true;
	}

	// Сохраняем документв БД

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

	CaplInstance *doc_rev=0;
	CaplInstance *doc_rep=0;

	CaplInstance *ciType = NULL;
	ciType = m_api->m_doc_mgr.GetDocumentTypeBN(_T("Спецификация"));

	if(0==ciType)
	{
		ciType = m_api->m_data.CreateInstance(m_api->m_doc_mgr.e_dt);
		m_api->m_data.PutAttr(ciType, m_api->m_doc_mgr.a_dt_pdt, _T("Спецификация"));
	}


	if(0==doc_rep)doc_rep=m_api->m_doc_mgr.FindDocByIdAndType(sPrdId,ciType);
	bool bNewDoc=false;

	if(0==doc_rep)
	{
		// создаем новый документ
		bNewDoc=true;
		m_api->SetModeInteractive(false);
		doc_rep=m_api->m_doc_mgr.CreateDocument(sPrdId, sPrdName, "", ciType, sPathRep, true, pdf);	
		m_api->SetModeInteractive(true);
		if(0==doc_rep) {AfxMessageBox(_T("Ошибка создания документа в БД!"),MB_ICONSTOP|MB_OK); return false;}
		m_api->m_data.GetAttr(doc_rep, m_api->m_doc_mgr.a_apl_doc_active, doc_rev);
	}
	else
	{
		// проверяем изменился ли документ
		int  new_crc=GetFileCRC32(sPathRep);
		CaplInstance *act_rev=0;
		m_api->m_data.GetAttr(doc_rep, m_api->m_doc_mgr.a_apl_doc_active, act_rev);
		if(0!=act_rev)
		{
			int old_crc;
			m_api->m_doc_mgr.LoadDocInfo(act_rev);
			m_api->m_data.GetAttr(act_rev,m_api->m_doc_mgr.a_apl_doc_rev_crc, old_crc);

			if(old_crc==new_crc)
			{
				CaplInstance *stor=0;
				m_api->m_data.GetAttr(act_rev,m_api->m_doc_mgr.a_apl_doc_rev_access_form,stor);
				if(stor!=0 && stor->GetType()!=0)
				{
					if(m_api->m_data.IsKindOf(stor,m_api->m_doc_mgr.e_apl_stored_document))
					{
						long old_size;
						if(m_api->m_data.NET_GetBlobSize(stor,m_api->m_doc_mgr.a_apl_stored_document_source,old_size))
						{
							if(old_size==file_size)
							{
								AfxMessageBox(_T("Файл не изменился!\n\n(Размер и CRC32 совпадают с активной версией)\n\nСоздание версии не требуется."), MB_ICONINFORMATION); 

								if(0!=new_spec_rev) *new_spec_rev=act_rev;

								return true;
							}
						}
					}
				}
			}
		}

		m_api->SetModeInteractive(false);
		doc_rev = m_api->m_doc_mgr.CreateDocumentRevision(doc_rep, sPathRep,true,false);
		m_api->SetModeInteractive(true);
		if(0==doc_rev) {AfxMessageBox(_T("Ошибка создания версии документа!"),MB_ICONSTOP|MB_OK); return false;}
	
		if(m_bSetNewVersionActive)
		{
			if(!m_api->m_doc_mgr.CreateChange(doc_rev,true,false))
				AfxMessageBox(_T("Ошибка при назначении версии активной!"),MB_ICONSTOP|MB_OK);
		}
	}
	SetFileAttributes(sPathRep,FILE_ATTRIBUTE_NORMAL);
	DeleteFile(sPathRep);

	buf.Format("%i",report.m_pages.GetSize());

	if(0!=new_spec_rev) *new_spec_rev=doc_rev;

	m_api->m_data.PutAttr(doc_rev, m_api->m_doc_mgr.a_apl_doc_rev_format, "A4");
	m_api->m_data.PutAttr(doc_rev, m_api->m_doc_mgr.a_apl_doc_rev_total_sheets, buf);
	m_api->m_data.PutAttr(doc_rev, m_api->m_doc_mgr.a_apl_doc_rev_total_sheets_count_a4,buf );

	m_api->m_data.PutAttr(doc_rep, m_api->m_doc_mgr.a_apl_doc_system,"GenerateSpec" );

	aplExtent ext4Access; ext4Access.Add(doc_rev); ext4Access.Add(doc_rep); 
	m_api->m_data.NET_SetInstancesAccess(&ext4Access,(CaplInstance*)-1,aplRO);
	adminmode.ExitAdminMode();

	if(bNewDoc) buf=_T("Документ создан!  Открыть для просмотра ?");
	else  buf=_T("Cоздана новая версия документа!  Открыть для просмотра ?");

	if(IDYES==AfxMessageBox(buf,MB_ICONINFORMATION|MB_YESNO))
	{
		m_api->m_doc_mgr.ViewWithMarkView(doc_rev);
	}

	return true;
}
//***********************************************************************************
//***********************************************************************************
//***********************************************************************************
bool CSpecGenerator::SetPosition4Section(T_LIST_CSpecLine &section_data)
{
	int i;
	for(i=0;i<section_data.GetSize();i++)
	{
		CSpecLine *line=section_data[i];
		if(0== line->rel || line->sPos!=_T("")) continue;  // 0==line->rel  у документов

		m_max_position++;
		line->sPos.Format(_T("%i"),m_max_position);
		if(m_api->m_data.IsKindOf(line->rel,m_api->m_prd_mgr.e_pdr))
			m_api->m_data.PutAttr(line->rel,m_api->m_prd_mgr.a_pdr_id,line->sPos);
		else if(m_api->m_data.IsKindOf(line->rel,m_api->m_prd_mgr.e_pdf_subst))
			m_api->m_data.PutAttr(line->rel,m_api->m_prd_mgr.a_pdf_subst_id,line->sPos);
		else if(m_api->m_data.IsKindOf(line->rel,m_api->m_change_mgr.e_psc))
			m_api->m_data.PutAttr(line->rel,m_api->m_change_mgr.a_psc_position,line->sPos);

		//!!! line->sPos4Sort задавать не надо, т.к. на этот момент все отсортировано
	}
	return true;
}
//***********************************************************************************
bool CSpecGenerator::SetUsage4Section(T_LIST_CSpecLine &section_data)
{
	if(0==m_pdf) return false;
	CaplInstance *mainFirstUsage,*subUsage;
	m_api->m_data.GetAttr(m_pdf,m_api->m_prd_mgr.a_apl_pdf_first_usage,mainFirstUsage);
	//if(0==mainFirstUsage) return false;

	int i;
	for(i=0;i<section_data.GetSize();i++)
	{
		CSpecLine *line=section_data[i];
		if(0== line->pdf ) continue;  // на всякий случай

		m_api->m_data.GetAttr(line->pdf ,m_api->m_prd_mgr.a_apl_pdf_first_usage,subUsage);
		if(subUsage==mainFirstUsage ||  subUsage==m_pdf) continue;

		if(line->sPrim!=_T("")) line->sPrim+=_T(" ");
		line->sPrim+=_T("заим.");
	}
	return true;
}
//***********************************************************************************
bool CSpecGenerator::AppendEmptyLine(bool bKeepWithNext)
{
	CaplReportDataRow *row=new CaplReportDataRow;  /*row->m_cy=m_higt_row;*/ row->m_bKeepWithNext=false; row->m_top_indent=0;
	m_all_rows.Add(row); 
	row->m_bKeepWithNext=bKeepWithNext;
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_format));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_zone));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_pos));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_id));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_name));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_kol));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_prim));
	
	return true;
}
//***********************************************************************************
bool  CSpecGenerator::AppendSectionHeader(LPCSTR section_name)
{
	AppendEmptyLine(true);

	// Заголовок
	CaplReportDataRow *row=new CaplReportDataRow;  /*row->m_cy=m_higt_row;*/ row->m_top_indent=0;
	row->m_bKeepWithNext=true;
	m_all_rows.Add(row); 
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_format));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_zone));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_pos));
	row->m_data_fields.Add(new CaplReportField(0,m_comumn_id));
	row->m_data_fields.Add(new CaplReportField(section_name, m_comumn_name));
		CaplReportField *field=row->m_data_fields[row->m_data_fields.GetSize()-1];
		field->m_font_id= _T("g5ui");
		field->m_h_allign=_T('l');
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_kol));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_prim));

	AppendEmptyLine(true); //Пустая строка
	
	return true;
}
//************************************************************************************
bool  CSpecGenerator::AppendGroupHeader(LPCSTR group_header)
{
	AppendEmptyLine(true);

	// Заголовок
	CaplReportDataRow *row=new CaplReportDataRow;  /*row->m_cy=m_higt_row;*/ row->m_top_indent=0;
	row->m_bKeepWithNext=true;
	m_all_rows.Add(row); 

	row->m_data_fields.Add(new CaplReportField(0, m_comumn_format));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_zone));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_pos));
	row->m_data_fields.Add(new CaplReportField(0,m_comumn_id));
	row->m_data_fields.Add(new CaplReportField(group_header, m_comumn_name));
		CaplReportField *field=row->m_data_fields[row->m_data_fields.GetSize()-1];
		field->m_font_id= _T("g5");
		field->m_h_allign=_T('l');
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_kol));
	row->m_data_fields.Add(new CaplReportField(0, m_comumn_prim));
	//AppendEmptyLine(true);

	return true;
}

//************************************************************************************
bool CSpecGenerator::AppendSection(LPCSTR section_name, T_LIST_CSpecLine &section_data)
{
	if(0==section_data.GetSize()) return true;

	AppendSectionHeader(section_name); //Пустая строка

	int i;
	CaplReportField *field=0;

	// Данные
	for(i=0;i<section_data.GetSize();i++)
	{
		CSpecLine *line=section_data[i];

		//А не отменено ли часом
		if(m_map_eff.QFindByIn(line->rel)>=0) continue;

		CaplReportDataRow *row=new CaplReportDataRow;   /*row->m_cy=m_higt_row;*/ row->m_bKeepWithNext=false; row->m_top_indent=0;
		if(0==i) row->m_bKeepWithNext=true;
		m_all_rows.Add(row); 

		// Формат
		row->m_data_fields.Add(new CaplReportField(line->sFormat, m_comumn_format));

		// Зона
		row->m_data_fields.Add(new CaplReportField(line->sZone, m_comumn_zone));

		// Позиция
		row->m_data_fields.Add(new CaplReportField(line->sPos, m_comumn_pos));

		// Обозначение
		row->m_data_fields.Add(new CaplReportField(line->sId, m_comumn_id));

		// Наименование
		row->m_data_fields.Add(new CaplReportField(line->sName, m_comumn_name));

		// Кол-во
		row->m_data_fields.Add(new CaplReportField(line->sKol, m_comumn_kol));

		// Прим.
		row->m_data_fields.Add(new CaplReportField(line->sPrim, m_comumn_prim));
	}
	return true;
}
//*****************************************************************************************

bool CSpecGenerator::AppendSectionWithGroup(LPCSTR section_name, T_LIST_CSpecLine &section_data)
{
	if(0==section_data.GetSize()) return true;

	AppendSectionHeader(section_name); 

	int i;
	CString sName,sPrevName;
	bool bPrevInGroup=false;
	CaplReportField *field=0;

	// Данные
	for(i=0;i<section_data.GetSize();i++)
	{
		//sName="";
		CSpecLine *line=section_data[i];

		//А не отменено ли часом
		if(m_map_eff.QFindByIn(line->rel)>=0) 	continue;

		if(line->iGrpByNameSize>0 && line->iGrpByNameSize!=line->iGrpByStdSize) // Заголовок группы по имены
		{
			sName=m_plural_map.Get(line->sNameLower);
			if(sName==_T(""))sName=line->sName;
			AppendGroupHeader(sName); 
			AppendEmptyLine(true);
		}
		if(line->iGrpByStdSize>0)
		{
			sName=m_plural_map.Get(line->sNameLower);
			if(sName==_T(""))sName=line->sName;

			sName+=_T(" ");
			sName+=line->sStandart;
			AppendGroupHeader(sName); 
		}


		// Формируем строку в поле наименование
		if(line->iGrpByName==-1)
		{
			 // Элемент не в группе по наименованию
			if(bPrevInGroup) AppendEmptyLine(true);
			bPrevInGroup=false;
			sName=line->sName;
			if(line->sId!=_T(""))
			{
				if(sName!=_T("")) sName+=_T(" ");
				sName+=line->sId;
			}
			if(line->sStandart!=_T(""))
			{
				if(sName!=_T("")) sName+=_T(" ");
				sName+=line->sStandart;
			}
		}
		else
		{
			 // Элемент в группе по наименованию
			bPrevInGroup=true;
			if(line->iGrpByStd==-1)
			{
				// Элемент не в группе по стандарту
				sName=line->sId;

				if(line->sStandart!=_T(""))
				{
					if(sName!=_T("")) sName+=_T(" ");
					sName+=line->sStandart;
				}
			}
			else
			{
				// Элемент в группе по стандарту и наименованию
				sName="";
				if(line->sId.GetLength()<4) sName=line->sName; // Для очень коротких, например Шайба 8
				
				if(sName!=_T("")) sName+=_T(" ");
				sName+=line->sId;
			}

		}
		int iProcherk=0;
		if(sName==sPrevName) {iProcherk=sName.GetLength(); if(iProcherk>20) iProcherk=20;}
		else sPrevName=sName;

		CaplReportDataRow *row=new CaplReportDataRow;   /*row->m_cy=m_higt_row;*/ row->m_bKeepWithNext=false; row->m_top_indent=0;
		if(0==i) row->m_bKeepWithNext=true;
		m_all_rows.Add(row); 

		// Формат
		row->m_data_fields.Add(new CaplReportField(line->sFormat, m_comumn_format));

		// Зона
		row->m_data_fields.Add(new CaplReportField(line->sZone, m_comumn_zone));

		// Позиция
		row->m_data_fields.Add(new CaplReportField(line->sPos, m_comumn_pos));

		// Обозначение
		row->m_data_fields.Add(new CaplReportField(_T(""),m_comumn_id));

		// Наименование
		if(0==iProcherk)
			row->m_data_fields.Add(new CaplReportField(sName, m_comumn_name));
		else
		{
			sName="";
			for(int k=0;k<iProcherk;k++) sName+=_T("—");
			row->m_data_fields.Add(new CaplReportField(sName, m_comumn_name));
			row->m_data_fields[row->m_data_fields.GetSize()-1]->m_font_id=_T("t2");
			m_all_rows[m_all_rows.GetSize()-2]->m_bKeepWithNext=true;  
		}

		// Кол-во
		row->m_data_fields.Add(new CaplReportField(line->sKol, m_comumn_kol));

		// Прим.
		row->m_data_fields.Add(new CaplReportField(line->sPrim, m_comumn_prim));
	}
	return true;
}

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

bool CSpecGenerator::GenerateSpDocs(CaplInstance *pdf, T_LIST_CaplReportDataRow &data_rows)
{
	if(0==m_api || 0==pdf) return false;  // Считаем что остальные проверки были раньше

	aplExtent  ext_rel,ext_doc;

	m_api->m_doc_mgr.FindAssociatedDocuments(pdf,ext_doc,ext_rel);

	int i;
	CString buf,sDocId,sDocName;
	T_LIST_CSpecLine section_docs;

	// Документация
	for(i=0;i<ext_doc.GetSize();i++)
	{
		CaplInstance *doc_rev,*doc=ext_doc[i];
		if(0==doc) continue;
		if(doc->GetAccessmode()>aplRO) continue;

		CSpecLine *line=new CSpecLine();

		m_api->m_data.GetAttr(doc,m_api->m_doc_mgr.a_doc_id,sDocId);

		buf=sDocId.Left(m_sMainPdвIdUpper.GetLength()); buf.MakeUpper();
		if(buf==m_sMainPdвIdUpper) line->bIsMainDoc=true;  // Это основной документ

		m_api->m_data.GetAttr(doc,m_api->m_doc_mgr.a_doc_name,sDocName);

		line->sId=sDocId; 	line->sIdUpper=sDocId;   line->sIdUpper.MakeUpper();
		line->sName=sDocName;

		m_api->m_data.GetAttr(doc,m_api->m_doc_mgr.a_apl_doc_active,doc_rev);
		if(0!=doc_rev)
		{
			m_api->m_data.GetAttr(doc_rev,m_api->m_doc_mgr.a_apl_doc_rev_format,buf);
			if(buf!=_T(""))
			{
				if(buf[0]==_T('*')) {line->sFormat=_T('*'); line->sPrim=buf;}
				else line->sFormat=buf;
			}
		}

		CaplInstance *dt;
		m_api->m_data.GetAttr(doc,m_api->m_doc_mgr.a_doc_kind,dt);
		if(0!=dt)
		{
			m_api->m_data.GetAttr(dt,m_api->m_doc_mgr.a_dt_sort_num,line->iSortNum );

			m_api->m_data.GetAttr(dt,m_api->m_doc_mgr.a_dt_pdt,buf );

			if(line->bIsMainDoc) line->sName=buf;
			else
			{
				if(line->sName!=_T("")) line->sName+=_T(". ");
				line->sName+=buf;
			}
		}

		if(line->bIsMainDoc && line->sName==_T("Спецификация"))// && line->sIdUpper==m_sMainPdвIdUpper) // Саму себя не записываем
		{
			delete line;
			continue;
		}

		section_docs.Add(line);
	}
	if(section_docs.GetSize()>0)
	{
		if(section_docs.GetSize()>1)
		{
			qsort(section_docs.Data,section_docs.GetSize(),sizeof(CSpecLine*),CompareDocsLine);
		}
		AppendSection(_T("Документация"),section_docs);
	}

	return true;
}
//*****************************************************************************************
//*****************************************************************************************
//*****************************************************************************************
bool IsSym(TCHAR c)
{
	if(c>=_T('a') && c<=_T('z')) return true;
	if(c>=_T('A') && c<=_T('Z')) return true;
	if(c>=_T('а') && c<=_T('я')) return true;
	if(c>=_T('А') && c<=_T('Я')) return true;
	return false;
}
//*****************************************************************************************
void PrepareIdName(CString &sId, CString &sName) // обработка кривого задания обозначений и наименований
{
	if(sId==_T("")) return;

	if(sName!=_T(""))
	{
		// Обрабатываем вхождение наименования в обозначение
		CString sIdL=sId,sNameL=sName; sIdL.MakeLower(); sNameL.MakeLower();
		if(sNameL.Find(sIdL)>=0) {sId=sName; sName=_T("");} // Для кривого задания
		else if(sIdL.Find(sNameL)>=0)
		{
			// Наименование оставляем как есть, но из обозначения его удаляем
			int i=sId.Find(sNameL), l=sName.GetLength();
			CString buf;
			if(i>0)	{ buf=sId.Left(i); buf.TrimRight();}
			i+=l;
			buf+=sId.Right(sId.GetLength()-(i+1));
			buf.TrimLeft();
			sId=buf;
			return;
		}
	}

	// выделяем наименование из обозначения
	int i=0,j=0;
	while(true)
	{
		TCHAR c=sId[i];
		if(c==_T('\0')) break;
		if(c==_T(' ')) j=i;
		else if(!IsSym(c)) break;
		i++;
	}
	if(0==j) return; // Нет имени в обозначении
	
	CString sId1,sName1;
	sName1=sId.Left(j);
	sId1=sId.Right(sId.GetLength()-(j+1));
	sId1.TrimLeft();

	if(sName==_T(""))
	{
		sName=sName1;
		sId=sId1;
		return;
	}
	CString buf,buf1;
	buf=sName; buf.MakeLower();
	buf1=sName1; buf1.MakeLower();

	if(buf.Find(buf1)>=0) 
		sId=sId1; // В наименовании есть часть обозначения
};

//*****************************************************************************************
void NormalizeNumber(CString &sNumber)
{
	static int nlen=10; // Кол-во цифр, к которому приводить.
	if(sNumber==_T("")) return;
	if(sNumber==_T(".")) return;

	int i,j,l;
	i=sNumber.Find(_T('.'));
	l=sNumber.GetLength();
	CString sOutNumber;
	
	if(i<0)
	{
		// число без точки
		for(j=l;j<nlen; j++)  sOutNumber+=_T('0'); // нули слева
		sOutNumber+=sNumber;
		sOutNumber+=_T('.');
		for(j=0;j<nlen; j++)  sOutNumber+=_T('0');
		sNumber=sOutNumber;
		return;
	}
	j=sNumber.Find(_T('.'),i+1);
	if(j<0)
	{
		//число с точкой
		CString s1,s2;
		s1=sNumber.Left(i);
		s2=sNumber.Right(l-(i+1));

		for(j=s1.GetLength();j<nlen; j++)  sOutNumber+=_T('0');
		sOutNumber+=s1;
		sOutNumber+=_T('.');
		sOutNumber+=s2;
		for(j=s2.GetLength();j<nlen; j++)  sOutNumber+=_T('0');
		sNumber=sOutNumber;
		return;
	}
	//в строке несколько точек

	CString s1;
	int k=0;
	while(true)
	{
		s1=sNumber.Mid(k,i);
		for(j=s1.GetLength();j<nlen; j++)  sOutNumber+=_T('0');
		sOutNumber+=s1;

		if(k+i>=l) break;
		sOutNumber+=_T('.');
		j=sNumber.Find(_T('.'),k+i+1);
		if(j < 0) j=l; 
		k+=(i+1);
		i=j-k;
	}
	sNumber=sOutNumber;
	return;
}
//*****************************************************************************************
void MakeId4Sort(CString &sId) //Заменяtn числа длинными числами для сортировки как строи
{
	if(sId==_T("")) return;

	int i=0;
	CString sOut,sNumber;

	bool bNotEmpyNumber=false;

	while(true)
	{
		TCHAR c=sId[i];
		if(c==_T('\0')) break;
		if(c==_T(',')) c=_T('.'); // Для сортировки пофиг, но упрощает с цифрами
		if( (c>=_T('0') && c<=_T('9')  ) || c==_T('.') )
		{
			bNotEmpyNumber=true;
			sNumber+=c;
		}
		else
		{
			if(bNotEmpyNumber)
			{
				bNotEmpyNumber=false;
				NormalizeNumber(sNumber);
				sOut+=sNumber;
				sNumber="";
			}
			sOut+=c;
		}
		i++;
	}
	if(bNotEmpyNumber)
	{
		NormalizeNumber(sNumber);
		sOut+=sNumber;
	}
	sId=sOut;
}

//*****************************************************************************************
bool CSpecGenerator::GenerateSpParts(CaplInstance *pdf, T_LIST_CaplReportDataRow &data_rows)
{
	// Изделия
	int i;
	CString buf;

	aplExtent ext_pdf_rel,ext_sub_pdf;
	m_api->m_prd_mgr.LoadAllSubProduct(0,0,pdf,ext_pdf_rel,ext_sub_pdf);

	if(m_bUseSubst)
	{
		aplExtent ext_subst;
		LoadSubstitutions(ext_pdf_rel,ext_subst);
		ext_pdf_rel.Append(ext_subst);
	}

	// Определяем список отмененных
	aplExtent ext_dated_eff;
	m_api->m_data.GetEntityExtent(m_api->m_prd_mgr.e_dat_eff,ext_dated_eff);
	for(i=0;i<ext_dated_eff.GetSize();i++)
	{
		CaplInstance *eff=ext_dated_eff[i];
		m_api->m_data.GetAttr(eff,m_api->m_prd_mgr.a_dat_eff_end_s,buf);
		if(buf==_T("")) continue;
		CaplInstance *usage;
		m_api->m_data.GetAttr(eff,m_api->m_prd_mgr.a_pde_usage,usage);
		if(0==usage) continue;
		m_map_eff.Add(usage,eff);
	}


	CaplMap m_map_pdr_change; // правило изменения для pdr
	aplExtent ext_add_changes;
	if(0!=m_change)
	{
		aplExtent ext_chng_items;
		m_api->m_data.GetAttr(m_change,m_api->m_change_mgr.a_chng_items,ext_chng_items);
		for(i=0;i<ext_chng_items.GetSize();i++)
		{
			CaplInstance *ch_item=ext_chng_items[i];
			if(!m_api->m_data.IsKindOf(ch_item,m_api->m_change_mgr.e_psc)) continue;

			CaplInstance *assembly;
			m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_changed_product,assembly);

			if(m_api->m_data.IsKindOf(assembly,m_api->m_prd_mgr.e_pdf))
			{
				if(assembly!=pdf) continue;
				ext_add_changes.Add(ch_item);
			}
			else if(m_api->m_data.IsKindOf(assembly,m_api->m_prd_mgr.e_pdr))
			{
				CaplInstance *asm1;
				m_api->m_data.GetAttr(assembly,m_api->m_prd_mgr.a_pdr_r_ting,asm1);

				if(asm1!=pdf) continue;
				m_map_pdr_change.Add(assembly,ch_item);
			}
		}
		if(0!=ext_add_changes.GetSize()) ext_pdf_rel.Append(ext_add_changes);
	}

	bool bIsPdfStandart;
	CString sPdfSource,sPdfType, sSubPdfId,sSubPdfName, sPrim, sCount;

	T_LIST_CSpecLine section_kit, section_komplex, section_assembly, section_part, section_material,section_prochee, section_standart;


	CString sPos,sDescr;
	double dCount;
	CaplInstance *unit;


	for(i=0;i<ext_pdf_rel.GetSize();i++)
	{
		CaplInstance *rel=ext_pdf_rel[i];
		CaplInstance *sub_pdf, *sub_prd;

		if(m_api->m_data.IsKindOf(rel,m_api->m_prd_mgr.e_pdr))
		{
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdr_r_ted,sub_pdf);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_apl_qacu_value, dCount);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdr_id,sPos);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_apl_qacu_unit, unit);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdr_descr,sDescr);

		}
		else if(m_api->m_data.IsKindOf(rel,m_api->m_prd_mgr.e_pdf_subst))
		{
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdf_subst_pdf,sub_pdf);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdf_subst_value, dCount);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdf_subst_id,sPos);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdf_subst_unit, unit);
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdf_subst_description,sDescr);

			CaplInstance *subst_pdr=0;
			m_api->m_data.GetAttr(rel,m_api->m_prd_mgr.a_pdf_subst_pdr,subst_pdr);
			if(0!=subst_pdr) m_map_subst.Add((long)subst_pdr,rel);
		}
		else if(m_api->m_data.IsKindOf(rel,m_api->m_change_mgr.e_psc))
		{
			// Правило добавления
			CaplInstance *ch_item=rel;
			m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_changing_product,sub_pdf);
			m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_value,dCount);
			m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_position,sPos);
			m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_unit,unit);
			m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_chng_item_dscr,sDescr);
		}
		else continue;

		if(0!=m_change)
		{
			CaplInstance *ch_item=(CaplInstance *)m_map_pdr_change.GetByInP(rel);
			if(0!=ch_item)
			{
				// Заменяем данные на результат изменения
				m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_changing_product,sub_pdf);
				if(0==sub_pdf)	m_map_eff.Add(rel,ch_item); // это правило удаления, но позиция и сортировка должны обрабатываться и для удаленных
				m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_value,dCount);
				m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_psc_position,sPos);
				m_api->m_data.GetAttr(ch_item,m_api->m_change_mgr.a_chng_item_dscr,sDescr);
			}
		}

		if(0==sub_pdf) continue;
		if(sub_pdf->GetAccessmode()>aplRO) continue;

		m_api->m_data.GetAttr(sub_pdf,m_api->m_prd_mgr.a_pdf_prd,sub_prd);
		if(0==sub_prd) continue;
		if(sub_prd->GetAccessmode()>aplRO) continue;


		CSpecLine *line=new CSpecLine();
		line->sFormat=_T("");
		line->sZone=_T("");
		line->rel=rel;
		line->pdf=sub_pdf;
		m_map_pdr_line.Add(rel,line);

		// Определение обозначения
		m_api->m_data.GetAttr(sub_prd,m_api->m_prd_mgr.a_prd_id, sSubPdfId);
		m_api->m_data.GetAttr(sub_pdf,m_api->m_prd_mgr.a_pdf_id, buf);
		if(buf!="")
		{
			if( (buf.Find(_T("ОСТ"))>=0) || (buf.Find(_T("ТУ"))>=0) ) sSubPdfId+=_T(" ");
			else sSubPdfId+=_T("-");

			sSubPdfId+=buf;
		}
		line->sId=sSubPdfId;

		m_api->m_data.GetAttr(sub_prd,m_api->m_prd_mgr.a_prd_name, line->sName);

		PrepareIdName(line->sId,line->sName);
		line->sIdUpper=line->sId;  line->sIdUpper.MakeUpper();

		// Унифицируем позицию для сортировки как строки
		if(m_bClearExistPos && m_bSetEmptyPosition) line->sPos=_T("");
		else
		{
			line->sPos=sPos;
			if(line->sPos!=_T(""))
			{
				int iPos=atoi(line->sPos);
				if(iPos>m_max_position) m_max_position=iPos;
			}
		}
		// Делаем выравненную позицию для сортировки
		{
			const int nPosSrtLen=5;
			int k,pos_len=line->sPos.GetLength();
			if(0==pos_len) {for(k=0; k<nPosSrtLen;k++)	line->sPos4Sort+=_T("Z");} // Без позиции должны быть в конце
			else
			{
				for(k=pos_len; k<nPosSrtLen;k++)	line->sPos4Sort+=_T("0");
				line->sPos4Sort+=line->sPos;
				line->sPos4Sort.MakeUpper();
			}
		}
		
		buf.Format(_T("%4f"),dCount);
		buf.TrimRight(_T('0'));
		buf.TrimRight(_T('.'));
		line->sKol=buf;

		if(0!=unit)
		{
			m_api->m_data.GetAttr(unit,m_api->m_charact_mgr.a_apl_unit_id, buf);	
			buf.MakeLower(); 
			if(buf.Find(_T("шт"))<0) line->sPrim=buf;
			line->sUnit=buf;
		}

		if(sDescr!=_T(""))
		{
			if (line->sPrim!=_T("")) line->sPrim+=_T(", "); // есть кол-во
			line->sPrim+=sDescr;
		}

		m_api->m_data.GetAttr(sub_pdf,m_api->m_prd_mgr.a_pdfwss_source,sPdfSource);
		m_api->m_data.GetAttr(sub_pdf,m_api->m_prd_mgr.a_apl_pdf_type,sPdfType);
		m_api->m_data.GetAttr(sub_pdf,m_api->m_prd_mgr.a_apl_pdf_standard,bIsPdfStandart);

		bool bOnlyName=false; // Когда в разделе не пишется обозначение, но есть группировка

		if(sPdfType==_T("material")) {section_material.Add(line); bOnlyName=true;}
		else if(sPdfType==_T("kit")) section_kit.Add(line); 
		else if(sPdfType==_T("komplex")) section_komplex.Add(line); 
		else if(bIsPdfStandart) { section_standart.Add(line);  bOnlyName=true;}
		else if(sPdfSource==_T("bought")) {section_prochee.Add(line);   bOnlyName=true;}
		else if(sPdfType==_T("assembly")) section_assembly.Add(line); 
		else if(sPdfType==_T("part"))section_part.Add(line); 
		else if(sPdfType==_T("module"))section_kit.Add(line); 
		//else AfxMessageBox(_T("Неведомая фигня!"));

		if( bOnlyName)
		{
			line->sNameLower=line->sName;  line->sNameLower.MakeLower();
			
			// Выделяем стандарт
			int k=line->sIdUpper.Find(_T("ГОСТ"));
			if(k<0) k=line->sIdUpper.Find(_T("ОСТ"));
			if(k<0)
			{
				k=line->sIdUpper.Find(_T("ТУ"));
				if(k>0)
				{
					if((line->sIdUpper.GetLength()-k)==2) // ТУ в конце
					{
						while(k>0) // Мотаем до первого пробела
						{
							k--;
							if(line->sIdUpper[k]==_T(' ')) {k++;break;}
						}
					}
				}
			}
			if(k>0)
			{
				line->sStandart=line->sIdUpper.Right(line->sIdUpper.GetLength()-k); // Стандарт берем маленькими буквами
				buf=line->sId.Left(k); buf.TrimRight();
				line->sId=buf;
				line->sIdUpper=line->sId;  line->sIdUpper.MakeUpper();
			}
			MakeId4Sort(line->sIdUpper);
		}
	}

	// Сортировка
	if(section_komplex.GetSize()>1)	
		qsort(section_komplex.Data,section_komplex.GetSize(),sizeof(CSpecLine*),CompareKomplexLine);
	if(section_assembly.GetSize()>1)
		qsort(section_assembly.Data,section_assembly.GetSize(),sizeof(CSpecLine*),CompareKomplexLine);
	if(section_part.GetSize()>1)
		qsort(section_part.Data,section_part.GetSize(),sizeof(CSpecLine*),CompareKomplexLine);
	if(section_standart.GetSize()>1)
		qsort(section_standart.Data,section_standart.GetSize(),sizeof(CSpecLine*),CompareStandartLine);
	if(section_prochee.GetSize()>1)
		qsort(section_prochee.Data,section_prochee.GetSize(),sizeof(CSpecLine*),CompareStandartLine);
	if(section_material.GetSize()>1)
		qsort(section_material.Data,section_material.GetSize(),sizeof(CSpecLine*),CompareStandartLine);
	if(section_kit.GetSize()>1)
		qsort(section_kit.Data,section_kit.GetSize(),sizeof(CSpecLine*),CompareKomplexLine);


	// Раздача позиций
	if(m_bSetEmptyPosition)
	{
		SetPosition4Section(section_komplex);
		SetPosition4Section(section_assembly);
		SetPosition4Section(section_part);
		SetPosition4Section(section_standart);
		SetPosition4Section(section_prochee);
		SetPosition4Section(section_material);
		SetPosition4Section(section_kit);
	}

	// Обработка замен в контексте сборки
	if(m_bUseSubst) PrepareSubstitutions();

	if(m_bUsage)
	{
		SetUsage4Section(section_assembly);
		SetUsage4Section(section_part);
	}


	// Вывод в отчет


	// Комплексы
	if(section_komplex.GetSize()>0)
	{
		AppendSection(_T("Комплексы"),section_komplex);
	}

	// Сборочные единицы
	if(section_assembly.GetSize()>0)
	{
		AppendSection(_T("Сборочные единицы"),section_assembly);
	}	

	// Детали
	if(section_part.GetSize()>0)
	{
		GetFormatForParts(section_part);
		AppendSection(_T("Детали"),section_part);
	}
	
	// Стандартные изделия
	if(section_standart.GetSize()>0)
	{
		if(section_standart.GetSize()>1) GroupElements(section_standart);

		AppendSectionWithGroup(_T("Стандартные изделия"),section_standart);
	}

	// Прочие изделия
	if(section_prochee.GetSize()>0)
	{
		if(section_prochee.GetSize()>1) GroupElements(section_prochee);

		AppendSectionWithGroup(_T("Прочие изделия"),section_prochee);
	}

	// Материалы
	if(section_material.GetSize()>0)
	{
		if(section_material.GetSize()>1) GroupElements(section_material);

		AppendSectionWithGroup(_T("Материалы"),section_material);
	}

	// Комплекты
	if(section_kit.GetSize()>0)
	{
		AppendSection(_T("Комплекты"),section_kit);
	}

	return true;
}


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

int CSpecGenerator::CompareDocsLine( const void *p1,const void *p2)
{
	CSpecLine *l1,*l2;
	l1=*((CSpecLine**)p1);
	l2=*((CSpecLine**)p2);


	if(l1->bIsMainDoc!=l2->bIsMainDoc)
	{
		if(l1->bIsMainDoc) return -1;
		else return 1;
		
	}
	if(l1->iSortNum!=l2->iSortNum)
	{
		if(l1->iSortNum<l1->iSortNum) return -1;
		else if(l1->iSortNum>l1->iSortNum) return 1;

		// если равно идем дальше
	}

	return l1->sIdUpper.Compare(l2->sIdUpper);
}
//**************************************************************************
int CSpecGenerator::CompareKomplexLine( const void *p1,const void *p2)
{
	CSpecLine *l1,*l2;
	l1=*((CSpecLine**)p1);
	l2=*((CSpecLine**)p2);

	int i= l1->sPos4Sort.Compare(l2->sPos4Sort);
	if(0==i) return l1->sIdUpper.Compare(l2->sIdUpper);
	return i;
}
//**************************************************************************
int CSpecGenerator::CompareStandartLine( const void *p1,const void *p2)
{
	CSpecLine *l1,*l2;
	l1=*((CSpecLine**)p1);
	l2=*((CSpecLine**)p2);

	int i= l1->sNameLower.Compare(l2->sNameLower);
	if(0!=i) return i;

	i= l1->sStandart.Compare(l2->sStandart);
	if(0!=i) return i;

	i= l1->sIdUpper.Compare(l2->sIdUpper);
	if(0!=i) return i;

	i= l1->sPos4Sort.Compare(l2->sPos4Sort);
	return i;
}
//**************************************************************************
//**************************************************************************
//**************************************************************************

bool CSpecGenerator::GroupElements(T_LIST_CSpecLine &section_data)
{
	int i,j,k;

	// группируем по имени
	for(i=0;i<(section_data.GetSize()-1); i++)
	{
		int numWithEquName=0;
		CString &name=section_data[i]->sNameLower;

		CaplInstance *pdf=section_data[i]->pdf;
		bool bAllPdfEqu=true; // Чтобы исключить случаи, когда вся группа - это одно и тоже

		if(name==_T("")) continue;
		for(j=i+1; j<section_data.GetSize();j++)
		{
			if(name==section_data[j]->sNameLower)
			{
				numWithEquName++;
				if(pdf!=section_data[j]->pdf) bAllPdfEqu=false;
			}
			else break;
		}
		if(numWithEquName>0 && (!bAllPdfEqu) )
		{
			section_data[i]->iGrpByName=i;
			section_data[i]->iGrpByNameSize=numWithEquName;
			for(k=1; k<=numWithEquName;k++) section_data[i+k]->iGrpByName=i; 

			i=j-1;
		}
	}

	//группируем по станларту
	for(i=0;i<(section_data.GetSize()-1); i++)
	{
		int numWithEquName=0;
		CString &name=section_data[i]->sStandart;
		if(name==_T("")) continue;

		CaplInstance *pdf=section_data[i]->pdf;
		bool bAllPdfEqu=true; // Чтобы исключить случаи, когда вся группа - это одно и тоже

		for(j=i+1; j<section_data.GetSize();j++)
		{
			if(name==section_data[j]->sStandart)
			{
				numWithEquName++;
				if(pdf!=section_data[j]->pdf) bAllPdfEqu=false;
			}
			else break;
		}
		if(numWithEquName>0 && (!bAllPdfEqu) )
		{
			section_data[i]->iGrpByStd=i;
			section_data[i]->iGrpByStdSize=numWithEquName;
			for(k=1; k<=numWithEquName;k++) section_data[i+k]->iGrpByStd=i; 
			i=j-1;
		}
	}
	
	return true;
}
//**************************************************************************
//**************************************************************************
//**************************************************************************

bool CSpecGenerator::GetFormatForParts(T_LIST_CSpecLine &section_data)
{
	if(!m_bGetFormatForPart) return true;
	if(0==section_data.GetSize()) return true;
	
	int iWaitDlg=aplStartWaitDlg(_T("Определение форматов для деталей ..."));

	int i;
	CString sFormat;
	for(i=0; i<section_data.GetSize();i++)
	{
		CSpecLine *line=section_data[i];

		if(0==line->pdf) continue; // На всякий случай

		CaplInstance *doc=m_api->m_doc_mgr.FindDocById(line->sId);
		if(0==doc)
		{
			if(m_bSetBCHForPart) line->sFormat=_T("БЧ");
			continue;
		}
		
		m_api->m_doc_mgr.LoadDocInfo(doc);

		sFormat=_T("");
		CaplInstance *doc_rev;
		m_api->m_data.GetAttr(doc,m_api->m_doc_mgr.a_apl_doc_active,doc_rev);
		if(0!=doc_rev)
		{
			m_api->m_data.GetAttr(doc_rev,m_api->m_doc_mgr.a_apl_doc_rev_format,sFormat);
		}
		if(sFormat==_T("")) sFormat=_T("?");
		line->sFormat=sFormat;
	}

	aplEndWaitDlg(iWaitDlg);
	return true;
}
//************************************************

bool CSpecGenerator::LoadSubstitutions(aplExtent &ext_pdr, aplExtent &ext_subst)  // Чтение замен
{
	ext_subst.Clear();
	if(0==ext_pdr.GetSize()) return true;

	CSortClass::SortExtentByInst(ext_pdr);

	int i;
	CaplLoadData ld(&(m_api->m_data));
	for(i=0; i<ext_pdr.Size; ++i)
	{
		CaplInstance *rel=ext_pdr[i];
		if(0==rel) 	continue;
		if(0==rel->GetId()) continue;
		ld.AddQuery(0, rel);
	}
	int i1=	ld.AddQuery(_T('r'), 0, m_api->m_prd_mgr.e_pdf_subst, m_api->m_prd_mgr.a_pdf_subst_pdr, true,true);
	int i8=	ld.AddQuery(_T('d'), i1, 0, m_api->m_prd_mgr.a_pdf_subst_pdf, true,true);//на что можно заменить
	ld.AddQuery(_T('d'), i8, 0, m_api->m_prd_mgr.a_pdf_prd, true,true);

	aplExtent ext_result;
	ld.LoadEx(true,&ext_result);

	CaplInstance *rel;

	ext_subst.Unique=false;
	
	for(i=0; i<ext_result.GetSize(); ++i)
	{
		CaplInstance *subst=ext_result[i];
		if(subst==NULL) continue;
		if(subst->GetType()==NULL) continue;
		if(subst->GetAccessmode()>aplRO) continue;
		if(!m_api->m_data.IsKindOf(subst,m_api->m_prd_mgr.e_pdf_subst)) continue;

		m_api->m_data.GetAttr(subst, m_api->m_prd_mgr.a_pdf_subst_pdr, rel);

		int j=aplQFindInstInExtent(ext_pdr,rel,false);

		if(j<0) continue;
		ext_subst.Add(subst);
	}
	ext_subst.Unique=true;;
	return true;
}

bool CSpecGenerator::PrepareSubstitutions()
{
	int i,j,k;
	CString buf,sSubstText;
	for(i=0;i<m_map_subst.GetSize();i++)
	{
		CaplInstance *rel=	(CaplInstance*)(m_map_subst.Data[i].in);
		CaplAggr &aggr=m_map_subst.Data[i].out;
		if(0==aggr.GetSize()) continue; // На всякий сдучай

		CSpecLine *main_line=(CSpecLine *)m_map_pdr_line.QGetByIn(rel);
		if(0==main_line) continue; // На всякий сдучай

		CaplAggr aggr2; aggr2.Append(aggr); // копия для отметки заменяющих

		// Формируием строку для заменяемого

		if(main_line->sKol!=_T(""))
		{
			// переносим ед. изм в примечание
			main_line->sPrim=main_line->sKol; 
			main_line->sKol=_T("");
			main_line->sPrim+=_T(" ");

			if(main_line->sUnit!=_T("")) main_line->sPrim+=main_line->sUnit;
			else main_line->sPrim+=_T("шт.");
		}

		if(main_line->sPrim!=_T("")) main_line->sPrim+=_T(" ");
		main_line->sPrim+=_T("допуск. замена на .");

		sSubstText=_T("");
		for(j=0;j<aggr.GetSize();j++)
		{
			CaplInstance *subst_j=0;
			(aggr.GetByIndex(j))->Get(subst_j);

			CSpecLine *subst_line=(CSpecLine *)m_map_pdr_line.QGetByIn(subst_j);
			CString sSubstNameJ;
			m_api->m_data.GetAttr(subst_j,m_api->m_prd_mgr.a_pdf_subst_name,sSubstNameJ);


			if(j>0) sSubstText+=_T(" или ");

			sSubstText+=_T("поз. "); sSubstText+=subst_line->sPos;

			int nGroup=1;
			for(k=j+1;k<aggr.GetSize();k++)
			{
				CaplInstance *subst_k=0;
				(aggr.GetByIndex(k))->Get(subst_k);
				CString sSubstNameK;
				m_api->m_data.GetAttr(subst_k,m_api->m_prd_mgr.a_pdf_subst_name,sSubstNameK);

				if(sSubstNameK!=sSubstNameJ)continue;

				CSpecLine *sl_k=(CSpecLine *)m_map_pdr_line.QGetByIn(subst_k);
				
				if(1==nGroup)	sSubstText+=_T(" совместно с поз. "); 
				else sSubstText+=_T(", "); 
				sSubstText+=sl_k->sPos;

				nGroup++;
				aggr.Remove(k); k--;
			}
		}
		//sSubstText+=_T(".");
		main_line->sPrim+=sSubstText;


		// Формируием строки для заменяющих

		sSubstText=_T("");
		for(j=0;j<aggr2.GetSize();j++)
		{
			CaplInstance *subst_j=0;
			(aggr2.GetByIndex(j))->Get(subst_j);

			CSpecLine *subst_line=(CSpecLine *)m_map_pdr_line.QGetByIn(subst_j);

			if(subst_line->sKol!=_T(""))
			{
				// переносим ед. изм в примечание
				subst_line->sPrim=subst_line->sKol; 
				subst_line->sKol=_T("");
				subst_line->sPrim+=_T(" ");

				if(subst_line->sUnit!=_T("")) subst_line->sPrim+=subst_line->sUnit;
				else subst_line->sPrim+=_T("шт.");
			}
			if(subst_line->sPrim!=_T("")) subst_line->sPrim+=_T(" ");

			subst_line->sPrim+=_T("примен. ");


			CString sSubstNameJ;
			m_api->m_data.GetAttr(subst_j,m_api->m_prd_mgr.a_pdf_subst_name,sSubstNameJ);

/*			if(j>0) sSubstText+=_T(" или ");

			sSubstText+=_T("поз. "); sSubstText+=subst_line->sPos;*/

			int nGroup=0;
			for(k=0;k<aggr2.GetSize();k++)
			{
				if(k==j) continue;
				CaplInstance *subst_k=0;
				(aggr2.GetByIndex(k))->Get(subst_k);
				CString sSubstNameK;
				m_api->m_data.GetAttr(subst_k,m_api->m_prd_mgr.a_pdf_subst_name,sSubstNameK);
				if(sSubstNameK!=sSubstNameJ) continue;

				CSpecLine *sl_k=(CSpecLine *)m_map_pdr_line.QGetByIn(subst_k);

				if(0==nGroup) subst_line->sPrim+=_T("с поз. ");
				else  subst_line->sPrim+=_T(", ");
				nGroup++;
				subst_line->sPrim+=sl_k->sPos;
			}
			subst_line->sPrim+=_T(" взамен поз. ");
			subst_line->sPrim+=main_line->sPos;
			//subst_line->sPrim+=_T(".");
		}
	}
	return true;
}

//***************************************************************************************
//***************************************************************************************
//***************************************************************************************
bool CSpecGenerator::GenerateSpNotes(CaplInstance *pdf, T_LIST_CaplReportDataRow &data_rows)
{
	if(!m_bUseAassemblyDescr4Notes) return true;
	if(0==pdf) return false;

	CString sDescr,sNote;
	m_api->m_data.GetAttr(pdf,m_api->m_prd_mgr.a_pdf_descr,sDescr);
	if(sDescr==_T("")) return true;

	sDescr.Replace(_T("\r"),_T(""));
	int i=0,j;
	bool bFirstLine=true;

	float fwidth=m_comumn_id->m_cx+m_comumn_name->m_cx+m_comumn_kol->m_cx+m_comumn_prim->m_cx;
	int width=(int) fwidth;
	
	while(true)
	{
		j=sDescr.Find(_T('\n'),i);
		int j1=sDescr.Find(_T('\n'));
		if(j<0) sNote=sDescr.Right(sDescr.GetLength()-i);
		else sNote=sDescr.Mid(i,j-i);

		if(bFirstLine)AppendEmptyLine(true);

		CaplReportDataRow *row=new CaplReportDataRow;  /*row->m_cy=m_higt_row;*/ row->m_top_indent=0;
		m_all_rows.Add(row); 
		
		row->m_data_fields.Add(new CaplReportField(0, m_comumn_format));
		row->m_data_fields.Add(new CaplReportField(0, m_comumn_zone));
		row->m_data_fields.Add(new CaplReportField(0, m_comumn_pos));
		CaplReportField *field=new CaplReportField(sNote, m_comumn_id);
		field->m_cx=(float)width;
		row->m_data_fields.Add(field);

		if(bFirstLine)
		{
			row->m_bKeepWithNext=true;
			bFirstLine=false;
		}

		if(j<0) break;
		i=j+1;
	}
	return true;
}