Калькулятор доставки из Китая
:root{--primary:#2c3e50;--secondary:#3498db;--accent:#e74c3c;--light:#ecf0f1;--dark:#2c3e50}
*{margin:0;padding:0;box-sizing:border-box;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}
body{background:#f5f7fa;color:#333;line-height:1.6}
.container{max-width:1200px;margin:0 auto;padding:20px}
header{background:linear-gradient(135deg,var(--primary),var(--secondary));color:#fff;padding:20px;text-align:center;border-radius:10px;margin-bottom:18px}
h1{font-size:2rem;margin-bottom:6px}.subtitle{opacity:.9}
.grid{display:grid;gap:18px;grid-template-columns:1fr 1fr}@media(max-width:900px){.grid{grid-template-columns:1fr}}
.card{background:#fff;padding:18px;border-radius:10px;box-shadow:0 4px 6px rgba(0,0,0,.05)}
.section-title{color:var(--primary);margin-bottom:12px;padding-bottom:8px;border-bottom:2px solid var(--light)}
.row{display:flex;gap:12px;flex-wrap:wrap}
.form-group{margin-bottom:12px;flex:1 1 230px}
label{display:block;margin-bottom:6px;font-weight:600;color:var(--dark)}
input,select,textarea{width:100%;padding:10px 12px;border:1px solid #ddd;border-radius:6px;font-size:15px}
input:focus,select:focus,textarea:focus{border-color:var(--secondary);outline:none}
.btn{background:var(--secondary);color:#fff;border:none;padding:10px 16px;border-radius:6px;cursor:pointer;font-weight:600}
.btn:hover{background:#2980b9}.btn-accent{background:var(--accent)}.btn-accent:hover{background:#c0392b}
.btn-ghost{background:#eef5ff;color:#0b3b53;border:1px solid #d7e6ff}
.form-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:8px}
.pill{display:inline-block;background:#eef5ff;border:1px solid #d7e6ff;border-radius:999px;padding:4px 8px;margin-right:6px;font-size:.9rem}
.error{display:none;color:var(--accent);font-size:.9rem;margin-top:4px}
.result-item{display:flex;justify-content:space-between;gap:10px;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid #eee}
.result-total{font-size:1.2rem;font-weight:800;color:var(--primary);margin-top:10px;padding-top:10px;border-top:2px solid #e5e7eb}
.muted{color:#637184;font-size:.92rem}
textarea.k{min-height:110px;font-family:ui-monospace,Menlo,Consolas,monospace}
pre.debug{white-space:pre-wrap;background:#0b1221;color:#dbeafe;border:1px solid #223048;border-radius:8px;padding:10px;max-height:240px;overflow:auto}
Получите расчёт — результат сразу!
Безопасная дешёвая белая доставка за 3–4 недели
1) Параметры доставки
Город отправления
Иу (+3%)
Гуанчжоу (+6%)
Шанхай (0%)
Тип груза (база)
Обычный
Опасные (+20%)
Хрупкие (+10%)
Негабаритные (+15%)
Скоропортящиеся (+25%)
Доп.надбавки перевозчиков (можно несколько)
Литиевые батареи UN3480/3481
Аэрозоли/спреи
Магнитные грузы
Парфюм/спиртосодержащие
Порошки/хим.сырьё
Температурный режим/изотерма
Деревянная тара/фумигация
Негабарит по длине
Тяжёлый груз
Иные опасные (ADR/IATA)
Документарные сборы перевозчика
Проценты редактируются в «⚙️ Отладка / Настройки» ниже.
Стоимость товара (CNY, без НДС КНР 13%)
Введите стоимость товара
Количество товаров (SKU)
Вес фактический (кг)
Введите вес (кг)
Объём (м³)
Введите объём (м³)
Пошлина, %
НДС РФ, %
Страховая оценка (decl%), %
Оформление/документы (RUB)
Страхование, %
40ft контейнер
АвтовыборПринудительно ($8000)Отключить
Доступен если ≤ 66 м³ и ≤ 22 000 кг. В интерфейсе цена контейнера не показывается.
Рассчитать
Обновить курсы ЦБ
Сбросить
Минимальный заказ: 50 кг
Курсы ЦБ +4%
Скидки для малых грузов
2) Результаты и детализация
Введите параметры и нажмите «Рассчитать»
PDF (Коммерческое предложение)
Отправить заявку
3) Ограничения и примечания
Запрещено: оружие/боеприпасы, наркотические и психотропные вещества, взрывчатые, радиоактивные материалы, живые животные (без разрешений), ядовитые/токсичные вещества, контрафакт и т.п.
⚙️ Отладка / Настройки (для игры с параметрами)
Опорные точки $/кг (JSON)
{
"50":1.40,"100":1.35,"200":1.25,"300":1.20,"500":1.10,
"1000":0.95,"2000":0.90,"3000":0.84,"5000":0.72,"10000":0.65
}
Надбавки перевозчиков (JSON)
{
"un3480":0.20,"aerosol":0.15,"magnet":0.08,"perfume":0.20,
"powder":0.15,"temp":0.15,"woodpack":0.03,"oversize_len":0.15,
"oversize_wt":0.20,"dg_other":0.25,"docs_carrier":0.02
}
Курсы вручную (с учётом +4%)
Или нажмите «Обновить курсы ЦБ».
Текущие: USD/RUB —, CNY/RUB —
Порог плотности (кг/м³)
Минимальный биллинговый вес (кг)
Мин. ставка после скидок ($/кг)
Позволяет реально удешевить малые грузы ниже прежнего пола $1.40.
Скидочная кривая для малых грузов (JSON, доли)
{
"50":0.15, "100":0.12, "200":0.10, "300":0.08, "500":0.06, "1000":0.05
}
Плавная скидка 5–15%: лог-интерполяция между точками; выше максимального веса — 0%.
Включить скидки
onoff
Показать отладочные переменные
—
/* ====== УТИЛИТЫ ====== */
const $ = id => document.getElementById(id);
const money = v => new Intl.NumberFormat('ru-RU',{style:'currency',currency:'RUB',minimumFractionDigits:0}).format(v);
const enc = s => encodeURIComponent(s);
const readJSON = (id, fb) => { try{return JSON.parse($(id).value)}catch(e){return fb} };
/* ====== КУРСЫ ЦБ +4% ====== */
let FX={usd:NaN,cny:NaN,markup:0.04};
async function fetchCBR(){
try{
const r=await fetch('https://www.cbr-xml-daily.ru/daily_json.js',{cache:'no-store'});
const j=await r.json();
const usd=j?.Valute?.USD?.Value, cny=j?.Valute?.CNY?.Value;
if(usd&&cny){ FX.usd=usd*(1+FX.markup); FX.cny=cny*(1+FX.markup); }
}catch(e){}
paintFX();
}
function paintFX(){
const u=parseFloat($('usdManual').value), c=parseFloat($('cnyManual').value);
const usd=Number.isFinite(u)?u:FX.usd, cny=Number.isFinite(c)?c:FX.cny;
$('usdRate').textContent=Number.isFinite(usd)?usd.toFixed(4):'—';
$('cnyRate').textContent=Number.isFinite(cny)?cny.toFixed(4):'—';
}
/* ====== ТАРИФЫ, СКИДКИ ====== */
let baseRates={"50":1.40,"100":1.35,"200":1.25,"300":1.20,"500":1.10,"1000":0.95,"2000":0.90,"3000":0.84,"5000":0.72,"10000":0.65};
let discMap={"50":0.15,"100":0.12,"200":0.10,"300":0.08,"500":0.06,"1000":0.05};
let carrierCfg={"un3480":0.20,"aerosol":0.15,"magnet":0.08,"perfume":0.20,"powder":0.15,"temp":0.15,"woodpack":0.03,"oversize_len":0.15,"oversize_wt":0.20,"dg_other":0.25,"docs_carrier":0.02};
function nonIncreasing(obj){
const ks=Object.keys(obj).map(Number).sort((a,b)=>a-b);
for(let i=1;iobj[ks[i-1]]) obj[ks[i]]=obj[ks[i-1]]; }
return ks;
}
function interpLog(x, points){
const ks=Object.keys(points).map(Number).sort((a,b)=>a-b);
if(x=ks[ks.length-1]) return points[ks[ks.length-1]];
let lo=ks[0],hi=ks[ks.length-1];
for(let i=1;i0 ? (w/v) : NaN;
let bill = (Number.isFinite(density) && densityextraPct+=(carrierCfg[k]||0));
const usdRub = Number.isFinite(parseFloat($('usdManual').value)) ? parseFloat($('usdManual').value) : FX.usd;
const surchMul = cityMul * cargoMul * (1 + extraPct);
// Контейнер учитываем «за кадром»
const fits = (v`${a}${b}`;
res.innerHTML = `
${row('Плотность', Number.isFinite(m.density)?m.density.toFixed(2)+' кг/м³':'—')}
${row('Режим', m.modeText)}
${row('Биллинг', m.bill.toFixed(2)+' кг')}
${row('Ставка', '$ '+m.rate.toFixed(2)+'/кг')}
${row('Фрахт (итог)', '$ '+m.freightUSD.toFixed(2)+' · '+money(m.freightRUB))}
Детализация начислений
${row('Стоимость товара (+13% КНР)', money(m.goodsRub))}
${row('Страхование', money(m.insRub)+' ('+m.ins.toFixed(1)+'%)')}
${row('Контрактные услуги', money(m.contractRub)+' ('+m.sku+' SKU)')}
${row('CIF-база', money(m.cifBase))}
${row('Пошлина', money(m.dutyRub)+' ('+m.duty.toFixed(1)+'%)')}
${row('Оформление/документы', money(m.docs))}
${row('НДС РФ', money(m.vatRub)+' ('+m.vat.toFixed(1)+'%)')}
Итого${money(m.grand)}
Надбавки: город×тип×перевозчик = множитель ${m.surchMul.toFixed(3)} (доп.перевозчик: ${(m.extraPct*100).toFixed(0)}%).
`;
}
/* ====== ОСНОВНОЙ РАСЧЁТ ====== */
function calculate(){
if(!validate()) return;
const cityMul=parseFloat($('city').value), cargoMul=parseFloat($('cargo').value);
const extraKeys=Array.from($('extra').selectedOptions).map(o=>o.value);
const w=parseFloat($('w').value), v=parseFloat($('v').value);
const sku=parseInt($('sku').value)||1, priceCNY=parseFloat($('price').value);
const densCut=parseFloat($('densCut').value)||300, minKg=parseFloat($('minKg').value)||50;
const ship = calcShipping({w,v,cityMul,cargoMul,extraKeys,containerMode:$('containerMode').value,densCut,minKg});
const gRub = goodsRub(priceCNY, sku);
const contr = contractRub(sku);
const inputs = {
duty:parseFloat($('duty').value)||0,
vat:parseFloat($('vat').value)||0,
decl:Math.min(100,Math.max(10,parseFloat($('decl').value)||100)),
ins:parseFloat($('ins').value)||0,
docs:parseFloat($('docs').value)||0
};
const tax = totals({goodsRub:gRub, freightRUB:ship.freightRUB, contractRub:contr}, inputs);
const model = {
...ship,
goodsRub:gRub, contractRub:contr,
insRub:tax.insRub, cifBase:tax.cifBase, dutyRub:tax.dutyRub, vatRub:tax.vatRub, grand:tax.grand,
docs:inputs.docs, duty:inputs.duty, vat:inputs.vat, ins:inputs.ins, sku
};
window.__model=model;
render(model);
// debug dump
$('dbg').textContent = JSON.stringify({
inputs:{cityMul,cargoMul,extraKeys,w,v,sku,priceCNY,densCut,minKg},
fx:{usd:FX.usd,cny:FX.cny,usdManual:$('usdManual').value,cnyManual:$('cnyManual').value},
bill:ship.bill, density:ship.density, rate:ship.rate, chosenUSD:ship.freightUSD,
surchMul:ship.surchMul, extraPct:ship.extraPct,
totals:{goods:gRub, freight:ship.freightRUB, ins:tax.insRub, cif:tax.cifBase, duty:tax.dutyRub, vat:tax.vatRub, docs:inputs.docs, contract:contr, grand:tax.grand}
}, null, 2);
}
/* ====== PDF ЧЕРЕЗ doc.html (кириллица ок) ====== */
function makePDF(){
if(!window.__model) return;
const m = window.__model;
const { jsPDF } = window.jspdf;
const cityTxt = $('city').options[$('city').selectedIndex].text;
const cargoTxt = $('cargo').options[$('cargo').selectedIndex].text;
const html = `
Белая доставка · КП
Коммерческое предложение
Дата: ${new Date().toLocaleString('ru-RU')}
Город: ${escapeHtml(cityTxt)} · Тип груза: ${escapeHtml(cargoTxt)}
${row('Плотность', isFinite(m.density)? m.density.toFixed(2)+' кг/м³' : '—')}
${row('Режим', m.modeText)}
${row('Биллинг', m.bill.toFixed(2)+' кг')}
${row('Ставка', '$ '+(m.rate??0).toFixed(2)+'/кг (с учётом скидок)')}
${row('Фрахт (итог)', '$ '+m.freightUSD.toFixed(2)+' · '+fmt(m.freightRUB))}
Детализация начислений
${row('Стоимость товара (+13% КНР)', fmt(m.goodsRub))}
${row('Страхование', fmt(m.insRub)+' ('+m.ins.toFixed(1)+'%)')}
${row('Контрактные услуги', fmt(m.contractRub)+' ('+m.sku+' SKU)')}
${row('CIF-база', fmt(m.cifBase))}
${row('Пошлина', fmt(m.dutyRub)+' ('+m.duty.toFixed(1)+'%)')}
${row('Оформление/документы', fmt(m.docs))}
${row('НДС РФ', fmt(m.vatRub)+' ('+m.vat.toFixed(1)+'%)')}
ИТОГО: ${fmt(m.grand)}
Надбавки: город×тип×перевозчик = множитель ${m.surchMul.toFixed(3)}
(доп.перевозчик: ${(m.extraPct*100).toFixed(0)}%).
Предложение носит справочный характер и не является публичной офертой. Финальные ставки закрепляются инвойсом.
`;
// скрытый контейнер
const holder = document.createElement('div');
holder.style.position='fixed'; holder.style.left='-10000px';
holder.innerHTML=html; document.body.appendChild(holder);
const doc = new jsPDF({ unit:'pt', format:'a4' });
const pageWidth = doc.internal.pageSize.getWidth();
const margin = 24;
const renderWidth = pageWidth - margin*2;
doc.html(holder.firstElementChild, {
x: margin, y: margin, width: renderWidth,
html2canvas: { scale: 0.95, useCORS: true, backgroundColor:'#ffffff' },
callback: function(d){ d.save('KP_belaya_dostavka.pdf'); document.body.removeChild(holder); }
});
function fmt(v){ return new Intl.NumberFormat('ru-RU',{style:'currency',currency:'RUB',minimumFractionDigits:0}).format(v); }
function row(a,b){
return `
${escapeHtml(a)}
${escapeHtml(String(b))}
`;
}
function escapeHtml(s){ return String(s).replace(/[&"]/g,c=>({'&':'&','':'>','"':'"'}[c])); }
}
/* ====== MAILTO ====== */
function sendMail(){
if(!window.__model) return;
const m=window.__model;
const body=[
'Заявка на белую доставку из Китая',
'----------------------------------',
`Плотность: ${Number.isFinite(m.density)?m.density.toFixed(2)+' кг/м³':'—'}`,
`Режим: ${m.modeText}`,
`Биллинг: ${m.bill.toFixed(2)} кг`,
`Ставка: $ ${m.rate.toFixed(2)}/кг (с учётом скидок)`,
`Фрахт (итог): $ ${m.freightUSD.toFixed(2)} · ${money(m.freightRUB)}`,
'',
`Товар (+13% КНР): ${money(m.goodsRub)}`,
`Страхование: ${money(m.insRub)}`,
`Контрактные услуги: ${money(m.contractRub)}`,
`CIF-база: ${money(m.cifBase)}`,
`Пошлина (${m.duty.toFixed(1)}%): ${money(m.dutyRub)}`,
`Документы: ${money(m.docs)}`,
`НДС РФ (${m.vat.toFixed(1)}%): ${money(m.vatRub)}`,
`ИТОГО: ${money(m.grand)}`
].join('\n');
window.location.href=`mailto:a.d@sierra.market?subject=${enc('Заявка с калькулятора')}&body=${enc(body)}`;
}
/* ====== СОБЫТИЯ ====== */
$('calc').addEventListener('click', ()=>{ if(!validate()) return; calculate(); });
$('pdf').addEventListener('click', makePDF);
$('mail').addEventListener('click', sendMail);
$('fx').addEventListener('click', fetchCBR);
$('reset').addEventListener('click', ()=>{
['price','sku','w','v','usdManual','cnyManual'].forEach(id=>$(id).value='');
$('city').value='1.00'; $('cargo').value='1.00'; $('containerMode').value='auto';
$('densCut').value='300'; $('minKg').value='50'; $('minAfter').value='1.20'; $('discOn').value='on';
$('res').innerHTML='Введите параметры и нажмите «Рассчитать»';
$('dbg').textContent='—';
});
fetchCBR().then(()=>paintFX());