parser

Написать ответ на текущее сообщение

 

 
   команды управления поиском

Я лучше покажу кусок модели...

Sumo 23.02.2015 17:36 / 23.02.2015 17:52

...проекта http://voda-uzao.ru

Я оставил в коде вещи, значимые для понимания структуры модулей.


app/models/hydCore.p:
@CLASS
hydCore

@USE
pf/types/pfClass.p
pf/tests/pfAssert.p
pf/debug/pfRuntime.p

hydBaseModel.p

@BASE
pfClass

@auto[aFilespec]
  $[__^CLASS_NAME.upper[]_FILESPEC__][^aFilespec.match[^^(^taint[regex][$request:document-root])][][]]
  $[__^CLASS_NAME.upper[]_ABSOLUTE_FILESPEC__][$aFilespec]

@create[aOptions]
  ^pfAssert:isTrue($aOptions.sql is pfSQL)[SQL-объект должен быть наследником pfSQL.]
  ^BASE:create[]
  ^cleanMethodArgument[]

  $_sql[$aOptions.sql]

  $_binPath[^file:dirname[$__HYDCORE_FILESPEC__]/../bin]
  ^defReadProperty[binPath]

  $_coreRootPath[^file:dirname[$__HYDCORE_ABSOLUTE_FILESPEC__]]
  ^defReadProperty[coreRootPath]

  $_storagePath[^file:dirname[$__HYDCORE_FILESPEC__]/../../../hydra-storage]
  ^defReadProperty[storagePath]

  $_modules[^hash::create[]]

  ^addModule[regions;hydCoreRegions]
  ^addModule[counters;hydCoreCounters]
  ^addModule[mc;hydCoreManagementCompanies]
  ^addModule[vts;hydCoreVTS]
  ^addModule[feedback;hydCoreFeedback]

  ^addModule[payments;hydCorePayments]
  ^addModule[debts;hydCoreDebts]
  ^addModule[reports;hydCoreReports]

  ^addModule[logs;hydCoreLogs]

  $_speechKitKey[...]
  ^addModule[utils;hydCoreUtils;
    $.args[
      $.yandexSpeechkitKey[$_speechKitKey]
    ]
  ]

  ^addModule[security;hydCoreSecurity;
    $.args[
      $.secretKey[...]
      $.cryptKey[...]
    ]
  ]

  $_sphinxDaemonAddress[127.0.0.1:9306]
  ^addModule[search;hydCoreSearch;
    $.args[
      $.sphinxAddress[$_sphinxDaemonAddress]
    ]
  ]
  
  ^addModule[settings;pfSQLSettings;
    $.path[pf/sql/generics/settings/pfSQLSettings.p]
    $.args[
      $.sql[$_sql]
      $.ignoreKeyCase(true)
    ]
  ]

  $_uiTypes[
    $._default[
     $.id[0]
     $.title[]
    ]
    $.hydra[
      $.id[1]
      $.title[Гидра]
    ]
    $.terminal[
      $.id[2]
      $.title[Терминал]
    ]
    $.web[
      $.id[3]
      $.title[Интернет]
    ]
  ]
  ^defReadProperty[uiTypes]

@GET_CSQL[]
  $result[$_sql]

@GET_speechKit[]
  ^if(!def $_speechKit){
    ^use[pf/api/yandex/speechkit/pfYandexSpeechKit.p]
    $_speechKit[^pfYandexSpeechKit::create[$_speechKitKey]]
  }
  $result[$_speechKit]

@addModule[aModuleName;aClassName;aOptions]
## aOptions.path
## aOptions.args
  ^cleanMethodArgument[]
  $_modules.[$aModuleName][$.className[$aClassName] $.object[] $.options[$aOptions]]

@getModule[aName]
  ^if(^_modules.contains[$aName]){
    ^if(!def $_modules.[$aName].object){
      $_modules.[$aName].object[^_coreFabric[$_modules.[$aName].className;$_modules.[$aName].options]]
    }
    $result[$_modules.[$aName].object]
  }{
     ^throw[hydCore.fail;Модуль "$aName" не найден.]
   }

@GET_DEFAULT[aName]
  $result[]
  ^if(^_modules.contains[$aName]){
    ^if(!def $_modules.[$aName].object){
      $_modules.[$aName].object[^_coreFabric[$_modules.[$aName].className;$_modules.[$aName].options]]
    }
    $result[$_modules.[$aName].object]
  }

@_coreFabric[aClass;aOptions]
  ^use[^if(def $aOptions.path){$aOptions.path}{hydra/${aClass}.p}]
  $result[^reflection:create[$aClass;create;^hash::create[$aOptions.args] $.core[$self]]]
app/models/hydBaseModel.p:
 
@USE
pf/types/pfClass.p
pf/sql/orm/pfSQLTable.p


## Базовая модель

@CLASS
hydBaseModel

@BASE
pfClass

@create[aOptions]
## aOptions.core
  ^cleanMethodArgument[]
  ^pfAssert:isTrue($aOptions.core is hydCore)[core должен быть наследником hydCore.]
  $_options[$aOptions]
  $_core[$aOptions.core]
  $_now[^date::now[]]
  $_today[^date::today[]]

  $_modules[^hash::create[]]

#----- Properties -----

@GET_core[]
  $result[$_core]

@GET_CSQL[]
  $result[$core.CSQL]

@GET_precision[]
  $result[%.3f]


#----- Modules -----

@GET_DEFAULT[aName][locals]
  $result[^getModule[$aName]]

@addModule[aModuleName;aClassName;aOptions]
  ^cleanMethodArgument[]
  $_modules.[$aModuleName][$.className[$aClassName] $.object[] $.options[$aOptions]]

@_moduleFabric[aClass;aOptions]
  ^unsafe{^use[hydra/${aClass}.p]}
  $result[^reflection:create[^file:justname[$aClass];create;^hash::create[$aOptions] $.core[$_core]]]

@getModule[aName]
  ^if(^_modules.contains[$aName]){
    ^if(!def $_modules.[$aName].object){
      $_modules.[$aName].object[^_moduleFabric[$_modules.[$aName].className;$_modules.[$aName].options]]
    }
    $result[$_modules.[$aName].object]
  }{
     ^throw[hydBaseModel.fail;Модуль "$aName" не найден.]
   }


# Модель, наследник pfSQLTable, но реализующая интерфейс hydBaseModel

@CLASS
hydDBTable

@BASE
pfSQLTable

@create[aOptions]
## aOptions.tableName
## aOptions.core
  ^cleanMethodArgument[]
  ^pfAssert:isTrue(def $aOptions.core && $aOptions.core is hydCore)[core должен быть наследником hydCore.]
  $_core[$aOptions.core]
  $_options[$aOptions]
  $_now[^date::now[]]
  $_today[^date::today[]]

  ^BASE:create[^if(def $aOptions.tableName){$aOptions.tableName};^hash::create[$aOptions] $.sql[$_core.CSQL]]

  $_modules[^hash::create[]]

#----- Properties -----

@GET_core[]
  $result[$_core]

@GET_precision[]
  $result[%.3f]


#----- Modules -----

@GET_DEFAULT[aName][locals]
  ^if(^_modules.contains[$aName]){
    $result[^getModule[$aName]]
  }{
     $result[^BASE:GET_DEFAULT[$aName]]
   }

@addModule[aModuleName;aClassName;aOptions]
  ^cleanMethodArgument[]
  $_modules.[$aModuleName][$.className[$aClassName] $.object[] $.options[$aOptions]]

@_moduleFabric[aClass;aOptions]
  ^unsafe{^use[hydra/${aClass}.p]}
  $result[^reflection:create[^file:justname[$aClass];create;^hash::create[$aOptions] $.core[$_core]]]

@getModule[aName]
  ^if(^_modules.contains[$aName]){
    ^if(!def $_modules.[$aName].object){
      $_modules.[$aName].object[^_moduleFabric[$_modules.[$aName].className;$_modules.[$aName].options]]
    }
    $result[$_modules.[$aName].object]
  }{
     ^throw[hydDBTable.fail;Модуль "$aName" не найден.]
   }
Дальше пошло «мясо», которое и реализует бизнес-правила.

app/models/hydra/hydCoreRegions.p:
 
@CLASS
hydCoreRegions

@BASE
hydDBTable

@create[aOptions][locals]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[regions]
    $.tableAlias[reg]
#    $.allAsTable(true)
  ]

  ^addFields[
    $.regionID[$.dbField[region_id] $.primary(true) $.sequence(false) $.processor[uint] $.label[Код] $.plural[regions]]
    $.name[$.label[Название района] $.default[___ Безымянный район __]]
    $.districtID[$.dbField[district_id] $.plural[districts] $.processor[uint_null] $.label[Округ] $.widget[select]]
    $.slug[$.widget[none]]
    $.comment[$.label[Комментарий] $.widget[textarea]]
    $.lastImport[$.dbField[last_import] $.processor[datetime] $.widget[none]]

    $.allowWeb[$.dbField[allow_web] $.processor[bool] $.default(false) $.label[Доступ из веб-терминала] $.widget[checkbox]]

    $.firstDay[$.dbField[first_day] $.processor[uint]  $.default(0) $.label[Начало приема]]
    $.deadlineDay[$.dbField[deadline_day] $.processor[uint] $.default(0) $.label[Окончание приема]]
    $.waterDelta[$.dbField[water_delta] $.processor[uint] $.default(0) $.label[Максимальная разница]]
    $.waterMonthes[$.dbField[water_monthes] $.processor[uint] $.default(0) $.label[Акт снятия каждые]]

    $.firstActionDate[$.dbField[first_action_date] $.processor[date] $.label[]]

    $.housesCnt[$.dbField[houses_cnt] $.processor[uint] $.widget[none]]
    $.flatesCnt[$.dbField[flates_cnt] $.processor[uint] $.widget[none]]
    $.accountsCnt[$.dbField[accounts_cnt] $.processor[uint] $.widget[none]]
    $.waterCountersCnt[$.dbField[water_counters_cnt] $.processor[uint] $.widget[none]]
    $.waterFlatesCnt[$.dbField[water_flates_cnt] $.processor[uint] $.widget[none]]
    $.smsPhonesCnt[$.dbField[sms_phones_cnt] $.processor[uint] $.label[]]

    $.title1[$.dbField[title_1] $.label[Что]]
    $.title2[$.dbField[title_2] $.label[Где (в)]]
    $.title3[$.dbField[title_3] $.label[Кого]]
    $.titleSMS[$.dbField[title_sms] $.expression[(case when title_sms != "" then title_sms else title_2 end)] $.label[Для СМС, где (в)]]

    $.phones[$.label[Телефоны]]

    $.createdAt[$.dbField[created_at] $.processor[auto_now] $.skipOnUpdate(true) $.widget[none]]
    $.updatedAt[$.dbField[updated_at] $.processor[auto_now] $.widget[none]]
  ]

  $lFirstDayOfMonth[^date::create($_today.year;$_today.month;1)]
  $lFirstDayOfMonth[^lFirstDayOfMonth.sql-string[date]]

  ^addFields[
    $.deadlineDate[
      $.expression[
        (date_sub(case
           when $deadlineDay > 0 and $deadlineDay < $firstDay then
              date_add(date_add("$lFirstDayOfMonth", interval $deadlineDay - 1 day), interval 1 month)
           when deadline_day > 0
                and date_add("$lFirstDayOfMonth", interval $deadlineDay - 1 day) < last_day("^_today.sql-string[date]")
              then
                date_add("$lFirstDayOfMonth", interval $deadlineDay - 1 day)
           else
             last_day("^_today.sql-string[date]")
         end, interval if($deadlineDay < $firstDay and $_today.day <= $deadlineDay, 1, 0) month))
      ]
      $.widget[none]
    ]

    $.firstDate[
      $.expression[
        (date_sub(case
           when $firstDay > 0
                and date_add("$lFirstDayOfMonth", interval $firstDay - 1 day) <= last_day("^_today.sql-string[date]")
              then
                date_add("$lFirstDayOfMonth", interval $firstDay - 1 day)
           else
             "$lFirstDayOfMonth"
         end, interval if($deadlineDay < $firstDay and $_today.day <= $deadlineDay, 1, 0) month))
      ]
      $.widget[none]
    ]

    $.aggregateDate[
      $.expression[
        (case
          when $deadlineDay < $firstDay and $_today.day <= $deadlineDay
            then date_sub("$lFirstDayOfMonth", interval 1 month)
          else
            "$lFirstDayOfMonth"
        end)
      ]
    ]
  ]

  $lSettingsModule[^core.getModule[settings]]
  $lDefaultWebSite[$lSettingsModule.web_site]

  ^addModule[districts;hydCoreRegionsDistricts]
  $lDistricts[^getModule[districts]]
  ^addFields[
    $.district[
      $.expression[$lDistricts.name]
      $.widget[none]
    ]
    $.districtSlug[
      $.expression[$lDistricts.slug]
      $.widget[none]
    ]
    $.districtOrder[
      $.expression[$lDistricts.order]
      $.widget[none]
    ]
    $.districtTitle[
      $.expression[$lDistricts.title]
      $.widget[none]
    ]
    $.smsSystem[
      $.expression[$lDistricts.smsSystem]
      $.widget[none]
    ]
    $.webSite[
      $.expression[if($lDistricts.webSite != "", $lDistricts.webSite, "^taint[$lDefaultWebSite]")]
      $.widget[none]
    ]
  ]

  $self._defaultOrderBy[$.districtOrder[asc] $.name[asc] $.regionID[asc]]

  ^addModule[streets;hydCoreRegionsStreets]
  ^addModule[houses;hydCoreRegionsHouses]
  ^addModule[flates;hydCoreRegionsFlates]
  ^addModule[accounts;hydCoreRegionsAccounts]
  ^addModule[terminals;hydCoreRegionsTerminals]

  $self._asuCharset[windows-1251]

@_allJoin[aOptions]
  $result[^BASE:_allJoin[$aOptions]
    left join $districts.TABLE_EXPRESSION on $districtID = $districts.districtID
  ]

#---------------------------------------------------------------------------

@CLASS
hydCoreRegionsDistricts

@BASE
hydDBTable

@create[aOptions]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[districts]
    $.tableAlias[dstr]
    $.allAsTable(true)
  ]

  ^addFields[
    $.districtID[$.dbField[district_id] $.plural[districts] $.processor[uint] $.primary(true) $.widget[none]]
    $.name[$.label[]]
    $.title[$.label[]]
    $.slug[$.label[]]
    $.smsSystem[$.dbField[sms_system] $.label[]]
    $.webSite[$.dbField[web_site] $.label[]]
    $.order[$.processor[uint] $.label[]]
  ]

  $_defaultOrderBy[$.order[asc] $.name[asc]]

#---------------------------------------------------------------------------

@CLASS
hydCoreRegionsStreets

@BASE
hydDBTable

@create[aOptions]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[regions_streets]
    $.tableAlias[str]
    $.allAsTable(true)
  ]

  ^addFields[
    $.streetID[$.dbField[street_id] $.primary(true) $.sequence(false) $.processor[uint] $.plural[streets]]
    $.streetName[$.dbField[street_name]]
    $.createdAt[$.dbField[created_at] $.processor[auto_now] $.skipOnUpdate(true)]
    $.updatedAt[$.dbField[updated_at] $.processor[auto_now]]
  ]

  $self._defaultOrderBy[$.streetName[asc] $.streetID[asc]]

#---------------------------------------------------------------------------

@CLASS
hydCoreRegionsHouses

@BASE
hydDBTable

@create[aOptions][locals]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[regions_houses]
    $.tableAlias[hs]
    $.allAsTable(true)
  ]

  $lRegions[^core.getModule[regions]]
  $lStreets[^lRegions.getModule[streets]]
  ^addFields[
    $.houseID[$.dbField[house_id] $.primary(true) $.sequence(false) $.processor[uint] $.plural[houses]]
    $.houseBtiID[$.dbField[house_bti_id] $.processor[uint]]
    $.streetID[$.dbField[street_id] $.processor[uint] $.plural[streets]]
    $.houseNumber[$.dbField[house_number]]
    $.houseStr[$.dbField[house_str]]
    $.houseKorp[$.dbField[house_korp]]
    $.regionID[$.dbField[region_id] $.plural[regions] $.processor[uint]]
    $.createdAt[$.dbField[created_at] $.processor[auto_now] $.skipOnUpdate(true)]
    $.updatedAt[$.dbField[updated_at] $.processor[auto_now]]

    $.__houseKey[$.dbField[__house_key] $.widget[none]]
  ]

# Вычисляемые поля
  ^addField[streetName;$.expression[$lStreets.streetName]]
  ^addField[address;
    $.expression[
      concat($lStreets.streetName,
             ", д. ", $houseNumber,
             if($houseKorp != "", concat(", корп. ", $houseKorp), ""),
             if($houseStr != "", concat(", стр. ", $houseStr), "")
            )
    ]
  ]
  ^addField[building;
    $.expression[
      concat($houseNumber,
             if($houseKorp != "", concat(", корп. ", $houseKorp), ""),
             if($houseStr != "", concat(", стр. ", $houseStr), "")
            )
    ]
  ]

  $self._defaultOrderBy[$.streetName[asc] $.houseNumber[asc] $.houseKorp[asc] $.houseStr[asc]]

@_allJoin[aOptions]
  $result[join $core.regions.streets.TABLE_EXPRESSION on ($streetID = $core.regions.streets.streetID)]

#---------------------------------------------------------------------------

@CLASS
hydCoreRegionsFlates

@BASE
hydDBTable

@create[aOptions][locals]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[regions_flates]
    $.tableAlias[flt]
    $.allAsTable(true)
  ]

  $lRegions[^core.getModule[regions]]
  $lStreets[^lRegions.getModule[streets]]
  $lHouses[^lRegions.getModule[houses]]
  $lAccounts[^lRegions.getModule[accounts]]
  ^addFields[
    $.flatID[$.dbField[flat_id] $.primary(true) $.sequence(false) $.processor[uint] $.plural[flates]]
    $.regionID[$.dbField[region_id] $.plural[regions] $.processor[uint] $.label[]]
    $.asuFlatID[$.dbField[asu_flat_id] $.processor[uint] $.label[]]
    $.houseID[$.dbField[house_id] $.processor[uint] $.plural[houses]]
    $.flatNumber[$.dbField[flat_number]]
    $.createdAt[$.dbField[created_at] $.processor[auto_now] $.skipOnUpdate(true)]
    $.updatedAt[$.dbField[updated_at] $.processor[auto_now]]
    $.__houseKey[$.dbField[__house_key] $.widget[none]]

#   Поля для выборок
    $.accountID[$.fieldExpression[$lAccounts.accountID] $.widget[none]]
  ]

# Вычисляемые поля
  ^addField[address;
    $.expression[
      concat($lStreets.streetName,
             ", д. ", $lHouses.houseNumber,
             if($lHouses.houseKorp != "", concat(", корп. ", $lHouses.houseKorp), ""),
             if($lHouses.houseStr != "", concat(", стр. ", $lHouses.houseStr), ""),
             if($flatNumber != "", concat(", кв. ", $flatNumber), "")
            )
    ]
  ]

  ^addField[house;
    $.expression[
      concat($lStreets.streetName,
             ", д. ", $lHouses.houseNumber,
             if($lHouses.houseKorp != "", concat(", корп. ", $lHouses.houseKorp), ""),
             if($lHouses.houseStr != "", concat(", стр. ", $lHouses.houseStr), "")
            )
    ]
  ]

  ^addField[accounts;
    $.expression[group_concat(distinct $lAccounts.accountID separator ", ")]
  ]

  $self._defaultGroupBy[flatID]
  $self._defaultOrderBy[flatID+1]

@_allJoin[aOptions]
  $result[
    join $core.regions.houses.TABLE_EXPRESSION on ($houseID = $core.regions.houses.houseID)
    join $core.regions.streets.TABLE_EXPRESSION on ($core.regions.houses.streetID = $core.regions.streets.streetID)
    left join $core.regions.accounts.TABLE_EXPRESSION on ($flatID = $core.regions.accounts.flatID)
  ]

@similarTo[aFlat;aOptions]
## Ищет квартиры, «похожие» на образец
## aOptions.includeSelf(false) — ыулючить в результат квартиру-источник
## aOptions.phones — номера телефонов
## aOptions.regions[] — доступные районы
  ^cleanMethodArgument[]
  $result[^hash::create[]]

  ^if(^aOptions.contains[phones] && $aOptions.phones){
    $result[^all[
      ^if($aOptions.regions){
        $.regions[$aOptions.regions]
      }
      ^if(!^aOptions.includeSelf.bool(false)){
        $.[flatID !=][$aFlat.flatID]
      }
      $.[flatID in][^core.feedback.sms.phones.aggregate[_fields(flatID);
        $.[number in][$aOptions.phones]
        $.isActive(true)
      ]]

      $.orderBy[$.address[asc]]
    ]]
  }

#---------------------------------------------------------------------------

@CLASS
hydCoreRegionsAccounts

@BASE
hydDBTable

@create[aOptions][locals]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[regions_accounts]
    $.tableAlias[acc]
    $.allAsTable(true)
  ]

  ^addFields[
    $.accountID[$.dbField[account_id] $.widget[none]]
    $.flatID[$.dbField[flat_id] $.processor[uint] $.widget[none]]
    $.regionID[$.dbField[region_id] $.plural[regions] $.processor[uint] $.widget[none]]
    $.asuFlatID[$.dbField[asu_flat_id] $.processor[uint] $.label[]]
    $.createdAt[$.dbField[created_at] $.processor[auto_now] $.skipOnUpdate(true) $.widget[none]]
    $.updatedAt[$.dbField[updated_at] $.processor[auto_now] $.widget[none]]
  ]

#---------------------------------------------------------------------------

@CLASS
hydCoreRegionsTerminals

@BASE
hydDBTable

@create[aOptions][locals]
  ^BASE:create[^hash::create[$aOptions]
    $.tableName[regions_terminals]
    $.tableAlias[reg_term]
    $.allAsTable(true)
  ]

  $self._statuses[
    $.unset[$.id[0] $.title[Не установлен] $.badge[не установлен] $.class[] $.btnClass[]]
    $.enabled[$.id[1] $.title[Работает] $.badge[работает] $.class[success] $.btnClass[success]]
    $.disabled[$.id[2] $.title[Выключен] $.badge[выключен] $.class[important] $.btnClass[danger]]
    $.repair[$.id[3] $.title[В ремонте] $.badge[в ремонте] $.class[warning] $.btnClass[warning]]
  ]
  ^defReadProperty[statuses]

  $self._rdProtos[
    $.rdp[$.id[1] $.title[Remote desktop (Windows)] $.uriPrefix[rdp://]]
    $.vnc[$.id[2] $.title[VNC] $.uriPrefix[vnc://]]
    $.ssh[$.id[3] $.title[Secure shell (SSH)] $.uriPrefix[ssh://]]
    $.telnet[$.id[4] $.title[Telnet] $.uriPrefix[telnet://]]
  ]
  ^defReadProperty[rdProtos]

  ^addFields[
    $.terminalID[$.dbField[terminal_id] $.plural[terminals] $.processor[uint] $.primary(true) $.widget[none]]
    $.description[$.default[__Неизвестный терминал__] $.label[Место установки]]
    $.ip[$.dbField[ip] $.expression{inet_ntoa(^_builder.quoteIdentifier[$TABLE_ALIAS].^_builder.quoteIdentifier[ip])} $.processor[inet_ip] $.label[IP-адрес]]
    $.regionID[$.dbField[region_id] $.plural[regions] $.processor[uint_null] $.label[Район] $.widget[select]]

    $.comment[$.label[Комментарий] $.widget[textarea]]
    $.externalIP[$.dbField[external_ip] $.expression{inet_ntoa(^_builder.quoteIdentifier[$TABLE_ALIAS].^_builder.quoteIdentifier[external_ip])} $.processor[inet_ip] $.label[«Внешний» IP]]
    $.rdPort[$.dbField[rd_port] $.processor[uint_null] $.label[Порт]]

    $.isActive[$.dbField[is_active] $.processor[bool] $.default(1) $.widget[none]]
    $.createdAt[$.dbField[created_at] $.processor[auto_now] $.skipOnUpdate(true) $.widget[none]]
    $.updatedAt[$.dbField[updated_at] $.processor[auto_now] $.widget[none]]
  ]

  ^addFields[
    $.status[
      $.dbField[status] $.processor[uint] $.widget[none]
      $.expression[(case ^_builder.quoteIdentifier[$TABLE_ALIAS].^_builder.quoteIdentifier[status] ^_statuses.foreach[k;v]{when ^v.id.int(-1) then "^taint[$k]"}[ ] else 0 end)]
    ]
  ]

  ^addFields[
    $.rdProtocol[
      $.dbField[rd_protocol] $.processor[uint] $.label[Протокол удаленного доступа] $.widget[select]
      $.expression[(case ^_builder.quoteIdentifier[$TABLE_ALIAS].^_builder.quoteIdentifier[rd_protocol] ^_rdProtos.foreach[k;v]{when ^v.id.int(-1) then "^taint[$k]"}[ ] else 0 end)]
    ]
  ]

  $self._defaultOrderBy[(case when $regionID is null then 100000 else $regionID end) asc, $description asc, $ip asc]

@delete[aTerminalID]
  $result[^modify[$aTerminalID;
    $.isActive(false)
    $.status[$_statuses.unset.id]
  ]]

@restore[aTerminalID]
  $result[^modify[$aTerminalID;$.isActive(true)]]
И кусочек из сервисной модельки.

app/models/hydra/hydCoreUtils.p:
 
@CLASS
hydCoreUtils

## Полезные функции.

@USE
hlpFormater.p
pf/types/pfString.p

@BASE
hydBaseModel

@OPTIONS
locals

@create[aOptions]
## aOptions.yandexSpeechkitKey[]
  ^cleanMethodArgument[]
  ^BASE:create[$aOptions]
  $self._whoisScript[$core.binPath/whois]

  $self._regex[
    $.numeric[^regex::create[^^\s*\-?\s*([\d\s]+)\s*(?:(?:[\.\,]\s*)(\d+))?.*^$]]
    $.weight[^regex::create[^^[-+]?\s*(\d[\d\s]*)(?:[\.,ю]\s*(\d+))?\s*(\p{L})?][i]]
    $.allSpaces[^regex::create[\s+][g]]
    $.phone1[^regex::create[((?:\d\-)?[\d\-]+\d)][g]]
    $.phone2[^regex::create[((?:\+?[78]\s*)?\(\d+\)\s*[\d\-]+\d)][g]]
    $.phone3[^regex::create[(?:[78])(\d{10})][g]]
    $.hexValue[^regex::create[^^[0-9a-zA-Z]*^$][n]]
  ]

  $self._formater[^hlpFormater::create[]]
  ^defReadProperty[formater]

  $self.parseFailException[core.utils.parse.fail]

  ^addModule[speechkit;utils/hydCoreUtilsSpeechkit;
    $.yandexSpeechkitKey[$aOptions.yandexSpeechkitKey]
  ]
  ^addModule[phonesConverter;utils/hydCoreUtilsPhonesConverter]

#----- Strings -----

@cleanPhones[aPhones;aOptions]
## aOptions.removePrefix(false) - удалить ведущий префикс (7, 8)
  $result[^aPhones.match[$_regex.phone1][]{^match.1.match[[\-]][g][]}]
  $result[^result.match[$_regex.phone2][]{^match.1.match[[\-\+\(\)\s]][g][]}]
  ^if(^aOptions.removePrefix.bool(false)){
    $result[^result.match[$_regex.phone3][]{$match.1}]
  }

@cleanMAC[aMAC]
## Удаляет из МАК-адреса лишние символы
  $result[]
  $aMAC[^aMAC.match[[\s:\-_\.]+][gi][]]
  ^if(^aMAC.match[^^[a-fA-F0-9]{12}^$][n]){
    $result[^aMAC.lower[]]
  }

@buildTrigrams[aWord]
  $aWord[__${aWord}__]
  $result[^for[i](0;^aWord.length[]-3){^aWord.mid($i;3)}[ ]]

@levenshtein[aStr1;aStr2]
## Возвращает «расстояние Левеншетйна» между двумя строками
  $result(^pfString:levenshteinDistance[$aStr1;$aStr2])

@levenshteinForTable[aStr;aTable;aColumnName]
## Возвращает расстояния Левенштейна между aStr и всеми значениями колонки aColumnName в aTable
## aColumnName[keyword] — имя колонки в таблице
  ^if(!def $aColumnName){$aColumnName[keyword]}
  $result[^hash::create[]]
  ^aTable.menu{
    $result.[^aTable.line[]][^levenshtein[$aTable.[$aColumnName]]]
  }

@levenshteinRatio[aStr1;aStr2][locals]
## Возвращает «расстояние Левеншетйна» между двумя строками в процентах
  $lLen1(^aStr1.length[])
  $lLen2(^aStr2.length[])
  $lMaxLen(^if($lLen1 > $lLen2){$lLen1}{$lLen2})
  $result(^math:round((1 - (^levenshtein[$aStr1;$aStr2] / $lMaxLen)) * 100))

@splitByWord[aStr;aOptions][locals]
## Разбирает строку на слова
## aOptions.convertLayoutTo[] — преобразовать раскладку
## aOptions.stripSpecial(false) — удалить служебные символы
  ^cleanMethodArgument[]
  $result[^hash::create[]]

  $lConvertLayoutTo[$aOptions.convertLayoutTo]
  $lStripSpecial(^aOptions.stripSpecial.bool(false))

  ^if(def $lConvertLayoutTo){
    $aStr[^convertLayout[$lConvertLayoutTo;^aStr.trim[both]]]
  }

  $lParts[^aStr.split[ ;lv]]
  ^lParts.menu{
    $lWord[^lParts.piece.trim[both]]
    ^if($lStripSpecial){
      $lWord[^lWord.match[[\p{P}]+][g][ ]]
      $lWord[^lWord.trim[both]]
    }
    $result.[^result._count[]][^lWord.lower[]]
  }

@convertLayout[aLayout;aString][lRus;lEng;lFrom;lTo;lRegex]
## aLayout[lat|rus] - в какой раскладке хотим получить результат
  $result[]
  ^if(def $aString){
     $lEng[qwertyuiop[]asdfghjkl^;'zxcvbnm,.<>{}:"`~]
     $lRus[йцукенгшщзхъфывапролджэячсмитьбюбюхъжэёё]
     ^if($aLayout eq "rus"){
       $lFrom[$lEng^lEng.upper[]] $lTo[$lRus^lRus.upper[]] $lRegex[^taint[regex][$lEng]]
     }{
        $lFrom[$lRus^lRus.upper[]] $lTo[$lEng^lEng.upper[]] $lRegex[^taint[regex][$lRus]]
      }
     $result[^aString.match[([$lRegex])][gi]{^lTo.mid(^lFrom.pos[$match.1];1)}]
  }

@parseHexValue[aString;aOptions]
## Возвращает число из строки с шестнадуцатиричным значением
## aOptions.maxWidth[] — максимальное количество цифр в числе
## aOptions.allowEmpty(true) — допустимы пустые значения (преобразуется в 0)
  ^cleanMethodArgument[]
  $aString[^aString.match[[\p{P}\s]+][g][]]
  ^if(!^aString.match[$_regex.hexValue]
      || ($aOptions.maxWidth && ^aString.length[] > ^aOptions.maxWidth.int(0))
      || (!^aOptions.allowEmpty.bool(true) && !^aString.length[])
  ){
    ^throw[$parseFailException;Не удалось преобразовать $aString в число.]
  }
  $result(^if(def $aString){^math:convert[$aString](16;10)}{0})

#----- Dates -----

@monthRange[aDate][locals]
## Возвращает диапазон первую и конечную дату месяца для aDate
## result[$.first $.last]
  $result[^hash::create[]]
  $lDate[^date::create[$aDate]]
  $result.first[^date::create($lDate.year;$lDate.month;1;0;0;0)]
  $result.last[^date::create($lDate.year;$lDate.month;^lDate.last-day[];23;59;59)]

@weeks[aDate;aOptions][locals]
## Вычисляем недели относительно aDate
## aOptions.before(0) — недель до
## aOptions.after(0) — недель после
## result[$.[year_num,week_num][$.monday $.sunday $weekYear $.current(bool)]]
  $result[^hash::create[]]
  $aDate[^if(def $aDate){^date::create[$aDate]}{$_today}]
  $lCurMonday[^date::create[^aDate.sql-string[date]]]
  ^lCurMonday.roll[day](-1 * ^if($lCurMonday.weekday > 0)($lCurMonday.weekday - 1)(6))

  $lBefore(^aOptions.before.int(0))
  $lAfter(^aOptions.after.int(0))

  $lMonday[^date::create[$lCurMonday]]
  ^lMonday.roll[day](-7*$lBefore)
  ^for[i](1;$lBefore + $lAfter + 1){
    $result.[$lMonday.year,^lMonday.week.format[%02d]][^weekFor[$lMonday]]
    ^lMonday.roll[day](+7)
  }

@weekFor[aDate][locals]
## Вычисляет данные для недели по дате
## result[hash]
  $aDate[^if(def $aDate){^date::create[$aDate]}{$_today}]
  $lMonday[^date::create[^aDate.sql-string[date]]]
  ^lMonday.roll[day](-1 * ^if($lMonday.weekday > 0)($lMonday.weekday - 1)(6))
  $result[
    $.week[$lMonday.week]
    $.year[$lMonday.weekyear]
    $.monday[^date::create[$lMonday]]
    $.sunday[^date::create[$lMonday]]
    $.current($lMonday.weekyear == $_today.weekyear && $lMonday.week == $_today.week)
  ]
  ^result.sunday.roll[day](+6)

@getWeek[aYear;aWeek][locals]
## Получает неделю по номеру
  $lDate[^date::create($aYear;1;1)]
  ^if($lDate != 1){
    ^lDate.roll[day](+7)
  }
  ^lDate.roll[day](7 * ($aWeek - 1))
  $result[^weekForDate[$lDate]]

@fromUTC[aDatetime][locals]
## Возвращает время из UTC в зоне сервера.
  $lUTCNow[^date::create[$_now]]
  ^lUTCNow.roll[TZ;UTC]
  $result[^date::create(^date::create[$aDatetime] + ($_now - ^date::create($lUTCNow.year;$lUTCNow.month;$lUTCNow.day;$lUTCNow.hour;$lUTCNow.minute;$lUTCNow.second)))]

#----- Parsers -----

@parseNumeric[aSumma;aPrecision;aDefault]
## Парсит сумму
## aPrecision(2) - точность
  $result[^aSumma.match[$_regex.numeric][]{^match.1.match[$_regex.allSpaces][][]^if(def $match.2){.${match.2}}}]
  $result[^eval(^result.double(^aDefault.double(0)))[%.^aPrecision.int(2)f]]

#----- Networks -----

@whois[aObject][lExec]
  $result[]
  $lExec[^file::exec[$_whoisScript;;$aObject]]
  ^if($lExec.status == 0){
    $result[^unsafe{^lExec.text.match[^^#.*?\n+][gm][]}{$lExec.text}]
  }