Программный интерфейс

Показывать:
/**
 * Конструкторы объектов данных
 *
 * © Evgeniy Malyarov http://www.oknosoft.ru 2014-2016
 *
 * @module  metadata
 * @submodule meta_objs
 * @requires common
 */


/**
 * ### Абстрактный объект данных
 * Прародитель как ссылочных объектов (документов и справочников), так и регистров с суррогатным ключом и несохраняемых обработок<br />
 * См. так же:
 * - {{#crossLink "EnumObj"}}{{/crossLink}} - ПеречислениеОбъект
 * - {{#crossLink "CatObj"}}{{/crossLink}} - СправочникОбъект
 * - {{#crossLink "DocObj"}}{{/crossLink}} - ДокументОбъект
 * - {{#crossLink "DataProcessorObj"}}{{/crossLink}} - ОбработкаОбъект
 * - {{#crossLink "TaskObj"}}{{/crossLink}} - ЗадачаОбъект
 * - {{#crossLink "BusinessProcessObj"}}{{/crossLink}} - БизнеспроцессОбъект
 * - {{#crossLink "RegisterRow"}}{{/crossLink}} - ЗаписьРегистраОбъект
 *
 * @class DataObj
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {RefDataManager}
 * @constructor
 * @menuorder 20
 * @tooltip Объект данных
 */
function DataObj(attr, manager) {

  var tmp,
    _ts_ = {},
    _obj = {},
    _data = {
      _is_new: !(this instanceof EnumObj)
    };

  // если объект с такой ссылкой уже есть в базе, возвращаем его и не создаём нового
  if(!(manager instanceof DataProcessorsManager) && !(manager instanceof EnumManager))
    tmp = manager.get(attr, false, true);

  if(tmp){
    attr = null;
    return tmp;
  }


  if(manager instanceof EnumManager)
    _obj.ref = attr.name;

  else if(!(manager instanceof RegisterManager)){
    _obj.ref = $p.utils.fix_guid(attr);

  }else
    _obj.ref = manager.get_ref(attr);


  this.__define({

    /**
     * ### Фактическое хранилище данных объекта
     * Оно же, запись в таблице объекта локальной базы данных
     * @property _obj
     * @type Object
     * @final
     */
    _obj: {
      value: _obj,
      configurable: true
    },

    /**
     * Хранилище ссылок на табличные части - не сохраняется в базе данных
     * @property _ts_
     */
    _ts_: {
      value: function( name ) {
        if( !_ts_[name] ) {
          _ts_[name] = new TabularSection(name, this);
        }
        return _ts_[name];
      },
      configurable: true
    },

    /**
     * Указатель на менеджер данного объекта
     * @property _manager
     * @type DataManager
     * @final
     */
    _manager: {
      value : manager
    },

    /**
     * Пользовательские данные - аналог `AdditionalProperties` _Дополнительные cвойства_ в 1С
     * @property _data
     * @type DataManager
     * @final
     */
    _data: {
      value : _data,
      configurable: true
    }

  });


  if(manager.alatable && manager.push){
    manager.alatable.push(_obj);
    manager.push(this, _obj.ref);
  }

  attr = null;

}


DataObj.prototype._getter = function (f) {

  var mf = this._metadata.fields[f].type,
    res = this._obj ? this._obj[f] : "",
    mgr, ref;

  if(f == "type" && typeof res == "object")
    return res;

  else if(f == "ref"){
    return res;

  }else if(mf.is_ref){
    if(mf.digits && typeof res === "number")
      return res;

    if(mf.hasOwnProperty("str_len") && !$p.utils.is_guid(res))
      return res;

    if(mgr = _md.value_mgr(this._obj, f, mf)){
      if($p.utils.is_data_mgr(mgr))
        return mgr.get(res, false);
      else
        return $p.utils.fetch_type(res, mgr);
    }

    if(res){
      console.log([f, mf, this._obj]);
      return null;
    }

  }else if(mf.date_part)
    return $p.utils.fix_date(this._obj[f], true);

  else if(mf.digits)
    return $p.utils.fix_number(this._obj[f], !mf.hasOwnProperty("str_len"));

  else if(mf.types[0]=="boolean")
    return $p.utils.fix_boolean(this._obj[f]);

  else
    return this._obj[f] || "";
};

DataObj.prototype.__setter = function (f, v) {

  var mf = this._metadata.fields[f].type,
    mgr;

  if(f == "type" && v.types)
    this._obj[f] = v;

  else if(f == "ref")

    this._obj[f] = $p.utils.fix_guid(v);

  else if(mf.is_ref){

    if(mf.digits && typeof v == "number" || mf.hasOwnProperty("str_len") && typeof v == "string" && !$p.utils.is_guid(v)){
      this._obj[f] = v;

    }else {
      this._obj[f] = $p.utils.fix_guid(v);

      mgr = _md.value_mgr(this._obj, f, mf, false, v);

      if(mgr){
        if(mgr instanceof EnumManager){
          if(typeof v == "string")
            this._obj[f] = v;

          else if(!v)
            this._obj[f] = "";

          else if(typeof v == "object")
            this._obj[f] = v.ref || v.name || "";

        }else if(v && v.presentation){
          if(v.type && !(v instanceof DataObj))
            delete v.type;
          mgr.create(v);
        }else if(!$p.utils.is_data_mgr(mgr))
          this._obj[f] = $p.utils.fetch_type(v, mgr);
      }else{
        if(typeof v != "object")
          this._obj[f] = v;
      }
    }

  }else if(mf.date_part)
    this._obj[f] = $p.utils.fix_date(v, true);

  else if(mf.digits)
    this._obj[f] = $p.utils.fix_number(v, !mf.hasOwnProperty("str_len"));

  else if(mf.types[0]=="boolean")
    this._obj[f] = $p.utils.fix_boolean(v);

  else
    this._obj[f] = v;
  
};

DataObj.prototype.__notify = function (f) {
  if(!this._data._silent)
    Object.getNotifier(this).notify({
      type: 'update',
      name: f,
      oldValue: this._obj[f]
    });
};

DataObj.prototype._setter = function (f, v) {

  if(this._obj[f] == v)
    return;
  
  this.__notify(f);
  this.__setter(f, v);
  this._data._modified = true;

};

DataObj.prototype._getter_ts = function (f) {
  return this._ts_(f);
};

DataObj.prototype._setter_ts = function (f, v) {
  var ts = this._ts_(f);
  if(ts instanceof TabularSection && Array.isArray(v))
    ts.load(v);
};

DataObj.prototype.__define({

  /**
   * ### valueOf
   * для операций сравнения возвращаем guid
   */
  valueOf: {
    value: function () {
      return this.ref;
    }
  },

  /**
   * ### toJSON
   * для сериализации возвращаем внутренний _obj
   */
  toJSON: {
    value: function () {
      return this._obj;
    }
  },

  /**
   * ### toString
   * для строкового представления используем
   */
  toString: {
    value: function () {
      return this.presentation;
    }
  },

  /**
   * Метаданные текущего объекта
   * @property _metadata
   * @for DataObj
   * @type Object
   * @final
   */
  _metadata: {
    get : function(){
      return this._manager.metadata()
    }
  },

  /**
   * Пометка удаления
   * @property _deleted
   * @for DataObj
   * @type Boolean
   */
  _deleted: {
    get : function(){
      return !!this._obj._deleted
    }
  },

  /**
   * Признак модифицированности
   */
  _modified: {
    get : function(){
      if(!this._data)
        return false;
      return !!(this._data._modified)
    }
  },

  /**
   * Возвращает "истина" для нового (еще не записанного или не прочитанного) объекта
   * @method is_new
   * @for DataObj
   * @return {boolean}
   */
  is_new: {
    value: function(){
      return this._data._is_new;
    }
  },

  /**
   * Метод для ручной установки признака _прочитан_ (не новый)
   */
  _set_loaded: {
    value: function(ref){
      this._manager.push(this, ref);
      this._data._modified = false;
      this._data._is_new = false;
    }
  },

  /**
   * Установить пометку удаления
   * @method mark_deleted
   * @for DataObj
   * @param deleted {Boolean}
   */
  mark_deleted: {
    value: function(deleted){
      this._obj._deleted = !!deleted;
      this.save();
      this.__notify('_deleted');
    }
  },

  /**
   * guid ссылки объекта
   * @property ref
   * @for DataObj
   * @type String
   */
  ref: {
    get : function(){ return this._obj.ref},
    set : function(v){ this._obj.ref = $p.utils.fix_guid(v)},
    enumerable : true,
    configurable: true
  },

  /**
   * Проверяет, является ли ссылка объекта пустой
   * @method empty
   * @return {boolean} - true, если ссылка пустая
   */
  empty: {
    value: function(){
      return $p.utils.is_empty_guid(this._obj.ref);
    }
  },

  /**
   * Читает объект из внешней или внутренней датабазы асинхронно.
   * В отличии от _mgr.get(), принудительно перезаполняет объект сохранёнными данными
   * @method load
   * @for DataObj
   * @return {Promise.<DataObj>} - промис с результатом выполнения операции
   * @async
   */
  load: {
    value: function(){

      var reset_modified = function () {
          reset_modified = null;
          this._data._modified = false;
          return this;
        }.bind(this);

      if(this.ref == $p.utils.blank.guid){
        if(this instanceof CatObj)
          this.id = "000000000";
        else
          this.number_doc = "000000000";

        return Promise.resolve(this);

      }else{
        if(this._manager.cachable && this._manager.cachable != "e1cib"){
          return $p.wsql.pouch.load_obj(this).then(reset_modified);

        } else
          return _rest.load_obj(this).then(reset_modified);
      }


    }
  },

  /**
   * Освобождает память и уничтожает объект
   * @method unload
   * @for DataObj
   */
  unload: {
    value: function(){
      var f, obj = this._obj;

      this._manager.unload_obj(this.ref);

      if(this._observers)
        this._observers.length = 0;

      if(this._notis)
        this._notis.length = 0;

      for(f in this._metadata.tabular_sections)
        this[f].clear(true);

      for(f in this){
        if(this.hasOwnProperty(f))
          delete this[f];
      }
      for(f in obj)
        delete obj[f];
      ["_ts_","_obj","_data"].forEach(function (f) {
        delete this[f];
      }.bind(this));
      f = obj = null;
    }
  },

  /**
   * ### Записывает объект
   * Ввыполняет подписки на события перед записью и после записи<br />
   * В зависимости от настроек, выполняет запись объекта во внешнюю базу данных
   *
   * @method save
   * @for DataObj
   * @param [post] {Boolean|undefined} - проведение или отмена проведения или просто запись
   * @param [operational] {Boolean} - режим проведения документа (Оперативный, Неоперативный)
   * @param [attachments] {Array} - массив вложений
   * @return {Promise.<DataObj>} - промис с результатом выполнения операции
   * @async
   */
  save: {
    value: function (post, operational, attachments) {

      if(this instanceof DocObj && typeof post == "boolean"){
        this.posted = post;
      }

      var saver,
        
        before_save_res = this._manager.handle_event(this, "before_save"),
        
        reset_modified = function () {

          if(before_save_res !== false)
            this._data._modified = false;

          saver = null;
          before_save_res = null;
          reset_modified = null;
          
          return this;
        }.bind(this);

      // для объектов с иерархией установим пустого родителя, если иной не указан
      if(this._metadata.hierarchical && !this._obj.parent)
        this._obj.parent = $p.utils.blank.guid;

      // для справочников, требующих код и пустым кодом, присваиваем код
      if(!this.id && this._metadata.code_length && this._manager.cachable != "ram"){

        var prefix = (($p.current_acl && $p.current_acl.prefix) || "") + ($p.wsql.get_user_param("zone") + "-"),
          code_length = this._metadata.code_length - prefix.length,
          part = "",
          res = $p.wsql.alasql("select max(id) as id from ? where id like '" + prefix + "%'", [this._manager.alatable]);

        // TODO: вынести установку кода в отдельную функцию

        if(res.length){
          var num0 = res[0].id || "";
          for(var i = num0.length-1; i>0; i--){
            if(isNaN(parseInt(num0[i])))
              break;
            part = num0[i] + part;
          }
          part = (parseInt(part || 0) + 1).toFixed(0);
        }else{
          part = "1";
        }
        while (part.length < code_length)
          part = "0" + part;

        this.id = prefix + part;
      }

      // для документов, контролируем заполненность даты
      if(this instanceof DocObj && $p.utils.blank.date == this.date)
        this.date = new Date();

      // если не указаны обязательные реквизиты
      if($p.msg && $p.msg.show_msg){
        for(var mf in this._metadata.fields){
          if(this._metadata.fields[mf].mandatory && !this._obj[mf]){
            $p.msg.show_msg({
              title: $p.msg.mandatory_title,
              type: "alert-error",
              text: $p.msg.mandatory_field.replace("%1", this._metadata.fields[mf].synonym)
            });
            before_save_res = false;
            return Promise.reject(reset_modified());
          }
        }  
      }

      // если процедуры перед записью завершились неудачно или запись выполнена нестандартным способом - не продолжаем
      if(before_save_res === false)
        return Promise.resolve(this).then(reset_modified);

      // если пользовательский обработчик перед записью вернул промис, его и возвращаем
      else if(before_save_res instanceof Promise || typeof before_save_res === "object" && before_save_res.then)
        return before_save_res.then(reset_modified);

      
      // в зависимости от типа кеширования, получаем saver
      if(this._manager.cachable && this._manager.cachable != "e1cib"){
        saver = $p.wsql.pouch.save_obj;

      } else {
        // запрос к серверу 1C по сети
        saver = _rest.save_irest;

      }

      // Сохраняем во внешней базе
      return saver(
        this, {
          post: post,
          operational: operational,
          attachments: attachments
        })
      // и выполняем обработку после записи
        .then(function (obj) {
          return obj._manager.handle_event(obj, "after_save");
        })
        .then(reset_modified);
    }
  },

  /**
   * ### Возвращает присоединенный объект или файл
   * @method get_attachment
   * @for DataObj
   * @param att_id {String} - идентификатор (имя) вложения
   */
  get_attachment: {
    value: function (att_id) {
      return this._manager.get_attachment(this.ref, att_id);
    }
  },

  /**
   * ### Сохраняет объект или файл во вложении
   * Вызывает {{#crossLink "DataManager/save_attachment:method"}} одноименный метод менеджера {{/crossLink}} и передаёт ссылку на себя в качестве контекста
   * 
   * @method save_attachment
   * @for DataObj
   * @param att_id {String} - идентификатор (имя) вложения
   * @param attachment {Blob|String} - вложениe
   * @param [type] {String} - mime тип
   * @return Promise.<DataObj>
   * @async
   */
  save_attachment: {
    value: function (att_id, attachment, type) {
      return this._manager.save_attachment(this.ref, att_id, attachment, type);
    }
  },

  /**
   * ### Удаляет присоединенный объект или файл
   * Вызывает одноименный метод менеджера и передаёт ссылку на себя в качестве контекста
   * 
   * @method delete_attachment
   * @for DataObj
   * @param att_id {String} - идентификатор (имя) вложения
   * @async
   */
  delete_attachment: {
    value: function (att_id) {
      return this._manager.get_attachment(this.ref, att_id);
    }
  },

  /**
   * ### Включает тихий режим
   * Режим, при котором объект не информирует мир об изменениях своих свойств.<br />
   * Полезно, например, при групповых изменениях, чтобы следящие за объектом формы не тратили время на перерисовку при изменении каждого совйтсва
   *
   * @method _silent
   * @for DataObj
   * @param [v] {Boolean}
   */
  _silent: {
    value: function (v) {
      if(typeof v == "boolean")
        this._data._silent = v;
      else{
        this._data._silent = true;
        setTimeout(function () {
          this._data._silent = false;  
        }.bind(this));
      }      
    }
  },

  /**
   * ### Выполняет команду печати
   * Вызывает одноименный метод менеджера и передаёт себя в качестве объекта печати
   *
   * @method print
   * @for DataObj
   * @param model {String} - идентификатор макета печатной формы
   * @param [wnd] - указатель на форму, из которой произведён вызов команды печати
   * @return {*|{value}|void}
   * @async
   */
  print: {
    value: function (model, wnd) {
      return this._manager.print(this, model, wnd);
    }
  }
  
});


/**
 * ### Абстрактный класс СправочникОбъект
 * @class CatObj
 * @extends DataObj
 * @constructor
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {RefDataManager}
 */
function CatObj(attr, manager) {

  var _presentation = "";

  // выполняем конструктор родительского объекта
  CatObj.superclass.constructor.call(this, attr, manager);

  /**
   * Представление объекта
   * @property presentation
   * @for CatObj
   * @type String
   */
  this.__define('presentation', {
    get : function(){

      if(this.name || this.id){
        // return this._metadata.obj_presentation || this._metadata.synonym + " " + this.name || this.id;
        return this.name || this.id || this._metadata.obj_presentation || this._metadata.synonym;
      }else
        return _presentation;

    },
    set : function(v){
      if(v)
        _presentation = String(v);
    }
  });

  if(attr && typeof attr == "object"){
    if(attr._not_set_loaded){
      delete attr._not_set_loaded;
      this._mixin(attr);
    }else{
      this._mixin(attr);
      if(!$p.utils.is_empty_guid(this.ref) && (attr.id || attr.name))
        this._set_loaded(this.ref);
    }
  }

  attr = null;

}
CatObj._extend(DataObj);

/**
 * ### Код элемента справочника
 * @property id
 * @type String|Number
 */
CatObj.prototype.__define('id', {
  get : function(){ return this._obj.id || ""},
  set : function(v){
    this.__notify('id');
    this._obj.id = v;
  },
  enumerable: true
});

/**
 * ### Наименование элемента справочника
 * @property name
 * @type String
 */
CatObj.prototype.__define('name', {
  get : function(){ return this._obj.name || ""},
  set : function(v){
    this.__notify('name');
    this._obj.name = String(v);
  },
  enumerable: true
});


/**
 * ### Абстрактный класс ДокументОбъект
 * @class DocObj
 * @extends DataObj
 * @constructor
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {RefDataManager}
 */
function DocObj(attr, manager) {

  var _presentation = "";

  // выполняем конструктор родительского объекта
  DocObj.superclass.constructor.call(this, attr, manager);

  /**
   * Представление объекта
   * @property presentation
   * @for DocObj
   * @type String
   */
  this.__define('presentation', {
    get : function(){

      if(this.number_doc)
        return (this._metadata.obj_presentation || this._metadata.synonym) + ' №' + this.number_doc + " от " + $p.moment(this.date).format($p.moment._masks.ldt);
      else
        return _presentation;

    },
    set : function(v){
      if(v)
        _presentation = String(v);
    }
  });

  if(attr && typeof attr == "object")
    this._mixin(attr);

  if(!$p.utils.is_empty_guid(this.ref) && attr.number_doc)
    this._set_loaded(this.ref);

  attr = null;
}
DocObj._extend(DataObj);

function doc_props_date_number(proto){
  proto.__define({

    /**
     * Номер документа
     * @property number_doc
     * @type {String|Number}
     */
    number_doc: {
      get : function(){ return this._obj.number_doc || ""},
      set : function(v){
        this.__notify('number_doc');
        this._obj.number_doc = v;
      },
      enumerable: true
    },

    /**
     * Дата документа
     * @property date
     * @type {Date}
     */
    date: {
      get : function(){ return this._obj.date || $p.utils.blank.date},
      set : function(v){
        this.__notify('date');
        this._obj.date = $p.utils.fix_date(v, true);
      },
      enumerable: true
    }
  });
}

DocObj.prototype.__define({

  /**
   * Признак проведения
   * @property posted
   * @type {Boolean}
   */
  posted: {
    get : function(){ return this._obj.posted || false},
    set : function(v){
      this.__notify('posted');
      this._obj.posted = $p.utils.fix_boolean(v);
    },
    enumerable: true
  }

});
doc_props_date_number(DocObj.prototype);


/**
 * ### Абстрактный класс ОбработкаОбъект
 * @class DataProcessorObj
 * @extends DataObj
 * @constructor
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {DataManager}
 */
function DataProcessorObj(attr, manager) {

  // выполняем конструктор родительского объекта
  DataProcessorObj.superclass.constructor.call(this, attr, manager);

  var f, cmd = manager.metadata();
  for(f in cmd.fields)
    attr[f] = $p.utils.fetch_type("", cmd.fields[f].type);
  for(f in cmd["tabular_sections"])
    attr[f] = [];

  this._mixin(attr);

}
DataProcessorObj._extend(DataObj);


/**
 * ### Абстрактный класс ЗадачаОбъект
 * @class TaskObj
 * @extends CatObj
 * @constructor
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {DataManager}
 */
function TaskObj(attr, manager) {

  // выполняем конструктор родительского объекта
  TaskObj.superclass.constructor.call(this, attr, manager);


}
TaskObj._extend(CatObj);
doc_props_date_number(TaskObj.prototype);


/**
 * ### Абстрактный класс БизнесПроцессОбъект
 * @class BusinessProcessObj
 * @extends CatObj
 * @constructor
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {DataManager}
 */
function BusinessProcessObj(attr, manager) {

  // выполняем конструктор родительского объекта
  BusinessProcessObj.superclass.constructor.call(this, attr, manager);


}
BusinessProcessObj._extend(CatObj);
doc_props_date_number(BusinessProcessObj.prototype);


/**
 * ### Абстрактный класс значения перечисления
 * Имеет fake-ссылку и прочие атрибуты объекта данных, но фактически - это просто значение перечисления
 *
 * @class EnumObj
 * @extends DataObj
 * @constructor
 * @param attr {Object} - объект с реквизитами в свойствах или строка guid ссылки
 * @param manager {EnumManager}
 */
function EnumObj(attr, manager) {

  // выполняем конструктор родительского объекта
  EnumObj.superclass.constructor.call(this, attr, manager);

  if(attr && typeof attr == "object")
    this._mixin(attr);

}
EnumObj._extend(DataObj);

EnumObj.prototype.__define({

  /**
   * Порядок элемента перечисления
   * @property order
   * @for EnumObj
   * @type Number
   */
  order: {
    get : function(){ return this._obj.sequence},
    set : function(v){ this._obj.sequence = parseInt(v)},
    enumerable: true
  },

  /**
   * Наименование элемента перечисления
   * @property name
   * @for EnumObj
   * @type String
   */
  name: {
    get : function(){ return this._obj.ref},
    set : function(v){ this._obj.ref = String(v)},
    enumerable: true
  },

  /**
   * Синоним элемента перечисления
   * @property synonym
   * @for EnumObj
   * @type String
   */
  synonym: {
    get : function(){ return this._obj.synonym || ""},
    set : function(v){ this._obj.synonym = String(v)},
    enumerable: true
  },

  /**
   * Представление объекта
   * @property presentation
   * @for EnumObj
   * @type String
   */
  presentation: {
    get : function(){
      return this.synonym || this.name;
    }
  },

  /**
   * Проверяет, является ли ссылка объекта пустой
   * @method empty
   * @for EnumObj
   * @return {boolean} - true, если ссылка пустая
   */
  empty: {
    value: function(){
      return !this.ref || this.ref == "_";
    }
  }
});


/**
 * ### Запись (строка) регистра
 * Используется во всех типах регистров (сведений, накопления, бухгалтерии)
 *
 * @class RegisterRow
 * @extends DataObj
 * @constructor
 * @param attr {object} - объект, по которому запись будет заполнена
 * @param manager {InfoRegManager|AccumRegManager}
 */
function RegisterRow(attr, manager){

  // выполняем конструктор родительского объекта
  RegisterRow.superclass.constructor.call(this, attr, manager);

  if(attr && typeof attr == "object")
    this._mixin(attr);

  for(var check in manager.metadata().dimensions){
    if(!attr.hasOwnProperty(check) && attr.ref){
      var keys = attr.ref.split("¶");
      Object.keys(manager.metadata().dimensions).forEach(function (fld, ind) {
        this[fld] = keys[ind];
      }.bind(this));
      break;
    }
  }

}
RegisterRow._extend(DataObj);

RegisterRow.prototype.__define({

  /**
   * Метаданные строки регистра
   * @property _metadata
   * @for RegisterRow
   * @type Object
   */
  _metadata: {
    get: function () {
      var _meta = this._manager.metadata();
      if (!_meta.fields)
        _meta.fields = ({})._mixin(_meta.dimensions)._mixin(_meta.resources)._mixin(_meta.attributes);
      return _meta;
    }
  },

  /**
   * Ключ записи регистра
   */
  ref: {
    get : function(){ 
      return this._manager.get_ref(this);
    },
    enumerable: true
  },

  presentation: {
    get: function () {
      return this._metadata.obj_presentation || this._metadata.synonym;
    }
  }
});