367 lines
23 KiB
Plaintext
367 lines
23 KiB
Plaintext
<cfsilent>
|
||
<cfimport prefix="m" taglib="lib"/>
|
||
<cfimport prefix="c" taglib="lib/controls"/>
|
||
<cfimport prefix="d" taglib="lib/data"/>
|
||
<cfimport prefix="layout" taglib="layout"/>
|
||
</cfsilent><m:silent silent="No">
|
||
|
||
<m:prepare_ls entity="service_price" pageInfoOut="pageInfo" trackOut="tr"/>
|
||
|
||
<m:filter_settings target="#pageInfo.entity#_ls">
|
||
<m:filterparam filter=#filter# param="quickfilter" ftype="string" prefix="%" suffix="%" expression="((p.param_class_type like ?) OR (p.division like ?) )" default=""/>
|
||
</m:filter_settings>
|
||
<cfset pageInfo.settings.filter=#filter#/>
|
||
|
||
<!---<cftry>--->
|
||
<cfquery name="qRead" datasource="#request.DS#">
|
||
select
|
||
<d:field_set titleMapOut="qReadTitleMap" lengthOut="qReadFieldCount">
|
||
<d:field title="service_price_id" cfSqlType="CF_SQL_VARCHAR">prc.service_price_id</d:field>
|
||
<d:field title="service_param_price_id" cfSqlType="CF_SQL_VARCHAR">prc.service_param_price_id</d:field>
|
||
<d:field title="Модель цены">prm.pricing_model</d:field>
|
||
<d:field title="Модель цены">prm.pricing_model_short</d:field>
|
||
<d:field title="Модель цены">prm.pricing_model_code</d:field>
|
||
<d:field title="Период цены">prc.pricing_period</d:field>
|
||
<d:field title="Период опроса">prc.rating_period</d:field>
|
||
|
||
<d:field title="НДС%">s.vat_perc</d:field>
|
||
<d:field title="НДС не обл.">s.vat_free</d:field>
|
||
<d:field>case when s.vat_free then 0. else vat_perc/100. end as vat_rate</d:field>
|
||
|
||
<d:field title=#i18("GPL без НДС, ₽","GPL w/VAT, ₽")# cfSqlType="CF_SQL_DECIMAL">prc.price</d:field>
|
||
<d:field title=#i18("Цена со ск. без НДС, ₽","Price disc. w/VAT, ₽")# cfSqlType="CF_SQL_DECIMAL">case when price*.95 > min_price then price*.95 else min_price end as discount_price</d:field>/***/
|
||
<d:field title="Дно" cfSqlType="CF_SQL_DECIMAL">min_price</d:field>
|
||
<d:field title="Статус цены" cfSqlType="CF_SQL_DECIMAL">prc.status as price_status</d:field>
|
||
<d:field title="Начальная дата действия цены" cfSqlType="CF_SQL_TIMESTAMP">prc.dt_from</d:field>
|
||
<d:field title="Конечная дата действия цены" cfSqlType="CF_SQL_TIMESTAMP">prc.dt_to</d:field>
|
||
|
||
<d:field title=#i18("Макс. скидка, %","Max discount %")# cfSqlType="CF_SQL_DECIMAL">case when prc.price >0 then (prc.price-prc.min_price)/prc.price else NULL end as max_discount</d:field>
|
||
|
||
<d:field title="abstract_service_id" cfSqlType="CF_SQL_INTEGER">a.abstract_service_id</d:field>
|
||
<d:field title="service_id" cfSqlType="CF_SQL_INTEGER">s.service_id</d:field>
|
||
|
||
<d:field title=#i18("Код группы","Group Code")#>g.area_code</d:field>
|
||
<d:field title="Группа">g.area</d:field>
|
||
<d:field title="Группа">g.analytic_code</d:field>
|
||
<d:field title="Catalog Group">g.area_en</d:field>
|
||
|
||
<d:field title="Статус">u.status</d:field>
|
||
<d:field title="Status">u.status_en</d:field>
|
||
|
||
<d:field>g.area_code||'.'||a.code||'.'||m.code||'.'||p.code as code</d:field>
|
||
<d:field>a.code as abstract_service_code</d:field>
|
||
<d:field>m.code as modifier_code</d:field>
|
||
<d:field>p.code as param_code</d:field>
|
||
|
||
<d:field title="ID статуса абстрактной услуги">a.status_id</d:field>
|
||
<d:field title="Абстрактная услуга">a.abstract_service</d:field>
|
||
<d:field title="Abstract Service">a.abstract_service_en</d:field>
|
||
|
||
<d:field title="modifier_id" cfSqlType="CF_SQL_INTEGER">m.modifier_id</d:field>
|
||
|
||
<d:field title="Класс модификатора">mc.modifier_class</d:field>
|
||
<d:field title="Модификатор">m.modifier</d:field>
|
||
<d:field title="Modifier class">mc.modifier_class_en</d:field>
|
||
<d:field title="Modifier">m.modifier_en</d:field>
|
||
|
||
<d:field title="service_param_id" cfSqlType="CF_SQL_INTEGER">sp.service_param_id</d:field>
|
||
<d:field title="param_class_id" cfSqlType="CF_SQL_INTEGER">pc.param_class_id</d:field>
|
||
<d:field title="param_class">pc.param_class</d:field>
|
||
<d:field title="Сортировка компонента">ac.sort as param_class_sort</d:field><!--- порядок сортировки в списке компонентов, например CPU-RAM-HDD-GPU --->
|
||
<d:field title="Сортировка варианта компонента">p.sort as param_sort</d:field><!--- порядок сортировки в списке вариантов компонентов, например SATA-SAS-SSD --->
|
||
<d:field title="Компонент">p.param</d:field>
|
||
<d:field title="Resource">p.param_en</d:field>
|
||
<d:field title="param_id" cfSqlType="CF_SQL_INTEGER">p.param_id</d:field>
|
||
<d:field title="Ед.изм.">case when p.param_id IS NULL then se.measure_short else e.measure_short end as measure_short</d:field>
|
||
<d:field title="Measure">case when p.param_id IS NULL then se.measure_short_en else e.measure_short_en end as measure_short_en</d:field>
|
||
|
||
</d:field_set>
|
||
from (
|
||
select
|
||
t.service_param_price_id, t.price, t.min_price, t.service_param_id, t.pricing_model_id
|
||
, null as service_price_id, sp.service_id, t.pricing_period, t.rating_period
|
||
,t.status, t.dt_from, t.dt_to
|
||
from service_param_price t join service_param sp on (t.service_param_id=sp.service_param_id)
|
||
union all
|
||
select
|
||
null, st.price, st.min_price, null, st.pricing_model_id, st.service_price_id, st.service_id, st.pricing_period, st.rating_period
|
||
,st.status, st.dt_from, st.dt_to
|
||
from service_price st
|
||
) prc
|
||
left outer join service s on (prc.service_id=s.service_id)
|
||
left outer join service_param sp on (prc.service_param_id=sp.service_param_id)
|
||
left outer join abstract_service a on (s.abstract_service_id=a.abstract_service_id)
|
||
left outer join modifier m on (s.modifier_id=m.modifier_id)
|
||
left outer join modifier_class mc on (m.modifier_class_id=mc.modifier_class_id)
|
||
left outer join abstract_service_param_class ac on (sp.abstract_service_param_class_id=ac.abstract_service_param_class_id)
|
||
left outer join param_class pc on (ac.param_class_id=pc.param_class_id)
|
||
left outer join param p on (sp.param_id=p.param_id)
|
||
left outer join measure e on (p.measure_id=e.measure_id)
|
||
left outer join measure se on (s.measure_id=se.measure_id)
|
||
left outer join area g on (a.area_id=g.area_id)
|
||
left outer join status u on (a.status_id=u.status_id)
|
||
left outer join pricing_model prm on (prc.pricing_model_id=prm.pricing_model_id)
|
||
where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/>
|
||
order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#qReadFieldCount#/>
|
||
</cfquery>
|
||
|
||
<!---
|
||
<cfcatch type="database">
|
||
<m:ls_catch catch=#cfcatch# status=#pageInfo.status#/>
|
||
</cfcatch>
|
||
</cftry>--->
|
||
<!--- <cfdump var=#qRead#/> --->
|
||
|
||
<cfquery name="qCountTotal" datasource="#request.DS#">
|
||
select (select count(*) from service_param_price) + (select count(*) from service_price) as cnt;
|
||
</cfquery>
|
||
|
||
</m:silent><!---
|
||
-----------------------------------------------------------------------------------------
|
||
-----------------------------------------------------------------------------------------
|
||
-----------------------------------------------------------------------------------------
|
||
---><cfif isDefined("output_xls")>
|
||
<layout:xml qRead=#qRead# titleMap=#qReadTitleMap# filename="#pageInfo.entity#.xml"/>
|
||
<cfabort/><!--- *** nonstandard name for titleMap --->
|
||
</cfif><cfif isDefined("output_json")><!--- *** incorrect dt_from, dt_to format --->
|
||
<layout:json qRead=#qRead# titleMap=#qReadTitleMap# filename="#pageInfo.entity#.json"/>
|
||
<cfabort/><!--- *** nonstandard name for titleMap --->
|
||
</cfif><!---
|
||
---><layout:page section="header" pageInfo=#pageInfo#>
|
||
|
||
<layout:attribute name="title">
|
||
<cfoutput><b>#i18("Прайс-лист базовый","General Price List")#</b></cfoutput>
|
||
</layout:attribute>
|
||
</layout:page>
|
||
|
||
|
||
|
||
<cfif pageInfo.readPermitted() AND !pageInfo.status.errorState>
|
||
|
||
<layout:grid_summary
|
||
recordCount=#qRead.recordCount#
|
||
totalCount=#qCountTotal.cnt#
|
||
footerOut="gridFooter"
|
||
excelLink="YES"<!--- #pageInfo.writePermitted()# --->
|
||
jsonLink="YES"<!--- #pageInfo.writePermitted()# --->
|
||
/>
|
||
<!---<cfdump var=#qRead#/>--->
|
||
|
||
|
||
|
||
<cffunction name="formatPrice" output="true">
|
||
<cfargument name="price"/>
|
||
<cfif isNumeric(price)><cfreturn numberFormat(price,'.00')/><cfelse><cfreturn "(по запросу)"></cfif>
|
||
</cffunction>
|
||
<cfset request.formatPrice=formatPrice/>
|
||
|
||
<!--- add formatted fields (запись форматированного значения в поле на место исходного выглядит неопрятно) --->
|
||
<!--- можно заметить, что если многие поля оформлены ссылками, то форматировать много, и выглядит это громоздко --->
|
||
<cfset queryAddColumn(qRead,'f_link_view_edit')/>
|
||
<!--- <cfset qReadTitleMap.f_link_view_edit={ordinal=#StructCount(qReadTitleMap)+1#}/> --->
|
||
<cfset qReadTitleMap.f_link_view_edit={ordinal=0}/><!--- сортировки по этой колонке нет, ее порядок в исходном резалтсете не нужен, как и заголовок --->
|
||
|
||
<cfset queryAddColumn(qRead,'f_link_del')/>
|
||
<cfset qReadTitleMap.f_link_del={ordinal=0}/><!--- сортировки по этой колонке нет, ее порядок в исходном резалтсете не нужен, как и заголовок --->
|
||
|
||
<cfset queryAddColumn(qRead,'f_service')/>
|
||
<!--- <cfset qReadTitleMap.f_service={ordinal=#qReadTitleMap.abstract_service.ordinal#}/> --->
|
||
<cfset qReadTitleMap.f_service=qReadTitleMap.abstract_service/>
|
||
<cfset qReadTitleMap.f_service.title="Вариант услуги"/>
|
||
<!--- "кодовый" стиль, плохо читается. А все ради лаконичного формата c:table (который скорее тривиален) --->
|
||
|
||
<cfset queryAddColumn(qRead,'f_area_code')/>
|
||
<cfset qReadTitleMap.f_area_code=qReadTitleMap.analytic_code/><!--- просто копируем метаданные колонки аналитического кода --->
|
||
|
||
<cfset queryAddColumn(qRead,'f_component')/>
|
||
<cfset qReadTitleMap.f_component=qReadTitleMap.param_class/>
|
||
<cfset qReadTitleMap.f_component.title="Компонент"/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_pricing_model')/>
|
||
<cfset qReadTitleMap.f_pricing_model={ordinal=qReadTitleMap.pricing_model.ordinal,title="Модель цены"}/>
|
||
<!--- *** Неудобно высчитывать ordinal, надо привязаться к существующей колонке
|
||
*** Хочется иметь возможность сортировки по нескольким колонкам, раз уж нереально по синтетической - поскольку мы вынесли форматирование из БД --->
|
||
<cfset queryAddColumn(qRead,'f_code')/>
|
||
<cfset qReadTitleMap.f_code={ordinal=qReadTitleMap.area_code.ordinal,title="Код"}/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_price')/>
|
||
<cfset qReadTitleMap.f_price=qReadTitleMap.price/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_min_price')/>
|
||
<cfset qReadTitleMap.f_min_price=qReadTitleMap.min_price/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_discount_price')/>
|
||
<cfset qReadTitleMap.f_discount_price=qReadTitleMap.discount_price/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_max_discount')/>
|
||
<cfset qReadTitleMap.f_max_discount=qReadTitleMap.max_discount/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_area')/>
|
||
<cfset qReadTitleMap.f_area=qReadTitleMap.analytic_code/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_param_class_sort')/>
|
||
<cfset qReadTitleMap.f_param_class_sort=qReadTitleMap.param_class_sort/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_param_sort')/>
|
||
<cfset qReadTitleMap.f_param_sort=qReadTitleMap.param_sort/>
|
||
|
||
<cfset queryAddColumn(qRead,'f_vat_free')/>
|
||
<cfset qReadTitleMap.f_vat_free=qReadTitleMap.vat_free/>
|
||
|
||
<!--- preformat resultset fields --->
|
||
<cfoutput query=#qRead# startRow=#pageInfo.nStart# maxRows=#pageInfo.recordsPerPage#>
|
||
<cfsavecontent variable="qRead.f_link_view_edit">
|
||
<cfif len(service_param_price_id)>
|
||
<a href="service_param_price.cfm?service_param_price_id=#service_param_price_id#&#tr.fwx#" name="#service_param_price_id#"
|
||
<cfif pageInfo.writePermitted()>title="редактировать"
|
||
<cfelse>title="просмотр"</cfif>><img src="img/piece.png" width="13" height="13"/></a>
|
||
<cfelse>
|
||
<a href="service_price.cfm?service_price_id=#service_price_id#&#tr.fwx#" name="#service_price_id#"
|
||
<cfif pageInfo.writePermitted()>title="редактировать"
|
||
<cfelse>title="просмотр"</cfif>><img src="img/svc.jpg" width="16" height="16"/></a>
|
||
</cfif>
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_link_del">
|
||
<cfif len(service_param_price_id)>
|
||
<a href="service_param_price_del.cfm?service_param_price_id=#service_param_price_id#&#tr.fwx#" class="del"></a>
|
||
<cfelse>
|
||
<a href="service_price_del.cfm?service_price_id=#service_price_id#&#tr.fwx#" class="del"></a>
|
||
</cfif>
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_area_code">
|
||
#analytic_code# #area_code#
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_service">
|
||
<a href="service.cfm?service_id=#service_id#&#tr.fwx#">
|
||
#abstract_service#<!--- <cfif modifier_id GT 0>:</cfif> ---> #modifier#
|
||
</a>
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_component">
|
||
<a href="service_param.cfm?service_param_id=#service_param_id#&#tr.fwx#">
|
||
#param_class#
|
||
#param# <!--- *** может быть param_id для тривиального варианта - а как правильно? Разница с вариантом услуги - в механизме образования варианта, для услуги тривиальный вариант образуется null-модификатором, а для параметра - пустым названием (оба способа не слишком изящны, а наличие двух еще усложняет игру) --->
|
||
</a>
|
||
</cfsavecontent>
|
||
<cfsavecontent variable="qRead.f_pricing_model">
|
||
#pricing_model#
|
||
</cfsavecontent>
|
||
<cfsavecontent variable="qRead.f_code">
|
||
#request.skuCode(area_code,abstract_service_code,modifier_code,param_code,pricing_model_code)#
|
||
</cfsavecontent>
|
||
<cfsavecontent variable="qRead.f_area">
|
||
#analytic_code# #area#
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_price">
|
||
<cfif isNumeric(price)>
|
||
<cfif len(service_price_id)>
|
||
<a href="service_price.cfm?service_price_id=#service_price_id#&#tr.fwx#">
|
||
#numberFormat(price,'.00')#
|
||
</a>
|
||
<cfelseif len(service_param_price_id)>
|
||
<a href="service_param_price.cfm?service_param_price_id=#service_param_price_id#&#tr.fwx#">
|
||
#numberFormat(price,'.00')#
|
||
</a>
|
||
</cfif>
|
||
<cfelse>
|
||
<i>по запросу</i>
|
||
</cfif>
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_discount_price">
|
||
<cfif isNumeric(discount_price)>
|
||
#numberFormat(discount_price,'.00')#
|
||
<cfelse>
|
||
<i>по запросу</i>
|
||
</cfif>
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_min_price">
|
||
<cfif isNumeric(min_price)>
|
||
#numberFormat(min_price,'.00')#
|
||
<cfelse>
|
||
<i>по запросу</i>
|
||
</cfif>
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_max_discount">
|
||
#numberFormat(f_max_discount,'.0')#
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_param_class_sort">
|
||
#f_param_class_sort#
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_param_sort">
|
||
#f_param_sort#
|
||
</cfsavecontent>
|
||
|
||
<cfsavecontent variable="qRead.f_vat_free">
|
||
<cfif qRead.vat_free GT 0>Не облагается</cfif>
|
||
</cfsavecontent>
|
||
</cfoutput>
|
||
|
||
<!---build table markup --->
|
||
<c:table query=#qRead# recordsPerPage=#pageInfo.recordsPerPage# nStart=#pageInfo.nStart# titleMap=#qReadTitleMap# sortArray=#pageInfo.settings.sort.sortArray# class="worktable wide">
|
||
<c:column width="1%" sortable=false>
|
||
<c:th><c:link_add canWrite=#pageInfo.writePermitted()# entity=#pageInfo.entity# key="service_price_id" fwx=#tr.fwx#/></c:th>
|
||
<c:td field="f_link_view_edit" class="c"/>
|
||
</c:column>
|
||
<c:column width="7%" field="f_area"/>
|
||
<c:column width="7%" field="f_code"/>
|
||
<c:column width="3%" field="status"><c:td class="c"/><!--- иллюстрация передачи атрибута ---></c:column>
|
||
<c:column width="20%" field="f_service"/>
|
||
<c:column width="15%" field="f_component"/><!---columnOrder="3"--->
|
||
<c:column width="5%" field="f_pricing_model"><c:td class="l"/></c:column>
|
||
<c:column width="3%" field="pricing_period"><c:td class="c"/></c:column>
|
||
<c:column width="3%" field="rating_period"><c:td class="c"/></c:column>
|
||
<c:column width="1%" field="measure_short"/>
|
||
<c:column width="3%" field="f_price"><c:td class="r"/></c:column>
|
||
<!--- <c:column width="3%" field="f_max_discount"><c:td class="r"/></c:column> --->
|
||
<c:column width="3%" field="f_discount_price"><c:td class="r"/></c:column>
|
||
<c:column width="3%" field="f_min_price"><c:td class="r"/></c:column>
|
||
<c:column width="2%" field="f_vat_free"><c:td class="c"/></c:column>
|
||
<c:column width="2%" field="vat_perc"><c:td class="r"/></c:column>
|
||
<c:column width="1%" sortable=false>
|
||
<c:th><!--- ---><!---*** пришлось что-нибудь написать, иначе c:th ищет field.title и, не найдя, бросает исключение ---></c:th>
|
||
<c:td field="f_link_del" class="c"/>
|
||
</c:column>
|
||
</c:table>
|
||
|
||
<cfoutput>#gridFooter#</cfoutput>
|
||
|
||
|
||
</cfif>
|
||
<layout:page section="footer"/>
|
||
|
||
<!---*** критика синтаксиса: CALLER в данном контексте выглядит ужасно. Когда применять #service_id#, когда ##service_id##
|
||
Может быть, стоит отказаться от пользовательского тега и обратиться к более ровному способу форматирования.
|
||
Например (явно плохая идея, но для примера) печатать таблицу, а потом ее парсить и переформатировать... некоторые это могли сделать на клиенте.
|
||
На самом деле все, что нужно нам из скоупа CALLER, уже определено в момент вызова тега, и мы можем заменить CALLER на <cfoutput>. Это тоже неуклюже. То есть тащить весь CALLER внутрь как бы избыточно (не говоря о том, что можно словить каких-то сложносочиненных глюков)
|
||
Сортировка должна происходить на сервере БД, иначе паджинатор бесполезен. Вытаскивать резалтсет на сервер приложений и там его сортировать выглядит очень плохим решением (еще глупее сортировать на клиенте). Для корректной сортировки нужно делать композицию колонок на сервере, что не очень хорошо, например, с нуллами надо обращаться осторожно (вот аргумент в пользу NOT NULL), и синтаксис становится зависим от диалекта SQL. Возможный вариант, если у нас композиция из 2 колонок, сортировать по обеим. Либо отказаться от сортировки по композитной колонке, что кажется проще всего.
|
||
|
||
Контекст query выглядит естественно, но эта простота вводит в заблуждение - на вид обычный синтаксис, а функции недоступны (паковать в реквест еще то удовольствие), контекст страницы недоступен, ищи его через CALLER - догадаться невозможно.
|
||
|
||
Можно передавать внутрь тега все, что может понадобиться - функции, переменные?
|
||
Можно придумать собственный синтаксис, заменяющий cfif
|
||
--->
|
||
<!--- тонкости
|
||
в примере ниже
|
||
Атрибуты тега вычисляются до вызова, в контексте вызывающей страницы, а содержимое обрабатывается самим тегом в его контексте (собственно, мы сказали ему так делать). Но при чтении исходника это совсем не очевидно и может порвать мозг.
|
||
Можно, конечно, обрабатывать исключение и подставлять контекст вызывающей страницы? Как заставить тег видеть контекст вызывающей страницы? Попробовал взять весь тег в cfoutput. В результате перестал видеть поля query. Откатился.
|
||
Печаль в том, что мы в одном месте (чего и хотели - чтобы рядом) получаем контекст вызывающей страницы и контекст query, поля которого хотим называть неквалифицированными именами.
|
||
Вариант: действуем в 2 прохода, сначала формируем структуру, а потом рендерим ее. В принципе, с учетом пагинации, может потреблять памяти меньше, чем сам query
|
||
<c:th><c:link_add canWrite=#pageInfo.writePermitted()# entity=#pageInfo.entity# fwx=#tr.fwx#/><cfoutput>#pageInfo.entity#</cfoutput>#pageInfo.entity#</c:th> --->
|
||
<!--- Хотелось бы:
|
||
чтобы контекст страницы был явно доступен во всех вложенных тегах. >> The custom tag's Caller scope is a reference to the calling page's Variables scope (добавить все из CALLER в Variables? А не зациклимся?
|
||
чтобы итератор работал прозрачно, как в cfoutput query
|
||
Как обойтись без динамического исполнения? Для всех форматтеров снаружи объявлять closure? нечитабельно.
|
||
Хотя читабельность простыней ниже - так себе
|
||
В общем, получается пока плохо
|
||
? полиморфизм i18(measure_short) - как бы избежать отдельной далекой декларации длинного списка похожих функций. Можно полю придать признак языка? Соглашение с суффиксами? Это уже какая-то морфология получается!
|
||
|
||
---> |