Kaynağa Gözat

added Zamówienie Kosztorys, updated UI and DBG

Piotr Labudda 9 yıl önce
ebeveyn
işleme
9fae140750

+ 9 - 7
SE/se-lib/DBG.php

@@ -90,6 +90,7 @@ class DBG {
 	}
 
 	public static function nicePrint($variable, $varName) {
+		$col = ['green'=>'#96c178', 'red'=>'#de6b74', 'blue'=>'#55b5c1', 'bg-dark'=>'#282c34', 'white'=>'#abb2bf', 'orange'=>'#d19a66', 'violet'=>'#c476db'];
 		$cnt = '';
 		ob_start();
 			print_r($variable);
@@ -98,28 +99,29 @@ class DBG {
 
 		$outLines = array();
 		$lines = explode("\n", $cnt);
-		if ($varName) $lines[0] = '<b style="color:#ff5252">' . $varName . "</b> => {$lines[0]}";
+		if ($varName) $lines[0] = '<b style="color:'.$col['red'].'">' . $varName . "</b> => {$lines[0]}";
 		foreach ($lines as $line) {
 			if ('(' == trim($line)) continue;
 			if (')' == trim($line)) continue;
 			if ('' == trim($line)) continue;
 			if ('Array' == substr($line, -5) || 'stdClass' == substr($line, -5)) {
-				$line = str_replace('Array', '<span style="color:#41a541">Array</span>', $line);
-				$line = str_replace('stdClass', '<span style="color:#41a541">stdClass</span>', $line);
+				$line = str_replace('Array', '<span style="color:'.$col['blue'].'">Array</span>', $line);
+				$line = str_replace('stdClass', '<span style="color:'.$col['blue'].'">stdClass</span>', $line);
 				$line .= ':';
 			}
 			if (($firstBracket = strpos($line, '[')) > 0) {
 				$line = str_replace("\t", '  ', $line);
-				$splitPos = ($firstBracket > 4)? ($firstBracket - 4) / 4 + 2 : 2;
+				$splitPos = ($firstBracket > 4)? ($firstBracket - 4) / 2 + 4 : 4;
 				$line = substr($line, $firstBracket - $splitPos);
 
-				$line = preg_replace('/\[(\w+)\]/', '[<span style="color:#5a5aff">\1</span>]', $line);
-				$line = preg_replace('/\] \=\> (\w+)$/', '] => <span style="color:#e88501">\1</span>', $line);
+				$line = preg_replace('/\[(\w+)\]/', '[<span style="color:'.$col['green'].'">\1</span>]', $line);
+				$line = preg_replace('/\] \=\> (.+)$/', '] => <span style="color:'.$col['orange'].'">\1</span>', $line);
 			}
 			$outLines[] = $line;
 		}
 		$outLines = implode("\n", $outLines);
-		echo $outLines;
+		echo '<pre style="background-color:'.$col['bg-dark'].'; color:'.$col['white'].'; font-size:x-small">' . $outLines . '</pre>';
+		// echo '<pre>';			print_r($variable);echo'</pre>';
 	}
 
 }

+ 1951 - 0
SE/se-lib/Route/UrlAction/ProjektyKosztorysBase.php

@@ -0,0 +1,1951 @@
+<?php
+
+Lib::loadClass('RouteBase');
+Lib::loadClass('ProjectKosztorysSchema');
+Lib::loadClass('ProjectKosztorysModel');
+Lib::loadClass('ProjectKosztorysCennik');
+Lib::loadClass('UI');
+Lib::loadClass('Response');
+
+class Route_UrlAction_ProjektyKosztorysBase extends RouteBase {// TODO: UrlActionBase @see Route_UrlAction
+
+	public $_model = array();
+
+	public function handleAuth() {
+		if (!User::logged()) {
+			User::authByRequest();
+		}
+	}
+
+	public function przedmiarAction() {
+		// TODO: check if user is allowed to run this action
+		UI::gora();
+		if (1 != V::get('_print', '', $_GET)) UI::menu();
+		try {
+			$idProject = V::get('ID_PROJECT', 0, $_REQUEST, 'int');
+			$idCompany = V::get('ID_COMPANY', 0, $_REQUEST, 'int');
+			if (!$idProject) throw new Exception("Wrong param in 'ID_PROJECT' - expected integer!");
+			$this->panel($idProject, $idCompany);
+			$this->przedmiar($idProject, $idCompany);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function zestawienieSwMikRurAction() {
+		UI::gora();
+		if (1 != V::get('_print', '', $_GET)) UI::menu();
+		try {
+			$idProject = V::get('ID_PROJECT', 0, $_REQUEST, 'int');
+			$idCompany = V::get('ID_COMPANY', 0, $_REQUEST, 'int');
+			if (!$idProject) throw new Exception("Wrong param in 'ID_PROJECT' - expected integer!");
+			$this->panel($idProject, $idCompany);
+			$this->zestawienieSwMikRur($idProject, $idCompany);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function ofertaAdminAction() {
+		// TODO: check if user is allowed to run this action
+		UI::gora();
+		UI::menu();
+		try {
+			$idProject = V::get('ID_PROJECT', 0, $_REQUEST, 'int');
+			$idCompany = V::get('ID_COMPANY', 0, $_REQUEST, 'int');
+			$admin = true;
+
+			$ofertaArgs = compact('idProject', 'idCompany', 'admin');
+			$this->panel($idProject, $idCompany);
+			if ($idProject > 0) {
+				$this->oferta($ofertaArgs);
+			} else {
+				$this->defaultOferta();
+			}
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function ofertaCompanyAction() {
+		// TODO: check if user is allowed to run this action
+		UI::gora();
+		UI::menu();
+		try {
+			$idProject = V::get('ID_PROJECT', 0, $_REQUEST, 'int');
+			$idCompany = V::get('ID_COMPANY', 0, $_REQUEST, 'int');// TODO: $idCompany from ADMIN_USERS
+			$admin = false;
+			$companyAdmin = true;
+
+			$ofertaArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin');
+			$this->panel($idProject, $idCompany);
+			if ($idProject > 0) {
+				$this->oferta($ofertaArgs);
+			} else {
+				$this->defaultOferta();
+			}
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function ofertaDefaultAdminAction() {
+		// TODO: check if user is allowed to run this action
+		UI::gora();
+		UI::menu();
+		try {
+			$idProject = 0;
+			$idCompany = V::get('ID_COMPANY', 0, $_REQUEST, 'int');
+			$this->panel($idProject, $idCompany);
+			$this->defaultOferta();
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function ofertaAction() {
+		// TODO: check if user is allowed to run this action
+		UI::gora();
+		UI::menu();
+		try {
+			$idProject = V::get('ID_PROJECT', 0, $_REQUEST, 'int');
+			$idCompany = V::get('ID_COMPANY', 0, $_REQUEST, 'int');
+			$admin = false;
+
+			if (!$idProject) throw new Exception("Wrong param in 'ID_PROJECT' - expected integer!");// TODO: show select box if not defined
+			$ofertaArgs = compact('idProject', 'idCompany', 'admin');
+			$this->panel($idProject, $idCompany);
+			$this->oferta($ofertaArgs);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function getArgsList() {// TODO: override UrlActionBase::getArgsList action
+		$args = array();
+		$args[] = 'ID_PROJECT';
+		return $args;
+	}
+
+	public function reinstallAction() {// TODO: mv to UrlActionBase
+		$jsonData = new stdClass();
+		$jsonData->type = 'success';
+		$jsonData->msg = 'Gotowe';
+		try {
+			$this->reinstall();
+		} catch (Exception $e) {
+			$jsonData->type = 'danger';
+			$jsonData->msg = $e->getMessage();
+		}
+		echo json_encode($jsonData);
+	}
+
+	public function reinstall() {// TODO: mv struct check to UrlActionBase
+		$pdo = DB::getPDO();
+		{
+			$dropGDistanceFuncion = "DROP FUNCTION IF EXISTS `GDistance`";
+			$pdo->exec($dropGDistanceFuncion);
+			// Test for GDistance:
+			// select '' as test, GDistance('') as GDistance, IF(GDistance('') = 0, 1, 0) as test_ok;
+			// select 'NULL' as test, GDistance(NULL) as GDistance, IF(GDistance(NULL) = 0, 1, 0) as test_ok;
+			// select 'GeomFromText(POLYGON((18.5573431614953 54.3472831082951,18.5612466579916 54.3460308284799,18.5617610722586 54.346524689865,18.5619728898979 54.3488528136092,18.5609743210268 54.3496376437764,18.5600057772984 54.3494578097971,18.559603951991 54.350742572617,18.5590637025025 54.3507194967965,18.5578575757622 54.349434823939,18.5573431614953 54.3472831082951)))' as test, GDistance(GeomFromText('POLYGON((18.5573431614953 54.3472831082951,18.5612466579916 54.3460308284799,18.5617610722586 54.346524689865,18.5619728898979 54.3488528136092,18.5609743210268 54.3496376437764,18.5600057772984 54.3494578097971,18.559603951991 54.350742572617,18.5590637025025 54.3507194967965,18.5578575757622 54.349434823939,18.5573431614953 54.3472831082951))')) as GDistance, IF(1 = GDistance(GeomFromText('POLYGON((18.5573431614953 54.3472831082951,18.5612466579916 54.3460308284799,18.5617610722586 54.346524689865,18.5619728898979 54.3488528136092,18.5609743210268 54.3496376437764,18.5600057772984 54.3494578097971,18.559603951991 54.350742572617,18.5590637025025 54.3507194967965,18.5578575757622 54.349434823939,18.5573431614953 54.3472831082951))')), 1, 0) as test_ok;
+			// select 'GeomFromText(LINESTRING(18.5573431614953 54.3472831082951,18.5612466579916 54.3460308284799))' as test, GDistance(GeomFromText('LINESTRING(18.5573431614953 54.3472831082951,18.5612466579916 54.3460308284799)')) as GDistance, IF(GDistance(GeomFromText('LINESTRING(18.5573431614953 54.3472831082951,18.5612466579916 54.3460308284799)')) is not null, 1, 0) as test_ok;
+			// select 'GeomFromText(POINT(18.5573431614953 54.3472831082951))' as test, GDistance(GeomFromText('POINT(18.5573431614953 54.3472831082951)')) as GDistance, IF(GDistance(GeomFromText('POINT(18.5573431614953 54.3472831082951)')) is not null, 1, 0) as test_ok;
+			$gDistanceFuncion = <<<SQL_FUN
+				CREATE DEFINER=`root`@`localhost` FUNCTION `GDistance`(`LS` LINESTRING) RETURNS double NO SQL
+				BEGIN
+					DECLARE LON_A, LAT_A, LON_B, LAT_B, R, RESULT DOUBLE;
+					DECLARE A, B POINT;
+					DECLARE I, N INT;
+					-- if LS is not geometry type then return 0
+					-- if GeometryType is not 'LINESTRING' then return 1
+					-- if GeometryType is 'LINESTRING' then return length
+
+					IF LS is null or GeometryType(LS) is null THEN
+						RETURN 0;
+					END IF;
+					IF 'LINESTRING' != GeometryType(LS) THEN
+						RETURN 1;
+					END IF;
+
+					SET R = 6372795.477598;
+
+					SET N = NUMPOINTS(LS);
+					IF N is null or N = 1 THEN
+						RETURN 1;
+					END IF;
+
+					SET RESULT = 0;
+					SET I = 1;
+
+					WHILE I < N DO
+						SET A = POINTN(LS, I);
+						SET B = POINTN(LS, I + 1);
+						SET LON_A = RADIANS(X(A));
+						SET LAT_A = RADIANS(Y(A));
+						SET LON_B = RADIANS(X(B));
+						SET LAT_B = RADIANS(Y(B));
+						SET RESULT = RESULT + ACOS(SIN(LAT_A) * SIN(LAT_B) + COS(LAT_A) * COS(LAT_B) * COS(LON_A - LON_B));
+						SET I = I + 1;
+					END WHILE;
+
+					SET RESULT = RESULT * R;
+					RETURN RESULT;
+				END
+SQL_FUN;
+			$pdo->exec($gDistanceFuncion);
+		}
+
+		/* required Zasoby tree structure - XML (parent rel in PARENT_ID field):
+<zasob:tree>
+	<zasob:TYPESPECIALS>
+		<zasob:URL_ACTION zasob:key=":action_id" zasob:desc=":url_action_name">
+			<zasob:PARAM_IN zasob:key=":action_param_1_id" zasob:desc=":url_action_param_1_name"/>
+		</zasob:URL_ACTION>
+	</zasob:TYPESPECIALS>
+	<zasob:BAZA_DANYCH zasob:key=":main_db_id" altername_types="DATABASE_MYSQL,DATABASE_POSTGRESQL">
+		<zasob:TABELA zasob:key=":tbl_id" zasob:desc="IN7_MK_BAZA_DYSTRYBUCJI">
+			<zasob:KOMORKA zasob:key=":cell_id" zasob:desc="ID"/>
+			<zasob:URL_ACTION zasob:key=":link_action_id" zasob:desc=":url_action_name" zasob:alias_id=":action_id">
+				<zasob:PARAM_IN zasob:key=":cell_id" zasob:desc="ID_PROJECT" zasob:alias_id=":cell_id"/>
+			</zasob:URL_ACTION>
+		</zasob:TABELA>
+	</zasob:BAZA_DANYCH>
+</zasob:tree>
+		*/
+		/* required Zasoby tree structure:
+- required base structure
+[:ts_main_id] TYPESPECIALS
+  [:action_id] URL_ACTION :url_action_name
+    [:action_param_1_id] PARAM_IN :url_action_param_1_name
+- current function from schema
+[:main_db_id] (DATABASE_MYSQL, BAZA_DANYCH, DATABASE_POSTGRESQL)
+  [:tbl_id] TABELA 'IN7_MK_BAZA_DYSTRYBUCJI'
+    [:cell_id] KOMORKA 'ID'
+    [:link_action_id] (ALIAS DO :action_id) URL_ACTION :url_action_name
+      [:link_param_id] (ALIAS DO :cell_id) PARAM_IN :url_action_param_1_name
+
+:url_action_name = 'ProjektyKosztyWstepnychRobot'
+:url_action_param_1_name = 'ID_PROJECT'
+		*/
+		$flatConfig_main = array();
+		{
+			$flatConfig_main[] = (object)array(// Zasob::assert(TYPESPECIALS)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.TYPE='TYPESPECIALS'
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'TYPESPECIALS', 'DESC' => 'Typespecials'),
+				'out' => array(':ts_main_id' => 'ID')
+			);
+			$flatConfig_main[] = (object)array(// Zasob::assert(TYPESPECIALS/:url_action_name)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = :ts_main_id
+						and z.TYPE = 'URL_ACTION'
+						and z.DESC = :url_action_name
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'URL_ACTION', 'PARENT_ID' => ':ts_main_id', 'DESC' => ':url_action_name'),
+				'in' => array(':ts_main_id', ':url_action_name'),
+				'out' => array(':action_id' => 'ID')
+			);
+			$flatConfig_main[] = (object)array(// Zasob::assert(TYPESPECIALS/:url_action_name/:url_action_param_1_name)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = :action_id
+						and z.TYPE = 'PARAM_IN'
+						and z.DESC = :url_action_param_1_name
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'PARAM_IN', 'PARENT_ID' => ':action_id', 'DESC' => ':url_action_param_1_name'),
+				'in' => array(':action_id', ':url_action_param_1_name'),
+				'out' => array(':action_param_1_id' => 'ID')
+			);
+		}
+		//DBG::_('DBG', '>1', "flatConfig_main", $flatConfig_main, __CLASS__, __FUNCTION__, __LINE__);
+		$flatConfig_link_in_projekt = array();
+		{
+			$flatConfig_link_in_projekt[] = (object)array(// Zasob::assert(default_db)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.ID = :main_db_id
+						and z.TYPE in('DATABASE_MYSQL', 'BAZA_DANYCH', 'DATABASE_POSTGRESQL')
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'BAZA_DANYCH', 'DESC' => 'Baza danych'),
+				'in' => array(':main_db_id'),
+				'out' => array(':main_db_id' => 'ID')
+			);
+			$flatConfig_link_in_projekt[] = (object)array(// Zasob::assert(default_db/IN7_MK_BAZA_DYSTRYBUCJI)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = :main_db_id
+						and z.TYPE = 'TABELA'
+						and z.DESC = 'IN7_MK_BAZA_DYSTRYBUCJI'
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'TABELA', 'PARENT_ID' => ':main_db_id', 'DESC' => 'IN7_MK_BAZA_DYSTRYBUCJI'),
+				'in' => array(':main_db_id'),
+				'out' => array(':tbl_id' => 'ID')
+			);
+			$flatConfig_link_in_projekt[] = (object)array(// Zasob::assert(default_db/IN7_MK_BAZA_DYSTRYBUCJI/ID)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = :tbl_id
+						and z.TYPE = 'KOMORKA'
+						and z.DESC = 'ID'
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'KOMORKA', 'PARENT_ID' => ':tbl_id', 'DESC' => 'ID'),
+				'in' => array(':tbl_id'),
+				'out' => array(':cell_id' => 'ID')
+			);
+			$flatConfig_link_in_projekt[] = (object)array(// Zasob::assert(default_db/IN7_MK_BAZA_DYSTRYBUCJI/:url_action_name)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = :tbl_id
+						and z.ALIAS_ID = ':action_id'
+						and z.TYPE = 'URL_ACTION'
+						and z.DESC = ':url_action_name'
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'URL_ACTION', 'PARENT_ID' => ':tbl_id', 'ALIAS_ID' => ':action_id', 'DESC' => ':url_action_name'),
+				'in' => array(':tbl_id', ':action_id', ':url_action_name'),
+				'out' => array(':link_action_id' => 'ID')
+			);
+//      [:link_param_id] (ALIAS DO :cell_id) PARAM_IN :url_action_param_1_name
+			$flatConfig_link_in_projekt[] = (object)array(// Zasob::assert(default_db/IN7_MK_BAZA_DYSTRYBUCJI/:url_action_name/:url_action_param_1_name)
+				'query' => "
+					select z.ID from CRM_LISTA_ZASOBOW z
+					where z.PARENT_ID = :link_action_id
+						and z.ALIAS_ID = ':cell_id'
+						and z.TYPE = 'PARAM_IN'
+						and z.DESC = ':url_action_param_1_name'
+						and z.A_STATUS not in('DELETED')
+				",
+				'createTable' => 'CRM_LISTA_ZASOBOW',
+				'createArgs' => array('TYPE' => 'PARAM_IN', 'PARENT_ID' => ':link_action_id', 'ALIAS_ID' => ':cell_id', 'DESC' => ':url_action_param_1_name'),
+				'in' => array(':cell_id', ':link_action_id', ':url_action_param_1_name'),
+				'out' => array(':link_action_id' => 'ID')
+			);
+		}
+		//DBG::_('DBG', '>1', "flatConfig_link_in_projekt", $flatConfig_link_in_projekt, __CLASS__, __FUNCTION__, __LINE__);
+		$flatConf = array_merge($flatConfig_main, $flatConfig_link_in_projekt);
+		$knownArgs = array();
+		$knownArgs[':url_action_name'] = 'Test';
+		$knownArgs[':url_action_param_1_name'] = 'ID_PROJECT';
+		$knownArgs[':main_db_id'] = '36';// from DB::getDB()->getZasobId(); or DB::getPDO()->getZasobId();
+		$this->_debugFlatConfig($flatConf, $knownArgs);
+
+		$args = $this->getArgsList();
+		$clsName = __CLASS__;
+		$urlActionName = str_replace('Route_UrlAction_', '', $clsName);
+		DBG::_('DBG', '>1', "reinstall class", __CLASS__, __CLASS__, __FUNCTION__, __LINE__);
+		DBG::_('DBG', '>1', "reinstall funName", $urlActionName, __CLASS__, __FUNCTION__, __LINE__);
+
+		DBG::_('DBG', '>1', "reinstall test", $args, __CLASS__, __FUNCTION__, __LINE__);
+		$dbFunction = $this->fetchZasobInfo();
+		$usrLogin = User::getLogin();
+		{// check if URL_ACTION already exists
+			if (!$dbFunction) throw new Exception("Brak id zasobu typu TYPESPECIALS - TODO: CREATE");// $this->createZasobTypespecials();
+			DBG::_('DBG', '>1', "dbFunction", $dbFunction, __CLASS__, __FUNCTION__, __LINE__);
+			if ($dbFunction['action_id_is_null']) {
+				$sth = $pdo->prepare("
+					insert into CRM_LISTA_ZASOBOW (
+						TYPE,
+						PARENT_ID,
+						`DESC`,
+						A_RECORD_CREATE_AUTHOR,
+						A_RECORD_CREATE_DATE
+					)
+					values (
+						'URL_ACTION',
+						:ts_id,
+						:url_action_name,
+						'system-by-{$usrLogin}',
+						NOW()
+					)
+				");
+				//$sth->bindValue('ts_id', $dbFunction['ts_id'], PDO::PARAM_INT);
+				//$sth->bindValue('url_action_name', $urlActionName, PDO::PARAM_STR);
+				$bindValues = array();
+				$bindValues['ts_id'] = array($dbFunction['ts_id'], PDO::PARAM_INT);
+				$bindValues['url_action_name'] = $urlActionName;
+				$pdo->bindValues($sth, $bindValues);
+				DBG::_('DBG', '>1', "sql", $pdo->getRawSql($sth), __CLASS__, __FUNCTION__, __LINE__);
+				$sth->execute();
+				$dbFunction = $this->fetchZasobInfo();
+				if (!$dbFunction || $dbFunction['action_id_is_null']) throw new Exception("Brak zasobu typu URL_ACTION, nie udało się go utworzyć!");
+			}
+		}
+		{// check params
+			if (!empty($args)) {
+				$todoArgs = array();
+				foreach ($args as $argName) {
+					if (empty($dbFunction['url_params'])) {
+						$todoArgs[$argName] = true;
+					} else if (!array_key_exists($argName, $dbFunction['url_params'])) {
+						$todoArgs[$argName] = true;
+					}
+					// OK PARAM_IN exists
+				}
+				$todoArgs = array_keys($todoArgs);
+				DBG::_('DBG', '>1', "todoArgs", $todoArgs, __CLASS__, __FUNCTION__, __LINE__);
+				if (!empty($todoArgs)) {
+					$sqlValues = array();
+					foreach ($todoArgs as $argName) {
+						$sqlValues[] = "(
+								'PARAM_IN',
+								{$dbFunction['action_id']},
+								'{$argName}',
+								'system-by-{$usrLogin}',
+								NOW()
+							)
+						";
+					}
+					$sqlValues = implode(", ", $sqlValues);
+					$sth = $pdo->prepare("
+						insert into CRM_LISTA_ZASOBOW (
+							TYPE,
+							PARENT_ID,
+							`DESC`,
+							A_RECORD_CREATE_AUTHOR,
+							A_RECORD_CREATE_DATE
+						)
+						values {$sqlValues}
+					");
+					DBG::_('DBG', '>1', "sql", $pdo->getRawSql($sth), __CLASS__, __FUNCTION__, __LINE__);
+					//$sth->execute();
+					$dbFunction = $this->fetchZasobInfo();
+				}
+				if (empty($dbFunction['url_params'])) {
+					throw new Exception("Brak zdefiniowanych parametrów, nie udało się ich utworzyć!");
+				}
+			}
+		}
+		{// check links in tables
+			// $info['links']['IN7_MK_BAZA_DYSTRYBUCJI'] = $linkInfo;
+			if (empty($dbFunction['links']['IN7_MK_BAZA_DYSTRYBUCJI'])) throw new Exception("Brak poprawne wprowadzonej tabeli 'IN7_MK_BAZA_DYSTRYBUCJI' w drzewie zasobów!");
+			$projInfo = $dbFunction['links']['IN7_MK_BAZA_DYSTRYBUCJI'];
+			/*
+				[link_action_id] =>
+				[db_id] => 36
+				[tbl_id] => 636
+				[cell_id] => 763
+				[param1_id] =>
+			*/
+			if (!$projInfo['link_action_id']) {
+				$sth = $pdo->prepare("
+					insert into CRM_LISTA_ZASOBOW (
+						TYPE,
+						PARENT_ID,
+						ALIAS_ID,
+						`DESC`,
+						A_RECORD_CREATE_AUTHOR,
+						A_RECORD_CREATE_DATE
+					)
+					values (
+						'URL_ACTION',
+						{$projInfo['tbl_id']},
+						{$dbFunction['action_id']},
+						'{$urlActionName}',
+						'system-by-{$usrLogin}',
+						NOW()
+					)
+				");
+				//DBG::_('DBG', '>1', "sql", $pdo->getRawSql($sth), __CLASS__, __FUNCTION__, __LINE__);
+				$sth->execute();
+				$dbFunction = $this->fetchZasobInfo();
+				$projInfo = $dbFunction['links']['IN7_MK_BAZA_DYSTRYBUCJI'];
+				if (!$projInfo['link_action_id']) throw new Exception("Brak zainstalowanej akcji dla tabeli 'IN7_MK_BAZA_DYSTRYBUCJI' - nie udało się zainstalować");
+			}
+			if (!$projInfo['param1_id']) {// TODO: get from params
+				$sth = $pdo->prepare("
+					insert into CRM_LISTA_ZASOBOW (
+						TYPE,
+						PARENT_ID,
+						ALIAS_ID,
+						`DESC`,
+						A_RECORD_CREATE_AUTHOR,
+						A_RECORD_CREATE_DATE
+					)
+					values (
+						'PARAM_IN',
+						{$projInfo['link_action_id']},
+						{$projInfo['cell_id']},
+						'ID_PROJECT',
+						'system-by-{$usrLogin}',
+						NOW()
+					)
+				");
+				$sth->execute();
+				$dbFunction = $this->fetchZasobInfo();
+				$projInfo = $dbFunction['links']['IN7_MK_BAZA_DYSTRYBUCJI'];
+				if (!$projInfo['param1_id']) throw new Exception("Brak zainstalowanego parametru 1 w akcji dla tabeli 'IN7_MK_BAZA_DYSTRYBUCJI' - nie udało się zainstalować");
+			}
+
+		}
+	}
+
+	public function _debugFlatConfig($flatConfig, $args = array()) {
+		//DBG::_('DBG', '>1', "flatConfig", $flatConfig, __CLASS__, __FUNCTION__, __LINE__);
+		echo '<pre style="border:1px solid green">';
+		foreach ($flatConfig as $conf) {
+			/*
+				[query] => string
+				[createTable] => srting - eg. CRM_LISTA_ZASOBOW
+				[createArgs] => array - eg. [ TYPE: 'TYPESPECIALS', DESC: 'Typespecials' ]
+				[in] => array - eg. [ ':ts_main_id', ':url_action_name' ]
+				[out] => array - eg. [ ':ts_main_id': 'ID' ]
+			*/
+			echo "Zasob::assert({$conf->createTable}): " . json_encode($conf->createArgs) . " returns: " . json_encode($conf->out) . "\n";
+		}
+		echo '</pre>';
+
+		echo '<pre style="border:1px solid green">';
+		echo "DBG: Simulate iter loop:\n";
+		$simulateGeneratedId = 1000;
+		$i = 0; $limit = 10; $todoIdx = array(); $knownArgsMap = array();
+		foreach ($flatConfig as $idx => $conf) $todoIdx[] = $idx;
+		foreach ($args as $arg => $val) $knownArgsMap[$arg] = $val;
+		for ($i = 0; $i < $limit; $i++) {
+			echo ">> DBG loop({$i}):\n";
+			$isExecuted = false;
+			foreach ($todoIdx as $idx) {
+				$conf = $flatConfig[$idx];
+				/*
+					[query] => string
+					[createTable] => srting - eg. CRM_LISTA_ZASOBOW
+					[createArgs] => array - eg. [ TYPE: 'TYPESPECIALS', DESC: 'Typespecials' ]
+					[in] => array - eg. [ ':ts_main_id', ':url_action_name' ]
+					[out] => array - eg. [ ':ts_main_id': 'ID' ]
+				*/
+				echo ">>>> Loop({$i}): idx({$idx}) Zasob::assert({$conf->createTable}): " . json_encode($conf->createArgs) . " returns: " . json_encode($conf->out) . "\n";
+				$canExecute = true;
+				if (!empty($conf->in)) foreach ($conf->in as $argName) {
+					if (!array_key_exists($argName, $knownArgsMap)) {
+						$canExecute = false;
+					}
+				}
+				if ($canExecute) {
+					// TODO: execute - $r = $this->_assertZasobConf($conf, $knownArgsMap);
+					// fetch params out to $knownArgsMap
+					foreach ($conf->out as $argName => $outFieldName) {
+						$knownArgsMap[$argName] = $simulateGeneratedId++;// TODO: fetch from returned object ($r->{$outFieldName})
+					}
+					$todoIdx = array_diff($todoIdx, array($idx));// remove $idx from $todoIdx
+					echo ">>>> Loop({$i}): knownArgsMap: " . json_encode($knownArgsMap) . " \n";
+					$isExecuted = true;
+				} else {
+					echo ">>>> Loop({$i}): cannot execute conf [{$idx}] missing args: " . json_encode($conf->in) . " \n";
+				}
+			}
+			if (!$isExecuted) {
+				echo "DBG: Stop";
+				if (empty($todoIdx)) {
+					echo " - OK";
+				} else {
+					echo " - Error - TODO:\n";
+					foreach ($todoIdx as $idx) {
+						$conf = $flatConfig[$idx];
+						echo "Zasob::assert({$conf->createTable}): " . json_encode($conf->createArgs) . " returns: " . json_encode($conf->out) . "\n";
+					}
+				}
+				break;
+			}
+		}
+		echo '</pre>';
+	}
+
+	public function fetchZasobInfo() {
+		$pdo = DB::getPDO();
+		$args = $this->getArgsList();
+		$clsName = __CLASS__;
+		$urlActionName = str_replace('Route_UrlAction_', '', $clsName);
+		$sth = $pdo->prepare("
+			select zp.ID as ts_id
+				, IF(z.ID is null, 1, 0) as action_id_is_null
+				, z.ID as action_id
+				, z.DESC as action_desc
+			from CRM_LISTA_ZASOBOW zp
+				left join CRM_LISTA_ZASOBOW z on(z.PARENT_ID = zp.ID
+						and z.TYPE = 'URL_ACTION'
+						and z.DESC = :url_action_name
+						and z.A_STATUS not in('DELETED')
+					)
+			where zp.TYPE = 'TYPESPECIALS'
+				and zp.A_STATUS not in('DELETED')
+		");
+		$sth->bindValue('url_action_name', $urlActionName);
+		$sth->execute();
+		$info = $sth->fetch();
+		if (!$info) return $info;
+		$info['url_params'] = array();
+		$info['links'] = array();// where action is installed - expected `IN7_MK_BAZA_DYSTRYBUCJI`
+
+		if ($info['action_id'] > 0) {
+			$sthParams = $pdo->prepare("
+				select z.ID as param_id
+					, z.DESC as param_desc
+				from CRM_LISTA_ZASOBOW z
+				where z.TYPE = 'PARAM_IN'
+					and z.A_STATUS not in('DELETED')
+					and z.PARENT_ID = :url_action_id
+			");
+			$sthParams->bindValue('url_action_id', $info['action_id']);
+			$sthParams->execute();
+			$urlParams = array();
+			$rawUrlParams = $sthParams->fetchAll();
+			foreach ($rawUrlParams as $urlParam) {
+				$urlParams[$urlParam['param_desc']] = $urlParam;
+			}
+			$info['url_params'] = $urlParams;
+		}
+		{
+			$main_db_id = $pdo->getZasobId();
+			$sth_dbInfo = $pdo->prepare("
+				select za.id as link_action_id
+					, zd.id as db_id
+					, zt.id as tbl_id
+					, zi.id as cell_id
+					, zp1.id as param1_id -- for every param
+				from CRM_LISTA_ZASOBOW zt
+					join CRM_LISTA_ZASOBOW zd on(zd.ID = zt.PARENT_ID
+						and zd.TYPE in('DATABASE_MYSQL', 'BAZA_DANYCH', 'DATABASE_POSTGRESQL')
+						and zd.ID = {$main_db_id}
+					)
+					join CRM_LISTA_ZASOBOW zi on(zi.PARENT_ID = zt.ID
+						and zi.TYPE = 'KOMORKA'
+						and zi.DESC = 'ID'
+					)
+					left join CRM_LISTA_ZASOBOW za on(za.PARENT_ID = zt.ID
+						and za.TYPE = 'URL_ACTION'
+						and za.ALIAS_ID = {$info['action_id']}
+					)
+					-- TODO: for every param:
+					left join CRM_LISTA_ZASOBOW zp1 on(zp1.PARENT_ID = za.ID
+						and zp1.TYPE = 'PARAM_IN'
+						and zp1.ALIAS_ID = zi.id
+						and zp1.DESC = 'ID_PROJECT'
+					)
+				where zt.TYPE = 'TABELA'
+					and zt.A_STATUS not in('DELETED')
+					and zt.DESC = 'IN7_MK_BAZA_DYSTRYBUCJI'
+			");
+			DBG::_('DBG', '>1', "dbInfo query", $pdo->getRawSql($sth_dbInfo), __CLASS__, __FUNCTION__, __LINE__);
+			$sth_dbInfo->execute();
+			$linkInfo = $sth_dbInfo->fetch();
+			if ($linkInfo) {
+				$info['links']['IN7_MK_BAZA_DYSTRYBUCJI'] = $linkInfo;
+			}
+		}
+		return $info;
+	}
+
+	public function kosztorysXmlAction() {// TODO: using old schema fields Agr_*
+/*
+		$idProject = 1921;
+		$schema = ProjectKosztorysSchema::getSchema();
+		$data = $this->_fetchKosztorysData($idProject);
+		//DBG::_('DBG', '>1', "XMLWriter", class_exists('XMLWriter'), __CLASS__, __FUNCTION__, __LINE__);
+		//header('Content-type: application/xml; charset=utf-8');
+		header('Content-type: text/plain; charset=utf-8');
+		$xmlWriter = new XMLWriter();
+		$xmlWriter->openUri('php://output');
+		$xmlWriter->setIndent(true);
+		if ($xmlWriter) {
+			$xmlWriter->startDocument('1.0','UTF-8');
+			//$xmlWriter->startElementNS(null, 'kosztorysy', 'https://biuro.biall-net.pl/wfs');// adds @xmlns=...
+			$xmlWriter->startElement('kosztorysy');
+			$xmlWriter->writeAttribute('targetNamespace', 'https://biuro.biall-net.pl/wfs');
+			$xmlWriter->writeAttributeNS('xmlns', 'p5', 'http://www.w3.org/2000/xmlns/', 'https://biuro.biall-net.pl/wfs');
+			for ($i = 1; $i <= 10; $i++) {
+				$idProject += 1;
+				$schema = ProjectKosztorysSchema::getSchema();
+				$data = $this->_fetchKosztorysData($idProject);
+				$xmlWriter->startElement('kosztorys');
+					$xmlWriter->startElement('projekt');
+						$xmlWriter->writeAttribute('id', $idProject);
+						$xmlWriter->startElement('projekt');
+							$xmlWriter->text("TODO L." . __LINE__);
+						$xmlWriter->endElement();
+					$xmlWriter->endElement();
+				$xmlWriter->endElement();
+			}
+
+			// $memXmlWriter = new XMLWriter();
+			// $memXmlWriter->openMemory();
+			// $memXmlWriter->setIndent(true);
+			//
+			// for ($i = 1; $i <= 10; $i++) {
+			// 	$idProject += 1;
+			// 	$schema = ProjectKosztorysSchema::getSchema();
+			// 	$data = $this->_fetchKosztorysData($idProject);
+			//
+			// 	$memXmlWriter->startElement('kosztorys');
+			// 		$memXmlWriter->writeAttribute('id', $idProject);
+			// 		$memXmlWriter->writeAttributeNS('p5', 'typeName', 'https://biuro.biall-net.pl/wfs', 'Kosztorys');
+			// 	$memXmlWriter->text('book_'.$i);
+			// 	$memXmlWriter->endElement();
+			//
+			// 	if ($i % 5 == 0) {
+			// 		$batchXmlString = $memXmlWriter->outputMemory(true);
+			// 		$xmlWriter->writeRaw($batchXmlString);
+			// 	}
+			// }
+			// $memXmlWriter->flush();
+			// unset($memXmlWriter);
+
+			$xmlWriter->endElement();
+			$xmlWriter->endDocument();
+		}
+*/
+	}
+
+	public function panel($idProject, $idCompany = 0) {
+		if (User::get('ADM_ADMIN_LEVEL') > 6 || 'Pracownik' != User::getType()) return;// Only for workers with admin level < 6
+		if (1 == V::get('_print', '', $_GET)) return;// print mode
+		$task = V::get('_task', '', $_GET);
+		?>
+<div class="jumbotron">
+  <div class="container">
+		<div class="row">
+			<div class="col-md-12">
+				<?php if ($idProject > 0) : ?>
+					<a class="btn btn-default" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>">Kosztorys</a>
+					<a class="btn btn-default" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>&_task=przedmiar&_print=1" target="_blank">Przedmiar</a>
+					<a class="btn btn-default" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>&_task=zestawienieSwMikRur&_print=1" target="_blank">zestawienie (światłowód i mikrorurki)</a>
+					<a class="btn btn-warning" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>&_task=oferta">oferta</a>
+					<a class="btn btn-warning" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>&_task=ofertaAdmin">oferta (Admin)</a>
+					<a class="btn btn-warning" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>&_task=ofertaCompany">oferta company</a>
+				<?php endif; ?>
+				<a class="btn btn-default" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&_task=ofertaDefaultAdmin" target="_blank">oferta domyślna (Admin)</a>
+			</div>
+		</div>
+		<div class="row">
+			<div class="col-md-12">
+<?php
+	switch ($task) {
+	case 'oferta': break;
+	case '': ?><a class="btn btn-link" href="index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&ID_PROJECT=<?php echo $idProject; ?>&_print=1" target="_blank"><i class="glyphicon glyphicon-print"></i> Wydruk Kosztorysu</a><?php break;
+	}
+?>
+		</div>
+		</div>
+		<br>
+		<?php if ('ofertaAdmin' == $task) : ?>
+			<form action="" method="get" class="form-inline">
+				<input type="hidden" name="_route" value="UrlAction_ProjektyKosztyWstepnychRobot">
+				<input type="hidden" name="_task" value="ofertaAdmin">
+				<label for="ID_PROJECT">Nr projektu:</label>
+				<input type="text" name="ID_PROJECT" value="<?php echo $idProject; ?>" class="form-control">
+				<label for="ID_COMPANY">Nr kontrahenta (0 = oferta wewnętrzna/kosztorys)</label>
+				<input type="text" name="ID_COMPANY" value="<?php echo $idCompany; ?>" class="form-control">
+				<input type="submit" value="Wybierz" class="btn btn-primary">
+			</form>
+		<?php elseif ('oferta' == $task || 'ofertaDefaultAdmin' == $task) : ?>
+			<form action="" method="get" class="form-inline">
+				<input type="hidden" name="_route" value="UrlAction_ProjektyKosztyWstepnychRobot">
+				<input type="hidden" name="_task" value="oferta">
+				<input type="hidden" name="ID_PROJECT" value="<?php echo $idProject; ?>">
+				<label for="ID_COMPANY">Nr kontrahenta</label>
+				<input type="text" name="ID_COMPANY" value="<?php echo $idCompany; ?>" class="form-control">
+				<input type="submit" value="Wybierz" class="btn btn-primary">
+			</form>
+		<?php endif; ?>
+  </div>
+</div>
+<?php
+	}
+
+	public function saveOffer($idProject, $idCompany, $args, $admin = false) {
+		DBG::_('DBG', '>1', "args", $args, __CLASS__, __FUNCTION__, __LINE__);
+		if (!$admin) return;
+		$cennik = ProjectKosztorysCennik::getCennik($idProject, $idCompany);
+		DBG::_('DBG', '>1', "cennik", $cennik, __CLASS__, __FUNCTION__, __LINE__);
+		$pdo = DB::getPDO();
+		{
+			$add_id_zasob = 0;
+			$add_unit = '';
+			$add_price = 0;
+			$add_quantity = 0;
+			$add__sth = $pdo->prepare("
+				insert into CRM_LISTA_ZASOBOW_OFFERS (
+					CRM_LISTA_ZASOBOW_ID
+					, COMPANIES_ID
+					, ID_PROJECT
+					, OFFER_PRICE_PER_RESOURCE_UNIT
+					, RESOURCE_UNIT_TYPE
+					, OFFER_UNIT_TYPE
+					, REQUIRED_RESOURCE_UNITS
+					, A_RECORD_CREATE_AUTHOR
+					, A_RECORD_CREATE_DATE
+				) values (
+					:id_zasob
+					, :id_company
+					, :id_project
+					, :price
+					, :unit
+					, :unit
+					, :quantity
+					, :author
+					, NOW()
+				)
+			");
+			$add__sth->bindValue(':author', User::getLogin(), PDO::PARAM_STR);
+			$add__sth->bindValue(':id_project', $idProject, PDO::PARAM_INT);
+			$add__sth->bindValue(':id_company', $idCompany, PDO::PARAM_INT);
+			$add__sth->bindValue(':quantity', $add_quantity, PDO::PARAM_STR);
+			$add__sth->bindParam(':id_zasob', $add_id_zasob, PDO::PARAM_INT);
+			$add__sth->bindParam(':price', $add_price, PDO::PARAM_STR);
+			$add__sth->bindParam(':unit', $add_unit, PDO::PARAM_STR);
+		}
+		{
+			$edit_id_company = $idCompany;
+			$edit_id_zasob = 0;
+			$edit_unit = '';
+			$edit_price = 0;
+			$edit_author = 0;
+			$edit_quantity = 0;
+			$edit__sth = $pdo->prepare("
+				update CRM_LISTA_ZASOBOW_OFFERS
+				set CRM_LISTA_ZASOBOW_ID = :id_zasob
+					, COMPANIES_ID = :id_company
+					, ID_PROJECT = :id_project
+					, OFFER_PRICE_PER_RESOURCE_UNIT = :price
+					, RESOURCE_UNIT_TYPE = :unit
+					, OFFER_UNIT_TYPE = :unit
+					, REQUIRED_RESOURCE_UNITS = :quantity
+					, A_RECORD_UPDATE_AUTHOR = :author
+					, A_RECORD_UPDATE_DATE = NOW()
+				where ID = :id
+			");
+			$edit__sth->bindParam(':id', $edit_id, PDO::PARAM_INT);
+			$edit__sth->bindValue(':author', User::getLogin(), PDO::PARAM_STR);
+			$edit__sth->bindValue(':id_project', $idProject, PDO::PARAM_INT);
+			$edit__sth->bindValue(':id_company', $idCompany, PDO::PARAM_INT);
+			$edit__sth->bindValue(':quantity', $edit_quantity, PDO::PARAM_STR);
+			$edit__sth->bindParam(':id_zasob', $edit_id_zasob, PDO::PARAM_INT);
+			$edit__sth->bindParam(':price', $edit_price, PDO::PARAM_STR);
+			$edit__sth->bindParam(':unit', $edit_unit, PDO::PARAM_STR);
+		}
+		$schema = ProjectKosztorysSchema::getSchema();
+		foreach ($schema['config']['layer'] as $idLayer => $layData) {
+			DBG::_('DBG', '>1', "layData", $layData, __CLASS__, __FUNCTION__, __LINE__);
+			foreach ($layData['type'] as $idType => $typeLabel) {
+				$edit_id = V::get("edit_price_id_{$idType}", '', $args);
+				$add_price = V::get("price_{$idType}", '', $args);
+				DBG::_('DBG', '>1', "typeLabel (edit={$edit_id}, price={$add_price})", $typeLabel, __CLASS__, __FUNCTION__, __LINE__);
+				if ($edit_id > 0) {
+					$edit_price = V::get("price_{$idType}", '', $args);
+					$edit_price = str_replace(',', '.', $edit_price);
+					if ($edit_id > 0 && $edit_price > 0) {
+						$edit_id_zasob = $idType;
+						$edit_unit = $layData['jednostka'];
+						// TODO: check if anything change
+						DBG::_('DBG', '>1', "EDIT price for idZasob({$idType}) {price:{$edit_price}, id_offer:{$edit_id}}", $typeLabel, __CLASS__, __FUNCTION__, __LINE__);
+						$edit__sth->execute();
+						// TODO: add to hist
+					}
+				} else {
+					$add_price = V::get("price_{$idType}", '', $args);
+					$add_price = str_replace(',', '.', $add_price);
+					if ($add_price > 0) {
+						$add_id_zasob = $idType;
+						$add_unit = $layData['jednostka'];
+						DBG::_('DBG', '>1', "ADD price for idZasob({$idType}) {price:{$add_price}}", $typeLabel, __CLASS__, __FUNCTION__, __LINE__);
+						$add__sth->execute();
+					}
+				}
+			}
+		}
+	}
+	public function saveDefaultOffer($args) {
+		DBG::_('DBG', '>1', "args", $args, __CLASS__, __FUNCTION__, __LINE__);
+		$cennik = ProjectKosztorysCennik::getDefaultCennik();
+		DBG::_('DBG', '>1', "cennik", $cennik, __CLASS__, __FUNCTION__, __LINE__);
+		$idCompany = 0;
+		$idProject = 0;
+		$pdo = DB::getPDO();
+		{
+			$add_id_zasob = 0;
+			$add_unit = '';
+			$add_price = 0;
+			$add_quantity = 0;
+			$add__sth = $pdo->prepare("
+				insert into CRM_LISTA_ZASOBOW_OFFERS (
+					CRM_LISTA_ZASOBOW_ID
+					, COMPANIES_ID
+					, ID_PROJECT
+					, OFFER_PRICE_PER_RESOURCE_UNIT
+					, RESOURCE_UNIT_TYPE
+					, OFFER_UNIT_TYPE
+					, REQUIRED_RESOURCE_UNITS
+					, A_RECORD_CREATE_AUTHOR
+					, A_RECORD_CREATE_DATE
+				) values (
+					:id_zasob
+					, :id_company
+					, :id_project
+					, :price
+					, :unit
+					, :unit
+					, :quantity
+					, :author
+					, NOW()
+				)
+			");
+			$add__sth->bindValue(':author', User::getLogin(), PDO::PARAM_STR);
+			$add__sth->bindValue(':id_project', $idProject, PDO::PARAM_INT);
+			$add__sth->bindValue(':id_company', $idCompany, PDO::PARAM_INT);
+			$add__sth->bindValue(':quantity', $add_quantity, PDO::PARAM_STR);
+			$add__sth->bindParam(':id_zasob', $add_id_zasob, PDO::PARAM_INT);
+			$add__sth->bindParam(':price', $add_price, PDO::PARAM_STR);
+			$add__sth->bindParam(':unit', $add_unit, PDO::PARAM_STR);
+		}
+		{
+			$edit_id_zasob = 0;
+			$edit_unit = '';
+			$edit_price = 0;
+			$edit_author = 0;
+			$edit_quantity = 0;
+			$edit__sth = $pdo->prepare("
+				update CRM_LISTA_ZASOBOW_OFFERS
+				set CRM_LISTA_ZASOBOW_ID = :id_zasob
+					, COMPANIES_ID = :id_company
+					, ID_PROJECT = :id_project
+					, OFFER_PRICE_PER_RESOURCE_UNIT = :price
+					, RESOURCE_UNIT_TYPE = :unit
+					, OFFER_UNIT_TYPE = :unit
+					, REQUIRED_RESOURCE_UNITS = :quantity
+					, A_RECORD_UPDATE_AUTHOR = :author
+					, A_RECORD_UPDATE_DATE = NOW()
+				where ID = :id
+			");
+			$edit__sth->bindParam(':id', $edit_id, PDO::PARAM_INT);
+			$edit__sth->bindValue(':author', User::getLogin(), PDO::PARAM_STR);
+			$edit__sth->bindValue(':id_project', $idProject, PDO::PARAM_INT);
+			$edit__sth->bindValue(':id_company', $idCompany, PDO::PARAM_INT);
+			$edit__sth->bindValue(':quantity', $edit_quantity, PDO::PARAM_STR);
+			$edit__sth->bindParam(':id_zasob', $edit_id_zasob, PDO::PARAM_INT);
+			$edit__sth->bindParam(':price', $edit_price, PDO::PARAM_STR);
+			$edit__sth->bindParam(':unit', $edit_unit, PDO::PARAM_STR);
+		}
+		$schema = ProjectKosztorysSchema::getSchema();
+		foreach ($schema['config']['layer'] as $idLayer => $layData) {
+			DBG::_('DBG', '>1', "layData", $layData, __CLASS__, __FUNCTION__, __LINE__);
+			foreach ($layData['type'] as $idType => $typeLabel) {
+				DBG::_('DBG', '>1', "typeLabel", $typeLabel, __CLASS__, __FUNCTION__, __LINE__);
+				$edit_id = V::get("edit_price_id_{$idType}", '', $args);
+				if ($edit_id > 0) {
+					$edit_price = V::get("price_{$idType}", '', $args);
+					$edit_price = str_replace(',', '.', $edit_price);
+					if ($edit_id > 0 && $edit_price > 0) {
+						$edit_id_zasob = $idType;
+						$edit_unit = $layData['jednostka'];
+						// TODO: check if anything change
+						$edit__sth->execute();
+						// TODO: add to hist
+					}
+				} else {
+					$add_price = V::get("price_{$idType}", '', $args);
+					$add_price = str_replace(',', '.', $add_price);
+					if ($add_price > 0) {
+						$add_id_zasob = $idType;
+						$add_unit = $layData['jednostka'];
+						$add__sth->execute();
+					}
+				}
+			}
+		}
+	}
+
+	public function defaultOferta($idCompany = 0) {
+		if ('1' == V::get('save_offer', '', $_POST)) {
+			$this->saveDefaultOffer($_POST);
+		}
+		$schema = ProjectKosztorysSchema::getSchema();
+		if (empty($schema['config']['type'])) throw new Exception("Schema error - brak zdefiniowanych typów");
+		$cennik = ProjectKosztorysCennik::getDefaultCennik($idCompany);
+		//DBG::_(true, true, "cennik", $cennik, __CLASS__, __FUNCTION__, __LINE__);
+		UI::setTitleJsTag("Cennik domyślny dla Kosztorysów");
+		?>
+<div class="container">
+<form action="" method="post">
+	<?php foreach ($schema['config']['layer'] as $idLayer => $layData) : ?>
+		<h4 style="padding:0 6px"><?php echo $layData['label']; ?></h4>
+		<table class="tabel table-bordered" style="width:100%">
+		<tbody>
+			<tr>
+				<th style="padding:0 6px">id zasobu</th>
+				<th style="padding:0 6px">typ</th>
+				<th style="padding:0 6px">jednostka miary</th>
+				<th style="padding:0 6px">cena jednostkowa</th>
+			</tr>
+			<?php foreach ($layData['type'] as $idType => $typeLabel) : ?>
+				<tr>
+					<td style="width:100px; padding:0 6px"><?php echo $idType; ?></td>
+					<td style="padding:0 6px" title="[<?php echo $idType; ?>] <?php echo $typeLabel; ?>"><?php echo $typeLabel; ?></td>
+					<td style="padding:0 6px"><?php echo $layData['jednostka']; ?></td>
+					<td style="padding:3px 6px"><input type="text" class="form-control input-sm" name="price_<?php echo $idType; ?>" value="<?php echo $cennik[$idType]['price']; ?>"/></td>
+				</tr>
+			<?php endforeach; ?>
+		</tbody>
+		</table>
+	<?php endforeach; ?>
+	<br>
+	<?php foreach ($cennik as $item) : ?>
+		<input type="hidden" name="edit_price_id_<?php echo $item['id_zasob']; ?>" value="<?php echo $item['ID']; ?>">
+	<?php endforeach; ?>
+	<input type="hidden" name="save_offer" value="1">
+	<input type="submit" value="Zapisz" class="btn btn-primary">
+</form>
+</div>
+<?php
+// Fix Zasoby [22444] INNE Kosztorys - zasoby
+
+// SELECT *  FROM `CRM_LISTA_ZASOBOW_OFFERS`
+// WHERE `CRM_LISTA_ZASOBOW_ID` IN (22801,22445,22446,22447,22448,22449,22450,22451,22452,22453,22454,22455,22456,22457,22458,22459,22552,22517,22514,22481,22484,22515,22516,22518,22519,22520,22521,22550,22551,22617,22618,22638,22639,22640,22641,22642,22643,22644,22645,22646,22724,22725,22797,22798)
+// ORDER BY `CRM_LISTA_ZASOBOW_OFFERS`.`CRM_LISTA_ZASOBOW_ID` ASC
+// limit 100
+
+// <edittype widgetv2type="ValueMap" name="Typ_kosztu">
+//   <widgetv2config fieldEditable="1" labelOnTop="0">
+//     <value zasobID="22643" key="Montaż zapasu w studni" value="Montaż zapasu w studni"/>
+//     <value zasobID="22638" key="Montaż złączek mikrokanalizacji" value="Montaż złączek mikrokanalizacji"/>
+//     <value zasobID="22645" key="Mufa z montażem" value="Mufa z montażem"/>
+//     <value zasobID="22642" key="Pigtail od puszki do terminala" value="Pigtail od puszki do terminala"/>
+//     <value zasobID="22640" key="Przewiert w budynku z uszczelnieniem" value="Przewiert w budynku z uszczelnieniem"/>
+//     <value zasobID="22639" key="Puszka abonencka z montażem" value="Puszka abonencka z montażem"/>
+//     <value zasobID="22641" key="Spaw poza mufą" value="Spaw poza mufą"/>
+//     <value zasobID="22724" key="Spaw w mufie" value="Spaw w mufie"/>
+//     <value zasobID="22617" key="Spliter 1/16" value="Spliter 1/16"/>
+//     <value zasobID="22618" key="Spliter 1/4" value="Spliter 1/4"/>
+//     <value zasobID="22725" key="Spliter 1/8" value="Spliter 1/8"/>
+//     <value zasobID="22644" key="Wprowadzenie kabli do mufy" value="Wprowadzenie kabli do mufy"/>
+//     <value zasobID="22646" key="Zamek PIOCH do studni" value="Zamek PIOCH do studni"/>
+//     <value zasobID="22515" key="Zapas kabla" value="Zapas kabla"/>
+//     <value zasobID="22514" key="Złącze" value="Złącze "/>
+//   </widgetv2config>
+// </edittype>
+// 22574 ->22515 INNE TYPE Zapas kabla
+// 22620 ->22618 INNE TYPE Spliter 1/4
+// 22621 ->22617 INNE TYPE Spliter 1/16
+// 22647 ->22638 INNE TYPE Montaż złączek mikrokanalizacji
+// 22648 ->22639 INNE TYPE Puszka abonencka z montażem
+// 22649 ->22640 INNE TYPE Przewiert w budynku z uszczelnieniem
+// 22650 ->22641 INNE TYPE Spaw poza mufą
+// 22651 ->22642 INNE TYPE Pigtail od puszki do terminala
+// 22652 ->22643 INNE TYPE Montaż zapasu w studni
+// 22653 ->22644 INNE TYPE Wprowadzenie kabli do mufy
+// 22654 ->22645 INNE TYPE Mufa z montażem
+// 22655 ->22646 INNE TYPE Zamek PIOCH do studni
+// 22881 ->22514 INNE TYPE Złącze
+
+// TODO: ? 22484
+
+	}
+
+	/**
+	 * @returns [ 'cost_total' => :numeric, 'sub_costs' => [ $id_zasob => 'price' ] ]
+	 */
+	public function getProjectCostByCennik($idProject, $idCompany = 0) {
+		$projCost = array();
+		$projCost['cost_total'] = 0;
+		$projCost['sub_costs'] = array();
+
+		$defCennik = ProjectKosztorysCennik::getDefaultCennik($idCompany);
+		// $defCennik = [ $id_zasob => [ 'price' => $price, 'ID', 'id_zasob', 'id_company', 'id_project', 'unit', 'quantity' ] ]
+		$cennik = ProjectKosztorysCennik::getCennik($idProject, $idCompany);
+		$workCennik = ProjectKosztorysCennik::getWorkCennik($idProject, $idCompany);
+		$data = $this->getData($idProject, $admin = false);
+		foreach ($data['summary'] as $idLayer => $layData) {
+			foreach ($layData['data'] as $typeData) {
+				if (!$typeData['idType']) continue;
+				$price = (!empty($cennik[$typeData['idType']]['price']))? $cennik[$typeData['idType']]['price'] : 0;
+				$defPrice = (!empty($defCennik[$typeData['idType']]['price']))? $defCennik[$typeData['idType']]['price'] : 0;
+				$workPrice = (!empty($workCennik[$typeData['idType']]['price']))? $workCennik[$typeData['idType']]['price'] : 0;
+				$cost = ($price > 0)? $price : $defPrice;
+				if ($workPrice > 0) $cost += $workPrice;
+				$cost = $typeData['ilosc'] * $cost;
+				$projCost['sub_costs'][$typeData['idType']] = $cost;
+				$projCost['cost_total'] += $cost;
+			}
+		}
+		// fetch sub costs for sub projects
+		// $data['by_project'] = array_reduce($data['rawDataByType'], function($result, $row) {
+		// 	$idProj = $row['ID_PROJECT'];
+		// 	$idLayer = $row['idLayer'];
+		// 	$idType = $row['idType'];
+		// 	if (!array_key_exists($idProj, $result)) $result[$idProj] = array();
+		// 	if (!array_key_exists($idLayer, $result)) $result[$idProj][$idLayer] = array();
+		// 	if (!array_key_exists($idType, $result[$idProj][$idLayer])) {
+		// 		$result[$idProj][$idLayer][$idType] = $row;
+		// 	} else {
+		// 		$result[$idProj][$idLayer][$idType]['ilosc'] += $row['ilosc'];
+		$projCost['sub_proj'] = array();
+		foreach ($data['by_project'] as $idSubProj => $subProjData) {
+			foreach ($subProjData as $idLayer => $layData) {
+				foreach ($layData as $idType => $typeData) {
+					$price = (!empty($cennik[$typeData['idType']]['price']))? $cennik[$typeData['idType']]['price'] : 0;
+					$defPrice = (!empty($defCennik[$typeData['idType']]['price']))? $defCennik[$typeData['idType']]['price'] : 0;
+					$cost = ($price > 0)? $price : $defPrice;
+					$cost = $typeData['ilosc'] * $cost;
+					$projCost['sub_proj'][$idSubProj][$idType] = $cost;
+				}
+			}
+		}
+		return $projCost;
+	}
+
+	public function getModel($idProject) {
+		if (array_key_exists($idProject, $this->_model)) return $this->_model[$idProject];
+		$this->_model[$idProject] = new ProjectKosztorysModel($idProject);
+		return $this->_model[$idProject];
+	}
+
+	public function fetchBudynki($idProject) {
+		return DB::getPDO()->fetchAll("
+			select b.*
+			from BUILDINGS b
+			where b.ID_PROJECT = '{$idProject}'
+		");
+	}
+	public function fetchProject($idProject) {
+		$rows = DB::getPDO()->fetchAll("
+			select p.*
+			from IN7_MK_BAZA_DYSTRYBUCJI p
+			where p.ID = '{$idProject}'
+		");
+		return reset($rows);
+	}
+
+	public function zestawienieSwMikRur($idProject) {
+		$idCompany = 0;
+		$admin = false;
+		$companyAdmin = false;
+		$model = $this->getModel($idProject);
+		//DBG::table("subProjectList", $model->subProjectList, __CLASS__, __FUNCTION__, __LINE__);
+		$schema = ProjectKosztorysSchema::getSchema();
+
+		$projCost = $this->getProjectCostByCennik($idProject, $idCompany);
+		//DBG::_(true, true, "projCost", $projCost, __CLASS__, __FUNCTION__, __LINE__);
+		$viewLayerDataArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin', 'projCost');
+?>
+<div class="container">
+	<h1></h1>
+	<table class="table">
+		<tr>
+			<th><?php echo $schema['nr']; ?></th>
+			<th><?php echo $schema['title']; ?></th>
+			<th><?php echo $schema['ownerName']; ?></th>
+			<th style="text-align:right"><?php echo $schema['cost_total']; ?></th>
+		</tr>
+		<tr>
+			<td><?php echo $model->idProject; ?></td>
+			<td><?php echo $model->title; ?></td>
+			<td><?php echo $model->ownerName; ?></td>
+			<td style="text-align:right"><?php echo number_format($projCost['cost_total'], 2, ',', ' '); ?></td>
+		</tr>
+	</table>
+	<?php $args = $viewLayerDataArgs;
+		$idProject = $args['idProject'];
+		$idCompany = V::get('idCompany', 0, $args);
+		$admin = V::get('admin', false, $args);
+		$companyAdmin = V::get('companyAdmin', false, $args);
+		$projCosts = V::get('projCosts', array(), $args);
+
+		$schema = ProjectKosztorysSchema::getSchema();
+		$conf = $schema['config'];
+		DBG::_('DBG', '>1', "conf", $conf, __CLASS__, __FUNCTION__, __LINE__);
+
+		$summaryTypeCost = V::get('sub_costs', array(), $projCosts);
+		$subProjCost = V::get('sub_proj', array(), $projCosts);
+
+		$data = $this->getData($idProject, $admin);
+		$dataSummary = $data['summary'];
+		$dataSubProj = $data['by_project'];
+		// $dataSubProj = array();
+		// foreach ($data['by_project'] as $idSubProj => $subProjData) {
+		// 	if ($idProject != $idSubProj) $dataSubProj[$idSubProj] = $subProjData;
+		// }
+		DBG::_('DBG', '>1', "data", $data, __CLASS__, __FUNCTION__, __LINE__);
+
+		$defCennik = ProjectKosztorysCennik::getDefaultCennik($idCompany);
+		$cennik = ProjectKosztorysCennik::getCennik($idProject, $idCompany);
+		$workCennik = ProjectKosztorysCennik::getWorkCennik($idProject, $idCompany);
+		$additionalCosts = array();
+		$additionalCosts[] = array();
+		$additionalCosts[] = array();
+		$additionalCosts[] = array();
+		$additionalSummaryTypeCost = array();
+?>
+<style type="text/css">
+/* Print Styles */
+@media print {
+	body { font-size:10px; }
+	th, td { font-size:10px; }
+	h1 { font-size:2em; }
+	h2 { font-size:1.6em; }
+	h3 { font-size:1.4em; }
+	h4 { font-size:1.2em; }
+}
+</style>
+<?php if (!empty($dataSubProj)) : ?>
+	<table class="table table-bordered">
+	<thead>
+		<tr>
+			<th>ID PROJ.</th>
+			<th>ID BUDYNKU</th>
+			<th>WŁAŚCICIEL</th>
+			<th>ADRES</th>
+			<th>KONTAKT</th>
+			<th>NR DZIAŁKI</th>
+			<th>DŁ. ŚWIATŁOWODU</th>
+			<th>DŁ. MIKRORURKI DO KLIENTA</th>
+		</tr>
+	</thead>
+	<tbody>
+	<?php foreach ($dataSubProj as $idSubProj => $subProjData) : ?>
+		<?php
+		// 22460 Światłowód
+		// 22467 Mikrokanalizacja do klienta
+		$sumSwiatlo = 0;
+		if (!empty($subProjData[22460])) foreach ($subProjData[22460] as $swiatlo) {
+			$sumSwiatlo += $swiatlo['ilosc'];
+		}
+		$sumMikroRur = 0;
+		if (!empty($subProjData[22467])) foreach ($subProjData[22467] as $mikroRur) {
+			$sumMikroRur += $mikroRur['ilosc'];
+		}
+		?>
+		<?php $budynki = $this->fetchBudynki($idSubProj); ?>
+		<?php $proj = $this->fetchProject($idSubProj); ?>
+		<?php if (empty($budynki)) : ?>
+			<tr>
+				<td><?php echo $idSubProj; ?></td>
+				<td>brak</td>
+				<td><?php echo $proj['IN_NAME']; ?></td>
+				<td><?php echo $proj['M_DISTRIBUTOR_ADDRESS']; ?></td>
+				<td><?php echo $proj['M_DISTRIBUTOR_CONTACT']; ?></td>
+				<td><?php echo $proj['INWDZ__obreby_i_nr_dzialek']; ?></td>
+				<td><?php echo $sumSwiatlo; ?></td>
+				<td><?php echo $sumMikroRur; ?></td>
+			</tr>
+		<?php else : ?>
+			<?php foreach ($budynki as $bud) : ?>
+				<tr>
+					<td><?php echo $idSubProj; ?></td>
+					<td><?php echo $bud['ID']; ?></td>
+					<td><?php echo $proj['IN_NAME']; ?></td>
+					<td><?php echo $proj['M_DISTRIBUTOR_ADDRESS']; ?></td>
+					<td><?php echo $proj['M_DISTRIBUTOR_CONTACT']; ?></td>
+					<td><?php echo $proj['INWDZ__obreby_i_nr_dzialek']; ?></td>
+					<td><?php echo $sumSwiatlo; ?></td>
+					<td><?php echo $sumMikroRur; ?></td>
+				</tr>
+			<?php endforeach; ?>
+		<?php endif; ?>
+	<?php endforeach; ?>
+	</tbody>
+	</table>
+<?php endif; ?>
+<?php
+		DBG::_('DBG', '>0', "schema", $schema, __CLASS__, __FUNCTION__, __LINE__);
+		DBG::_('DBG', '>0', "projCosts", $projCosts, __CLASS__, __FUNCTION__, __LINE__);
+	}
+
+	public function kosztorys($idProject) {
+		$idCompany = 0;
+		$admin = false;
+		$companyAdmin = false;
+		$priceEditJs = (1 != V::get('_print', '', $_GET));
+		$model = $this->getModel($idProject);
+		//DBG::table("subProjectList", $model->subProjectList, __CLASS__, __FUNCTION__, __LINE__);
+		$schema = ProjectKosztorysSchema::getSchema();
+
+		$projCosts = $this->getProjectCostByCennik($idProject, $idCompany);
+		//DBG::_(true, true, "projCosts", $projCosts, __CLASS__, __FUNCTION__, __LINE__);
+		$viewLayerDataArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin', 'projCosts', 'priceEditJs');
+		UI::setTitleJsTag("Kosztorys wstępny robót telekomunikacyjnych [{$idProject}]");
+?>
+<div class="container">
+	<h1>Kosztorys wstępny robót telekomunikacyjnych</h1>
+	<table class="table">
+		<tr>
+			<th><?php echo $schema['nr']; ?></th>
+			<th><?php echo $schema['title']; ?></th>
+			<th><?php echo $schema['ownerName']; ?></th>
+			<th style="text-align:right"><?php echo $schema['cost_total']; ?></th>
+		</tr>
+		<tr>
+			<td><?php echo $model->idProject; ?></td>
+			<td><?php echo $model->title; ?></td>
+			<td><?php echo $model->ownerName; ?></td>
+			<td style="text-align:right"><?php echo number_format($projCosts['cost_total'], 2, ',', ' '); ?></td>
+		</tr>
+	</table>
+	<?php $this->viewLayersData($viewLayerDataArgs); ?>
+</div>
+<?php
+		DBG::_('DBG', '>0', "schema", $schema, __CLASS__, __FUNCTION__, __LINE__);
+		DBG::_('DBG', '>0', "projCosts", $projCosts, __CLASS__, __FUNCTION__, __LINE__);
+	}
+
+	public function przedmiar($idProject) {
+		$idCompany = 0;
+		$admin = false;
+		$companyAdmin = false;
+		$model = $this->getModel($idProject);
+		//DBG::table("subProjectList", $model->subProjectList, __CLASS__, __FUNCTION__, __LINE__);
+		$schema = ProjectKosztorysSchema::getSchema();
+
+		$projCosts = $this->getProjectCostByCennik($idProject, $idCompany);
+		//DBG::_(true, true, "projCosts", $projCosts, __CLASS__, __FUNCTION__, __LINE__);
+		$hidePrices = true;
+		$viewLayerDataArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin', 'hidePrices');
+		UI::setTitleJsTag("Przedmiar robót telekomunikacyjnych [{$idProject}]");
+?>
+<div class="container">
+	<h1>Przedmiar robót telekomunikacyjnych</h1>
+	<table class="table">
+		<tr>
+			<th><?php echo $schema['nr']; ?></th>
+			<th><?php echo $schema['title']; ?></th>
+		</tr>
+		<tr>
+			<td><?php echo $model->idProject; ?></td>
+			<td><?php echo $model->title; ?></td>
+		</tr>
+	</table>
+	<?php $this->viewLayersData($viewLayerDataArgs); ?>
+</div>
+<?php
+		DBG::_('DBG', '>0', "schema", $schema, __CLASS__, __FUNCTION__, __LINE__);
+		DBG::_('DBG', '>0', "projCosts", $projCosts, __CLASS__, __FUNCTION__, __LINE__);
+	}
+
+	public function oferta($args) {
+		$idProject = $args['idProject'];
+		$idCompany = V::get('idCompany', 0, $args);
+		$admin = V::get('admin', false, $args);
+		$companyAdmin = V::get('companyAdmin', false, $args);
+
+		if ($admin && '1' == V::get('save_offer', '', $_POST)) {
+			$this->saveOffer($idProject, $idCompany, $_POST, $admin);
+		}
+		$defCennik = ProjectKosztorysCennik::getDefaultCennik($idCompany);
+		$cennik = ProjectKosztorysCennik::getCennik($idProject, $idCompany);
+
+		$viewLayerDataArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin');
+		UI::setTitleJsTag("Oferta " . (($idProject)? " [{$idProject}] " : '') . (($idCompany)? " Kontrahent({$idCompany})" : '') . " - Kosztorysy");
+		?>
+<div class="container">
+	<?php if ($admin) : ?>
+		<form action="" method="post">
+	<?php endif; ?>
+	<?php $this->viewLayersData($viewLayerDataArgs); ?>
+	<?php if ($admin || $companyAdmin) : ?>
+		<?php foreach ($cennik as $item) : ?>
+			<input type="hidden" name="edit_price_id_<?php echo $item['id_zasob']; ?>" value="<?php echo $item['ID']; ?>">
+		<?php endforeach; ?>
+			<input type="hidden" name="save_offer" value="1">
+			<hr><input class="btn btn-primary" type="submit" value="Zapisz ofertę">
+		</form>
+	<?php endif; ?>
+</div>
+<?php
+	}
+
+	public function viewLayersData($args) {
+		$idProject = $args['idProject'];
+		$idCompany = V::get('idCompany', 0, $args);
+		$admin = V::get('admin', false, $args);
+		$priceEditJs = V::get('priceEditJs', false, $args);
+		$companyAdmin = V::get('companyAdmin', false, $args);
+		$projCosts = V::get('projCosts', array(), $args);
+		$showPrices = ! V::get('hidePrices', 0, $args);
+
+		$schema = ProjectKosztorysSchema::getSchema();
+		$conf = $schema['config'];
+		DBG::_('DBG', '>1', "conf", $conf, __CLASS__, __FUNCTION__, __LINE__);
+
+		$summaryTypeCost = V::get('sub_costs', array(), $projCosts);
+		$subProjCost = V::get('sub_proj', array(), $projCosts);
+
+		$data = $this->getData($idProject, $admin);
+		$dataSummary = $data['summary'];
+		$dataSubProj = $data['by_project'];
+		// $dataSubProj = array();
+		// foreach ($data['by_project'] as $idSubProj => $subProjData) {
+		// 	if ($idProject != $idSubProj) $dataSubProj[$idSubProj] = $subProjData;
+		// }
+		DBG::_('DBG', '>1', "data", $data, __CLASS__, __FUNCTION__, __LINE__);
+
+		$defCennik = ProjectKosztorysCennik::getDefaultCennik($idCompany);
+		$cennik = ProjectKosztorysCennik::getCennik($idProject, $idCompany);
+		$workCennik = ProjectKosztorysCennik::getWorkCennik($idProject, $idCompany);
+		if (!empty($summaryTypeCost)) {
+			$additionalCosts = array();
+			$additionalCosts[] = array();
+			$additionalCosts[] = array();
+			$additionalCosts[] = array();
+			$additionalSummaryTypeCost = array();
+			$additionalSummaryTypeCost[] = 0;
+			$additionalSummaryTypeCost[] = 0;
+			$additionalSummaryTypeCost[] = 0;
+		}
+?>
+<style type="text/css">
+.type_price-UNDEFINED { color:silver }
+.type_price-UNDEFINED input { color:silver }
+.type_price-GLOBAL { color:red }
+.type_price-GLOBAL input { color:red }
+.type_price-PROJECT { color:green }
+.type_price-PROJECT input { color:green }
+
+/* Print Styles */
+@media print {
+	body { font-size:10px; }
+	th, td { font-size:10px; }
+	h1 { font-size:2em; }
+	h2 { font-size:1.6em; }
+	h3 { font-size:1.4em; }
+	h4 { font-size:1.2em; }
+}
+</style>
+<?php $tblCols = 6 + ($admin ? 1 : 0) + ((!empty($summaryTypeCost))? 1 : 0); ?>
+<table class="tabel table-bordered" style="width:100%; margin-bottom:6px; page-break-inside:avoid">
+	<tbody>
+		<?php foreach ($dataSummary as $idLayer => $layData) : ?>
+			<tr>
+				<td colspan="<?php echo $tblCols; ?>" style="padding:0 6px; font-size:1.2em; line-height:2em; font-style:italic"><?php echo $layData['label']; ?></td>
+			</tr>
+			<tr>
+				<td style="padding:0 6px">typ</td>
+				<td style="width:60px; padding:0 6px; text-align:right">ilość</td>
+				<td style="width:80px; padding:0 6px; text-align:right">jednostka</td>
+				<?php if ($showPrices) : ?>
+					<td style="padding:0 6px; text-align:right">cena jednostkowa</td>
+					<td style="padding:0 6px; text-align:right">cena jedn. (robocizna)</td>
+				<?php endif; ?>
+				<?php if ($admin) : ?>
+					<td style="padding:0 6px; text-align:right">cena jedn. (domyślna)</td>
+				<?php endif; ?>
+				<?php if (!empty($summaryTypeCost)) : ?>
+					<td style="padding:0 6px; text-align:right">szacowany koszt [zł]</td>
+				<?php endif; ?>
+				<td style="padding:0 6px; text-align:right">uwagi</td>
+			</tr>
+			<?php foreach ($layData['data'] as $typeData) : ?>
+				<?php if (!$typeData['idType']) : ?>
+					<tr>
+						<td colspan="4">
+							<?php if ($admin) {
+									$fixLink = 'index.php?MENU_INIT=VIEWTABLE_AJAX&ZASOB_ID=' . $conf['layer'][$idLayer]['tabela_id'];
+									$fixLink .= '&f_' . $conf['layer'][$idLayer]['zasob_field'] . '=%3D' . $typeData['type'];
+									$fixLink .= '&_hash=' . uniqid();
+									$fixProjectLink = '<a target="_blank" href="' . $fixLink . "&f_ID_PROJECT={$idProject}" . '">' . "Popraw" . '</a>' . " (dane dla projektu [{$idProject}])";
+									$fixAllLink = '<a target="_blank" href="' . $fixLink . '">' . "Popraw wszystkie" . '</a>';
+									UI::alert('danger', "Niezdefiniowany typ: <code>{$typeData['type']}</code> - {$fixProjectLink}, {$fixAllLink}");
+								} ?>
+							<?php DBG::_('DBG', '>1', "Error conf", $conf, __CLASS__, __FUNCTION__, __LINE__); ?>
+							<?php DBG::_('DBG', '>1', "Error", $typeData, __CLASS__, __FUNCTION__, __LINE__); ?>
+						</td>
+					</tr>
+				<?php else : ?>
+					<?php $idType = $typeData['idType']; ?>
+					<?php $defPrice = (!empty($defCennik[$idType]['price']))? $defCennik[$idType]['price'] : 0; ?>
+					<?php $price = (!empty($cennik[$idType]['price']))? $cennik[$idType]['price'] : $defPrice; ?>
+					<?php
+						$typePrice = 'UNDEFINED';
+						if (!empty($defCennik[$idType]['price'])) $typePrice = 'GLOBAL';
+						if (!empty($cennik[$idType]['price'])) $typePrice = 'PROJECT';
+					?>
+					<?php $workPrice = (!empty($workCennik[$idType]['price']))? $workCennik[$idType]['price'] : 0; ?>
+					<?php $uwagi = (!empty($cennik[$idType]['notes']))? $cennik[$idType]['notes'] : '';// TODO: uwagi ?>
+					<tr>
+						<td style="padding:0 6px" title="[<?php echo $idType; ?>] <?php echo $typeData['type']; ?>"> - <?php echo $typeData['type']; ?></td>
+						<td style="padding:0 6px; text-align:right"><?php echo $typeData['ilosc']; ?></td>
+						<td style="padding:0 6px; text-align:right"><?php echo ProjectKosztorysSchema::getLayerJednostka($idLayer); ?></td>
+					<?php if ($showPrices) : ?>
+						<td
+							<?php if ($priceEditJs) : ?>
+								<?php
+									$updateProjektyOferta = Request::getPathUri() . "index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&_task=updateProjektyOfertaAjax&idProject={$idProject}&idType={$idType}&unitType=zasob";
+									$onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Kosztorys:updateProjektyOferta', { href: '{$updateProjektyOferta}' })";
+								?>
+								onClick="<?= $onClick; ?>"
+								class="type_price-<?= $typePrice; ?>"
+							<?php endif; ?>
+							style="padding:3px 6px; text-align:right">
+						<?php if ($admin || $companyAdmin) : ?>
+							<input type="text" style="text-align:right" class="form-control input-sm" name="price_<?= $idType; ?>" value="<?= $price; ?>"/>
+						<?php else : ?>
+							<?php echo number_format($price, 2, ',', ' '); ?>
+						<?php endif; ?>
+						</td>
+						<td
+							<?php if ($priceEditJs) : ?>
+								<?php
+									$updateProjektyOferta = Request::getPathUri() . "index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&_task=updateProjektyOfertaAjax&idProject={$idProject}&idType={$idType}&unitType=robocizna";
+									$onClick = "return p5UI__ButtonAjax(this, 'p5UIBtnAjax:Kosztorys:updateProjektyOferta', { href: '{$updateProjektyOferta}' })";
+								?>
+								onClick="<?= $onClick; ?>"
+							<?php endif; ?>
+							style="padding:3px 6px; text-align:right">
+						<?php if ($admin || $companyAdmin) : ?>
+							<input type="text" style="text-align:right" class="form-control input-sm" name="work_price_<?php echo $idType; ?>" value="<?php echo $workPrice; ?>"/>
+						<?php else : ?>
+							<?php echo number_format($workPrice, 2, ',', ' '); ?>
+						<?php endif; ?>
+						</td>
+						<?php if ($admin) : ?>
+							<td style="padding:3px 6px; text-align:right"><?php echo $defPrice; ?></td>
+						<?php endif; ?>
+						<?php if (!empty($summaryTypeCost)) : ?>
+							<td style="padding:3px 6px; text-align:right"><?php echo number_format(V::get($idType, 0, $summaryTypeCost), 2, ',', ' '); ?></td>
+						<?php endif; ?>
+					<?php endif; ?>
+						<td style="padding:3px 6px; text-align:right">
+							<?php if ($admin || $companyAdmin) : ?>
+								<input type="text" style="text-align:left" class="form-control input-sm" name="uwagi_<?php echo $idType; ?>" value="<?php echo $uwagi; ?>"/>
+							<?php else : ?>
+								<?php echo $uwagi; ?>
+							<?php endif; ?>
+						</td>
+					</tr>
+				<?php endif; ?>
+			<?php endforeach; ?>
+		<?php endforeach; ?>
+
+		<?php if (!empty($additionalCosts)) : ?>
+			<tr>
+				<td colspan="<?php echo $tblCols; ?>" style="padding:0 6px; font-size:1.2em; line-height:2em; font-style:italic">Koszty dodatkowe</td>
+			</tr>
+			<tr>
+				<td style="padding:0 6px">typ</td>
+				<td style="width:60px; padding:0 6px; text-align:right">ilość</td>
+				<td style="width:80px; padding:0 6px; text-align:right">jednostka</td>
+				<td style="padding:0 6px; text-align:right">cena jednostkowa</td>
+				<td style="padding:0 6px; text-align:right">cena jedn. (robocizna)</td>
+				<?php if ($admin) : ?>
+					<td style="padding:0 6px; text-align:right">cena jedn. (domyślna)</td>
+				<?php endif; ?>
+				<?php if (!empty($summaryTypeCost)) : ?>
+					<td style="padding:0 6px; text-align:right">szacowany koszt [zł]</td>
+				<?php endif; ?>
+				<td style="padding:0 6px; text-align:right">uwagi</td>
+			</tr>
+			<?php foreach ($additionalCosts as $idType => $additionalCost) : ?>
+				<?php $additionalPrice =  0;//TODO: get from $additionalCennik or $additionalCost ?>
+				<?php $additionalWorkPrice =  0;//TODO: get from $additionalWorkCennik or $additionalCost ?>
+				<?php $additionalCount =  0;//TODO: fetch ?>
+				<?php $additionalUnit =  '';//TODO: fetch ?>
+				<?php $jednostka =  '';//TODO: fetch ?>
+				<?php $ilosc =  '';//TODO: fetch ?>
+				<?php $typeLabel =  '';//TODO: fetch ?>
+				<?php $price =  0;//TODO: fetch ?>
+				<?php $defPrice =  0;//TODO: fetch ?>
+				<?php $workPrice =  0;//TODO: fetch ?>
+				<?php $uwagi =  '';//TODO: fetch ?>
+				<tr>
+					<td style="padding:0 6px" >
+						<?php if ($admin || $companyAdmin) : ?>
+							<input type="text" style="text-align:right" class="form-control input-sm" name="additional_label_<?php echo $idType; ?>" value="<?php echo $typeLabel; ?>"/>
+						<?php else : ?>
+							- <?php echo $typeLabel; ?>
+						<?php endif; ?>
+					</td>
+					<td style="padding:0 6px; text-align:right">
+						<?php if ($admin || $companyAdmin) : ?>
+							<input type="text" style="text-align:right" class="form-control input-sm" name="additional_count_<?php echo $idType; ?>" value="<?php echo $additionalCount; ?>"/>
+						<?php else : ?>
+							<?php echo $ilosc; ?>
+						<?php endif; ?>
+					</td>
+					<td style="padding:0 6px; text-align:right">
+						<?php if ($admin || $companyAdmin) : ?>
+							<input type="text" style="text-align:right" class="form-control input-sm" name="additional_unit_<?php echo $idType; ?>" value="<?php echo $additionalUnit; ?>"/>
+						<?php else : ?>
+							<?php echo $jednostka; ?>
+						<?php endif; ?>
+					</td>
+					<td style="padding:3px 6px; text-align:right">
+					<?php if ($admin || $companyAdmin) : ?>
+						<input type="text" style="text-align:right" class="form-control input-sm" name="additional_price_<?php echo $idType; ?>" value="<?php echo $additionalPrice; ?>"/>
+					<?php else : ?>
+						<?php echo number_format($price, 2, ',', ' '); ?>
+					<?php endif; ?>
+					</td>
+					<td style="padding:3px 6px; text-align:right">
+					<?php if ($admin || $companyAdmin) : ?>
+						<input type="text" style="text-align:right" class="form-control input-sm" name="additional_work_price_<?php echo $idType; ?>" value="<?php echo $additionalWorkPrice; ?>"/>
+					<?php else : ?>
+						<?php echo number_format($workPrice, 2, ',', ' '); ?>
+					<?php endif; ?>
+					</td>
+					<?php if ($admin) : ?>
+						<td style="padding:3px 6px; text-align:right"></td>
+					<?php endif; ?>
+					<?php if (!empty($additionalSummaryTypeCost)) : ?>
+						<td style="padding:3px 6px; text-align:right"><?php echo number_format(V::get($idType, 0, $additionalSummaryTypeCost), 2, ',', ' '); ?></td>
+					<?php endif; ?>
+					<td style="padding:3px 6px; text-align:right">
+						<?php if ($admin || $companyAdmin) : ?>
+							<input type="text" style="text-align:left" class="form-control input-sm" name="additional_uwagi_<?php echo $idType; ?>" value="<?php echo $uwagi; ?>"/>
+						<?php else : ?>
+							<?php echo $uwagi; ?>
+						<?php endif; ?>
+					</td>
+				</tr>
+			<?php endforeach; ?>
+		<?php endif; ?>
+	</tbody>
+</table>
+
+
+<?php $tblCols = 4 + ($admin ? 1 : 0) + ((!empty($summaryTypeCost))? 1 : 0); ?>
+	<?php if (!empty($dataSubProj)) : ?>
+		<p style="page-break-before:always; margin-top:60px"></p>
+		<h3>Składowe wg projektów podrzędnych:</h3>
+		<?php foreach ($dataSubProj as $idSubProj => $subProjData) : ?>
+			<table class="tabel table-bordered" style="width:100%; margin-bottom:6px; page-break-inside:avoid">
+				<tbody>
+					<tr>
+						<th colspan="<?php echo $tblCols; ?>"
+								style="padding:0 6px">
+								<h4>Projekt <?php echo $idSubProj; ?>: <i><?php echo $this->getModel($idProject)->getProjectName($idSubProj); ?></i>:</h4>
+							</th>
+					</tr>
+					<?php foreach ($subProjData as $idLayer => $layData) : ?>
+						<tr>
+							<td colspan="<?php echo $tblCols; ?>"
+									style="padding:0 6px; font-size:1.2em; line-height:2em; font-style:italic"><?php echo $conf['layer'][$idLayer]['label']; ?></td>
+						</tr>
+						<tr>
+							<td style="padding:0 6px">typ</td>
+							<td style="padding:0 6px; text-align:right">ilość</td>
+							<td style="padding:0 6px; text-align:right">jednostka</td>
+							<?php if ($admin) : ?>
+								<td style="padding:0 6px; text-align:right">cena jednostkowa</td>
+								<td style="padding:0 6px; text-align:right">cena jedn. (domyślna)</td>
+							<?php endif; ?>
+							<?php if (!empty($subProjCost)) : ?>
+								<td style="padding:0 6px; text-align:right">szacowany koszt [zł]</td>
+							<?php endif; ?>
+						</tr>
+						<?php foreach ($layData as $idType => $typeData) : ?>
+							<?php if (!$typeData['idType']) : ?>
+								<tr>
+									<td colspan="4">
+										<?php if ($admin) {
+												$fixLink = 'index.php?MENU_INIT=VIEWTABLE_AJAX&ZASOB_ID=' . $conf['layer'][$idLayer]['tabela_id'];
+												$fixLink .= '&f_' . $conf['layer'][$idLayer]['zasob_field'] . '=%3D' . $typeData['type'];
+												$fixLink .= '&_hash=' . uniqid();
+												$fixProjectLink = '<a target="_blank" href="' . $fixLink . "&f_ID_PROJECT={$idProject}" . '">' . "Popraw" . '</a>' . " (dane dla projektu [{$idProject}])";
+												$fixAllLink = '<a target="_blank" href="' . $fixLink . '">' . "Popraw wszystkie" . '</a>';
+												UI::alert('danger', "Niezdefiniowany typ: <code>{$typeData['type']}</code> - {$fixProjectLink}, {$fixAllLink}");
+											} ?>
+										<?php DBG::_('DBG', '>1', "Error conf", $conf, __CLASS__, __FUNCTION__, __LINE__); ?>
+										<?php DBG::_('DBG', '>1', "Error", $typeData, __CLASS__, __FUNCTION__, __LINE__); ?>
+									</td>
+								</tr>
+							<?php else : ?>
+								<?php $defPrice = (!empty($defCennik[$typeData['idType']]['price']))? $defCennik[$typeData['idType']]['price'] : 0; ?>
+								<?php $price = (!empty($cennik[$typeData['idType']]['price']))? $cennik[$typeData['idType']]['price'] : $defPrice; ?>
+								<tr>
+									<td style="padding:0 6px" title="[<?php echo $typeData['idType']; ?>] <?php echo $typeData['type']; ?>"><?php echo $typeData['type']; ?></td>
+									<td style="padding:0 6px; text-align:right"><?php echo $typeData['ilosc']; ?></td>
+									<td style="padding:0 6px; text-align:right"><?php echo ProjectKosztorysSchema::getLayerJednostka($idLayer); ?></td>
+									<?php if ($admin) : ?>
+										<td style="padding:3px 6px; text-align:right">
+											<input type="text" style="text-align:right" class="form-control input-sm" name="price_<?php echo $typeData['idType']; ?>" value="<?php echo $price; ?>"/>
+										</td>
+									<?php endif; ?>
+									<?php if ($admin) : ?>
+										<td style="padding:3px 6px; text-align:right"><?php echo $defPrice; ?></td>
+									<?php endif; ?>
+									<?php if (!empty($subProjCost)) : ?>
+										<td style="padding:3px 6px; text-align:right"><?php echo number_format(V::get($typeData['idType'], 0, $subProjCost[$idSubProj]), 2, ',', ' '); ?></td>
+									<?php endif; ?>
+								</tr>
+							<?php endif; ?>
+						<?php endforeach; ?>
+					<?php endforeach; ?>
+				<?php endforeach; ?>
+			</tbody>
+		</table>
+	<?php endif; ?>
+	<link rel="stylesheet" type="text/css" href="static/sweetalert2.min.css">
+<script src="static/sweetalert2.min.js"></script>
+<script>
+(function(){
+	var _updateProjektyOfertaSaveLink = '<?= Request::getPathUri(); ?>index.php?_route=UrlAction_ProjektyKosztyWstepnychRobot&_task=updateProjektyOfertaAjax&idProject=<?= $idProject; ?>';
+
+	jQuery(document).on('p5UIBtnAjax:Kosztorys:updateProjektyOferta:click', function(e, n, payload) {
+		<?php if (DBG::isActive()) : ?>console.log('event p5UIBtnAjax:Kosztorys:updateProjektyOferta:click', n, payload);<?php endif; ?>
+	});
+
+	jQuery(document).on('p5UIBtnAjax:Kosztorys:updateProjektyOferta:ajaxLoaded', function(e, n, payload) {
+		<?php if (DBG::isActive()) : ?>console.log('event p5UIBtnAjax:Kosztorys:updateProjektyOferta:ajaxLoaded', n, payload);<?php endif; ?>
+		if ('success' != payload.type) {
+			jQuery.notify(payload.msg, payload.type);
+			return;
+		}
+		if (payload.body && payload.body.id > 0) {
+			jQuery(n).text(p5Utils__pricePrintPL(payload.body.price));
+		}
+		var id = payload.body.id;
+		var price = payload.body.price;
+		var unitType = payload.body.unitType;
+		var label = payload.body.label || '['+id+']';
+		if ('robocizna' != unitType) label += '<br><i style="color:#999">(cena domyślna: ' + p5Utils__pricePrintPL(payload.body.defaultPrice) + ')</i>';
+		swal({
+			title: 'Cena ' + (('robocizna' == unitType) ? 'robocizny' : 'zasobu') + ' [' + id + ']:',
+			html: label,
+			animation: false,
+			input: 'text',
+			inputPlaceholder: '0,00',
+			inputValue: p5Utils__pricePrintPL(price),
+			// inputAttributes: {'step': '0.01'},
+			showCancelButton: true,
+			confirmButtonText: 'Zapisz',
+			showLoaderOnConfirm: true,
+			preConfirm: Kosztorys__saveFormUpdateProjectOferta(id, unitType),
+			allowOutsideClick: false
+		}).then(function(responseBody) {
+			<?php if (DBG::isActive()) : ?>console.log('event p5UIBtnAjax:Kosztorys:savedFormUpdateProjectOferta:ajaxLoaded', responseBody);<?php endif; ?>
+			if ('success' != responseBody.type) {
+				jQuery.notify(responseBody.msg || 'Wystąpiły błędy podczas aktualizacji ceny dla ['+id+']', 'error')
+				return;
+			}
+			jQuery.notify(responseBody.msg || 'Zaktualizowano cenę za ['+id+']', 'success')
+			jQuery.notify('Odśwież stronę żeby zobaczyć zmiany', 'info')
+			jQuery(n).removeClass('type_price-UNDEFINED')
+			jQuery(n).removeClass('type_price-GLOBAL')
+			jQuery(n).addClass('type_price-PROJECT')
+			jQuery(n).text(p5Utils__pricePrintPL(responseBody.price))
+			// TODO: update dom price
+			// if (responseBody.update_data) {
+			// 	budget__renderCosts(responseBody.update_data['costs']);
+			// }
+		}).catch(function(e) {// eg. hit Cancel
+		})
+	});
+
+	function Kosztorys__saveFormUpdateProjectOferta(idType, type) {
+		var idType = idType, type = type;// TODO: zasob / robocizna
+		return function(price) {
+			return new Promise(function(resolve, reject) {
+				price = p5Utils__parseFloatOrZero(price)
+				if (price < 0) {
+					reject('Kwota musi być nie mniejsza od zera.')
+				} else {
+					superagent
+						.post(_updateProjektyOfertaSaveLink + '&idType=' + idType + '&unitType=' + type)
+						.type('json') // header ĺapplication/x-www-form-urlencoded' requires type('form');
+						.send({
+							price: price
+						})
+						.set('Accept', 'application/json')
+						.end(function(err, res) {
+							<?php if (DBG::isActive()) : ?>console.log('#widget::Kosztorys/saveFormUpdateProjectOferta: res:', res, 'res.body:', res.body);<?php endif; ?>
+							if (err || !res.ok || 'application/json' !== res.type) reject("Request error")
+							if (!res.body.id || res.body.id <= 0) reject(res.body.msg || "Wystąpiły błędy podczas dodawaniu kosztu")
+							resolve(res.body)
+						})
+				}
+			})
+		}
+	}
+
+})();
+</script>
+<?php
+	}
+
+	public function updateProjektyOfertaAjaxAction() {
+		$args = array();
+		$args['idProject'] = V::get('idProject', 0, $_GET, 'int');
+		$args['idType'] = V::get('idType', 0, $_GET, 'int');
+		$args['unitType'] = V::get('unitType', '', $_GET, 'word');
+		Response::sendTryCatchJson(array($this, 'updateProjektyOfertaAjax'), $args);
+	}
+	public function updateProjektyOfertaAjax($args) {
+		$idProject = V::get('idProject', 0, $args, 'int');
+		$idType = V::get('idType', 0, $args, 'int');
+		$unitType = V::get('unitType', '', $args, 'word');
+		if (empty($idProject) || $idProject <= 0) throw new Exception("Wrong param idProject");
+		if (empty($idType) || $idType <= 0) throw new Exception("Wrong param idType");
+		if (empty($unitType) || !in_array($unitType, array('zasob', 'robocizna'))) throw new Exception("Wrong param unitType");
+		$response = array();
+		if (DBG::isActive()) $response['_idProject'] = $idProject;
+		if (DBG::isActive()) $response['_idType'] = $idType;
+		$jednostka = '';
+		if ('robocizna' == $unitType) $jednostka = 'ROBOCIZNA';
+		else if ('zasob' == $unitType) {
+			$schema = ProjectKosztorysSchema::getSchema();
+			foreach ($schema['config']['layer'] as $idLayer => $layData) {
+				if (array_key_exists($idType, $layData['type'])) $jednostka = $layData['jednostka'];
+			}
+		}
+		if (DBG::isActive()) $response['_unit'] = $jednostka;
+
+		$reqJson = Request::getRequestJson();
+		if (!empty($reqJson)) {
+			if (!array_key_exists('price', $reqJson)) throw new Exception("Missing param price");
+			$price = V::get('price', 0, $reqJson, 'float');
+			ProjectKosztorysCennik::updatePriceProjectCennik($idType, $idProject, $price, $jednostka);
+		}
+		$response['id'] = $idType;
+		$response['unitType'] = $unitType;
+		$response['label'] = ProjectKosztorysCennik::getTypeLabel($idType);
+		if ('robocizna' != $unitType) $response['defaultPrice'] = ProjectKosztorysCennik::getPriceDefaultCennik($idType);
+		$response['price'] = ProjectKosztorysCennik::getPrice($idType, $idProject, $idCompany = 0, $jednostka);
+		$response['msg'] = "";
+		$response['type'] = "success";
+		return $response;
+	}
+
+	public function checkGeomDuplicate() {
+		// TODO: for every schema.layer
+		$sqlTblName = 'Rozdzielcza_Kabel_Swiatlowodowy_wsg84';
+		$sql = "select a.ID, b.ID, aswkt(a.the_geom)
+			from `{$sqlTblName}` a
+				join `$sqlTblName` b on(b.the_geom = a.the_geom and a.ID != b.ID)
+		";
+	}
+
+	public function getData($idProject, $admin = 0) {
+		static $_data = null;
+		if (null === $_data) {
+			$idSubProject = $this->getModel($idProject)->getSubProjectIds();
+			$idSubProject[] = $idProject;
+			$_data = $this->fetchData($idSubProject, $admin);
+		}
+		//if (!array_key_exists($idProject, $_data)) $_data[$idProject] = $this->fetchData($idProject, $admin);
+		//return $_data[$idProject];
+		return $_data;
+	}
+
+	public function fetchData($idProject, $admin = 0) {
+		$data = array();
+		$data['summary'] = array();// [ $idType ] = ['type', 'jednostka', 'ilosc']
+		$data['rawDataByType'] = array();
+		if (empty($idProject)) return $data;
+		$schema = ProjectKosztorysSchema::getSchema();// OK only 'config'
+		$conf = $schema['config'];
+		$pdo = DB::getPDO();
+		foreach ($conf['layer'] as $idLayer => $layer) {
+			if (!$layer['tabela_name']) continue;// TODO: throw error in validate
+			if ('ZASOB' == $layer['zasob_type']) {
+			} else if ('ZASOB_ID' == $layer['zasob_type']) {
+			} else {
+				continue;// TODO: validate config error
+			}
+
+			$tblName = $layer['tabela_name'];
+			$sqlIloscField = (!empty($layer['ilosc_field']))? $layer['ilosc_field'] : 'ID';
+			$sqlIlosc = ('SZTUKA' == $layer['jednostka'])? "count(1)" : "sum(t.{$sqlIloscField})";
+			$sqlZasobField = (!empty($layer['zasob_field']))? $layer['zasob_field'] : 'ID';
+			$sqlGroupBy = "group by t.ID_PROJECT" . ((!empty($layer['zasob_field']))? ", t.{$sqlZasobField}" : '');
+			$sqlIdProject = (is_array($idProject))? implode(",", $idProject) : (int)$idProject;
+			$sql = "
+				select t.{$sqlZasobField}
+						, t.ID_PROJECT
+						, {$sqlIlosc} as ilosc
+				from {$tblName} t
+				where t.ID_PROJECT in({$sqlIdProject})
+					and t.the_geom is not null
+				{$sqlGroupBy}
+			";
+			$data['_DBG_sql'][$idLayer] = $sql;
+			$rawLayData = $pdo->fetchAll($sql);
+			$rawLayData = array_filter($rawLayData, function($row) {
+				return ($row['ilosc'] > 0);
+			});
+			foreach ($rawLayData as $idx => &$row) {
+				if ('ZASOB' == $layer['zasob_type']) {
+					$row['type'] = trim($row[$sqlZasobField]);
+					$row['idType'] = array_search($row['type'], $conf['type']);
+				} else if ('ZASOB_ID' == $layer['zasob_type']) {
+					$row['type'] = $layer['zasob_label'];
+					$row['idType'] = (array_key_exists($layer['zasob_id'], $conf['type']))? $layer['zasob_id'] : null;
+				}
+				$row['idLayer'] = $idLayer;
+				unset($row[$sqlZasobField]);
+				$data['rawDataByType'][] = $row;
+			}
+		}
+		//DBG::_(true, true, "data", $data, __CLASS__, __FUNCTION__, __LINE__);
+		//DBG::table("data['rawDataByType']", $data['rawDataByType'], __CLASS__, __FUNCTION__, __LINE__);
+		if ($admin) {
+			foreach ($data['rawDataByType'] as $row) {
+				$layer = $conf['layer'][$row['idLayer']];
+				if ($row['idType'] <= 0) {
+					UI::alert('warning', "Pomijanie nieznanego oznaczenia '{$row['type']}' w ilości {$row['ilosc']} - warstwa '{$layer['label']}' ({$layer['tabela_name']}), projekt nr {$row['ID_PROJECT']}");
+				}
+			}
+		}
+		$data['rawDataByType'] = array_filter($data['rawDataByType'], function($row) {
+			return ($row['idType'] > 0);
+		});
+		//DBG::table("data['rawDataByType'] - clean", $data['rawDataByType'], __CLASS__, __FUNCTION__, __LINE__);
+		$data['by_layer'] = array_reduce($data['rawDataByType'], function($result, $row) {
+			$idLayer = $row['idLayer'];
+			$idType = $row['idType'];
+			if (!array_key_exists($idLayer, $result)) $result[$idLayer] = array();
+			if (!array_key_exists($idType, $result[$idLayer])) {
+				$result[$idLayer][$idType] = $row;
+			} else {
+				$result[$idLayer][$idType]['ilosc'] += $row['ilosc'];
+			}
+			return $result;
+		}, array());
+		//DBG::_(true, true, "data['by_layer']", $data['by_layer'], __CLASS__, __FUNCTION__, __LINE__);
+		$data['summary'] = array();
+		foreach ($data['by_layer'] as $idLayer => $iloscByType) {
+			$layData = array();
+			$layData['label'] = $conf['layer'][$idLayer]['label'];
+			$layData['data'] = $iloscByType;
+			foreach ($layData['data'] as &$iloscData) {
+				$iloscData['jednostka'] = $conf['layer'][$idLayer]['jednostka'];
+			}
+			$data['summary'][$idLayer] = $layData;
+		}
+		//DBG::_(true, true, "data['summary']", $data['summary'], __CLASS__, __FUNCTION__, __LINE__);
+
+		$data['by_project'] = array_reduce($data['rawDataByType'], function($result, $row) {
+			$idProj = $row['ID_PROJECT'];
+			$idLayer = $row['idLayer'];
+			$idType = $row['idType'];
+			if (!array_key_exists($idProj, $result)) $result[$idProj] = array();
+			if (!array_key_exists($idLayer, $result[$idProj])) $result[$idProj][$idLayer] = array();
+			if (!array_key_exists($idType, $result[$idProj][$idLayer])) {
+				$result[$idProj][$idLayer][$idType] = $row;
+			} else {
+				$result[$idProj][$idLayer][$idType]['ilosc'] += $row['ilosc'];
+			}
+			return $result;
+		}, array());
+		//DBG::_(true, true, "data['by_project']", $data['by_project'], __CLASS__, __FUNCTION__, __LINE__);
+		return $data;
+	}
+
+}

+ 328 - 0
SE/se-lib/Route/UrlAction/ProjektyZamowieniaKosztorys.php

@@ -0,0 +1,328 @@
+<?php
+
+Lib::loadClass('RouteBase');
+Lib::loadClass('ProjectKosztorysSchema');
+Lib::loadClass('ProjectKosztorysModel');
+Lib::loadClass('ProjectKosztorysCennik');
+Lib::loadClass('UI');
+Lib::loadClass('Response');
+Lib::loadClass('Route_UrlAction_ProjektyKosztorysBase');
+
+class Route_UrlAction_ProjektyZamowieniaKosztorys extends Route_UrlAction_ProjektyKosztorysBase {// TODO: UrlActionBase @see Route_UrlAction
+
+	public $_model = array();
+	public $_ID_ZASOB_ZAMOWIENIE = 24121;
+
+	public function defaultAction() {
+		// TODO: check if user is allowed to run this action
+		UI::gora();
+		if (1 != V::get('_print', '', $_GET)) UI::menu();
+		try {
+			$idProject = V::get('ID_PROJECT', 0, $_REQUEST, 'int');
+			if (!$idProject) throw new Exception("Wrong param in 'ID_PROJECT' - expected integer!");
+			$this->panel($idProject);
+			$this->zamowienia($idProject);
+		} catch (Exception $e) {
+			UI::alert('danger', "Error #" . $e->getCode() .  "|" . $e->getLine() .  ": " . $e->getMessage());
+		}
+		UI::dol();
+	}
+
+	public function zamowienia($idProject) {
+		$idProject = (int)$idProject;
+		if ($idProject <= 0) throw new Exception("Error Processing Request - wrong param id project");
+		$idOrder = V::get('ID_ORDER', 0, $_REQUEST, 'int');
+
+		$ordersRaw = DB::getPDO()->fetchAllByKey("
+			select k.ID
+						, k.A_STATUS
+						, k.K_DATA_OTRZYM_KORESP
+						, k.K_DATA_OTRZYMANEJ_KORESP
+						, k.K_OD_KOGO
+						, k.OD_KOGO_ADRES
+						, k.K_ZAWARTOS
+			from IN7_DZIENNIK_KORESP k
+			where k.CRM_LISTA_ZASOBOW_ID = {$this->_ID_ZASOB_ZAMOWIENIE}
+				and k.ID_PROJECT = {$idProject}
+				and k.K_TYP_KORESP = 'OUT'
+		", $key = 'ID');
+		$orders = array();
+		foreach ($ordersRaw as $r) {
+			$item = array();
+			$chkd = ($r['ID'] == $idOrder) ? ' checked="checked"' : '';
+			$item['ID'] = '<label><input type="radio" onClick="this.form.submit()" name="ID_ORDER" value="'.$r['ID'].'" '.$chkd.'> '.$r['ID'].'</label>';
+			$item['A_STATUS'] = $r['A_STATUS'];
+			$item['K_DATA_OTRZYM_KORESP'] = $r['K_DATA_OTRZYM_KORESP'];
+			$item['K_DATA_OTRZYMANEJ_KORESP'] = $r['K_DATA_OTRZYMANEJ_KORESP'];
+			$item['K_OD_KOGO'] = $r['K_OD_KOGO'];
+			$item['OD_KOGO_ADRES'] = $r['OD_KOGO_ADRES'];
+			$item['K_ZAWARTOS'] = $r['K_ZAWARTOS'];
+			$orders[] = $item;
+		}
+		$order = ($idOrder > 0 && array_key_exists($idOrder, $ordersRaw)) ? $ordersRaw[$idOrder] : null;
+
+		UI::startContainer();
+		if (1 != V::get('_print', '', $_GET)) {
+			UI::startTag('form', ['action'=>'', 'method'=>'get']);
+				UI::tag('input', ['type'=>'hidden', 'name'=>'_route', 'value'=>'UrlAction_ProjektyZamowieniaKosztorys']);
+				UI::tag('input', ['type'=>'hidden', 'name'=>'ID_PROJECT', 'value'=>$idProject]);
+				UI::table(['caption'=>"Wybierz zamówienie", 'rows'=>$orders]);
+			UI::endTag('form');
+			UI::tag('hr');
+			if (null != $order)  {
+				$printLink = Request::getPathUri() . "index.php?_route=UrlAction_ProjektyZamowieniaKosztorys";
+				$printLink .= "&ID_PROJECT={$idProject}";
+				$printLink .= "&ID_ORDER={$order['ID']}";
+				$printLink .= "&_print=1";
+				UI::startTag('div', ['style'=>'text-align:right']);
+					UI::tag('a', ['_target'=>'blank', 'href'=>$printLink], '<i class="glyphicon glyphicon-print"></i>'. "Drukuj zamówienie");
+				UI::endTag('div');
+			}
+		}
+
+		if (null !== $order) {
+			$schema = ProjectKosztorysSchema::getSchema();// OK only 'config'
+			$model = $this->getModel($idProject);
+			$data = $this->getData($idProject, $order['ID']);
+
+			{
+				$idCompany = 0;
+				$admin = false;
+				$companyAdmin = false;
+				$priceEditJs = (1 != V::get('_print', '', $_GET));
+				$model = $this->getModel($idProject);
+				//DBG::table("subProjectList", $model->subProjectList, __CLASS__, __FUNCTION__, __LINE__);
+				$schema = ProjectKosztorysSchema::getSchema();
+
+				$projCosts = $this->getProjectCostByCennik($idProject, $idCompany);
+				//DBG::_(true, true, "projCosts", $projCosts, __CLASS__, __FUNCTION__, __LINE__);
+				$idCompany = null;
+				$admin = null;
+				$companyAdmin = null;
+				$projCosts = null;
+				$priceEditJs = null;
+				$hidePrices = true;
+				$viewLayerDataArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin', 'projCosts', 'priceEditJs', 'hidePrices');
+				UI::startTag('h1', ['style'=>'margin-bottom:1em']);
+					echo "Zamówienie nr {$order['ID']}";
+					UI::tag('small', ['style'=>"display:block"], $order['K_ZAWARTOS']);
+				UI::endTag('h1');
+				UI::setTitleJsTag("Zamówienie nr {$order['ID']} (Kosztorys projektu [{$idProject}])");
+
+				UI::startTag('table', ['class'=>'table']);
+					UI::startTag('tr');
+						UI::tag('th', null, "Nr zamówienia");
+						UI::tag('th', null, $schema['nr']);
+						UI::tag('th', null, $schema['title']);
+						UI::tag('th', null, $schema['ownerName']);
+						// UI::tag('th', ['style'=>'text-align:right'], $schema['cost_total']);
+					UI::endTag('tr');
+					UI::startTag('tr');
+						UI::tag('td', null, $order['ID']);
+						UI::tag('td', null, $model->idProject);
+						UI::tag('td', null, $model->title);
+						UI::tag('td', null, $model->ownerName);
+						// UI::tag('td', ['style'=>'text-align:right'], number_format($projCosts['cost_total'], 2, ',', ' '));
+					UI::endTag('tr');
+				UI::endTag('table');
+				$this->viewLayersData($viewLayerDataArgs);
+			}
+
+			if (DBG::isActive()) DBG::nicePrint($data, '$data');
+			// if (DBG::isActive()) DBG::nicePrint($schema, '$schema');
+			// if (DBG::isActive()) DBG::nicePrint($model, '$model');
+		}
+
+		if (DBG::isActive()) UI::tag('hr');
+		if (DBG::isActive()) $this->checkDBData();
+		UI::endContainer();
+	}
+
+	public function checkDBData() {
+		$schema = ProjectKosztorysSchema::getSchema();// OK only 'config'
+		foreach ($schema['config']['layer'] as $idLayer => $layer) {
+			// <div class="panel panel-default">
+			//   <div class="panel-heading">Panel heading without title</div>
+			//   <div class="panel-body">
+			//     Panel content
+			//   </div>
+			// </div>
+
+			UI::startTag('div', ['class'=>'panel panel-default']);
+				UI::startTag('div', ['class'=>'panel-heading']);
+					echo "Warstwa: {$layer['label']}";
+				UI::endTag('div');// .panel-heading
+				UI::startTag('div', ['class'=>'panel-body']);
+					try {
+						$rowsByIdOrder = DB::getPDO()->fetchAllByKey("
+							select g.ID_KORESP_ZLECENIE, g.cnt
+							--		, k.ID
+									, k.A_STATUS
+									, k.K_DATA_OTRZYM_KORESP
+									, k.K_DATA_OTRZYMANEJ_KORESP
+									, k.K_OD_KOGO
+									, k.OD_KOGO_ADRES
+									, k.K_ZAWARTOS
+									, k.CRM_LISTA_ZASOBOW_ID
+									, k.ID_PROJECT
+									, k.K_TYP_KORESP
+							from (
+								select t.ID_KORESP_ZLECENIE, count(1) as cnt
+								from {$layer['tabela_name']} t
+								where t.ID_KORESP_ZLECENIE is not null and t.ID_KORESP_ZLECENIE > 0
+								group by t.ID_KORESP_ZLECENIE
+							) g
+								left join IN7_DZIENNIK_KORESP k on(k.ID = g.ID_KORESP_ZLECENIE)
+						", $key = 'ID_KORESP_ZLECENIE');
+					} catch (Exception $e) {
+						if ("SQLSTATE[42S22]: Column not found: 1054 Unknown column 't.ID_KORESP_ZLECENIE' in 'field list'" == $e->getMessage()) {
+							UI::alert('danger', "Brak komórki `ID_KORESP_ZLECENIE` w tabeli `{$layer['tabela_name']}`");
+						} else UI::alert('danger', $e->getMessage());
+					}
+					if (empty($rowsByIdOrder)) UI::tag('span', ['class'=>'text-warning'], "Brak wypełnionego pola ID_KORESP_ZLECENIE w tej warstwie");
+					else {
+						$rowsByIdOrder = array_map(function($r) {
+							$item = [];
+							$item['id'] = $r['ID_KORESP_ZLECENIE'];
+							$item['ile'] = $r['cnt'];
+							$item['K_OD_KOGO'] = $r['K_OD_KOGO'];
+							$item['OD_KOGO_ADRES'] = $r['OD_KOGO_ADRES'];
+							$item['K_ZAWARTOS'] = $r['K_ZAWARTOS'];
+							$item['Zasób?'] = (int)$r['CRM_LISTA_ZASOBOW_ID'];
+							$item['id Proj.'] = (int)$r['ID_PROJECT'];
+							$item['typ'] = $r['K_TYP_KORESP'];
+							$item['daty'] = "{$r['K_DATA_OTRZYM_KORESP']}<br>{$r['K_DATA_OTRZYMANEJ_KORESP']}";
+							if ($item['Zasób?'] <= 0) $item['Zasób?'] .= '<br><span class="label label-danger" title="Brak numeru zasobu - powinien być 24121">Brak</span>';
+							else if (24121 != $item['Zasób?']) $item['Zasób?'] .= '<br><span class="label label-danger" title="Błędny numer zasobu - powinien być 24121">Błąd</span>';
+							if ($item['id Proj.'] <= 0) $item['id Proj.'] .= '<br><span class="label label-danger" title="Brak id projektu">Brak</span>';
+							if ('OUT' != $item['typ']) $item['typ'] .= '<br><span class="label label-danger" title="Błędny typ korespondencji - powinien być OUT">Błąd</span>';
+							return $item;
+						}, $rowsByIdOrder);
+						UI::table(['caption'=>"Pole ID_KORESP_ZLECENIE", 'rows'=>$rowsByIdOrder]);
+					}
+				UI::endTag('div');// .panel-body
+			UI::endTag('div');
+		}
+	}
+
+	public function getData($idProject, $idOrder, $admin = 0) {
+		static $_data = null;
+		if (null === $_data) {
+			$idSubProject = $this->getModel($idProject)->getSubProjectIds();
+			$idSubProject[] = $idProject;
+			$_data = $this->fetchData($idSubProject, $admin);
+		}
+		return $_data;
+	}
+
+	public function fetchData($idProject, $idOrder, $admin = 0) {
+		$data = array();
+		$data['summary'] = array();// [ $idType ] = ['type', 'jednostka', 'ilosc']
+		$data['rawDataByType'] = array();
+		if (empty($idProject)) return $data;
+		$schema = ProjectKosztorysSchema::getSchema();// OK only 'config'
+		$conf = $schema['config'];
+		foreach ($conf['layer'] as $idLayer => $layer) {
+			if (!$layer['tabela_name']) continue;// TODO: throw error in validate
+			if ('ZASOB' == $layer['zasob_type']) {
+			} else if ('ZASOB_ID' == $layer['zasob_type']) {
+			} else {
+				continue;// TODO: validate config error
+			}
+
+			$tblName = $layer['tabela_name'];
+			$sqlIloscField = (!empty($layer['ilosc_field']))? $layer['ilosc_field'] : 'ID';
+			$sqlIlosc = ('SZTUKA' == $layer['jednostka'])? "count(1)" : "sum(t.{$sqlIloscField})";
+			$sqlZasobField = (!empty($layer['zasob_field']))? $layer['zasob_field'] : 'ID';
+			$sqlGroupBy = "group by t.ID_PROJECT" . ((!empty($layer['zasob_field']))? ", t.{$sqlZasobField}" : '');
+			$sqlIdProject = (is_array($idProject))? implode(",", $idProject) : (int)$idProject;
+			$sql = "
+				select t.{$sqlZasobField}
+						, t.ID_PROJECT
+						, {$sqlIlosc} as ilosc
+				from {$tblName} t
+				where t.ID_PROJECT in({$sqlIdProject})
+					and t.the_geom is not null
+				-- WHERE
+				{$sqlGroupBy}
+			";
+			$data['_DBG_sql'][$idLayer] = $sql;
+			try {
+				$sqlWithOrder = str_replace("-- WHERE", "and t.ID_KORESP_ZLECENIE = {$idOrder}", $sql);
+				$rawLayData = DB::getPDO()->fetchAll($sqlWithOrder);
+			} catch (Exception $e) {
+				if (1 != V::get('_print', '', $_GET)) UI::alert('danger', "Brak pola `ID_KORESP_ZLECENIE` w tabeli `$tblName`");
+				$rawLayData = DB::getPDO()->fetchAll($sql);
+			}
+			$rawLayData = array_filter($rawLayData, function($row) {
+				return ($row['ilosc'] > 0);
+			});
+			foreach ($rawLayData as $idx => &$row) {
+				if ('ZASOB' == $layer['zasob_type']) {
+					$row['type'] = trim($row[$sqlZasobField]);
+					$row['idType'] = array_search($row['type'], $conf['type']);
+				} else if ('ZASOB_ID' == $layer['zasob_type']) {
+					$row['type'] = $layer['zasob_label'];
+					$row['idType'] = (array_key_exists($layer['zasob_id'], $conf['type']))? $layer['zasob_id'] : null;
+				}
+				$row['idLayer'] = $idLayer;
+				unset($row[$sqlZasobField]);
+				$data['rawDataByType'][] = $row;
+			}
+		}
+		//DBG::_(true, true, "data", $data, __CLASS__, __FUNCTION__, __LINE__);
+		//DBG::table("data['rawDataByType']", $data['rawDataByType'], __CLASS__, __FUNCTION__, __LINE__);
+		if ($admin) {
+			foreach ($data['rawDataByType'] as $row) {
+				$layer = $conf['layer'][$row['idLayer']];
+				if ($row['idType'] <= 0) {
+					UI::alert('warning', "Pomijanie nieznanego oznaczenia '{$row['type']}' w ilości {$row['ilosc']} - warstwa '{$layer['label']}' ({$layer['tabela_name']}), projekt nr {$row['ID_PROJECT']}");
+				}
+			}
+		}
+		$data['rawDataByType'] = array_filter($data['rawDataByType'], function($row) {
+			return ($row['idType'] > 0);
+		});
+		//DBG::table("data['rawDataByType'] - clean", $data['rawDataByType'], __CLASS__, __FUNCTION__, __LINE__);
+		$data['by_layer'] = array_reduce($data['rawDataByType'], function($result, $row) {
+			$idLayer = $row['idLayer'];
+			$idType = $row['idType'];
+			if (!array_key_exists($idLayer, $result)) $result[$idLayer] = array();
+			if (!array_key_exists($idType, $result[$idLayer])) {
+				$result[$idLayer][$idType] = $row;
+			} else {
+				$result[$idLayer][$idType]['ilosc'] += $row['ilosc'];
+			}
+			return $result;
+		}, array());
+		//DBG::_(true, true, "data['by_layer']", $data['by_layer'], __CLASS__, __FUNCTION__, __LINE__);
+		$data['summary'] = array();
+		foreach ($data['by_layer'] as $idLayer => $iloscByType) {
+			$layData = array();
+			$layData['label'] = $conf['layer'][$idLayer]['label'];
+			$layData['data'] = $iloscByType;
+			foreach ($layData['data'] as &$iloscData) {
+				$iloscData['jednostka'] = $conf['layer'][$idLayer]['jednostka'];
+			}
+			$data['summary'][$idLayer] = $layData;
+		}
+		//DBG::_(true, true, "data['summary']", $data['summary'], __CLASS__, __FUNCTION__, __LINE__);
+
+		$data['by_project'] = array_reduce($data['rawDataByType'], function($result, $row) {
+			$idProj = $row['ID_PROJECT'];
+			$idLayer = $row['idLayer'];
+			$idType = $row['idType'];
+			if (!array_key_exists($idProj, $result)) $result[$idProj] = array();
+			if (!array_key_exists($idLayer, $result[$idProj])) $result[$idProj][$idLayer] = array();
+			if (!array_key_exists($idType, $result[$idProj][$idLayer])) {
+				$result[$idProj][$idLayer][$idType] = $row;
+			} else {
+				$result[$idProj][$idLayer][$idType]['ilosc'] += $row['ilosc'];
+			}
+			return $result;
+		}, array());
+		//DBG::_(true, true, "data['by_project']", $data['by_project'], __CLASS__, __FUNCTION__, __LINE__);
+		return $data;
+	}
+
+}

+ 21 - 3
SE/se-lib/UI.php

@@ -92,7 +92,12 @@ class UI {
 		}
 	}
 
-	public static function alert($alertType, $msg) {
+	public static function alert($alertType, $msg, $outputHtml = true) {
+		if (!$outputHtml) {
+			$type = ('danger' == $alertType) ? "ERROR" : strtoupper($alertType);
+			echo "{$type}: {$msg}\n";
+			return;
+		}
 ?>
 <div class="alert alert-<?php echo $alertType; ?>">
 	<?php echo $msg; ?>
@@ -165,14 +170,27 @@ class UI {
 <?php
 	}
 
-	public static function startContainer() { echo '<div class="container">' . "\n"; }
+	public static function startContainer($attrs = array()) {
+		// echo '<div class="container">' . "\n";
+		if (!empty($attrs['class'])) $attrs['class'] .= ' container';
+		$attrs['class'] = ' container';
+		self::startTag('div', $attrs);
+	}
 	public static function endContainer() { echo '</div>' . "\n"; }
 	public static function startTag($tag, $attrs = array()) {
 		$outAttrs = '';
-		foreach ($attrs as $attrName => $val) $outAttrs .= " {$attrName}=\"{$val}\"";
+		if (is_array($attrs)) {
+			foreach ($attrs as $attrName => $val) $outAttrs .= " {$attrName}=\"{$val}\"";
+		}
 		echo '<' . $tag . $outAttrs . '>';
 	}
 	public static function endTag($tag) { echo '</' . $tag . '>'; }
+	public static function tag($tag, $attrs = array(), $childrens = array()) {
+		self::startTag($tag, $attrs);
+		if (!empty($childrens) && is_array($childrens)) throw new Exception("UI::tag() children as nodes not implemented".json_encode($childrens));
+		if (is_scalar($childrens)) echo $childrens;
+		self::endTag($tag);
+	}
 
 	public static function jsAjaxTable($params) {