ソースを参照

DealsSales add inline edit

Piotr Labudda 10 年 前
コミット
f5a425adc0
3 ファイル変更458 行追加42 行削除
  1. 2 2
      SE/se-lib/ProcesHelper.php
  2. 455 40
      SE/se-lib/Route/DealsSales.php
  3. 1 0
      SE/se-lib/V.php

+ 2 - 2
SE/se-lib/ProcesHelper.php

@@ -510,9 +510,9 @@ SQL;
 			where z.`DESC`='{$tblName}'
 				and z.`TYPE`='TABELA'
 				and zp.`DESC`='{$dbName}'
-		--		and zp.`TYPE` in('DATABASE_MYSQL','DATABASE')
+		--		and zp.`TYPE` in('DATABASE_MYSQL','DATABASE','BAZA_DANYCH')
 		";
-		if('1' == V::get('DBG_SQL', '', $_GET)){echo'<pre style="max-height:200px;overflow:auto;border:1px solid green;text-align:left;">sql (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($sql);echo'</pre>';}
+		DBG::_('DBG_SQL', '1', "sql", $sql, __CLASS__, __FUNCTION__, __LINE__);
 		$res = $db->query($sql);
 		if ($res) {
 			$zasobObj = $db->fetch($res);

+ 455 - 40
SE/se-lib/Route/DealsSales.php

@@ -1,6 +1,8 @@
 <?php
 
 Lib::loadClass('RouteBase');
+Lib::loadClass('ProcesHelper');
+Lib::loadClass('Typespecial');
 
 class Route_DealsSales extends RouteBase {
 
@@ -71,11 +73,240 @@ class Route_DealsSales extends RouteBase {
 		}
 	}
 
-	public function ajaxAction() {
+	public function getDataAjaxAction() {
 		header("Content-type: application/json");
 		$response = new stdClass();
 		$response->msg = 'TODO: F. ' . __FUNCTION__ . ' L.' . __LINE__;
-		return json_encode($response);
+		echo json_encode($response);
+	}
+
+	public function inlineEditAjaxAction() {
+		//header("Content-type: application/json");
+		$response = new stdClass();
+		$response->msg = 'TODO: F. ' . __FUNCTION__ . ' L.' . __LINE__;// TODO: DBG
+		$response->_request = $_REQUEST;// TODO: DBG
+
+		$year = V::get('year', 0, $_REQUEST, 'int');
+		$month = V::get('month', 0, $_REQUEST, 'int');
+		$telbox = V::get('telbox', '', $_REQUEST, 'word');
+		if ($year <= 0) throw new Exception('Error: wrong year');
+		if ($month <= 0 || $month > 12) throw new Exception('Error: wrong month');
+		if (empty($telbox)) throw new Exception('Error: wrong telbox');
+
+		$record = $this->_fetchSaleByParams($telbox, $year, $month);
+		if (empty($record)) {
+			return $this->_renderInlineCreateForm($telbox, $year, $month);
+		} else {
+			return $this->_renderInlineEditForm($record);
+		}
+	}
+
+	public function saveInlineAjaxAction() {
+		header("Content-type: application/json");
+		$response = new stdClass();
+		$response->msg = 'TODO: F. ' . __FUNCTION__ . ' L.' . __LINE__;
+		$response->_request = $_REQUEST;// TODO: DBG
+
+		$id = V::get('ID', 0, $_REQUEST, 'int');
+		if ($id > 0) {
+			return $this->_saveInlineEdit($id, $_REQUEST);
+		} else {
+			return $this->_saveInlineCreate($_REQUEST);
+		}
+	}
+
+	private function _saveInlineEdit($id, $args)	{
+		$record = $this->_fetchSaleById($id);
+		if (empty($record)) throw new HttpException("Record {$id} not exists", 404);
+		DBG::_('DBG', '>2', "record", $record, __CLASS__, __FUNCTION__, __LINE__);// TODO: DBG
+		DBG::_('DBG', '>2', "args", $args, __CLASS__, __FUNCTION__, __LINE__);// TODO: DBG
+
+		$tblAcl = $this->_getTableAcl();
+		$primaryKeyFieldName = 'ID';
+		$primaryKey = V::get($primaryKeyFieldName, 0, $record);
+
+		$fieldName = 'SALES_VALUE';
+		$fieldID = $tblAcl->getFieldIdByName($fieldName);
+		if (!$fieldID) throw new Exception("No field by name ({$fieldName})");
+
+		if (!$tblAcl->isAllowed($fieldID, 'W', $record)) {
+			throw new Exception("Brak uprawnień do zapisu ({$fieldName})");
+		}
+
+		$argsFieldName = "f{$fieldID}";
+		if (!isset($args[$argsFieldName])) throw new Exception("Brak danych dla pola ({$fieldName})");
+
+		$itemPatch = new stdClass();
+		$itemPatch->{$primaryKeyFieldName} = $primaryKey;
+		$itemPatch->{$fieldName} = $args[$argsFieldName];
+		DBG::_('DBG', '>1', "TODO: save record", $itemPatch, __CLASS__, __FUNCTION__, __LINE__);// TODO: DBG
+
+		$response = new stdClass();
+		try {
+			$affected = $tblAcl->updateItem($itemPatch);
+
+			if ($affected > 0) {
+				$response->type = 'success';
+				$response->msg = "Rekord zapisany pomyślnie";//"Record saved successfully";
+			} else if ($affected == 0) {
+				$response->type = 'info';
+				$response->msg = "Nie wprowadzono żadnych zmian";
+			}
+			$response->record = $tblAcl->getItem($primaryKey);
+		}
+		catch (Exception $e) {
+			$response->type = 'error';
+			$response->msg = $e->getMessage();
+		}
+
+		echo json_encode($response);
+	}
+
+	private function _saveInlineCreate($args)	{
+		throw new Exception('TODO: F. ' . __FUNCTION__ . ' L.' . __LINE__);
+		$year = V::get('year', 0, $args, 'int');
+		$month = V::get('month', 0, $args, 'int');
+		$telbox = V::get('telbox', '', $args, 'word');
+		if ($year <= 0) throw new Exception('Error: wrong year');
+		if ($month <= 0 || $month > 12) throw new Exception('Error: wrong month');
+		if (empty($telbox)) throw new Exception('Error: wrong telbox');
+
+	}
+
+	private function _fetchSaleByParams($telbox, $year, $month)	{
+		$record = null;
+		$db = DB::getDB();
+		$sqlWhere = array();
+		$sqlWhere[] = " o.`A_STATUS`!='DELETED' ";
+		$sqlIdTelboxes = $db->_($telbox);
+		$sqlWhere[] = " o.`T_TELBOX_NEIGHBOUR_IN`='{$sqlIdTelboxes}' ";
+		$sqlWhere[] = " o.`SALES_YEAR`='{$year}' ";
+		$sqlWhere[] = " o.`SALES_MONTH`='{$month}' ";
+		$sqlWhere = (!empty($sqlWhere))? implode(" and ", $sqlWhere) : '';
+		$sql = "select o.`ID`
+				, o.`T_TELBOX_NEIGHBOUR_IN`
+				, o.`ID_DEALS_TABLE`
+				, o.`SALES_YEAR`
+				, o.`SALES_MONTH`
+				, o.`SALES_VALUE`
+				, o.`marka`
+			from `DEALS_SALES` as o
+			where
+				{$sqlWhere}
+		";
+		DBG::_('DBG_SQL', '>1', "sql:fetch({telbox:{$telbox},year:{$year},month:{$month}})", $sql, __CLASS__, __FUNCTION__, __LINE__);
+		$res = $db->query($sql);
+		while ($r = $db->fetch($res)) {
+			$record = $r;
+		}
+		return $record;
+	}
+
+	private function _fetchSaleById($idSale)	{
+		$record = null;
+		$db = DB::getDB();
+		$sqlWhere = array();
+		$sqlWhere[] = " o.`A_STATUS`!='DELETED' ";
+		$sqlWhere[] = " o.`ID`='{$idSale}' ";
+		$sqlWhere = (!empty($sqlWhere))? implode(" and ", $sqlWhere) : '';
+		$sql = "select o.`ID`
+				, o.`T_TELBOX_NEIGHBOUR_IN`
+				, o.`ID_DEALS_TABLE`
+				, o.`SALES_YEAR`
+				, o.`SALES_MONTH`
+				, o.`SALES_VALUE`
+				, o.`marka`
+			from `DEALS_SALES` as o
+			where
+				{$sqlWhere}
+		";
+		DBG::_('DBG_SQL', '>1', "sql:fetch({telbox:{$telbox},year:{$year},month:{$month}})", $sql, __CLASS__, __FUNCTION__, __LINE__);
+		$res = $db->query($sql);
+		while ($r = $db->fetch($res)) {
+			$record = $r;
+		}
+		return $record;
+	}
+
+	private function _renderInlineCreateForm($telbox, $year, $month) {
+		throw new Exception('TODO: L.' . __LINE__);// TODO: DBG
+		DBG::_(true, true, "TODO: Create Form for", array($telbox, $year, $month), __CLASS__, __FUNCTION__, __LINE__);// TODO: DBG
+	}
+
+	private function _renderInlineEditForm($record) {
+		//DBG::_(true, true, "TODO: Edit Form for record", $record, __CLASS__, __FUNCTION__, __LINE__);// TODO: DBG
+		$DBG = ('1' == V::get('DBG', '', $_REQUEST));
+		header("Content-type: text/plain");
+
+		$row = $record;// TODO: refactor
+
+		$tblAcl = $this->_getTableAcl();
+
+		// TODO: $cols = array('T_TELBOX_NEIGHBOUR_IN','SALES_YEAR','SALES_MONTH','SALES_VALUE','marka');
+		// TODO: form for only 'SALES_VALUE' and eventually 'marka'
+		$fieldName = 'SALES_VALUE';
+		$fieldID = $tblAcl->getFieldIdByName($fieldName);
+		if (!$fieldID) throw new Exception("No field by name ({$fieldName})");
+
+		$fieldVal = '';
+		if ($tblAcl->isAllowed($fieldID, 'R', $row)) {
+			$fieldVal = V::get($fieldName, $fieldVal, $row);
+		} else {
+			$fieldVal = '*****';
+		}
+
+		$fieldVal = V::get("f{$fieldID}", $fieldVal, $_POST);
+
+		$vCol = $tblAcl->getField($fieldID);
+		$vCol['label'] = (!empty($vCol['label']))? $vCol['label'] : $vCol['name'];
+
+		$tsValues = array();
+		$typeSpecial = Typespecial::getInstance($fieldID, $vCol['name']);
+		if ($typeSpecial) {
+			if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">Typespecial('.$fieldID.') (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($typeSpecial);echo'</pre>';}
+			$specialValues = $typeSpecial->getEditSelectedValuesByIds($this->_zasobID, $row->ID, $fieldName, V::get($fieldName, $fieldVal, $row));
+			if($DBG){echo'<pre style="max-height:200px;overflow:auto;border:1px solid red;text-align:left;">Typespecial('.$fieldID.') specialValues (' . __CLASS__ . '::' . __FUNCTION__ . ':' . __LINE__ . '): ';print_r($specialValues);echo'</pre>';}
+			if (!empty($specialValues)) {
+				$tsValues[$row->ID] = implode('<br>', $specialValues);
+			}
+		}
+?>
+		<label for="<?php echo "f{$fieldID}"; ?>" class="AjaxTableEdit-label">
+			<strong title="<?php echo "[{$fieldID}] {$fieldName}"; ?>"><?php echo "{$vCol['label']}"; ?></strong>
+			<?php if (!empty($vCol['opis'])) : ?>
+				<em><?php echo $vCol['opis']; ?></em>
+				<?php $perms = $tblAcl->getFieldPerms($fieldID); SE_Layout::hotKeyDBG($perms); ?>
+			<?php endif; ?>
+		</label>
+<?php
+		$fieldParams = array('widthClass'=>'inside-modal', 'maxGrid'=>6);
+		if (!empty($tsValues[$row->ID])) {
+			$fieldParams['typespecialValue'] = $tsValues[$row->ID];
+		}
+		$vDefault = $tblAcl->getColDefault($fieldName);
+		if (!empty($vDefault)) {
+			$fieldParams['default'] = $vDefault;
+		}
+		echo $tblAcl->showFormItem('W', $fieldID, "f{$fieldID}", $fieldVal, $fieldParams, $row);
+		if ($typeSpecial) {
+			echo '<p style="padding:100px 0;"></p>';
+		}
+
+?>
+		<input type="hidden" name="ID" value="<?php echo $record->ID; ?>" />
+<?php
+		exit;
+	}
+
+	private function _getTableAcl() {
+		$zasobObj = ProcesHelper::getZasobTableInfoByUri('default_db/deals_sales');
+		if (!$zasobObj) throw new Exception("Zasob tabela deals_sales nie istnieje.");
+
+		$userAcl = User::getAcl();
+		$userAcl->fetchGroups();
+		if (!$userAcl->hasTableAcl($zasobObj->ID)) throw new Exception("Brak uprawnień do tabeli ");
+		$tblAcl = $userAcl->getTableAcl($zasobObj->ID);
+		return $tblAcl;
 	}
 
 	public function legacy_DEALS_SALES() {
@@ -251,10 +482,14 @@ class Route_DealsSales extends RouteBase {
 		priv.options = {};
 		var defaults = {
 					url: '',
-					initData: {}
+					urlInlineEdit: '',
+					urlSaveInlineEdit: '',
+					initData: {},
+					debug: false
 				},
 				_uiNodeCont = null,// container node
 				_uiNode$Table = null,
+				_inlineEditBox$Node = null,
 				_state = {};// state - to replace variables below (date, ...)
 
 		priv.init = function () {
@@ -289,38 +524,204 @@ class Route_DealsSales extends RouteBase {
 				});
 				$('<th>suma</th>').appendTo(theadTr$Node);
 			}
-			{// body
-				_uiNode$TableBody = $('<tbody></tbody>').appendTo(_uiNode$Table);
-				var vSalesByYear = _state.obroty;
-				$.each(_state.years, function(yearIdx, vYearId) {
-					var vSalesByMonth = vSalesByYear[vYearId];
-					var tr$Node = $('<tr></tr>').appendTo(_uiNode$TableBody);
-					$('<th>' + vYearId + '</th>').appendTo(tr$Node);
-					var suma = 0;
-					$.each(_state.months, function(monthsIdx, vMonth) {
-						var td$Node = $('<td></td>').appendTo(tr$Node);
-						td$Node.css({textAlign: 'right'});
-						var saleValue = parseFloat(vSalesByMonth[vMonth]);
-						//if (!vSalesByMonth.hasOwnProperty(vMonth)) {
-						if (isNaN(saleValue)) {
-							td$Node.text('...');
-							td$Node.css({color: '#ddd'});
+			$('<tbody></tbody>').appendTo(_uiNode$Table);
+			_uiNode$Table.appendTo(_uiNodeCont);
+			priv.renderInitInlineEditBox();// .dealsSales__inlineEditBox
+
+			priv.renderTableBody();
+		};
+
+		priv.renderTableBody = function(record) {
+			var currentNode = _uiNode$Table.children('tbody'),
+					node = $('<tbody></tbody>'),
+					vSalesByYear = _state.obroty
+			;
+			$.each(_state.years, function(yearIdx, vYearId) {
+				var vSalesByMonth = vSalesByYear[vYearId];
+				var tr$Node = $('<tr></tr>').appendTo(node);
+				$('<th>' + vYearId + '</th>').appendTo(tr$Node);
+				var suma = 0;
+				$.each(_state.months, function(monthsIdx, vMonth) {
+					var td$Node = $('<td></td>').appendTo(tr$Node);
+					td$Node.css({textAlign: 'right'});
+					var saleValue = parseFloat(vSalesByMonth[vMonth]);
+					//if (!vSalesByMonth.hasOwnProperty(vMonth)) {
+					if (isNaN(saleValue)) {
+						td$Node.text('...');
+						td$Node.css({color: '#ddd'});
+					} else {
+						suma += saleValue;
+						$('<nobr>' + numeral(saleValue).format('0,0[.]00') + '</nobr>').appendTo(td$Node);
+						if (undefined !== _state.obrotyToMarki[vYearId][vMonth]) {
+							var marka = _state.obrotyToMarki[vYearId][vMonth];
+							td$Node.addClass('cell_marka_' + _state.marki[marka]);
+						}
+					}
+					td$Node.on('dblclick', {year:vYearId, month:vMonth}, priv.cellDblClicked);
+				});
+				var suma$Node = $('<td>' + numeral(suma).format('0,0[.]00') + '</td>').appendTo(tr$Node);
+				suma$Node.css({textAlign: 'right'});
+			});
+			currentNode.replaceWith(node);
+		};
+
+		priv.renderInitInlineEditBox = function() {
+			var nodeClass = 'dealsSales__inlineEditBox',
+					node;
+			var node = $('<div class="' + nodeClass + ' modal fade" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"></div>');
+			var modalWrap = $('<div class="modal-dialog"></div>').appendTo(node);
+			var modalWrap = $('<div class="modal-content"></div>').appendTo(modalWrap);
+			var frmInlineEdit = $('<form style="margin:0;padding:0;"></form>').appendTo(modalWrap);
+			var iebHead = $('<div class="modal-header">').appendTo(frmInlineEdit);
+			$('<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="glyphicon glyphicon-remove"></i></button>').appendTo(iebHead);
+			$('<h3 id="myModalLabel">Edytuj</h3>').appendTo(iebHead);
+			var iebBodyWrap = $('<div class="modal-body" style="padding:0"></div>').appendTo(frmInlineEdit);
+			var iebBody = $('<div style="padding:15px"></div>').appendTo(iebBodyWrap);
+			//$('<input type="hidden" name="ID" value="">').appendTo(iebBody);
+			//$('<input type="hidden" name="col" value="">').appendTo(iebBody);
+			$('<div class="inlineEditBox-cnt"></div>').appendTo(iebBody);
+			var iebFoot = $('<div class="modal-footer"></div>').appendTo(frmInlineEdit);
+			$('<button class="btn" data-dismiss="modal" aria-hidden="true">Zamknij</button>').appendTo(iebFoot);
+			var iebBtnSave = $('<input type="submit" value="Zapisz" class="btn btn-primary btn-save">').appendTo(iebFoot);
+
+			frmInlineEdit.on('submit', function() {
+				var data = _inlineEditBox$Node.find('form').serialize();
+				_inlineEditBox$Node.find('.inlineEditBox-cnt').html('<span class="loading-info"> loading ...</span>');
+				function notifyAjaxCallback(data) {
+					var notify = {};
+					notify.type = (data && data.type)? data.type : '';
+					notify.msg = (data && data.msg)? data.msg : '';
+					switch (notify.type) {
+						case 'success':
+							if (!notify.msg) notify.msg = 'Dane poprawnie zaktualizowane';
+							break;
+						case 'info':
+							if (!notify.msg) notify.msg = 'Nie wprowadzono żadnych zmian';
+							break;
+						case 'error':
+							if (!notify.msg) notify.msg = 'Wystąpiły błędy';
+							break;
+						case 'warning':
+							notify.type = 'warn';
+							if (!notify.msg) notify.msg = 'Wystąpiły błędy';
+							break;
+						default:
+							notify.msg = 'Nieznany błąd';
+							if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
+							notify.type = '';
+					}
+					jQuery.notify(notify.msg, notify.type);
+				}
+
+				$.ajax({
+					data: data,
+					dataType: 'json',
+					type: "POST",
+					url: priv.options.urlSaveInlineEdit
+				})
+				.done(function(data, textStatus, jqXHR){
+					notifyAjaxCallback(data);
+					if (data.record) {
+						var year = data.record.SALES_YEAR,
+								month = data.record.SALES_MONTH,
+								saleValue = parseFloat(data.record.SALES_VALUE),
+								state = {}
+						;
+						if (month < 10) month = '0' + month;
+						// TODO: if marka changed - fix state
+						state.obroty = {};
+						state.obroty[year] = {};
+						state.obroty[year][month] = saleValue;
+						priv.setState(state);
+					}
+					_inlineEditBox$Node.modal('hide');
+				})
+				.fail(function(jqXHR){// jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
+					if (jqXHR.responseJSON) {
+						notifyAjaxCallback(jqXHR.responseJSON);
+					}
+					else {
+						var txt = jqXHR.responseText || 'Wystąpiły błędy';
+						if (jqXHR.status == 404) {
+							jQuery.notify(jqXHR.responseText, 'error');
 						} else {
-							suma += saleValue;
-							$('<nobr>' + numeral(saleValue).format('0,0[.]00') + '</nobr>').appendTo(td$Node);
-							if (undefined !== _state.obrotyToMarki[vYearId][vMonth]) {
-								var marka = _state.obrotyToMarki[vYearId][vMonth];
-								td$Node.addClass('cell_marka_' + _state.marki[marka]);
-							}
+							jQuery.notify(jqXHR.responseText, 'warn');
 						}
-					});
-					var suma$Node = $('<td>' + numeral(suma).format('0,0[.]00') + '</td>').appendTo(tr$Node);
-					suma$Node.css({textAlign: 'right'});
+					}
+					_inlineEditBox$Node.modal('hide');
+				});
+
+				return false;
+			});
+
+			node.appendTo(_uiNodeCont);//currentNode.replaceWith(node);
+			_inlineEditBox$Node = node;
+		};
+
+		// td$Node.on('dblclick', {year:vYearId, month:vMonth}, priv.cellDblClicked);
+		priv.cellDblClicked = function(e) {
+
+			// e.clientX: 1002; e.clientY: 245
+			if ('year' in e.data && 'month' in e.data && e.data.year > 0 && e.data.month > 0) {
+				_inlineEditBox$Node.modal();
+				_inlineEditBox$Node.show();
+				_inlineEditBox$Node.on('shown.bs.modal', function(e) {
+					var dialogBox = jQuery(this).find('.modal-dialog'),
+							modalBody = dialogBox.find('.modal-body'),
+							boudingRect = dialogBox.get(0).getBoundingClientRect();
+					dialogBox.css({position: 'absolute', margin:0, top: boudingRect.top, left: boudingRect.left});
+					modalBody.css({overflow: 'scroll'});
+					dialogBox.resizable({minHeight: 300, minWidth: 300, alsoResize: modalBody});
+					dialogBox.draggable();
+				});
+				_inlineEditBox$Node.on('hidden.bs.modal', function(e) {
+					var dialogBox = jQuery(this).find('.modal-dialog'),
+							modalBody = dialogBox.find('.modal-body');
+					dialogBox.removeAttr('style');
+					modalBody.removeAttr('style');
+					modalBody.css({padding: '0'});
+					dialogBox.resizable();
+					dialogBox.draggable();
+					dialogBox.resizable('destroy');
+					dialogBox.draggable('destroy');
 				});
+				_inlineEditBox$Node.find('input[name=ID]').val(e.data.id);
+				_inlineEditBox$Node.find('input[name=col]').val(e.data.col);
+				_inlineEditBox$Node.find('.inlineEditBox-cnt').html('<span class="loading-info"> loading ...</span>');
+				$.ajax({
+					url: priv.options.urlInlineEdit,
+					type: 'GET',
+					dataType: 'text',
+					data: e.data,
+					async: true,
+					success: function (data) {
+						_inlineEditBox$Node.find('.inlineEditBox-cnt').html(data);
+						_inlineEditBox$Node.find('.btn-save').show();
 
+						initDateTimePicker(_inlineEditBox$Node);
+
+						_inlineEditBox$Node.find('textarea').autosize();
+
+						var fld = _inlineEditBox$Node.find('input[id^="f"]');
+						if (fld && !fld.hasClass('se_type-date')) {
+							fld.focus();
+
+							fld.keydown(function(event) {
+								if (event.which == 13) {
+									event.preventDefault();
+									_inlineEditBox$Node.find('form').submit();
+								}
+							});
+						}
+					},
+					error: function (err) {
+						if (priv.options.debug) console.log('err');
+					}
+				});
+			} else {
+				if (priv.options.debug) console.log('NO data');
+				return false;
 			}
-			_uiNode$Table.appendTo(_uiNodeCont);
-			// TODO: priv.renderInlineEditBox();// .tblAjax__inlineEditBox
 		};
 
 		priv.onRender = function(e) {
@@ -328,17 +729,29 @@ class Route_DealsSales extends RouteBase {
 			if (arguments.length > 1) {
 				for (var i=1; i<arguments.length; i++) {
 					switch (arguments[i]) {
-						case 'head': _head = undefined; break;
-						case 'body': _body = undefined; break;
-						case 'foot': _foot = undefined; break;
-						case 'footer__toolbar__info': priv.renderFooterInfo(); break;
-						case 'footer__toolbar__pagination': priv.renderFooterPagination(); break;
-						case 'footer__toolbar__pagesizes': priv.renderFooterPageSizes(); break;
-						case 'foot__columnPicker': priv.renderFooterColumnPicker(); break;
-						case 'head__specialFilters': priv.renderHeadSpecialFilters(); break;
+						case 'body': priv.renderTableBody(); break;
 					}
 				}
-				//priv.renderTable();
+			}
+		};
+
+		priv.setState = function(state) {
+			var oldState = _state,// TODO: use to check what really changed (use extend!)
+					renderParts = {};
+			if (state.obroty) {
+				$.each(state.obroty, function(kYear, vSalesByMonth) {
+					$.each(vSalesByMonth, function(kMonth, vSaleValue) {
+						_state.obroty[kYear][kMonth] = vSaleValue;
+					});
+				});
+				renderParts['body'] = true;
+			}
+
+			renderParts = Object.keys(renderParts);
+
+			if (priv.options.debug) console.log('setState::renderParts: ', renderParts);//TODO:DBG:RMME
+			if (renderParts.length > 0) {
+				jQuery(_uiNodeCont).trigger('DealsSalesTable:render', renderParts);
 			}
 		};
 
@@ -365,7 +778,9 @@ class Route_DealsSales extends RouteBase {
 
 	jQuery(document).ready(function() {
 		jQuery('#dealsSalesByMonth').DealsSalesTable({
-			url: 'index.php?_route=DealsSales&_task=ajax',
+			url: 'index.php?_route=DealsSales&_task=getDataAjax&telbox=<?php echo $id_telboxes; ?>',
+			urlInlineEdit: 'index.php?_route=DealsSales&_task=inlineEditAjax&telbox=<?php echo $id_telboxes; ?>',
+			urlSaveInlineEdit: 'index.php?_route=DealsSales&_task=saveInlineAjax&telbox=<?php echo $id_telboxes; ?>',
 			initData: <?php echo json_encode($initData); ?>
 		});
 	});

+ 1 - 0
SE/se-lib/V.php

@@ -11,6 +11,7 @@ class V {
 	 * Get variable from array or object.
 	 */
 	public static function get($name, $default, $from, $type = '', $filterCallback = null) {
+		if (empty($name)) return null;
 		$ret = null;
 		if (is_array($from)) {
 			if (array_key_exists($name, $from)) {