浏览代码

added js live update prices in Kosztorys

Piotr Labudda 9 年之前
父节点
当前提交
761d8561f4

+ 110 - 0
SE/se-lib/ProjectKosztorysCennik.php

@@ -27,6 +27,7 @@ class ProjectKosztorysCennik {
         and o.ID_PROJECT = :id_project
         and o.COMPANIES_ID = :id_company
         and o.RESOURCE_UNIT_TYPE != 'ROBOCIZNA'
+        and o.OFFER_PRICE_PER_RESOURCE_UNIT > 0
     ");
     $sth->bindValue(':id_project', $idProject, PDO::PARAM_INT);
     $sth->bindValue(':id_company', $idCompany, PDO::PARAM_INT);
@@ -64,6 +65,7 @@ class ProjectKosztorysCennik {
         and o.ID_PROJECT = :id_project
         and o.COMPANIES_ID = :id_company
         and o.RESOURCE_UNIT_TYPE = 'ROBOCIZNA'
+        and o.OFFER_PRICE_PER_RESOURCE_UNIT > 0
     ");
     $sth->bindValue(':id_project', $idProject, PDO::PARAM_INT);
     $sth->bindValue(':id_company', $idCompany, PDO::PARAM_INT);
@@ -116,4 +118,112 @@ class ProjectKosztorysCennik {
 		return $cennik;
 	}
 
+  public static function getTypeLabel($idType) {
+    $schema = ProjectKosztorysSchema::getSchema();
+    if (!array_key_exists($idType, $schema['config']['type'])) throw new Exception("Missing Type in schema ([{$idType}])");
+    DBG::_('DBG', '>1', 'type', $schema['config']['type'][$idType], __CLASS__, __FUNCTION__, __LINE__);
+    return V::get($idType, "[$idType]", $schema['config']['type']);
+  }
+
+  public static function updatePriceProjectCennik($idType, $idProject, $price, $jednostka) {
+    $item = self::getItemProjectCennik($idType, $idProject, $jednostka);
+    $usrLogin = User::getLogin();
+    $sqlObj = array();
+    $sqlObj['CRM_LISTA_ZASOBOW_ID'] = $idType;
+    $sqlObj['OFFER_PRICE_PER_RESOURCE_UNIT'] = $price;
+    $sqlObj['RESOURCE_UNIT_TYPE'] = $jednostka;
+    $sqlObj['OFFER_UNIT_TYPE'] = $jednostka;
+    if (empty($item)) {
+      $sqlObj['COMPANIES_ID'] = 0;
+      $sqlObj['ID_PROJECT'] = $idProject;
+      $sqlObj['REQUIRED_RESOURCE_UNITS'] = 0;
+      $idInserted = DB::getDB()->ADD_NEW_OBJ('CRM_LISTA_ZASOBOW_OFFERS', (object)$sqlObj);
+      if ($idInserted <= 0) throw new Exception("Nie udało się utworzyć rekordu");
+    } else {
+      $sqlObj['ID'] = $item['ID'];
+      $affected = DB::getDB()->UPDATE_OBJ('CRM_LISTA_ZASOBOW_OFFERS', (object)$sqlObj);
+      if ($affected < 0) throw new Exception("Nie udało się zaktualizować rekordu");
+    }
+  }
+
+  public static function getPriceDefaultCennik($idType) {
+    $item = self::getItemDefaultCennik($idType);
+    return (!empty($item)) ? round($item['price'], 2) : 0;
+  }
+  public static function getItemDefaultCennik($idType) {
+    $cennikRaw = self::_getPrices($idType);
+    foreach ($cennikRaw as $item) {// fetch price from Oferta Admin
+      if (!$item['id_company'] && !$item['id_project']) {
+        if (empty($jednostka) || (!empty($jednostka) && $jednostka == $item['unit'])) {
+          return $item;
+        }
+      }
+    }
+    return null;
+  }
+  public static function getPriceProjectCennik($idType, $idProject, $jednostka) {
+    $item = self::getItemProjectCennik($idType, $idProject, $jednostka);
+    return (!empty($item)) ? round($item['price'], 2) : 0;
+  }
+  public static function getItemProjectCennik($idType, $idProject, $jednostka) {
+    $cennikRaw = self::_getPrices($idType, $idProject);
+    foreach ($cennikRaw as $item) {// fetch price from Project Oferta
+      if ($idProject == $item['id_project'] && !$item['id_company']) {
+        if (empty($jednostka) || (!empty($jednostka) && $jednostka == $item['unit'])) {
+          return $item;
+        }
+      }
+    }
+    return null;
+  }
+  public static function getPrice($idType, $idProject = 0, $idCompany = 0, $jednostka = '') {
+    $cennikRaw = self::_getPrices($idType);
+    DBG::_('DBG', '>1', 'prices cennikRaw', $cennikRaw, __CLASS__, __FUNCTION__, __LINE__);
+    $price = 0;
+    foreach ($cennikRaw as $item) {// fetch price from Oferta Admin
+      if (!$item['id_project'] && !$item['id_company']) {
+        if (empty($jednostka) || (!empty($jednostka) && $jednostka == $item['unit'])) {
+          $price = round($item['price'], 2);
+        }
+      }
+    }
+    DBG::_('DBG', '>1', 'default cennik: price', $price, __CLASS__, __FUNCTION__, __LINE__);
+    foreach ($cennikRaw as $item) {// fetch price from Project Oferta
+      if ($idProject == $item['id_project'] && !$item['id_company']) {
+        if (empty($jednostka) || (!empty($jednostka) && $jednostka == $item['unit'])) {
+          $price = round($item['price'], 2);
+        }
+      }
+    }
+    DBG::_('DBG', '>1', 'project cennik: price', $price, __CLASS__, __FUNCTION__, __LINE__);
+    return $price;
+  }
+  public static function _getPrices($idType, $idProject = 0, $idCompany = 0) {
+    $schema = ProjectKosztorysSchema::getSchema();
+    if (!array_key_exists($idType, $schema['config']['type'])) throw new Exception("Missing Type in schema ([{$idType}])");
+    $sqlIdType = DB::getPDO()->quote($idType, PDO::PARAM_INT);
+    $sqlFiltrProject = "";
+    if ($idProject > 0) {
+      $sqlIdProject = DB::getPDO()->quote($idProject, PDO::PARAM_INT);
+      $sqlFiltrProject = "and (o.ID_PROJECT = 0 or o.ID_PROJECT = {$sqlIdProject})";
+    }
+    $cennikRaw = DB::getPDO()->fetchAll("
+      select o.ID
+        , o.CRM_LISTA_ZASOBOW_ID as id_zasob
+        , o.COMPANIES_ID as id_company
+        , o.ID_PROJECT as id_project
+        , o.OFFER_PRICE_PER_RESOURCE_UNIT as price
+        , o.RESOURCE_UNIT_TYPE as unit
+        -- , o.OFFER_UNIT_TYPE as unit
+        , o.REQUIRED_RESOURCE_UNITS as quantity
+      from CRM_LISTA_ZASOBOW_OFFERS o
+      where o.CRM_LISTA_ZASOBOW_ID = {$sqlIdType}
+        and (o.A_STATUS is null or o.A_STATUS not in ('DELETED'))
+        and o.OFFER_PRICE_PER_RESOURCE_UNIT > 0
+        {$sqlFiltrProject}
+    ");
+    DBG::_('DBG', '>1', 'cennikRaw', $cennikRaw, __CLASS__, __FUNCTION__, __LINE__);
+    return $cennikRaw;
+  }
+
 }

+ 11 - 0
SE/se-lib/Request.php

@@ -77,6 +77,17 @@ class Request {
 		return $requestBody;
 	}
 
+	// @usage: Request::getRequestJson();
+	public static function getRequestJson() {
+		static $requestJson = null;
+		$body = Request::getRequestBody();
+		if (!empty($body)) {
+			$requestJson = @json_decode($body, $assoc = true);
+			if (null == $requestJson && 0 !== json_last_error()) throw new Exception("Error Parsing Json from Request" . json_last_error());
+		}
+		return $requestJson;
+	}
+
 	// @usage: Request::getUserIp();
 	public static function getUserIp() {
 		static $ip = null;

+ 164 - 4
SE/se-lib/Route/UrlAction/ProjektyKosztyWstepnychRobot.php

@@ -5,6 +5,7 @@ Lib::loadClass('ProjectKosztorysSchema');
 Lib::loadClass('ProjectKosztorysModel');
 Lib::loadClass('ProjectKosztorysCennik');
 Lib::loadClass('UI');
+Lib::loadClass('Response');
 
 class Route_UrlAction_ProjektyKosztyWstepnychRobot extends RouteBase {// TODO: UrlActionBase @see Route_UrlAction
 
@@ -1087,13 +1088,16 @@ SQL_FUN;
 		$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;
@@ -1286,13 +1290,14 @@ SQL_FUN;
 		$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');
+		$viewLayerDataArgs = compact('idProject', 'idCompany', 'admin', 'companyAdmin', 'projCosts', 'priceEditJs');
 		UI::setTitleJsTag("Kosztorys wstępny robót telekomunikacyjnych [{$idProject}]");
 ?>
 <div class="container">
@@ -1387,6 +1392,7 @@ SQL_FUN;
 		$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);
@@ -1422,6 +1428,13 @@ SQL_FUN;
 		}
 ?>
 <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; }
@@ -1475,6 +1488,11 @@ SQL_FUN;
 					<?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>
@@ -1482,14 +1500,31 @@ SQL_FUN;
 						<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 style="padding:3px 6px; text-align:right">
+						<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_<?php echo $idType; ?>" value="<?php echo $price; ?>"/>
+							<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 style="padding:3px 6px; text-align:right">
+						<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 : ?>
@@ -1673,9 +1708,134 @@ SQL_FUN;
 			</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($idProject, $idType, $unitType) {
+		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';

+ 6 - 9
SE/se-lib/tmpl/_layout_gora.php

@@ -152,17 +152,14 @@ function p5UI__ButtonAjax(n, eventNamespace, props) {
 		.end(function(err, res) {
 			if(dbg)console.log('DBG: res:', res, 'res.body:', res.body);
 			$n.removeClass('btn-default disabled btn-loading').addClass(state.baseCssClassNames);
-			if (err || !res.ok) {
-				jQuery(document).trigger('DBG:notify', {type: 'warning', msg: 'Req error', err: err});
+			var payload;
+			if (err || !res.ok || 'application/json' !== res.type) {
+				payload = {type: 'warning', msg: res.body.msg || 'Request error', body: res.body};
 			} else {
-				//form.log('DBG:REQ('+reqCounter+'). res.status(' + res.status + ') body:' + JSON.stringify(res.body), 'info');
-				if ('application/json' !== res.type) {
-					jQuery(document).trigger('DBG:notify', {type: 'warning', msg: 'Wrong response type - required json. ' + res.text});
-					return;
-				}
-				jQuery(document).trigger('DBG:notify', {type: 'success', msg: 'res.status:' + res.status + '. res.body:' + JSON.stringify(res.body)});
-				jQuery(document).trigger(eventNamespace+':ajaxLoaded', [n, {type: 'success', msg: res.body.msg, body: res.body}]);
+				payload = {type: 'success', msg: res.body.msg || '', body: res.body};
 			}
+			jQuery(document).trigger('DBG:notify', payload);
+			jQuery(document).trigger(eventNamespace+':ajaxLoaded', [n, payload]);
 			req = null;
 		});