spec/service_param.cfm
2025-06-02 16:16:51 +03:00

498 lines
24 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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">
<cffunction name="plain2HtmClean">
<cfargument name="s" type="string"/>
<cfreturn request.plain2htm(request.cleanHtm(s))/>
</cffunction>
<cffunction name="cleanInput">
<cfargument name="s" type="string"/>
<cfreturn htmlEditFormat(s)/>
</cffunction>
<cffunction name="appendKVFromStructByList" output="No">
<!--- взять из структуры (container) все поля по списку (ls), которые найдутся, и добавить к предоставленному списку (prefix) в виде key=value (key - элемент ls, value - соответствующее поле из структуры container) --->
<cfargument name="prefix" type="string">
<cfargument name="container" type="struct">
<cfargument name="ls" type="string">
<cfargument name="inputDelimiter" type="string" default=",">
<cfargument name="outputDelimiter" type="string" defaul="&">
<cfset var output=#arguments.prefix#/>
<cfloop list=#arguments.ls# delimiters=#arguments.inputDelimiter# index="key">
<cfif structKeyExists(#arguments.container#,#key#)>
<cfset var value=structFind(#arguments.container#,#key#)/>
<cfset output=listAppend(output,"#key#=#value#","#arguments.outputDelimiter#")/>
</cfif>
</cfloop>
<cfreturn output/>
</cffunction>
<!--- <cfdump var=#form#/> --->
<!--- *** трэш какой-то получился: --->
<!--- попытка запихать параметры несозданной сущности в трек --->
<!--- дублируем декларацию части параметров в bean, а куда деваться... лучше не придумал --->
<cfparam name="service_param_id" default="-1"/><!--- Не проверяем тип, потому что только принимаем решение о валидности, в качестве которого сравнение с нулем прокатывает --->
<cfif service_param_id GT 0>
<m:prepare_detail entity="service_param" key="service_param_id" pageInfoOut="pageInfo"/>
<cfelse>
<!--- Можно отметить, что новая запись появляется только через URL (переходом через GET)
Можно отметить, что симметрично мы можем заходить не со стороны сервиса, а со стороны параметра
Из формы еще не существующей таблицы-связки мы пытаемся создать предметную сущность --->
<cfparam name="service_id" type="integer" default="-1"/>
<m:prepare_detail entity="service_param" key="service_param_id" pageInfoOut="pageInfo"
thisUrl='#request.thisPage#?#appendKVFromStructByList(
"service_param_id=-1",
URL,
"param_class_id,abstract_service_param_class_id,service_id,param_id",
",",
"&"
)#'/>
</cfif>
<cfparam name="abstract_service_param_class_id" default="-1"/>
<cfparam name="service_id" default="-1"/>
<cfparam name="param_id" default="-1"/>
<cfif NOT abstract_service_param_class_id GT 0>
<cfif service_id GT 0 AND param_id GT 0>
<!--- resolve abstract_service_param_class_id and patch service_param
*** выглядит криво
*** кажется, не гарантирует одной строчки, особенно при расшаренных параметрах?
*** а может быть, при упрощении структуры, без расшаренных параметров, сразу станет проще?--->
<!--- *** возможно, структура данных избыточна? --->
<cfquery name="qAbstractServiceParamClass" datasource="#request.DS#">
select abstract_service_param_class_id
from param p
join param_class pc on (p.param_class_id=pc.param_class_id)
join abstract_service_param_class ac on (pc.param_class_id=ac.param_class_id)
join abstract_service a on (ac.abstract_service_id=a.abstract_service_id)
join service s on (a.abstract_service_id=s.abstract_service_id)
where param_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#param_id#" null=#!isNumeric(param_id)#/>
AND service_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#service_id#" null=#!isNumeric(service_id)#/>
</cfquery>
<!--- <cfdump var=#qAbstractServiceParamClass#/> --->
<cfset abstract_service_param_class_id=qAbstractServiceParamClass.abstract_service_param_class_id/>
<!--- или надо инжектировать в форму?--->
</cfif>
</cfif>
<d:bean readonly=#!pageInfo.writePermitted()# table="#pageInfo.entity#" datasource="#request.DS#" output="d" status="status">
<d:param field="service_param_id" type="integer" key autoincrement/>
<d:param field="abstract_service_param_class_id" type="integer" required displayname="Компонент абстрактной услуги"/>
<!--- *** наверное, нужно как-то проверять консистентность с abstract_service, чтобы чужой компонент не мог попасть в abstract_service_param_class_id *** а такие случаи уже есть --->
<d:param field="service_id" type="integer" required/>
<d:param field="param_id" type="integer" forNull=""/>
<d:param field="min_value" type="numeric" init="0" default="0" forNull=""/>
<d:param field="max_value" type="numeric" init="1" default="1" forNull=""/>
<d:param field="incr" type="numeric" init="1" default="" forNull=""/>
<d:param field="descr" type="varchar" preprocessor=#plain2HtmClean#/>
<d:param field="creator_id" type="integer" value="#request.usr_id#" skipUpdate/>
<d:param field="updater_id" type="integer" value="#request.usr_id#" />
<d:param field="dt_created" type="timestamp" value="#Now()#" skipUpdate/>
<d:param field="dt_updated" type="timestamp" value="#Now()#"/>
</d:bean>
<cfif NOT abstract_service_param_class_id GT 0>
<cfset abstract_service_param_class_id=d.abstract_service_param_class_id/>
</cfif>
<cfset tr=pageInfo.track/>
<!--- <cfdump var=#d#/> --->
<m:dispatch_detail
usePRG="No"<!---*** --->
pageInfo=#pageInfo#
id="#d.service_param_id#"
status=#pageInfo.status#
<!--- trackOut="tr" --->
idAttributesOut="id"
/>
<cfquery name="qService" datasource="#request.DS#">
select
s.service_id
,a.abstract_service
,s.abstract_service_id
,a.code as abstract_service_code
,a.area_id
,g.area_code
,s.modifier_id
,m.modifier
,m.code as modifier_code
,c.modifier_class_id
,c.modifier_class
from service s
join abstract_service a on (s.abstract_service_id=a.abstract_service_id)
join area g on (a.area_id=g.area_id)
left outer join modifier m on (s.modifier_id=m.modifier_id)
left outer join modifier_class c on (m.modifier_class_id=c.modifier_class_id)
where service_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#d.service_id#" null=#!isNumeric(service_id)#/>
</cfquery>
<!---*** Имена запросов ниже по тексту переопределяются--->
<cfquery name="qParam" datasource="#request.DS#">
select p.param_id, p.param, m.measure_id, m.measure, m.measure_short, p.param_class_id, c.param_class, p.code, p.precision
from param p
left outer join measure m on m.measure_id=p.measure_id
left outer join param_class c on p.param_class_id=c.param_class_id
where param_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#d.param_id#" null=#!isNumeric(d.param_id)#/>
</cfquery>
<cfquery name="qParamClass" datasource="#request.DS#">
select p.param_class_id, p.param_class, p.precision, m.measure_id, m.measure, m.measure_short, ac.sort
from param_class p
join abstract_service_param_class ac on (p.param_class_id=ac.param_class_id)
left outer join measure m on m.measure_id=p.measure_id
where ac.abstract_service_param_class_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#abstract_service_param_class_id#" null=#!isNumeric(abstract_service_param_class_id)#/>
</cfquery>
<cfquery name="qDecoration" datasource="#request.DS#">
select
a.login as creator, a.shortname as creator_shortname, m.login as updater, m.shortname as updater_shortname
from #pageInfo.entity# e
left outer join usr a on (e.creator_id=a.usr_id)
left outer join usr m on (e.updater_id=m.usr_id)
where e.#pageInfo.key#=<cfqueryparam attributeCollection=#id#/>
</cfquery>
</m:silent><!---
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput>
Вариант компонента
<cfif d.param_id GT 0>
<b>#qParam.param#</b> [#d.param_id#]
</cfif>
<cfif len(qParamClass.param_class) GT 0>
<b>#qParamClass.param_class#</b> [#qParamClass.param_class_id#]
</cfif>
варианта услуги
<b>
#qService.abstract_service#
<cfif len(qService.modifier) GT 0>
- #qService.modifier#
</cfif>
</b>
[#qService.service_id#]
</cfoutput>
</layout:attribute>
</layout:page>
<cfif status.errorState GT 0>
<cfoutput><div class="err">#status.errorMessage#</div></cfoutput>
</cfif>
<cfoutput>
<!--- *** не много ли тут лишних ключей --->
<input type="hidden" name="service_param_id" value="#d.service_param_id#"/>
<input type="hidden" name="abstract_service_param_class_id" value="#d.abstract_service_param_class_id#"/>
<input type="hidden" name="service_id" value="#d.service_id#"/>
<input type="hidden" name="track" value="#tr.self#"/>
<input type="hidden" name="pass" value=""/><!--- pass marker to prevent save on submit --->
<!--- <cfdump var=#form#/><cfdump var=#d#/>
--->
<div class="detail">
<div class="tr">
<div class="th i">Абстрактная услуга</div>
<div class="td">
<a href="abstract_service.cfm?abstract_service_id=#qService.abstract_service_id#&#tr.fwx#">
<b>#request.skuCode(qService.area_code,qService.abstract_service_code)#</b>
#qService.abstract_service#</a>
</div>
</div>
<div class="tr">
<div class="th i">Характеристика</div>
<div class="td">
<cfif qService.modifier_class_id GT 0>
<a href="modifier_class.cfm?modifier_class_id=#qService.modifier_class_id#&#tr.fwx#">#qService.modifier_class#</a>: <a href="modifier.cfm?modifier_id=#qService.modifier_id#&#tr.fwx#">#qService.modifier#</a>
<cfelse>
(нет)
</cfif>
</div>
</div>
<div class="tr">
<div class="th i">Вариант услуги</div>
<div class="td">
<a href="service.cfm?service_id=#qService.service_id#&#tr.fwx#">
<b>#request.skuCode(qService.area_code,qService.abstract_service_code,qService.modifier_code)#</b>
#qService.abstract_service#
<!--- <a href="abstract_service.cfm?abstract_service_id=#qService.abstract_service_id#&#tr.fwx#"></a> --->
<cfif #qService.modifier_id# GT 0>:
#qService.modifier#
<!--- <a href="modifier.cfm?modifier_id=#qService.modifier_id#&#tr.fwx#">
</a> --->
</cfif>
</a>
</div>
</div>
<div class="tr">
<div class="th">Вариант компонента</div>
<div class="td">
<!--- abstract_service_param_class_id=#abstract_service_param_class_id# --->
<a href="param_class.cfm?param_class_id=#qParamClass.param_class_id#&#tr.fwx#">
#qParamClass.param_class#
<!--- имеющаяся конструкция обращает внимание только на param_class_id в URL, не d.param_class_id --->
<!--- важно: для вновь создаваемого компонента услуги qParam пустой, а qParamClass может быть не пустой, если берется из abstract_service_param_class (но не обязательно мы открываем форму создания параметр сервиса по конкретному шаблону abstract_service_param_class) --->
</a>
<!--- имеющаяся конструкция обращает внимание только на param_class_id в URL, не d.param_class_id - игнорирование d.param_class_id приводит к странным последствиям --->
<!--- это довольно показательный дефект, выявленный некорректным созданием записи на базе несуществующей записи (чтобы сохранить дочернюю запись, нужно сначала создать родительскую) --->
<cfquery name="qList" datasource="#request.DS#">
select p.param_id, p.param, p.param_short, c.param_class, p.code,
(select count(*) from service_param sp where sp.param_id=p.param_id AND sp.service_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#service_id#"/> AND sp.param_id<><cfqueryparam cfsqltype="cf_sql_integer" value="#d.param_id#"/>) as usage_cnt
from param p
join param_class c on (p.param_class_id=c.param_class_id)
join abstract_service_param_class ac on (c.param_class_id=ac.param_class_id)
where 1=1
AND ac.abstract_service_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#qService.abstract_service_id#"/>
<cfif qParam.param_class_id GT 0>
AND p.param_class_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#qParam.param_class_id#"/>
</cfif>
<cfif abstract_service_param_class_id GT 0>
AND p.param_class_id IN (select param_class_id from abstract_service_param_class where abstract_service_param_class_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#abstract_service_param_class_id#"/>)
</cfif>
order by c.param_class, p.sort, p.param
</cfquery>
<c:combo
query=#qList#
combo="param_id"
id="param_id"
key="param_id"
selected="#d.param_id#"
flag_disabled="usage_cnt"
displayf="##len(param)? param:('(основной вариант)')## ##code##"
<!--- displayf="##len(param)?param:param_class## ##code##" --->
<!--- *** Так получилось, что param постоянно оказывается пустым, а если выводить param и param_class, то часто дублирование Можно только param_short или param_class+param --->
class=""
<!--- onchange="document.getElementById('param_class_id').selectedIndex=0;" --->
/>
<!---<cfif d.service_param_id GT 0>--->
<cfif d.param_id GT 0>
<a href="param.cfm?param_id=#d.param_id#&#tr.fwx#">
<img src="img/edit.gif"/>
</a>
</cfif>
<a href="param.cfm?param_id=-1&param_class_id=#qParamClass.param_class_id#&#tr.fwx#">
<img src="img/add.gif"/>
</a>
<!---</cfif>--->
<!---<cfif d.param_id GT 0> Цена без НДС, ₽ (вне контекста услуги): #qParam.base_price#</cfif>--->
<cfif d.param_id GT 0>
Ед. изм.:
<b><cfif qParam.measure_id GT 0>#qParam.measure# (#qParam.measure_short#)<cfelse>(нет)</cfif></b>
Точность:
<b><cfif qParam.precision GE 0>#qParam.precision#<cfelse>(нет)</cfif></b>.
&nbsp;
</cfif>
<br/>
<i>Варианты компонента, которые уже использованы, недоступны для выбора.</i>
<i>Если вариант компонента не выбран здесь, он выбирается при формировании экземпляра (в спецификации/заявке)</i>
</div>
</div>
<div class="tr">
<div class="th i">Полный код</div>
<div class="td">
<!--- <a href="param.cfm?param_id=#d.param_id#&#tr.fwx#" title=""> --->
<b>#request.skuCode(qService.area_code,qService.abstract_service_code,qService.modifier_code,qParam.code)#</b>
<!--- </a> --->
<i>(состоит из кодов
группы <a href="area.cfm?area_id=#qService.area_id#&#tr.fwx#">#qService.area_code#</a>,
абстрактной услуги <a href="abstract_service.cfm?abstract_service_id=#qService.abstract_service_id#&#tr.fwx#">#qService.abstract_service_code#</a>,
характеристики <cfif qService.modifier_code GT 0><a href="modifier.cfm?modifier_id=#qService.modifier_id#&#tr.fwx#">#qService.modifier_code#</a><cfelse>0</cfif>,
варианта компонента <a href="param.cfm?param_id=#d.param_id#&#tr.fwx#">#qParam.code#</a>)</i>
<!--- *** неудобно, что skuCode генерирует строку, а не структуру, не получается поставить ссылки раздельно на сегменты кода - либо дублировать логику skuCode здесь (и так она может разойтись по приложению) --->
</div>
</div>
<div class="tr">
<div class="th i">Сортировка</div>
<div class="td">
#qParamClass.sort#
<i>(определяется для <a href="abstract_service_param_class.cfm?abstract_service_param_class_id=#d.abstract_service_param_class_id#&#tr.fwx#" title="">компонента абстрактной услуги</a>)</i>
</div>
</div>
<!--- <cffunction name="roundSafe" output="false">
<cfargument name=a type="string"/>
<cfargument name=precision type="string"/>
<cfreturn (isNumeric(a) AND isNumeric(precision))?round(a,precision):a/>
</cffunction> --->
<cffunction name="fmtDecimal" output="false">
<cfargument name=a type="string"/>
<cfargument name=precision type="string"/>
<cfif isNumeric(a) AND isNumeric(precision)>
<cfif precision GT 0>
<cfreturn numberFormat(a,"9.#repeatString(9,precision)#")/>
<cfelse>
<cfreturn round(a, precision)/>
</cfif>
<cfelse>
<cfreturn a/>
</cfif>
</cffunction>
<!--- *** сделать отображение соответствующего числа знаков--->
<div class="tr">
<div class="th i">Min</div>
<div class="td">
<input type="text" name="min_value" value="#fmtDecimal(d.min_value, qParam.precision)#" size="5" class="r"/>
<i>минимальное количество при формировании спецификации/заявки</i>
</div>
</div>
<div class="tr">
<div class="th i">Max</div>
<div class="td">
<input type="text" name="max_value" value="#fmtDecimal(d.max_value, qParam.precision)#" size="5" class="r"/>
<i>максимальное количество при формировании спецификации/заявки</i>
</div>
</div>
<div class="tr">
<div class="th i">Шаг</div>
<div class="td">
<input type="text" name="incr" value="#fmtDecimal(d.incr, qParam.precision)#" size="5" class="r"/>
<i>шаг изменения в интерфейсе пользователя при формировании спецификации/заявки</i>
</div>
</div>
<div class="tr">
<div class="th">Описание</div>
<div class="td">
<textarea name="descr" rows="10" cols="100">#request.htm2plain(d.descr)#</textarea>
</div>
</div>
<div class="tr">
<div class="th">Создано</div>
<div class="td">
#dateFormat(d.dt_created,'DD.MM.YYYY')# #timeFormat(d.dt_created,'HH:MM')#
#qDecoration.creator# <cfif len(qDecoration.creator_shortname)>(#qDecoration.creator_shortname#)</cfif>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Изменено
#dateFormat(d.dt_updated,'DD.MM.YYYY')# #timeFormat(d.dt_updated,'HH:MM')#
#qDecoration.updater# <cfif len(qDecoration.updater_shortname)>(#qDecoration.updater_shortname#)</cfif>
</div>
</div>
</div>
<!--- Это шаблон для компонента экземпляра услуги (строки спецификации). Если в компонентах услуги определен Компонент, а не конкретный вариант компонента, то нужно присвоить цены всем вариантам компонентам этого класса, которые актуальны для данной услуги. Это требуется не только для назначения цен, но и для ограничения списка возможных вариантов выбора компонента для данной услуги. *** Может быть, вместо "компонент" нужно ввести термин "тип компонента", потому что это не экземпляр. Например, Компонент - дисковое пространство, а компонент - дисковое пространство SSD, это уточнение класса компонента, но не конкретный объем на конкретном СХД, и даже не конкретная дисковая квота в рамках контракта (спецификации) --->
<br/>
</cfoutput>
<layout:page section="extension" closeForm="Yes"/>
<cfif d.service_param_id GT 0>
<cffunction name="formatPrice" output="true">
<cfargument name="price"/>
<cfif isNumeric(price)><cfreturn numberFormat(price,'.00')/><cfelse><cfreturn "(по запросу)"></cfif>
</cffunction>
<cfset request.formatPrice=formatPrice/>
<cfquery name="qServiceParamPrice" datasource="#request.DS#">
select
t.service_param_price_id
,t.service_param_id
,t.pricing_model_id
,m.pricing_model
,m.pricing_model_short
,t.pricing_period
,t.rating_period
,t.price
<!--- ,t.discount_policy_id --->
,t.dt_from
,t.dt_to
,t.status
,t.descr
from service_param_price t
left outer join pricing_model m on (t.pricing_model_id=m.pricing_model_id)
where t.service_param_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#d.service_param_id#"/>
order by 1
</cfquery>
<cfoutput>
<p class="b">
Цены компонента услуги (без НДС)
</p>
<cfif pageInfo.writePermitted()>
<cfset addUrl="service_param_price.cfm?service_param_id=#d.service_param_id#&#tr.fwx#"/>
<button type="button" class="maincontrol" onclick="document.location.href='#addUrl#'">
<a href="#addUrl#">Добавить</a>
</button>
</cfif>
</cfoutput>
<table class="worktable">
<thead>
<tr>
<th></th>
<th>ID</th>
<th>Ц-обр.</th>
<th>Период цены</th>
<th>Период опроса</th>
<!--- <th>Политика</th> --->
<th>Статус</th>
<th>С</th>
<th>По</th>
<th>Цена GPL без НДС</th>
<th></th>
</tr>
</thead>
<cfoutput query="qServiceParamPrice">
<tr>
<td>
<c:link_view_edit canWrite=#pageInfo.writePermitted()# entity="service_param_price" key="service_param_price_id" id=#service_param_price_id# fwx=#tr.fwx#/>
</td>
<td>#service_param_price_id#</td>
<td>#pricing_model_short#</td>
<td>#pricing_period#</td>
<td>#rating_period#</td>
<!--- <td>#discount_policy_id#</td> --->
<td>#status#</td>
<td>#dateFormat(dt_from,'DD.MM.YYYY')#</td>
<td>#dateFormat(dt_to,'DD.MM.YYYY')#</td>
<td>#request.formatPrice(price)#</td>
<td class="c">
<c:link_del canWrite=#pageInfo.writePermitted()# entity="service_param_price" key="service_param_price_id" id=#service_param_price_id# fwx=#tr.fwx#/>
</td>
</tr>
</cfoutput>
</table>
<!---
При формировании спецификации доступны только параметры (варианты компонент), для которых указаны базовые цены.
--->
</cfif>
<layout:page section="footer"/>