spec/agreement.cfm
2025-06-28 17:02:39 +03:00

406 lines
18 KiB
Plaintext
Raw 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>
<m:prepare_detail entity="agreement" key="undefined" pageInfoOut="pageInfo"/><!--- для составного ключа key бесполезен, но инициализировать пустой строкой не получается из-за cfparam name="#ATTRIBUTES.key#" default="-1" в prepare_detail --->
<cfparam name="contract_id"/>
<cfquery name="qInitVersion"><!--- *** надо бы только при сохранении, а так возможен конфликт --->
select coalesce(max(agreement_version)+1,0) as next_version
from agreement
where contract_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#contract_id#" null=#!isValid('integer', contract_id)#/>
</cfquery>
<d:bean readonly=#!pageInfo.writePermitted()# table="#pageInfo.entity#" datasource="#request.DS#" output="d" status="status">
<d:param field="contract_id" type="integer" key forNull=""/><!--- поле с атрибутом key не участвует в update --->
<!--- формировать номер версии нам придется самостоятельно --->
<d:param field="agreement_version" type="integer" key init=#qInitVersion.next_version# forNull=""/><!--- поле с атрибутом key не участвует в update --->
<d:param field="agreement" type="varchar" size="255" preprocessor=#cleanInput# init=#qInitVersion.next_version#/>
<d:param field="dt_agreement" type="timestamp" format="yyyy-MM-dd" init=#Now()#/>
<d:param field="is_actual" type="bit" init="0" default="0"/>
<d:param field="deal_uid" type="guid" skipInsert skipUpdate/>
<d:param field="probability_perc" type="integer" forNull="" init=100/>
<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>
<m:dispatch_detail
usePRG="No"<!---*** --->
pageInfo=#pageInfo#
id=""<!--- для составного ключа --->
queryString="contract_id=#d.contract_id#&agreement_version=#d.agreement_version#"
status=#pageInfo.status#
trackOut="tr"
idAttributesOut="id"
/>
<!--- decoration --->
<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 agreement 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.contract_id=<cfqueryparam cfsqltype="cf_sql_integer" value=#d.contract_id# null=#!isValid('integer',d.contract_id)#/>
AND e.agreement_version=<cfqueryparam cfsqltype="cf_sql_integer" value=#d.agreement_version# null=#!isValid('integer',d.agreement_version)#/>
</cfquery>
<cfquery name="qContract" datasource="#request.DS#">
select d.contract_id, d.contract, d.dt_contract, c.contragent_id, c.contragent
from contract d
left outer join contragent c on (d.contragent_id=c.contragent_id)
where d.contract_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#d.contract_id#"/>
</cfquery>
<cfquery name="qVersion" datasource="#request.DS#">
select
a.agreement
,a.dt_agreement
,a.agreement_version
,a.is_actual
from agreement a
where a.contract_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#d.contract_id#"/>
order by a.agreement_version
</cfquery>
</m:silent><!---
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput>
Соглашение (сделка)
<cfif d.agreement_version GE 0>
<b>#d.agreement#</b>
[#d.agreement_version#]
</cfif>
</cfoutput>
</layout:attribute>
</layout:page>
<cfif status.errorState GT 0>
<cfoutput><div class="err">#status.errorMessage#</div></cfoutput>
</cfif>
<cfoutput>
<input type="hidden" name="contract_id" value="#d.contract_id#"/>
<input type="hidden" name="agreement_version" value="#d.agreement_version#"/>
<input type="hidden" name="track" value="#tr.self#"/>
<input type="hidden" name="pass" value=""/><!--- pass marker to prevent save on submit --->
<div class="detail">
<div class="tr">
<div class="th">Договор</div>
<div class="td">
<a href="contract.cfm?contract_id=#d.contract_id#&#tr.fwx#">#qContract.contract# #dateFormat(qContract.dt_contract,'DD.MM.YYYY')#</a>
а как у нас поведет себя трек для составных ключей?
</div>
</div>
<div class="tr">
<div class="th">Номер версии</div>
<div class="td">
#d.agreement_version# (некорректный нейминг, в одном месте версией называется номер, а в другом сущность, и это отразилось в именовании таблиц и полей)
</div>
</div>
<div class="tr">
<div class="th">Ключ сделки</div>
<div class="td">
#d.deal_uid#
</div>
</div>
<div class="tr">
<div class="th">Вероятность</div>
<div class="td">
<input type="text" name="probability_perc" value="#d.probability_perc#" size="3"/>
</div>
</div>
<div class="tr">
<div class="th">Соглашение (номер для печати)</div>
<div class="td">
<input type="text" name="agreement" value="#d.agreement#" size="15"/>
</div>
</div>
<div class="tr">
<div class="th">Дата соглашения</div>
<div class="td">
<input type="date" name="dt_agreement" id="dt_agreement" value="#dateFormat(d.dt_agreement,'YYYY-MM-DD')#"/>
<button type="button" onclick="document.getElementById('dt_agreement').value='#DateFormat(Now(), 'YYYY-MM-DD')#';">Сегодня</button>
<i>для упрощения можно позволить соглашению иметь обратную силу - то есть просто не проверять, что дата изменения строки спецификации не раньше даты соглашения</i>
</div>
</div>
<div class="tr">
<div class="th">Действует</div>
<div class="td">
<input type="checkbox" name="is_actual" value="1"<cfif d.is_actual GT 0> checked</cfif>/>
<i>если соглашение действует, это означает ровно то, что его правки учитываются при формировании спецификации на любую дату</i>
</div>
</div>
<div class="tr">
<div class="th">Описание</div>
<div class="td">
<textarea name="descr" rows="3" 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 class="tr">
<div class="th">Версии</div>
<div class="td">
<cfoutput query=#qVersion#>
<cfif qVersion.agreement_version EQ d.agreement_version>
<b>#agreement_version#</b>
<cfelse>
<a href="agreement.cfm?contract_id=#contract_id#&agreement_version=#agreement_version#&track=#tr.self#">#agreement_version#</a>
</cfif>
&nbsp;
</cfoutput>
</div>
</div>
</div>
Соглашение просто объединяет правки строк спецификации в пакет и оформляет их документом (в случае с допником). То есть версии строк существуют не сами по себе, а связаны с конкретным соглашением. Между прочим, это означает, что в рамках одного доп. соглашения мы не можем сделать 2 изменения строки, например 2 разные цены с разных дат - нужно оформлять отдельными допниками.
Можно назвать нулевое соглашение базовым, а остальные дополнительными. (наверное, можно было бы синтезировать базовое соглашение с NULL номером, но NULL проблематично включить в первичный ключ, а любое специальное значение будет нарушать внешний ключ)
Дата соглашения не имеет никакого отношения к датам актуальности строк (это чисто формальное поле)
Предполагается, что даты актуальности строк согласованы с номерами версий (можно обнаруживать и репортить ошибки)
Поскольку соглашение не может дважды изменить строку спецификации, можно говорить о версии спецификации в соответствии с соглашением
Есть ли у нас понятие текущего соглашения?
Есть ли понятие действующего соглашения, или у нас все действуют, или строки из недействующего соглашения не должны участвовать в цепочке (тогда, видимо, не удастся активировать соглашение задним числом, цепочка может разрушиться)
Если к соглашению нет измененных строк спецификации, в нем нет большого смысла (но изменения строк надо к чему-то приписывать).
Надо было начать с моделирования данных в таблицах, до интерфейса?
</cfoutput>
<layout:page section="extension" closeForm="Yes"/>
<!--- для identity проверка существования записи простая, а тут как? В принципе, бин должен экспортировать флаг существования записи --->
<cfif d.agreement_version GE 0>
<cfquery name="qItem" datasource="#request.DS#">
select
iv.specification_item_uid
,iv.agreement_version /*лучше читается, когда все одинаково берется из резалтсета*/
,i.svc_id
,i.pricing_model_id
,p.pricing_model_short
,svc.svc
,svc.code
,iv.specification_item_version /*it is printable name not number*/
,iv.quantity
,iv.price
,iv.price*iv.quantity as cost
,iv.dt_from
,iv.dt_to
,i.specification_id
,s.specification
,s.contract_id
,d.contract
,d.dt_contract
,d.contragent_id
,k.contragent
,ver.agreement_version as prev_version
,ver.price as prev_price
,ver.quantity as prev_quantity
,ver.dt_from as prev_dt_from
,ver.dt_to as prev_dt_to
,case
when ver.agreement_version = iv.agreement_version then ''
when ver.agreement_version IS NULL then 'new'
else 'changed'
end as change_type /*удалить услугу нельзя, можно только завершить ее оказание (после этого в более поздних версиях она должна стать незаметной)*/
,COALESCE(iv.price*iv.quantity,0) - COALESCE(ver.price*ver.quantity,0) as cost_diff
from specification_item_version iv
join specification_item i on (iv.specification_item_uid=i.specification_item_uid)
join pricing_model p on (i.pricing_model_id=p.pricing_model_id)
join specification s on (i.specification_id=s.specification_id)
join contract d on (s.contract_id=d.contract_id)
join contragent k on (d.contragent_id=k.contragent_id)
left outer join svc on (i.svc_id=svc.svc_id)
left outer join (select /*предыдущая версия*/
siv.agreement_version
,siv.specification_item_uid
,a.agreement
,a.contract_id
,a.dt_agreement
,a.is_actual
,siv.specification_item_version
,siv.quantity
,siv.price
,siv.dt_from
,siv.dt_to
,ii1.specification_id
from specification_item_version siv
join specification_item ii1 on (siv.specification_item_uid=ii1.specification_item_uid)
join specification is1 on (ii1.specification_id=is1.specification_id)
join agreement a on (siv.agreement_version=a.agreement_version AND is1.contract_id=a.contract_id)
where a.agreement_version = (select max(iiv.agreement_version)
from specification_item_version iiv
join specification_item ii on (iiv.specification_item_uid=ii.specification_item_uid)
join specification isp on (ii.specification_id=isp.specification_id)
join agreement ia on (isp.contract_id=ia.contract_id AND iiv.agreement_version=ia.agreement_version)
where iiv.specification_item_uid=siv.specification_item_uid
AND ia.is_actual
AND ia.agreement_version < <cfqueryparam cfsqltype="cf_sql_integer" value="#d.agreement_version#"/>
)
) ver on (iv.specification_item_uid=ver.specification_item_uid AND ver.specification_id=s.specification_id) /*а тут не будет замножения впр*/
where s.contract_id=<cfqueryparam cfsqltype="cf_sql_integer" value="#d.contract_id#"/>
AND iv.agreement_version=(select max(iiv.agreement_version)
from specification_item_version iiv
join specification_item ii on (iiv.specification_item_uid=ii.specification_item_uid)
join specification isp on (ii.specification_id=isp.specification_id)
join agreement ia on (isp.contract_id=ia.contract_id AND iiv.agreement_version=ia.agreement_version)
where iiv.specification_item_uid=i.specification_item_uid
AND iiv.agreement_version <= <cfqueryparam cfsqltype="cf_sql_integer" value="#d.agreement_version#"/>
)
order by i.specification_id
</cfquery>
<cfquery dbtype="query" name="qTotal">
select sum(cost) as cost from qItem
</cfquery>
<cfoutput>
<p>
Строки спецификаций (#qItem.recordCount#) (все суммы без вероятности)
</p>
</cfoutput>
<table class="worktable">
<cfoutput query="qItem" group="specification_id">
<!--- <thead> --->
<tr>
<th></th>
<th>Специф-я</th>
<th>Договор</th>
<th>Контрагент</th>
<th>Стабильный ключ строки</th>
<th>Код услуги</th>
<th>Услуга</th>
<th>Имя для печати</th>
<th>Модель</th>
<th>Версия</th>
<th>Дата с</th>
<th>Дата по</th>
<th>Кол-во</th>
<th>Цена</th>
<th>Стоимость</th>
<th>&nbsp;</th>
<th>Прошлая версия</th>
<th>Прошлая дата с</th>
<th>Прошлая дата по</th>
<th>Прошлое кол-во</th>
<th>Прошлая цена</th>
<th>Изменение</th>
<th>Прибавка стоимости</th>
<th></th>
</tr>
<!--- </thead> --->
<cfoutput group="specification_item_uid">
<tr>
<td>
<a href="specification_item_version.cfm?specification_item_uid=#specification_item_uid#&agreement_version=#d.agreement_version#&#tr.fwx#" class=<cfif pageInfo.writePermitted()>"edit"<cfelse>"view"</cfif>></a>
</td>
<td>
<a href="specification.cfm?specification_id=#specification_id#&#tr.fwx#">#specification#</a>
</td>
<td>#contract# #dateFormat(dt_contract,'DD.MM.YYYY')#</td>
<td>#contragent#</td>
<td>
<a href="specification_item.cfm?specification_item_uid=#specification_item_uid#&#tr.fwx#">#specification_item_uid#</a>
</td>
<td>#code#</td>
<td>
<a href="svc.cfm?svc_id=#svc_id#&#tr.fwx#">#svc#</a>
</td>
<td>#specification_item_version#</td>
<td class="c">#pricing_model_short#</td>
<td class="c">#agreement_version#</td>
<td class="c">#dateFormat(dt_from,'DD.MM.YYYY')#</td>
<td class="c">#dateFormat(dt_to,'DD.MM.YYYY')#</td>
<td class="r">#quantity#</td>
<td class="r">#price#</td>
<td class="r">#cost#</td>
<td></td>
<td class="c"><a href="specification_item_version.cfm?specification_item_uid=#specification_item_uid#&agreement_version=#prev_version#&#tr.fwx#">#prev_version#</a></td>
<td class="c">#dateFormat(prev_dt_from,'DD.MM.YYYY')#</td>
<td class="c">#dateFormat(prev_dt_to,'DD.MM.YYYY')#</td>
<td class="r">#prev_quantity#</td>
<td class="r">#prev_price#</td>
<td class="c">#change_type#</td>
<td class="r">#cost_diff#</td>
<td class="c">
<cfif pageInfo.writePermitted()>
<a href="specification_item_version_del.cfm?specification_item_uid=#specification_item_uid#&agreement_version=#d.agreement_version#&#tr.fwx#" class="del"></a>
</cfif>
</td>
</tr>
</cfoutput>
</cfoutput>
<cfoutput query="qTotal">
<tr>
<td colspan="13"></td>
<td class="r" style="font-size:120%">#cost#</td>
<td colspan="99"></td>
</tr>
</cfoutput>
</table>
</cfif>
<layout:page section="footer"/>