Ценовая выдача для одного окна
This commit is contained in:
@@ -18,7 +18,7 @@ from django.contrib import admin
|
||||
from django.urls import path, re_path
|
||||
from django.conf.urls.static import static
|
||||
from oknardia.settings import *
|
||||
from web import views, autocomplete_addr, user_manager, blog, diagrams, report2, catalog
|
||||
from web import views, autocomplete_addr, user_manager, blog, diagrams, report2, catalog, prices
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -67,7 +67,9 @@ urlpatterns = [
|
||||
# --- --- Каталог производителей окон
|
||||
re_path(r'^catalog/company[/*]$', catalog.catalog_company),
|
||||
re_path(r'^catalog/company/(?P<company_id>\d+)-(?P<company_name_slug>\S*)[/*]$', catalog.catalog_company_detail),
|
||||
|
||||
# ЦЕНОВЫЕ ПРЕДЛОЖЕНИЯ
|
||||
re_path(r'^tsena-odnogo-okna/(?P<win_width_mm>\d+)x(?P<win_height_mm>\d+)mm/tip(?P<win_id>\d+)[/*]$',
|
||||
prices.report_one_win_price),
|
||||
|
||||
]
|
||||
|
||||
|
||||
141
oknardia/templates/report/report_precelist_one_flap_frame.html
Executable file
141
oknardia/templates/report/report_precelist_one_flap_frame.html
Executable file
@@ -0,0 +1,141 @@
|
||||
{% load static %}{% load filters %}{% for CurOffer in PRICE_FRAME %}
|
||||
<tr>
|
||||
<td colspan="7" style="font-size:xx-small;border-top:double black;border-bottom: solid 1px white;padding: 0;"></td>
|
||||
</tr>{% for CurInOffer in CurOffer.DIM %}
|
||||
<tr>
|
||||
{% if forloop.first %}
|
||||
<th rowspan="{% if CurOffer.DIM|length == 1 %}2{% else %}{{ CurOffer.DIM|length }}{% endif %}" title="Добавить коммерческое предложение окна к сравнению">{# красивые чекбоксы BEGIN #}<div class="checkbox"><label><input id="CHK{{ CurOffer.SETS_ID }}" type="checkbox" name="ForCompare" value="{{ CurOffer.SETS_ID }}" onChange="ChangeCountCheckedBox({{ CurOffer.SETS_ID }});" /><span class="cr"><i class="cr-icon glyphicon glyphicon-ok"></i></span></label></div>{# красивые чекбоксы END #}</th>
|
||||
<td rowspan="{% if CurOffer.DIM|length == 1 %}2{% else %}{{ CurOffer.DIM|length }}{% endif %}"{% if CurOffer.IS_COMMERCIAL %} style="background-image: url(/media/{{ CurOffer.MERCHANT_LOGO }})"{% endif %} title="Краткая спецификация коммерческого предложения">
|
||||
<span itemprop="description">
|
||||
<h3 class="set-name shake-trigger" id="btn{{ CurOffer.SETS_ID }}"><a href="javascript://" onclick="show_dtl({{ CurOffer.SETS_ID }})">{{ CurOffer.MERCHANT }} – {{ CurOffer.SETS_NAME }}<i class="glyphicon glyphicon-chevron-down shake-vertical"></i></a></h3>
|
||||
|
||||
<DiV id="dtl{{ CurOffer.SETS_ID }}" class="collapse">■ Профиль: <a href="/catalog/profile/{{ CurOffer.PVC_ID }}-{{ CurOffer.PVC_MANUFACTURER_T }}/{{ CurOffer.PVC_ID }}-{{ CurOffer.PVC_NAME_T }}">{{ CurOffer.PVC_NAME|safe }}</a> (<a href="/catalog/profile/{{ CurOffer.PVC_ID }}-{{ CurOffer.PVC_MANUFACTURER_T }}">{{ CurOffer.PVC_MANUFACTURER }}</a>)
|
||||
■ {{ CurOffer.GLAZING_NAME_B|safe }} <nobr>({{ CurOffer.GLAZING_MARK }})</nobr>
|
||||
■ Тонирование: {{ CurOffer.GLAZING_TONING }}
|
||||
{% if CurOffer.PVC_SEAL %}■ Уплотнитель: {{ CurOffer.PVC_SEAL }}
|
||||
{% endif %}{% if CurOffer.SETS_IMPLEMENT %}■ Фурнитура: {{ CurOffer.SETS_IMPLEMENT|capfirst|safe }}
|
||||
{% endif %}{% if CurOffer.SETS_IMPLEMENT_R %}■ Ручки: {{ CurOffer.SETS_IMPLEMENT_R|capfirst|safe }}
|
||||
{% endif %}{% if CurOffer.SETS_IMPLEMENT_P %}■ Петли: {{ CurOffer.SETS_IMPLEMENT_P|capfirst|safe }}
|
||||
{% endif %}{% if CurOffer.SETS_IMPLEMENT_Z %}■ Запоры: {{ CurOffer.SETS_IMPLEMENT_Z|capfirst|safe }}
|
||||
{% endif %}{% if CurOffer.SETS_IMPLEMENT_O %}■ Ограничители: {{ CurOffer.SETS_IMPLEMENT_O|capfirst|safe }}
|
||||
{% endif %}{% if CurOffer.SETS_IMPLEMENT_F %}■ Фиксаторы: {{ CurOffer.SETS_IMPLEMENT_F|capfirst|safe }}
|
||||
{% endif %}{% if CurOffer.SETS_CLIMATE_CONTROL %}<span {% if CurOffer.SETS_CLIMATE_CONTROL|capfirst != "Нет" or CurOffer.SETS_CLIMATE_CONTROL|length > 3 %}class="bullet-green"{% endif %}>■ Климат-контроль: {{ CurOffer.SETS_CLIMATE_CONTROL|capfirst|safe }}</span>
|
||||
{% endif %}{% if CurOffer.SETS_SILL %}<span {% if CurOffer.SETS_SILL|capfirst == "Нет" or CurOffer.SETS_SILL|length < 3 %}class="bullet-red"{% endif %}>■ Подоконник: {{ CurOffer.SETS_SILL|capfirst|safe }}</span>
|
||||
{% endif %}{% if CurOffer.SETS_SLOPE %}<span {% if CurOffer.SETS_SLOPE|capfirst == "Нет" or CurOffer.SETS_SLOPE|length < 3 %}class="bullet-red"{% endif %}>■ Откос: {{ CurOffer.SETS_SLOPE|capfirst|safe }}</span>
|
||||
{% endif %}{% if CurOffer.SETS_PANES %}<span {% if CurOffer.SETS_PANES|capfirst == "Нет" or CurOffer.SETS_PANES|length < 3 %}class="bullet-red"{% endif %}>■ Водоотлив: {{ CurOffer.SETS_PANES|capfirst|safe }}</span>
|
||||
{% endif %}{% if CurOffer.SETS_UNINSTALL_INSTALL %}<span {% if CurOffer.SETS_UNINSTALL_INSTALL_B %}class="bullet-green"{% else %}class="bullet-red"{% endif %}>■ Демонтаж/монтаж: {{ CurOffer.SETS_UNINSTALL_INSTALL|capfirst|safe }}</span>
|
||||
{% endif %}{% if CurOffer.SETS_DELIVERY %}<span {% if CurOffer.SETS_DELIVERY_B %}class="bullet-green"{% else %}class="bullet-red"{% endif %}>■ Доставка: {{ CurOffer.SETS_DELIVERY|capfirst|safe }}</span>{% endif %}{% comment %} {% if CurOffer.SETS_OTHER %}■ Прочие условия: {{ CurOffer.SETS_OTHER|capfirst|safe }}
|
||||
{% endif %}{% endcomment %}
|
||||
</DiV>
|
||||
<!-- Дата обновления -->
|
||||
<nobr class="badge badge4price" title="Дата обновления коммерческого предложения окон — {{ CurOffer.SETS_DATA_MODIFY|date:"d.M.Y" }}"><b class="glyphicon glyphicon-calendar"></b> {{ CurOffer.SETS_DATA_MODIFY|date:"d.M.Y" }}</nobr>
|
||||
<!-- Звездочки рейтинга -->
|
||||
<nobr class="badge badge4price" title="Рейтинг «Окнардии»{% if CurOffer.SETS_RATING > -0.1 %} — {{ CurOffer.SETS_RATING|stringformat:".2f" }} баллов{% endif %}"><a
|
||||
href="javascript://"
|
||||
id-set="{{ CurOffer.SETS_ID }}"
|
||||
data-trigger="focus" tabindex="0"
|
||||
title="{% if CurOffer.SETS_RATING > 0.01 %}<b> Рейтинг {{ CurOffer.SETS_RATING|stringformat:".2f" }}</b> для оконого набора «{{ CurOffer.SETS_NAME }}» компании «{{ CurOffer.MERCHANT }}» состоит из:{% else %}Рейтинг не присвоен{% endif %}"
|
||||
data-toggle="popover">рейтинг</a>: {% for Star in CurOffer.SETS_RATING_STARTS %}{% if Star == 0 %}<b class="glyphicon glyphicon-star-empty"></b>{% else %}<b class="glyphicon glyphicon-star"></b>{% endif %}{% endfor %} {% if CurOffer.SETS_RATING > -0.1 %} {{ CurOffer.SETS_RATING|stringformat:".2f" }}{% endif %}</nobr>
|
||||
</span>
|
||||
<span itemprop="brand" itemscope itemtype="http://schema.org/Brand">
|
||||
<meta itemprop="name" content="{{ CurOffer.MERCHANT }}" />
|
||||
<meta itemprop="logo" content="https://oknardia.ru/media/{{ CurOffer.MERCHANT_LOGO }}" />
|
||||
</span></td>
|
||||
<!--- Конец большой ячейки со спецификацией оконного предложения --->
|
||||
{% endif %}
|
||||
|
||||
<td class="rnw" title="Схема открывания окна или двери {{ CurInOffer.WIDTH|stringformat:".0f" }}0x{{ CurInOffer.HIGHT|stringformat:".0f" }}0 мм."><img src="{% static CurInOffer.IMG_MINI %}" alt="Схема открывания: {{ CurInOffer.DESCRIPTION }} — {{ CurInOffer.WIDTH|stringformat:".0f" }}0x{{ CurInOffer.HIGHT|stringformat:".0f" }}0 мм." /></td>
|
||||
|
||||
{% if forloop.first %}<!-- FIRST -->
|
||||
|
||||
<td class="rnw" title="Стоимость {{ CurOffer.TOTAL|stringformat:".2f" }} рублей за все окна квартиры {{ APART|safe }}.">{{ CurOffer.TOTAL|stringformat:".2f"|price_format }}</td>
|
||||
<th{% if CurOffer.DISCOUNT_COLOR2 != "" %} style="background-color:{{ CurOffer.DISCOUNT_COLOR2 }};"{% endif %} title="{% if CurOffer.DISCOUNT < 0.1 %}Нет скидки{% else %}Скидка — {{ CurOffer.DISCOUNT|stringformat:".1f" }}%{% endif %}">{% if CurOffer.DISCOUNT < 0.1 %}—{% else %}−{{ CurOffer.DISCOUNT|stringformat:".1f" }}%{% endif %}</th>
|
||||
<th{% if CurOffer.DISCOUNT_COLOR1 != "" %} style="background-color:{{ CurOffer.DISCOUNT_COLOR1 }};"{% endif %} itemprop="offers" itemscope itemtype="http://schema.org/Offer" title="Итого за все окна с учетом скидки: {{ CurOffer.FIN_PRICE|stringformat:".2f" }} рублей">
|
||||
Итого: {{ CurOffer.FIN_PRICE|stringformat:".2f"|price_format }} <small class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></small>
|
||||
<meta itemprop="price" content="{{ CurOffer.FIN_PRICE }}" />
|
||||
<meta itemprop="priceCurrency" content="RUB" />
|
||||
</th>
|
||||
{% if CurOffer.DIM|length == 1 %}
|
||||
|
||||
</tr><tr>
|
||||
<td colspan="2"> </td>
|
||||
<td colspan="3">
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% elif forloop.counter0 == 1 %}
|
||||
|
||||
<td colspan="3" rowspan="{{ CurOffer.DIM|length|add:"-1" }}">
|
||||
|
||||
{% endif %}
|
||||
{% if forloop.counter0 == 1 or CurOffer.DIM|length == 1 %}
|
||||
|
||||
<stong>{{ CurOffer.OFFICE_NAME }}</stong><br />
|
||||
<div id="hid{{ CurOffer.SETS_ID }}" class="collapse in">
|
||||
<a class="btn btn-info btn-xs shake-trigger" data-toggle="collapse" data-target="#tel{{ CurOffer.SETS_ID }}" onclick="show_phone_num({{ CurOffer.SETS_ID }})"><b class="glyphicon glyphicon-phone-alt shake-vertical"></b> Показать телефон</a>
|
||||
</div>
|
||||
<div id="tel{{ CurOffer.SETS_ID }}" class="collapse">
|
||||
<small>{{ CurOffer.OFFICE_ADDRESS }}</small><br /> <b class="glyphicon glyphicon-phone-alt"></b> {{ CurOffer.OFFICE_PHONES }}<br />
|
||||
<small><nobr>Пожалуйста, скажите, что нашли цены на oknardia.ru</nobr></small>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{% endif %}
|
||||
</tr>{% endfor %}<!----- ПРОВЕРКА --->{% endfor %}
|
||||
|
||||
<Tr id="preloader_{{ N }}">
|
||||
<tH colspan="7"><center><img src="{% static "img/preloader.gif" %}" alt="подгружаю цены на окна..." height="64" width="64" /></center></tH>
|
||||
</Tr>
|
||||
|
||||
<tR id="load_and_banner_{{ N }}">
|
||||
<!-- сюда подгружаем следующий фрейм прайслиста --->
|
||||
<script type="application/javascript">
|
||||
function get_next_pricelist_frame( ) {
|
||||
try{yaCounter32997984.reachGoal("MoreOffer");}catch(e){}
|
||||
// заменяем ряд с кнопочками кнопочками на рекламу
|
||||
// $("#load_and_banner_{{ N }}").html("<th colspan=10>РЕКЛАМА TMP</th>");
|
||||
// добавляем ячейки с дополнительными ценами и новые кнопочки
|
||||
$("#load_and_banner_{{ N }}").css({"display":"none"});
|
||||
$("#preloader_{{ N }}").css({"display":"table-row","transition":"1s"});// подсчитать сколько прочеканных оферов для сравнения
|
||||
$("#tmp").load(
|
||||
"/next_price_one_flap_frame/idW{{ WIN_ID }}N{{ N }}",
|
||||
function (){
|
||||
window.frame = $("#tmp").html();
|
||||
$("#price-list > tbody").append(window.frame);
|
||||
}
|
||||
);
|
||||
window.setTimeout(
|
||||
function show_adv() {
|
||||
$("#load_and_banner_{{ N }}").html("<td colspan='8' bgcolor='#CCCCCC' align='center'><center><a href='https://api.ozon.ru/partner-tools.affiliates/pankarta/11052421746600/link?partner=e-serg&&utm_content=banner&width=728&height=90' target='_blank'><img src='https://api.ozon.ru/partner-tools.affiliates/pankarta/11052421746600/image?partner=e-serg&utm_content=banner&width=728&height=90' width='728px' height='90px'><small><br/>↑ Здесь могла бы быть ваша реклама ↑</small></center></td>");
|
||||
$("#load_and_banner_{{ N }}").css({"display":"table-row","transition":"all 5s"});
|
||||
$("#preloader_{{ N }}").css({"display":"none","transition":"1s"});
|
||||
// (adsbygoogle=window.adsbygoogle || []).push({});
|
||||
window.count = 0;
|
||||
for (var i=0; i < ForCompare.length; i++)
|
||||
if (ForCompare[i].checked)
|
||||
window.count++;
|
||||
$("#NumberOfSelected").html(window.count);
|
||||
$('[data-toggle="popover"]').popover({ // реинициализировать подгружаемые поповеры
|
||||
placement: "top",
|
||||
html: true,
|
||||
content: function () {
|
||||
// сохранить текущий контекст
|
||||
var _this = this;
|
||||
$('#shadow_buffer').load('/show_rating_components/' + $(_this).attr("id-set"),
|
||||
function (response, status, xhr) {
|
||||
if (status == "error")
|
||||
$(_this).attr({'data-content': "Ошибка " + xhr.status + " (" + xhr.statusText + ")"});
|
||||
else
|
||||
$(_this).attr({'data-content': response});
|
||||
$(_this).popover('show');
|
||||
try{yaCounter32997984.reachGoal("ShowRating");}catch(e){};
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
2800);
|
||||
}
|
||||
</script>
|
||||
<td colspan="2"><a class="btn btn-info btn-block" data-toggle="collapse" onclick="Click2CompareOffers();">Сравнить выбранные (<span id="NumberOfSelected">0</span>) <small class="glyphicon-stats glyphicon"></small></a><p id="tmp"></p></td>
|
||||
<td colspan="5">{% if N != "-1" %}<a href="javascript://" class="btn btn-info btn-block" onclick="get_next_pricelist_frame();">Ещё коммерческие предложения окон <small class="glyphicon-refresh glyphicon"></small></a>{% endif %}</td>
|
||||
</tR>
|
||||
245
oknardia/templates/report/report_price-offers_for_one_window.html
Executable file
245
oknardia/templates/report/report_price-offers_for_one_window.html
Executable file
@@ -0,0 +1,245 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% load filters %}
|
||||
|
||||
{% block Title %}Цены на типовое окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }} для домов серий {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}{{ I.sName }}{% endfor %}{% endfor %} мм.{% endblock %}
|
||||
|
||||
{% block Add_Body_Attribute %} style="padding-top:70px;"{% endblock %}
|
||||
|
||||
{% block Date4Meta %}{{ META_DATA_PUBLISH|date:"c" }}{% endblock %}
|
||||
|
||||
{% block Last4Meta %}{{ META_DATA_PUBLISH|date:"c" }}{% endblock %}
|
||||
|
||||
{% block Top_JS4 %}
|
||||
<script type="text/javascript" src="//www.gstatic.com/charts/loader.js"></script>
|
||||
<script type="text/javascript">
|
||||
google.charts.load("current", {packages: ["corechart"]});
|
||||
google.charts.setOnLoadCallback(drawChart);
|
||||
function drawChart() {
|
||||
var data = google.visualization.arrayToDataTable([
|
||||
['Схема открывания', 'Число предложений'], {% for I in LIST_FLAP_VARIATION %}
|
||||
['{{ I.STR_NUM }}', {{ I.id }}]{% if not forloop.last %}, {% endif %}{% endfor %}
|
||||
]);
|
||||
var options = {
|
||||
legend: {position: "none"},
|
||||
backgroundColor: 'none',
|
||||
pieHole: 0.386,
|
||||
chartArea: {width: '80%', height: '90%'},
|
||||
pieStartAngle: 5,
|
||||
pieSliceTextStyle: {fontSize: 12},
|
||||
is3D: true,
|
||||
slices: {0: {offset: 0.1}},
|
||||
tooltip: {textStyle: {fontSize: 11, color: 'black', opacity: 0.8}, showColorCode: true}
|
||||
};
|
||||
var chart = new google.visualization.PieChart(document.getElementById('donutchart'));
|
||||
chart.draw(data, options);
|
||||
}
|
||||
</script>{% endblock %}
|
||||
|
||||
{% block Description %}Цены на типовое окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }}{% endfor %} cм. для домов серий {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}{{ I.sName }}{% endfor %}.{% endblock %}
|
||||
|
||||
{% comment %}{% block Description %}Цены на пластиковые окна для серии {{ BASE_SERIA }} ({{ APART }} квартира, {{ ADDRESS }}) :: {% for CurOffer in PRICE_FRAME %}Поставщик: {{ CurOffer.MERCHANT }}; Комплектация: {{ CurOffer.SETS_NAME }}; Цена: {{ CurOffer.FIN_PRICE }}₽ :: {% endfor %}{% endblock %}{% endcomment %}
|
||||
|
||||
{% block Keywords %}цены на окно, типовое окно, {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }}{% endfor %} cм., {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}x{{ I_WIN_DIM.iWinHight|floatformat:0 }}{% endfor %} см., стоимость окна, {% for I in SERIA_FOR_WIN %}серия {{ I.sName }}, {% endfor %}типовой проём, {{ META_KEYWORDS|default:"" }}{% endblock %}
|
||||
|
||||
{% block Top_JS3%}<script type="text/javascript">
|
||||
function show_phone_num( id ){ // колапсатор для отображения контатной информации постафшика окон
|
||||
$('#tel'+id).collapse('show');
|
||||
$('#hid'+id).collapse('hide');
|
||||
try {
|
||||
yaCounter32997984.reachGoal('ShowPhone')
|
||||
} catch (e) {
|
||||
}
|
||||
try {
|
||||
var _tmr = window._tmr || (window._tmr = []);
|
||||
_tmr.push({id: "2018432", type: "reachGoal", goal: "ViewPhone"});
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
function show_dtl( id ){ // колапсаторы для отобращения детальной информации оконого набора
|
||||
$('#dtl'+id).collapse('show');
|
||||
$('#btn'+id+' a .glyphicon-chevron-down').remove();
|
||||
var ContentInA = $('#btn'+id+' a').text();
|
||||
$('#btn'+id).empty();
|
||||
$('#btn'+id).text(ContentInA);
|
||||
try {
|
||||
yaCounter32997984.reachGoal('ShowDetails')
|
||||
} catch (e) {
|
||||
}
|
||||
try {
|
||||
var _tmr = window._tmr || (window._tmr = []);
|
||||
_tmr.push({id: "2018432", type: "reachGoal", goal: "ViewDetails"});
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
$(window).load( // уменьшение картинок .half для ретина-дисплеев
|
||||
function(){var images = $('.half');images.each(function(i){$(this).width($(this).width()/2);});}
|
||||
);
|
||||
var count = 0;
|
||||
var ForCompare = document.getElementsByName('ForCompare');
|
||||
$(window).load( // подсчитать сколько прочеканных оферов для сравнения (нужно, когда на страничку возращаются через back, ее подгрущают или она приходит из кеша
|
||||
function(){
|
||||
for (var i=0; i < ForCompare.length; i++)
|
||||
if (ForCompare[i].checked)
|
||||
count++;
|
||||
// alert(count);
|
||||
$('#NumberOfSelected').html(count)
|
||||
}
|
||||
);
|
||||
var ToURL = '';
|
||||
function Click2CompareOffers(){ // вызывается при клике "сравнить коммерческие предложения"
|
||||
try{yaCounter32997984.reachGoal('CompareOffers')}catch(e){}
|
||||
//ForCompare = $('[name]=ForCompare');
|
||||
for (var i=0; i < ForCompare.length; i++)
|
||||
if (ForCompare[i].checked)
|
||||
ToURL += ForCompare[i].value +',';
|
||||
if (ToURL.match(/[,]/g).length > 1 && ToURL.match(/[,]/g).length < 7 )
|
||||
location.href = '/compare_offers/' + ToURL.slice(0,-1);
|
||||
else {
|
||||
$('#modal-exclamation').modal('show');
|
||||
ToURL = '';
|
||||
}
|
||||
}
|
||||
|
||||
function ChangeCountCheckedBox(CHK_ID){ // изменение числа выбранных для сравнения оферов
|
||||
if ($("#CHK"+CHK_ID).is(":checked")) count++;
|
||||
else count--;
|
||||
$("#NumberOfSelected").html(count)
|
||||
}
|
||||
function get_more_info_about_building() {
|
||||
try{yaCounter32997984.reachGoal("ClickBuildingDetails");}catch(e){}
|
||||
}
|
||||
|
||||
$(function () { // инициализация и обработка поповеров рейтинга
|
||||
$('[data-toggle="popover"]').popover({
|
||||
placement: "top",
|
||||
html: true,
|
||||
content: function () {
|
||||
// сохранить текущий контекст
|
||||
var _this = this;
|
||||
$('#shadow_buffer').load('/show_rating_components/' + $(_this).attr("id-set"), function (response, status, xhr) {
|
||||
if (status == "error")
|
||||
$(_this).attr({'data-content': "Ошибка " + xhr.status + " (" + xhr.statusText + ")"});
|
||||
else
|
||||
$(_this).attr({'data-content': response});
|
||||
$(_this).popover('show');
|
||||
try{yaCounter32997984.reachGoal("ShowRating");}catch(e){};
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>{% endblock %}
|
||||
|
||||
{% block Top_CSS1 %}<link rel="stylesheet" type="text/css" href="{% static "css/csshake-vertical.min.css" %}">{% endblock %}
|
||||
|
||||
{% block Main_Content %}<div class="container-fluid">
|
||||
{# <!--- Хлебные крошки: НАЧАЛО --> #}<div class="row">
|
||||
<div class="col-md-11 col-xs-12">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/">Главная</a></li>
|
||||
<li><a href="/catalog">Каталог</a></li>
|
||||
<li><a href="/catalog/standard_opening/">Оконные проёмы и балконные блоки</a></li>
|
||||
<li>Окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}0x{{ I_WIN_DIM.iWinHight|floatformat:0 }}0{% endfor %} мм. </li>
|
||||
</ol>
|
||||
<h1>Стандартные оконные проёмы и балконные блоки</h1>
|
||||
</div>
|
||||
</div>{# <!--- Хлебные крошки: КОНЕЦ ---> #}
|
||||
<span itemscope itemtype="http://schema.org/Product">
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<h1>Цены на окно {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:0 }}0x{{ I_WIN_DIM.iWinHight|floatformat:0 }}0{% endfor %} мм. <small>(типовое)</small></h1>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<p>Типовой проём {% for I_WIN_DIM in FLAP_DIM %}{{ I_WIN_DIM.iWinWidth|floatformat:1 }}x{{ I_WIN_DIM.iWinHight|floatformat:1 }}{% endfor %} cм. представлен в домах серий: {% for I in SERIA_FOR_WIN %}{% if forloop.last %} и {% elif forloop.first %}{% else %}, {% endif %}<a href="/catalog/seria/{{ I.sNameLat }}/all{{ I.id }}">{{ I.sName }}</a>{% endfor %}. База «Окнардии» размещено {{ NUM_TOTAL_OFFER_N_WORD }} цен для окон в такой проем (из них в архиве {{ NUM_ARCHIVE_OFFER }}). Предложено {{ NUM_FLAP_VARIATION_IN_WORD }} открывания от {{ NUM_TOTAL_FIRM_N_WORD }}.</p>
|
||||
</div>
|
||||
{# Микроразмектка: названеи продукта #}<meta itemprop="name" content="Окна {{ APART|safe }} ({{ ADDRESS }})" />
|
||||
</div>
|
||||
|
||||
<div class="row ShowBigFlapPictures">
|
||||
<div class="col-sm-7 col-xs-12 ">{% include 'report/show_big_flap_pictures.html' %}</div>
|
||||
<div class="col-sm-2 col-xs-7 ap_list">
|
||||
<h6>Наиболее частые предложения схем открывания:</h6>
|
||||
<ul>{% for I in LIST_FLAP_VARIATION %}
|
||||
{% if not I.IMG_MINI == '' %}<li><img src="{% static I.IMG_MINI %}" alt="Схема открывания {{ I.sOfferFlapConfig }}" title="Схема открывания {{ I.sOfferFlapConfig }} — {{ I.id }} шт." vspace="2" /> — {{ I.STR_NUM }}</li>{% endif %}{% endfor %}
|
||||
</ul>
|
||||
<div id="donutchart" style="width:100%;"></div>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-5 ap_list">
|
||||
<h6>Типовые серии домой, в которых встречается данный типовой проём:</h6>
|
||||
<ul>{% for I in SERIA_FOR_WIN %}
|
||||
<li><a href="/catalog/seria/{{ I.sNameLat }}/all{{ I.id }}">{{ I.sName }}</a> —
|
||||
<small>входит в {{ I.num_variation_of_apartment }}</small></li>
|
||||
{% endfor %}</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p id="tab-note">В таблице представлены только цены поставщиков из базы «Окнардия». Клик на названии набора отобразит детальную спецификацию каждого предложения: профиль рамы и створки, схему стеклопакета, фурнитуру, элементы отлива, подоконника, откоса, системы <nobr>климат-контроля</nobr>) и сопутствующие услуги. Предложения выводятся блоками. Очередной блок выводится кнопкой «Ещё коммерческие предложения окон» под таблицей. Детальные технические характеристики стеклопакетов, профилей и описание сопутствующих услуг можно посмотреть и сравнить с помощью кнопки «Сравнить выбранные».</p>
|
||||
</div>
|
||||
{# Микроразмектка: названеи продукта #}
|
||||
<meta itemprop="name" content="Окна {{ APART|safe }} ({{ ADDRESS }})"/>
|
||||
</div>
|
||||
|
||||
|
||||
<form class="row" method="get" action="\" id="compare_offers"><div class="col-md-12">
|
||||
{% csrf_token %}
|
||||
<table class=" table" id="price-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><b class="glyphicon glyphicon-th-list" title="Добавление набора окон в список сравнения" aria-label="Добавление набора окон в список сравнения"></b></th>
|
||||
<th>Поставщик окон и название набора<small><br />
|
||||
кликните чтобы отобразить описание и характеристики предложения</small>
|
||||
</th>
|
||||
<th>Схема<small><br />
|
||||
открывания</small></th>
|
||||
<!-- th>Цена, <b class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></b><small><br />
|
||||
одно окно</small></th>
|
||||
<th>N</th>
|
||||
<th colspan="2" class="hidden-xs hidden-sm">Cумма, <b class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></b><small><br />
|
||||
однотипные окна</small></th -->
|
||||
<th style="text-align:right;">Стоимость, <b class="glyphicon glyphicon-ruble" aria-label="₽ (руб.)" title="₽ (руб.)"></b><small><br />
|
||||
окна для типового проёма</small></th>
|
||||
<th style="text-align:center;">Скидка</th>
|
||||
<th>Итого<small><br />
|
||||
за типовое окно с учётом скидки</small></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="offers">
|
||||
{% include "report/report_precelist_one_flap_frame.html" %}
|
||||
</tbody>
|
||||
</table></div>
|
||||
</form></span><p id="shadow_buffer"></p>
|
||||
{# --- Баннер: НАЧАЛО --- #}
|
||||
<div class="row"><div class="col-md-12 col-xs-12"><hr class="dotted-black" />{% include "ad/bannet-wide.html" %}</div></div>
|
||||
{# --- Баннер: конец --- #}
|
||||
<div class="row">
|
||||
{% include "report/report_last_user_visit.html" %}
|
||||
{% include "report/report_log_user_visit.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# модальное окно #}
|
||||
<div class="modal fade bs-example-modal-sm" id="modal-exclamation" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="mySmallModalLabel">Внимание:</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Извините, для сравнения коммерческих предложений окон нужно выбрать не менее двух и не более шести позиций.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Спасибо, все понятно.</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% comment %}
|
||||
{% block Top_Nav_Bar %}
|
||||
{# ОТЛАДКА, ГАСИМ ВЕРХНЕЕ МЕНЮ #}
|
||||
{% endblock %}
|
||||
{% endcomment %}
|
||||
|
||||
@@ -84,7 +84,7 @@ def get_rating_set_for_stars(rating: float = 0.) -> list:
|
||||
#
|
||||
#
|
||||
# # рассчитывает дистанцию в км. между двумя геокоординатами
|
||||
# def GetGeoDistance(lon1, lat1, lat2, lon2):
|
||||
# def get_geo_distance(lon1, lat1, lat2, lon2):
|
||||
# lonA, latA, latB, lonB = map(math.radians, [lon1, lat1, lat2, lon2])
|
||||
# distance = 2 * math.asin(math.sqrt(math.sin((latB - latA) / 2) ** 2 + math.cos(latA) * math.cos(latB) * math.sin(
|
||||
# (lonB - lonA) / 2) ** 2)) * 6371.032 # РАДИУС ЗЕМЛИ 6371.032 КМ.
|
||||
@@ -490,17 +490,38 @@ def make_flap_mini_pictures(path_to_img_file: str, str_flap_config: str) -> None
|
||||
return
|
||||
|
||||
|
||||
def get_flaps_for_mini_pictures(flap_cofig: str) -> str:
|
||||
image_file_name = flap_cofig
|
||||
image_file_name = image_file_name.replace(">", u"G")
|
||||
def get_flaps_for_mini_pictures(flap_config: str) -> str:
|
||||
"""
|
||||
Функция возвращает строку с именем файла мини-картинки для схемы открывания полученной в flap_config
|
||||
|
||||
:param flap_config: str - строка с схемой открывания.
|
||||
:return: str - строка с именем файла мини-картинки.
|
||||
"""
|
||||
image_file_name = flap_config.upper()
|
||||
image_file_name = image_file_name.replace(">", "G")
|
||||
image_file_name = image_file_name.replace("<", "L")
|
||||
image_file_name = image_file_name.replace("|", "I")
|
||||
image_file_name = image_file_name.replace("[", "(")
|
||||
image_file_name = image_file_name.replace("]", ")")
|
||||
image_file_name = image_file_name.replace("/", "-")
|
||||
image_file_name = image_file_name.replace("\\", "-")
|
||||
image_file_name = image_file_name.replace(".", "-") + u".png"
|
||||
image_file_name = image_file_name.replace(".", "-") + ".png"
|
||||
image_file_name = f"{PATH_FOR_IMG}/{PATH_FOR_IMGFLAPCONFIG}/{image_file_name}"
|
||||
if not os.path.isfile(f"{STATIC_BASE_PATH}/{image_file_name}"):
|
||||
make_flap_mini_pictures(f"{STATIC_BASE_PATH}/{image_file_name}", flap_cofig)
|
||||
make_flap_mini_pictures(f"{STATIC_BASE_PATH}/{image_file_name}", flap_config.upper())
|
||||
return image_file_name
|
||||
|
||||
|
||||
def get_geo_distance(lon1: float, lat1: float, lat2: float, lon2: float) -> float:
|
||||
""" Функция возвращает расстояние в км. между двумя геокоординатами.
|
||||
|
||||
:param lon1: float - долгота первой точки.
|
||||
:param lat1: float - широта первой точки.
|
||||
:param lat2: float - широта второй точки.
|
||||
:param lon2: float - долгота второй точки.
|
||||
:return: float - расстояние в км. между двумя геокоординатами.
|
||||
"""
|
||||
lon_a, lat_a, lat_b, lon_b = map(math.radians, [lon1, lat1, lat2, lon2])
|
||||
distance = 2 * math.asin(math.sqrt(math.sin((lat_b - lat_a) / 2) ** 2 + math.cos(lat_a) * math.cos(lat_b)
|
||||
* math.sin((lon_b - lon_a) / 2) ** 2)) * 6371.032 # РАДИУС ЗЕМЛИ 6371.032 КМ.
|
||||
return distance
|
||||
|
||||
441
oknardia/web/prices.py
Normal file
441
oknardia/web/prices.py
Normal file
@@ -0,0 +1,441 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from oknardia.models import Win_MountDim, PriceOffer
|
||||
from oknardia.settings import *
|
||||
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
|
||||
from web.add_func import normalize, get_rating_set_for_stars, get_flaps_for_big_pictures, get_flaps_for_mini_pictures,\
|
||||
get_geo_distance
|
||||
import django.utils.dateformat
|
||||
import time
|
||||
import os
|
||||
import re
|
||||
import pytils
|
||||
|
||||
|
||||
def report_price_frame(apartment_id: int, mount_dim_per_offer: int, address_longitude: float, address_latitude: float,
|
||||
frame_begin_n: int = 0, brand_id: int = 0, win_id: int = 0) -> dict:
|
||||
""" Формируем выдачу цен для фрейма
|
||||
|
||||
:param apartment_id: int -- ID типа квартиры, для которой получаем ценовые предложения
|
||||
:param mount_dim_per_offer: int -- число различных оконых проемов в этой квартире (чтобы отсеять предложения,
|
||||
в которых не представлены все проемы)
|
||||
:param address_longitude: float -- долгота адреса (геокоордината), чтобы рассчитать удаленность компании
|
||||
предоставившей коммерческие предложения
|
||||
:param address_latitude: float -- широта адреса (геокоордината), чтобы рассчитать удаленность компании
|
||||
предоставившей коммерческие предложения
|
||||
:param frame_begin_n: int -- Номер записи с которой начинается фрейм с ценами (с какого предложения начинать)
|
||||
:param brand_id: int -- ID бренда, если выбран бренд, то отображаем только предложения этого бренда
|
||||
(нужно для виджета, где отображаются предложения только от одной компании)
|
||||
если 0, то отображаем все предложения
|
||||
:param win_id: int -- ID окна, если выбрано окно, то отображаем только предложения этого окна
|
||||
:return: dict -- словарь данных для отображения в фрейме (цены предложений и их характеристики)
|
||||
"""
|
||||
# ценовая выдача
|
||||
time_for_meta = 0 # время для мета-данных в HTML-кода
|
||||
apartment_id = int(apartment_id)
|
||||
mount_dim_per_offer = int(mount_dim_per_offer)
|
||||
address_longitude = float(address_longitude)
|
||||
address_latitude = float(address_latitude)
|
||||
frame_begin_n = int(frame_begin_n)
|
||||
brand_id = int(brand_id)
|
||||
win_id = int(win_id)
|
||||
add_to_sql_for_widget = ""
|
||||
offer_per_frame = OFFER_PER_FRAME
|
||||
if brand_id != 0:
|
||||
# Это вывод для выджета. Нужны цены только по определенному поставщику
|
||||
add_to_sql_for_widget = f" AND oknardia_merchantbrand.id = {brand_id} "
|
||||
offer_per_frame = 1000 # Фреймовый вывод не нужен... фигачим сразу целую 1000 предложений.
|
||||
if int(apartment_id) == 0 and int(win_id) != 0:
|
||||
# если выводим цены только для одного проема
|
||||
offer_per_frame = OFFER_PER_FRAME_FOR_ONE_FLAP
|
||||
q_price_offer = PriceOffer.objects.raw(
|
||||
f"SELECT"
|
||||
f" oknardia_priceoffer.id, oknardia_priceoffer.iOfferImpressions,"
|
||||
f" oknardia_priceoffer.fOfferPrice, oknardia_priceoffer.dOfferModify,"
|
||||
f" oknardia_priceoffer.fOfferRating, oknardia_priceoffer.sOfferFlapConfig,"
|
||||
f" oknardia_priceoffer.iOfferViews, oknardia_priceoffer.sOfferActive,"
|
||||
f" oknardia_win_mountdim.sDescripion, oknardia_win_mountdim.id AS mID, "
|
||||
f" oknardia_win_mountdim.bIsNearDoor, oknardia_win_mountdim.bIsDoor,"
|
||||
f" oknardia_win_mountdim.iWinWidth, oknardia_win_mountdim.iWinHight,"
|
||||
f" oknardia_setkit.id AS setID,"
|
||||
f" oknardia_setkit.sSetName, oknardia_setkit.dSetModify,"
|
||||
f" oknardia_setkit.sSetClimateControl, oknardia_setkit.sSetSill,"
|
||||
f" oknardia_setkit.sSetImplementAll, oknardia_setkit.sSetImplementHandles,"
|
||||
f" oknardia_setkit.sSetImplementHinges, oknardia_setkit.sSetImplementLatch,"
|
||||
f" oknardia_setkit.sSetImplementLimiter, oknardia_setkit.sSetImplementCatch,"
|
||||
f" oknardia_setkit.sSetPanes, oknardia_setkit.sSetSlope,"
|
||||
f" oknardia_setkit.sSetOtherConditions, oknardia_setkit.sSetActive,"
|
||||
f" oknardia_setkit.bSetDelivery, oknardia_setkit.sSetDelivery,"
|
||||
f" oknardia_setkit.sSetUninstallInstall, oknardia_setkit.bSetUninstallInstall,"
|
||||
f" oknardia_setkit.fSetRating, oknardia_setkit.iSetNumEval,"
|
||||
f" oknardia_setkit.iSetImpressions, oknardia_setkit.iSetViews,"
|
||||
f" (oknardia_setkit.dSetCommercialUntil > NOW()) AS bCommercial,"
|
||||
f" oknardia_merchantoffice.sOfficePhones, "
|
||||
f" oknardia_merchantoffice.sOfficeDiscountMetaFormula,"
|
||||
f" oknardia_merchantoffice.sOfficeName, oknardia_merchantoffice.sOfficeAddress,"
|
||||
f" oknardia_glazing.fGlazingRating,"
|
||||
f" oknardia_glazing.sGlazingName, oknardia_glazing.sGlazingBriefDescription,"
|
||||
f" oknardia_glazing.sGlazingMark, oknardia_glazing.sGlazingToning,"
|
||||
f" oknardia_pvcprofiles.sProfileBriefDescription, oknardia_pvcprofiles.id AS pwc_id,"
|
||||
f" oknardia_pvcprofiles.sProfileReinforcement, oknardia_pvcprofiles.sProfileSealDescription,"
|
||||
f" oknardia_pvcprofiles.sProfileName, oknardia_pvcprofiles.sProfileColor,"
|
||||
f" oknardia_pvcprofiles.fProfileRating, oknardia_pvcprofiles.sProfileManufacturer,"
|
||||
f" oknardia_merchantbrand.sMerchantName, oknardia_merchantbrand.pMerchantLogo,"
|
||||
f" oknardia_merchantbrand.sMerchantMainURL, oknardia_merchantbrand.id AS brand_id,"
|
||||
f" 1 AS iQuantity, 0 AS fOfficeGeoCode_Longitude, 0 AS fOfficeGeoCode_Latitude "
|
||||
f"FROM oknardia_priceoffer"
|
||||
f" INNER JOIN oknardia_win_mountdim"
|
||||
f" ON oknardia_priceoffer.kOffer2MountDim_id = oknardia_win_mountdim.id"
|
||||
f" INNER JOIN oknardia_setkit"
|
||||
f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id"
|
||||
f" INNER JOIN oknardia_ouruser"
|
||||
f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id"
|
||||
f" INNER JOIN oknardia_merchantoffice"
|
||||
f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id"
|
||||
f" INNER JOIN oknardia_glazing"
|
||||
f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id"
|
||||
f" INNER JOIN oknardia_pvcprofiles"
|
||||
f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id"
|
||||
f" INNER JOIN oknardia_merchantbrand"
|
||||
f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id "
|
||||
f"WHERE oknardia_priceoffer.sOfferActive IS TRUE"
|
||||
f" AND oknardia_setkit.sSetActive IS TRUE "
|
||||
f" AND oknardia_win_mountdim.id = {int(win_id)}"
|
||||
f" {add_to_sql_for_widget} "
|
||||
f"ORDER BY"
|
||||
f" oknardia_priceoffer.dOfferModify DESC "
|
||||
f"LIMIT {int(frame_begin_n)}, 10000;")
|
||||
else:
|
||||
# если выводим цены для типовой квартиры
|
||||
# print("Нужно несколько окон для квартиры")
|
||||
q_price_offer = PriceOffer.objects.raw(
|
||||
f"SELECT"
|
||||
f" oknardia_priceoffer.*,"
|
||||
f" oknardia_win_mountdim.*,"
|
||||
f" oknardia_setkit.*,"
|
||||
f" oknardia_merchantoffice.*,"
|
||||
f" oknardia_glazing.*,"
|
||||
f" oknardia_pvcprofiles.*,"
|
||||
f" oknardia_merchantbrand.*,"
|
||||
f" oknardia_mountdim2apartment.iQuantity,"
|
||||
f" oknardia_win_mountdim.id AS mID, "
|
||||
f" oknardia_setkit.id AS setID,"
|
||||
f" (oknardia_setkit.dSetCommercialUntil > NOW()) AS bCommercial,"
|
||||
f" oknardia_pvcprofiles.id AS pwc_id,"
|
||||
f" oknardia_merchantbrand.id AS brand_id "
|
||||
f"FROM oknardia_priceoffer"
|
||||
f" INNER JOIN oknardia_win_mountdim"
|
||||
f" ON oknardia_priceoffer.kOffer2MountDim_id = oknardia_win_mountdim.id"
|
||||
f" INNER JOIN oknardia_setkit"
|
||||
f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id"
|
||||
f" INNER JOIN oknardia_ouruser"
|
||||
f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id"
|
||||
f" INNER JOIN oknardia_merchantoffice"
|
||||
f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id"
|
||||
f" INNER JOIN oknardia_glazing"
|
||||
f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id"
|
||||
f" INNER JOIN oknardia_pvcprofiles"
|
||||
f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id"
|
||||
f" INNER JOIN oknardia_mountdim2apartment"
|
||||
f" ON oknardia_mountdim2apartment.kMountDim_id = oknardia_win_mountdim.id"
|
||||
f" INNER JOIN oknardia_merchantbrand"
|
||||
f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id "
|
||||
f"WHERE oknardia_priceoffer.sOfferActive IS TRUE"
|
||||
f" AND oknardia_mountdim2apartment.kApartment_id = {int(apartment_id)}"
|
||||
f" AND oknardia_setkit.sSetActive IS TRUE {add_to_sql_for_widget} "
|
||||
f"ORDER BY"
|
||||
f" oknardia_setkit.dSetCreate DESC, " # Сейчас окна в наборе собираются через это
|
||||
f" oknardia_win_mountdim.bIsNearDoor DESC,"
|
||||
f" oknardia_win_mountdim.bIsDoor DESC,"
|
||||
f" oknardia_win_mountdim.iWinWidth,"
|
||||
f" oknardia_win_mountdim.iWinHight DESC "
|
||||
f"LIMIT {int(frame_begin_n)} , 10000;")
|
||||
# print list(qPO)
|
||||
price_frame = []
|
||||
count_mount_dim_in_offer = 0
|
||||
dim_in_offer = []
|
||||
total = 0
|
||||
cur_bullet = 0
|
||||
previous_set_id = 0
|
||||
count_mount_dim_in_frame_page = 0
|
||||
# Так как получен QuerySet начиная с frame_begin_n, то для того чтобы получить frame_begin_n следующего фрйма
|
||||
# считаем не от нуля, а от старого frame_begin_n
|
||||
n_begin = int(frame_begin_n)
|
||||
# Проверяем есть ли папка для хранения мини-картинки конфигурации схемы открывания.
|
||||
if not os.path.exists(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{PATH_FOR_IMGFLAPCONFIG}"):
|
||||
# создаем такую папку если её нет
|
||||
os.makedirs(f"{STATIC_BASE_PATH}/{PATH_FOR_IMG}/{PATH_FOR_IMGFLAPCONFIG}")
|
||||
# print(">>>>>>>>>>>>>", apartment_id)
|
||||
for i2 in q_price_offer:
|
||||
n_begin += 1
|
||||
count_mount_dim_in_frame_page += 1
|
||||
# Случается, что в том или ином наборе поставщиком просчитаны не все проёмы.
|
||||
# Чтобы не происходило формирование предложения (офера) из окон разных наборов делаем проверку
|
||||
# является ли текущий проём в предложении из того же набора, что и предыдущий.
|
||||
# Если он из другого набора, то удаляем предыдущий проём из предложения и начинаем
|
||||
# формирование предложения сначала.
|
||||
if count_mount_dim_in_offer == 0:
|
||||
previous_set_id = i2.setID
|
||||
else:
|
||||
if previous_set_id != i2.setID:
|
||||
# print("Сбой в наборе. Обнуляем набор")
|
||||
previous_set_id = i2.setID
|
||||
count_mount_dim_in_offer = 0
|
||||
total = 0
|
||||
cur_bullet = 0
|
||||
dim_in_offer.pop()
|
||||
dim_in_offer = []
|
||||
# print("mID:", i2.mID, " || pID:", i2.id, " || price:", i2.fOfferPrice, " || N:", i2.iQuantity,
|
||||
# " || set:", i2.sSetName, " || merchant:", i2.sOfficeName)
|
||||
total += i2.fOfferPrice * i2.iQuantity
|
||||
image_file = get_flaps_for_mini_pictures(i2.sOfferFlapConfig)
|
||||
dim_in_offer.append({
|
||||
'PRICE': i2.fOfferPrice,
|
||||
'FLAP': i2.sOfferFlapConfig,
|
||||
'DESCRIPTION': i2.sDescripion,
|
||||
'WIDTH': i2.iWinWidth,
|
||||
'HIGHT': i2.iWinHight,
|
||||
'ID': i2.id,
|
||||
'IMG_MINI': image_file,
|
||||
'QUANTITY': i2.iQuantity,
|
||||
'BULLET': [chr(65+cur_bullet+i) for i in range(i2.iQuantity)],
|
||||
# 'BULLET': range(CurBullet, CurBullet+i2.iQuantity),
|
||||
'SUBTOTAL': i2.fOfferPrice * i2.iQuantity,
|
||||
})
|
||||
cur_bullet += i2.iQuantity
|
||||
count_mount_dim_in_offer += 1
|
||||
if count_mount_dim_in_offer == mount_dim_per_offer:
|
||||
# print("-----------------")
|
||||
# узнаем скидку через разбор формулы на метаязыке
|
||||
discount = 0
|
||||
try:
|
||||
meta_keys = eval(i2.sOfficeDiscountMetaFormula)
|
||||
if KEY_DICSOUNT in meta_keys:
|
||||
# скидки рассчитываются исходя из общей суммы
|
||||
for CountVal in sorted(meta_keys[KEY_DICSOUNT]):
|
||||
# print(CountVal, "::", meta_keys[KEY_DICSOUNT][CountVal])
|
||||
if float(total) > float(CountVal):
|
||||
discount = meta_keys[KEY_DICSOUNT][CountVal]
|
||||
# # DiscountTXT += u"!!%d!!" % Discount
|
||||
# print("Значит DISCOUNT: ", Discount)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
fin_price = total * (100 - discount) / 100
|
||||
# уточняем, есть ли в принципе геокоординаты?
|
||||
if int(i2.fOfficeGeoCode_Longitude) != 0 and int(i2.fOfficeGeoCode_Latitude) != 0 and \
|
||||
int(address_longitude) != 0 and int(address_latitude) != 0:
|
||||
# рассчитываем дистанцию между адресом дома и офиса.
|
||||
distance = get_geo_distance(i2.fOfficeGeoCode_Longitude, i2.fOfficeGeoCode_Latitude, address_longitude,
|
||||
address_latitude)
|
||||
# т.к. из-за изменений в api яндекс карт поменялась местами широта-долгота и вообще, то
|
||||
# порядок переменных строчной выше... На самом деле должно быть как в закоментированной
|
||||
# строке ниже
|
||||
# distance = get_geo_distance(i2.fOfficeGeoCode_Longitude, i2.fOfficeGeoCode_Latitude, address_longitude,
|
||||
# address_latitude)
|
||||
else:
|
||||
distance = -1
|
||||
if discount > 99 or discount < 0.1:
|
||||
discount_color1 = ""
|
||||
discount_color2 = ""
|
||||
else:
|
||||
color_ratio = (discount + 0.) / 100
|
||||
discount_color1 = f"#{255 - int(color_ratio * 128): 02x}ff{255 - int(color_ratio * 128) :02x}"
|
||||
discount_color2 = f"#{255-int(color_ratio * 255): 02x}ff{255 - int(color_ratio * 255) :02x}"
|
||||
price_frame.append({
|
||||
'DISTANCE': distance,
|
||||
'DIM': dim_in_offer,
|
||||
'TOTAL': total,
|
||||
'DISCOUNT': discount,
|
||||
'DISCOUNT_COLOR1': discount_color1,
|
||||
'DISCOUNT_COLOR2': discount_color2,
|
||||
'FIN_PRICE': fin_price,
|
||||
'OFFICE_NAME': i2.sOfficeName,
|
||||
'OFFICE_ADDRESS': i2.sOfficeAddress,
|
||||
'OFFICE_PHONES': i2.sOfficePhones,
|
||||
'MERCHANT': i2.sMerchantName,
|
||||
'MERCHANT_LOGO': i2.pMerchantLogo,
|
||||
'MERCHANT_URL': i2.sMerchantMainURL,
|
||||
'MERCHANT_URL_SHOT': re.sub(r"(?:^http://|^https://|/$|www\.)", "", i2.sMerchantMainURL),
|
||||
'SETS_NAME': i2.sSetName,
|
||||
'GLAZING_NAME_B': i2.sGlazingBriefDescription,
|
||||
'GLAZING_MARK': i2.sGlazingMark,
|
||||
'GLAZING_TONING': i2.sGlazingToning,
|
||||
'PVC_ID': i2.pwc_id,
|
||||
'PVC_NAME': i2.sProfileName,
|
||||
'PVC_NAME_T': pytils.translit.slugify(i2.sProfileName).lower(),
|
||||
'PVC_MANUFACTURER': i2.sProfileManufacturer,
|
||||
'PVC_MANUFACTURER_T': pytils.translit.slugify(i2.sProfileManufacturer).lower(),
|
||||
'PVC_SEAL': i2.sProfileSealDescription,
|
||||
'SETS_CLIMATE_CONTROL': i2.sSetClimateControl,
|
||||
'SETS_SILL': i2.sSetSill,
|
||||
'SETS_IMPLEMENT': i2.sSetImplementAll,
|
||||
'SETS_IMPLEMENT_R': i2.sSetImplementHandles,
|
||||
'SETS_IMPLEMENT_P': i2.sSetImplementHinges,
|
||||
'SETS_IMPLEMENT_Z': i2.sSetImplementLatch,
|
||||
'SETS_IMPLEMENT_O': i2.sSetImplementLimiter,
|
||||
'SETS_IMPLEMENT_F': i2.sSetImplementCatch,
|
||||
'SETS_PANES': i2.sSetPanes,
|
||||
'SETS_SLOPE': i2.sSetSlope,
|
||||
'SETS_DELIVERY': i2.sSetDelivery,
|
||||
'SETS_DELIVERY_B': i2.bSetDelivery,
|
||||
'SETS_OTHER': i2.sSetOtherConditions,
|
||||
'SETS_ID': i2.setID,
|
||||
'SETS_UNINSTALL_INSTALL': i2.sSetUninstallInstall,
|
||||
'SETS_UNINSTALL_INSTALL_B': i2.bSetUninstallInstall,
|
||||
'SETS_RATING': i2.fSetRating,
|
||||
'SETS_RATING_STARTS': get_rating_set_for_stars(i2.fSetRating),
|
||||
'SETS_DATA_MODIFY': i2.dOfferModify,
|
||||
'IS_COMMERCIAL': i2.bCommercial,
|
||||
})
|
||||
if len(price_frame) == offer_per_frame:
|
||||
break
|
||||
count_mount_dim_in_offer = 0
|
||||
dim_in_offer = []
|
||||
total = 0
|
||||
cur_bullet = 0
|
||||
# узнаем дату-время самого свежего ценового предложения для размещения в META-тега
|
||||
if time_for_meta == 0 or django.utils.dateformat.format(time_for_meta, 'U') < \
|
||||
django.utils.dateformat.format(i2.dOfferModify, 'U'):
|
||||
time_for_meta = i2.dOfferModify
|
||||
if time_for_meta == 0 or django.utils.dateformat.format(time_for_meta, 'U') < \
|
||||
django.utils.dateformat.format(i2.dSetModify, 'U'):
|
||||
time_for_meta = i2.dSetModify
|
||||
# массив ценовых предложений (что бы предложения на одной дистанции были в случайном порядке)
|
||||
# random.shuffle(PriceFrame)
|
||||
# сортируем по удаленности
|
||||
price_frame = sorted(price_frame, key=lambda item: item['DISTANCE'])
|
||||
if len(price_frame) < offer_per_frame:
|
||||
n_begin = '-1'
|
||||
return {'META_DATA_PUBLISH': time_for_meta, 'PRICE_FRAME': price_frame, 'N': n_begin}
|
||||
|
||||
|
||||
def report_one_win_price(request: HttpRequest, win_width_mm: str = '670', win_height_mm: str = '2160',
|
||||
win_id: str = '16') -> HttpResponse:
|
||||
""" Формируем выдачу цен для единичного ТИПОВОГО окна (т.е. проема из серийного дома).
|
||||
|
||||
|
||||
:param request: HttpRequest -- входящий http-запрос
|
||||
:param win_width_mm: str -- Ширина проема в миллиметрах (это SEO-параметр, в реальности он будет получен из базы)
|
||||
:param win_height_mm: str -- Высота проема в миллиметрах (это SEO-параметр, в реальности он будет получен из базы)
|
||||
:param win_id: str -- ID проема (см. таблицу oknardia_win_mountdim)
|
||||
:return response: HttpResponse -- исходящий http-ответ
|
||||
"""
|
||||
time_start = time.time()
|
||||
to_template = {}
|
||||
try:
|
||||
# т.к. для вызова GetFlapDim4BigPictures нужно иметь внутри queryset поле iQuantity нельзя использовать
|
||||
# простой запрос (см. следующую строку).
|
||||
# qWinInfo = Win_MountDim.objects.filter(id=int(win_id))
|
||||
# Придется сделать запрос немного сложнее:
|
||||
q_win_info = Win_MountDim.objects.raw(
|
||||
f'SELECT oknardia_win_mountdim.iWinWidth,'
|
||||
f' oknardia_win_mountdim.iWinHight, oknardia_win_mountdim.iWinDepth,'
|
||||
f' oknardia_win_mountdim.sFlapConfig, oknardia_win_mountdim.bIsNearDoor,'
|
||||
f' oknardia_win_mountdim.bIsDoor, oknardia_win_mountdim.sDescripion,'
|
||||
f' oknardia_win_mountdim.id, 0 as iQuantity '
|
||||
f'FROM oknardia_win_mountdim '
|
||||
f'WHERE oknardia_win_mountdim.id = {int(win_id)};'
|
||||
)
|
||||
list_win_info = list(q_win_info)
|
||||
# Если размеры типового проема не совпадают с размерами из базы, то подменяем
|
||||
# на правильные и перевызываем страницу
|
||||
if (list_win_info[0].iWinWidth * 10 != int(win_width_mm)) or \
|
||||
(list_win_info[0].iWinHight * 10 != int(win_height_mm)):
|
||||
return redirect(f"/tsena-odnogo-okna/{list_win_info[0].iWinWidth * 10}x{list_win_info[0].iWinHight * 10}"
|
||||
f"mm/tip{win_id}")
|
||||
except (ObjectDoesNotExist, ValueError, IndexError, TypeError):
|
||||
return redirect("/tsena-odnogo-okna/670x2160mm/tip16")
|
||||
# все хорошо, засылаем картинку в шаблон
|
||||
to_template.update(get_flaps_for_big_pictures(list_win_info))
|
||||
# получаем варианты схемы открывания (для графиков)
|
||||
q_offer_flap_variation = PriceOffer.objects.raw(
|
||||
f'SELECT'
|
||||
f' COUNT(oknardia_priceoffer.sOfferFlapConfig) AS id,'
|
||||
f' "" AS IMG_MINI,'
|
||||
f' "" AS STR_NUM,'
|
||||
f' oknardia_priceoffer.sOfferFlapConfig '
|
||||
f'FROM oknardia_priceoffer '
|
||||
f'WHERE oknardia_priceoffer.sOfferActive <> 0'
|
||||
f' AND oknardia_priceoffer.kOffer2MountDim_id = {int(win_id)} '
|
||||
f'GROUP BY oknardia_priceoffer.sOfferFlapConfig,'
|
||||
f' oknardia_priceoffer.sOfferActive,'
|
||||
f' oknardia_priceoffer.kOffer2MountDim_id '
|
||||
f'ORDER BY id DESC;'
|
||||
)
|
||||
list_offer_flap_variation = list(q_offer_flap_variation)
|
||||
for i in range(0, len(list_offer_flap_variation)):
|
||||
if i < 3:
|
||||
list_offer_flap_variation[i].STR_NUM = "вариант " + pytils.numeral.in_words(i + 1)
|
||||
elif i == 3:
|
||||
list_offer_flap_variation[i].STR_NUM = "остальные варианты"
|
||||
continue
|
||||
else:
|
||||
list_offer_flap_variation[3].id += list_offer_flap_variation[i].id
|
||||
continue
|
||||
list_offer_flap_variation[i].IMG_MINI = get_flaps_for_mini_pictures(
|
||||
list_offer_flap_variation[i].sOfferFlapConfig
|
||||
)
|
||||
to_template.update({'LIST_FLAP_VARIATION': list_offer_flap_variation[:4]})
|
||||
to_template.update({'NUM_FLAP_VARIATION_IN_WORD': pytils.numeral.sum_string(len(list_offer_flap_variation),
|
||||
pytils.numeral.MALE,
|
||||
("вариант схемы",
|
||||
"варианта схем",
|
||||
"вариантов схем"))})
|
||||
#
|
||||
q = PriceOffer.objects.raw(f'SELECT'
|
||||
f' COUNT(oknardia_priceoffer.kOfferFromUser_id) AS id,'
|
||||
f' oknardia_priceoffer.kOfferFromUser_id,'
|
||||
f' oknardia_priceoffer.kOffer2MountDim_id '
|
||||
f'FROM oknardia_priceoffer '
|
||||
f'WHERE oknardia_priceoffer.kOffer2MountDim_id = {int(win_id)} '
|
||||
f'GROUP BY oknardia_priceoffer.kOffer2MountDim_id,'
|
||||
f' oknardia_priceoffer.kOfferFromUser_id;')
|
||||
to_template.update({'NUM_TOTAL_FIRM_N_WORD': pytils.numeral.get_plural(len(list(q)),
|
||||
("компании", "компаний", "компаний"))})
|
||||
q = PriceOffer.objects.filter(kOffer2MountDim_id=int(win_id))
|
||||
to_template.update({'NUM_TOTAL_OFFER_N_WORD': pytils.numeral.get_plural(q.count(),
|
||||
("готовый расчёт", "готовых расчёта",
|
||||
"готовых расчётов"))})
|
||||
to_template.update({'NUM_ARCHIVE_OFFER': q.filter(sOfferActive=0).count()})
|
||||
#
|
||||
q_seria_for_win = PriceOffer.objects.raw(
|
||||
f'SELECT'
|
||||
f' oknardia_seria_info.sName, oknardia_seria_info.id AS id,'
|
||||
f' "" AS sNameLat,'
|
||||
f' COUNT(oknardia_mountdim2apartment.id) AS num_variation_of_apartment '
|
||||
f'FROM oknardia_apartment_type'
|
||||
f' INNER JOIN oknardia_mountdim2apartment'
|
||||
f' ON oknardia_mountdim2apartment.kApartment_id = oknardia_apartment_type.id'
|
||||
f' INNER JOIN oknardia_win_mountdim'
|
||||
f' ON oknardia_mountdim2apartment.kMountDim_id = oknardia_win_mountdim.id'
|
||||
f' INNER JOIN oknardia_seria_info'
|
||||
f' ON oknardia_apartment_type.kSeria_id = oknardia_seria_info.id '
|
||||
f'WHERE oknardia_win_mountdim.id = {int(win_id)} '
|
||||
f'GROUP BY oknardia_win_mountdim.id,'
|
||||
f' oknardia_seria_info.sName,'
|
||||
f' oknardia_seria_info.id '
|
||||
f'ORDER BY oknardia_seria_info.sName;'
|
||||
)
|
||||
list_seria_for_win = list(q_seria_for_win)
|
||||
for i in list_seria_for_win:
|
||||
i.sNameLat = pytils.translit.slugify(i.sName)
|
||||
i.num_variation_of_apartment = pytils.numeral.sum_string(i.num_variation_of_apartment,
|
||||
pytils.numeral.MALE,
|
||||
("типовую планировку квартиры",
|
||||
"типовые планировки квартир",
|
||||
"типовых планировок квартир"))
|
||||
to_template.update(report_price_frame(0, 1, 0, 0, 0, 0, int(win_id)))
|
||||
to_template.update({
|
||||
'SERIA_FOR_WIN': list_seria_for_win,
|
||||
'WIN_ID': int(win_id),
|
||||
'MOUNT_DIM_PER_OFFER': 1,
|
||||
# получаем последние визиты клиента через куки
|
||||
'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]),
|
||||
# получаем последние визиты всех посетителей из базы
|
||||
# id2log, log_visit = get_last_all_user_visit_list()
|
||||
'LOG_VISIT': get_last_all_user_visit_list(),
|
||||
'ticks': float(time.time() - time_start)
|
||||
})
|
||||
return render(request, "report/report_price-offers_for_one_window.html", to_template)
|
||||
Reference in New Issue
Block a user