<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.pandoraburo.ru/" xml:lang="ru">
  <id>https://www.pandoraburo.ru/tumby-shvartovye-universalnyy</id>
  <link href="https://www.pandoraburo.ru/tumby-shvartovye-universalnyy.xml" rel="self"/>
  <title>Тумбы швартовые универсальный</title>
  <subtitle type="html">
&lt;html lang="ru"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;ТСО · Калькулятор литья · Маржа · PRO&lt;/title&gt;
&lt;style&gt;
  :root{--gold:#FFD700;--gold-light:#FFE55C;--bg:#0a0a0a;--muted:#9a8040;--border:rgba(212,175,55,.35);--green:#7dff7d;--red:#ff8a7a;--cyan:#6ee8e4;--orange:#ffb060;}
  *{box-sizing:border-box;margin:0;padding:0}
  body{background:#050505;color:#e8e8e8;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;padding:20px 14px;line-height:1.5;}
  .wrap{max-width:1900px;margin:0 auto}

  .header{background:radial-gradient(120% 120% at 20% 0%,rgba(255,215,0,.25),transparent 55%),linear-gradient(135deg,#1a1408,#0a0a0a);border:2px solid var(--gold);border-radius:14px;padding:28px 24px;text-align:center;margin-bottom:16px;box-shadow:0 0 40px rgba(255,215,0,.25);}
  .tag{display:inline-block;background:var(--gold);color:#0a0a0a;font-size:10px;font-weight:800;letter-spacing:2px;padding:4px 14px;border-radius:999px;margin-bottom:14px;box-shadow:0 0 16px rgba(255,215,0,.7);}
  .header h1{font-size:28px;font-weight:900;letter-spacing:3px;color:var(--gold);text-transform:uppercase;margin-bottom:12px;text-shadow:0 0 10px rgba(255,215,0,.95),0 0 25px rgba(255,199,0,.8);animation:goldPulse 3s ease-in-out infinite;}
  @keyframes goldPulse{0%,100%{text-shadow:0 0 10px rgba(255,215,0,.95),0 0 25px rgba(255,199,0,.8);}50%{text-shadow:0 0 15px rgba(255,215,0,1),0 0 35px rgba(255,199,0,.95);}}
  .line{width:80px;height:3px;background:var(--gold);margin:0 auto 12px;border-radius:2px;box-shadow:0 0 15px var(--gold);}
  .header p{color:var(--gold-light);font-size:14px;opacity:.95;}

  .cast-block{background:linear-gradient(135deg,rgba(255,140,0,.1),rgba(10,10,10,.95));border:3px solid var(--orange);border-radius:16px;padding:26px 22px;margin-bottom:16px;position:relative;box-shadow:0 0 30px rgba(255,140,0,.2);}
  .cast-block::before{content:'? ПАРАМЕТРЫ ЛИТЬЯ И ОБРАБОТКИ ?';position:absolute;top:-14px;left:50%;transform:translateX(-50%);background:#0a0a0a;color:var(--orange);padding:4px 22px;border:2px solid var(--orange);border-radius:999px;font-size:11px;font-weight:900;letter-spacing:2px;text-shadow:0 0 15px rgba(255,140,0,1);white-space:nowrap;box-shadow:0 0 20px rgba(255,140,0,.6);}
  .cast-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;}
  @media(max-width:900px){.cast-grid{grid-template-columns:repeat(2,1fr);}}
  @media(max-width:500px){.cast-grid{grid-template-columns:1fr;}}
  .cast-ctrl{background:rgba(0,0,0,.5);border:1px solid var(--orange);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:8px;}
  .cast-ctrl label{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:1.5px;color:var(--orange);}
  .cast-ctrl input{padding:10px 12px;border:2px solid var(--orange);border-radius:9px;font-size:18px;font-weight:800;background:#0a0a0a;color:var(--orange);outline:none;font-family:inherit;text-align:center;font-variant-numeric:tabular-nums;}
  .cast-ctrl input:focus{box-shadow:0 0 0 4px rgba(255,140,0,.25);}
  .cast-ctrl .unit{font-size:11px;color:var(--muted);text-align:center;font-weight:700;}

  .stats{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
  @media(max-width:1200px){.stats{grid-template-columns:repeat(3,1fr);}}
  @media(max-width:600px){.stats{grid-template-columns:repeat(2,1fr);}}
  .stat{background:var(--bg);border:1px solid var(--gold);border-radius:12px;padding:14px;text-align:center;}
  .lbl{font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--gold);opacity:.7;margin-bottom:6px;}
  .val{font-size:18px;font-weight:800;color:var(--gold-light);line-height:1.1;}
  .val.green{color:var(--green);}
  .val.red{color:var(--red);}
  .val.sm{font-size:14px;}

  .toolbar{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px;align-items:center;}
  .search{flex:1;min-width:250px;padding:10px 14px;border:1px solid var(--gold);border-radius:9px;font-size:13px;background:#0a0a0a;color:var(--gold-light);outline:none;font-family:inherit;font-weight:600;}
  .search::placeholder{color:rgba(212,175,55,.4);}
  .counter{color:var(--gold);font-size:13px;white-space:nowrap;padding:10px 16px;border:1px solid var(--gold);border-radius:9px;background:#0a0a0a;font-weight:800;}
  .btn{display:inline-flex;align-items:center;gap:6px;padding:10px 16px;border-radius:9px;border:1px solid var(--gold);background:transparent;color:var(--gold);font-size:13px;font-weight:700;cursor:pointer;transition:.15s;font-family:inherit;}
  .btn:hover{background:var(--gold);color:#0a0a0a;}
  .btn.primary{background:var(--gold);color:#0a0a0a;}
  .btn.green{background:linear-gradient(135deg,#7dff7d,#3aa03a);color:#0a0a0a;border-color:#7dff7d;}
  .btn.cyan{background:linear-gradient(135deg,#6ee8e4,#3aa0a0);color:#0a0a0a;border-color:#6ee8e4;font-weight:900;}
  .btn.red{background:transparent;color:var(--red);border-color:var(--red);}
  .btn.red:hover{background:var(--red);color:#0a0a0a;}

  .table-block{background:var(--bg);border:1px solid var(--gold);border-radius:14px;padding:20px;margin-bottom:16px;}
  .block-title{color:var(--gold);font-size:12px;font-weight:800;letter-spacing:2px;text-align:center;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border);text-transform:uppercase;text-shadow:0 0 12px rgba(255,215,0,.6);}
  .table-wrap{overflow:auto;max-height:70vh;border-radius:8px;border:1px solid var(--gold);}
  table{width:100%;table-layout:auto;border-collapse:collapse;background:#0a0a0a;font-size:13px;}
  th,td{padding:8px 6px;text-align:left;border-bottom:1px solid rgba(212,175,55,.2);vertical-align:middle;color:var(--gold);background:transparent;white-space:nowrap;}
  th{position:sticky;top:0;z-index:2;background:#0a0a0a;color:var(--gold);font-weight:800;text-transform:uppercase;font-size:10px;letter-spacing:1px;border-bottom:1px solid var(--gold);cursor:pointer;user-select:none;}
  th:hover{background:#1a1408;}
  th.sortable::after{content:' ⇅';opacity:.5;margin-left:4px;font-size:9px;}
  th.asc::after{content:' ▲';opacity:1;color:var(--gold-light);}
  th.desc::after{content:' ▼';opacity:1;color:var(--gold-light);}
  th.num,td.num{text-align:right;font-variant-numeric:tabular-nums;}
  tbody tr{transition:background .15s;}
  tbody tr:hover{background:#1a1408;}
  tbody tr.loss{background:rgba(255,50,50,.06);}
  td.article{font-family:'Courier New',monospace;font-size:12px;color:var(--gold-light);}
  td b{color:#fff;}
  td.margin-pos{color:var(--green);font-weight:800;}
  td.margin-neg{color:var(--red);font-weight:800;}
  input.qty{width:65px;padding:5px 4px;border:1px solid var(--border);border-radius:6px;background:#0a0a0a;color:var(--gold-light);font-size:13px;font-weight:700;text-align:center;font-family:inherit;outline:none;}
  input.qty:focus{border-color:var(--gold);box-shadow:0 0 0 3px rgba(255,215,0,.2);}
  .badge{display:inline-block;padding:2px 6px;border-radius:10px;font-size:9px;font-weight:800;text-transform:uppercase;background:rgba(212,175,55,.15);color:var(--gold-light);border:1px solid rgba(212,175,55,.5);white-space:nowrap;}

  .footnote{display:flex;flex-wrap:wrap;gap:20px;margin-top:14px;padding-top:12px;border-top:1px solid var(--gold);}
  .fn{color:var(--gold);font-size:12px;position:relative;padding-left:14px;opacity:.9;}
  .fn::before{content:"◆";position:absolute;left:0;color:var(--gold);font-size:8px;top:4px;}

  .modal{position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:1000;display:none;align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(4px);}
  .modal.active{display:flex;}
  .modal-content{background:#0a0a0a;border:3px solid var(--gold);border-radius:16px;max-width:900px;width:100%;max-height:90vh;overflow:auto;padding:26px;box-shadow:0 0 60px rgba(255,215,0,.4);}
  .modal-title{color:var(--gold);font-size:20px;font-weight:900;text-transform:uppercase;letter-spacing:2px;margin-bottom:16px;text-align:center;}
  .modal-body pre{background:#050505;border:1px solid var(--border);border-radius:8px;padding:16px;white-space:pre-wrap;font-family:'Courier New',monospace;font-size:12px;color:var(--cyan);max-height:400px;overflow:auto;line-height:1.6;}
  .modal-actions{display:flex;gap:10px;justify-content:center;margin-top:16px;flex-wrap:wrap;}

  .wrap ::-webkit-scrollbar{width:14px;height:14px;}
  .wrap ::-webkit-scrollbar-track{background:#050505;border:1px solid #2a2210;border-radius:9px;}
  .wrap ::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#FFE55C,#FFD700,#FF8C00);border-radius:9px;border:1px solid #FFD700;}

  @media print{
    body{background:#fff;color:#000;padding:0;}
    .header{background:#000 !important;color:#FFD700 !important;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
    .header h1{color:#FFD700 !important;}
    .tag{background:#FFD700 !important;color:#000 !important;}
    .line{background:#FFD700 !important;}
    .stats,.table-block,.cast-block{background:#fff !important;border-color:#666 !important;}
    .toolbar,.search,.counter,.modal{display:none !important;}
    .stat,.cast-ctrl{background:#fff !important;border-color:#999 !important;}
    .lbl,.cast-ctrl label{color:#000 !important;}
    .val{color:#000 !important;}
    .block-title{color:#000 !important;}
    th{background:#000 !important;color:#FFD700 !important;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
    td{color:#000 !important;border-color:#ccc !important;}
    input{background:#fff !important;color:#000 !important;border-color:#ccc !important;}
  }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="wrap"&gt;

  &lt;div class="header"&gt;
    &lt;div class="tag"&gt;КАЛЬКУЛЯТОР ЛИТЬЯ · ГОСТ 17424-72 · PRO&lt;/div&gt;
    &lt;h1&gt;Тумбы швартовые морские (ТСО)&lt;/h1&gt;
    &lt;div class="line"&gt;&lt;/div&gt;
    &lt;p&gt;Портовая инфраструктура · Крановое оборудование · Расчёт себестоимости и маржинальности партии&lt;/p&gt;
  &lt;/div&gt;

  &lt;div class="cast-block"&gt;
    &lt;div class="cast-grid"&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;? Себестоимость литья&lt;/label&gt;
        &lt;input type="number" id="pCastCost" value="110" min="0" step="5" oninput="render()" /&gt;
        &lt;div class="unit"&gt;₽ за кг (ваша себестоимость)&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;? Цена продажи литья&lt;/label&gt;
        &lt;input type="number" id="pCastSale" value="240" min="0" step="5" oninput="render()" /&gt;
        &lt;div class="unit"&gt;₽ за кг (рыночная цена)&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;⚙️ Мех. обработка&lt;/label&gt;
        &lt;input type="number" id="pMachining" value="25" min="0" step="5" oninput="render()" /&gt;
        &lt;div class="unit"&gt;₽ за кг (обдирка, сверловка)&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;◆ НДС&lt;/label&gt;
        &lt;input type="number" id="pVAT" value="22" min="0" max="30" step="1" oninput="render()" /&gt;
        &lt;div class="unit"&gt;%&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="stats"&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Всего позиций&lt;/div&gt;&lt;div class="val" id="statTotal"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Общий вес, кг&lt;/div&gt;&lt;div class="val sm" id="statWeight"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Себестоимость партии&lt;/div&gt;&lt;div class="val sm" id="statCost"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Сумма продажи&lt;/div&gt;&lt;div class="val sm green" id="statSale"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Маржа&lt;/div&gt;&lt;div class="val green" id="statMargin"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Рентабельность&lt;/div&gt;&lt;div class="val green" id="statMarginPct"&gt;—&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="toolbar"&gt;
    &lt;input type="text" class="search" id="search" placeholder="? Поиск по обозначению, наименованию или материалу..." /&gt;
    &lt;div class="counter" id="counter"&gt;Найдено: 8&lt;/div&gt;
    &lt;button class="btn cyan" onclick="openRequestModal()"&gt;? Запрос литейному заводу&lt;/button&gt;
    &lt;button class="btn green" onclick="openInvoiceModal()"&gt;? Счёт клиенту&lt;/button&gt;
    &lt;button class="btn primary" onclick="dlCSV()"&gt;⬇ CSV&lt;/button&gt;
    &lt;button class="btn primary" onclick="dlExcel()"&gt;⬇ Excel&lt;/button&gt;
    &lt;button class="btn" onclick="saveLocal()"&gt;? Сохранить&lt;/button&gt;
    &lt;button class="btn" onclick="loadLocal()"&gt;? Загрузить&lt;/button&gt;
    &lt;button class="btn" onclick="window.print()"&gt;? Печать&lt;/button&gt;
    &lt;button class="btn red" onclick="resetAll()"&gt;✕ Сброс&lt;/button&gt;
  &lt;/div&gt;

  &lt;div class="table-block"&gt;
    &lt;div class="block-title"&gt;Расчёт маржинальности по позициям ТСО&lt;/div&gt;
    &lt;div class="table-wrap"&gt;
      &lt;table id="tbl"&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th class="num sortable" data-key="num"&gt;№&lt;/th&gt;
            &lt;th class="sortable" data-key="article"&gt;Типоразмер&lt;/th&gt;
            &lt;th class="sortable" data-key="name"&gt;Наименование&lt;/th&gt;
            &lt;th class="sortable" data-key="material"&gt;Материал&lt;/th&gt;
            &lt;th class="num sortable" data-key="weight"&gt;Вес, кг&lt;/th&gt;
            &lt;th class="num"&gt;Кол-во, шт&lt;/th&gt;
            &lt;th class="num"&gt;Себест. 1 шт, ₽&lt;/th&gt;
            &lt;th class="num"&gt;Цена 1 шт, ₽&lt;/th&gt;
            &lt;th class="num sortable" data-key="margin"&gt;Маржа 1 шт, ₽&lt;/th&gt;
            &lt;th class="num sortable" data-key="marginPct"&gt;Маржа, %&lt;/th&gt;
            &lt;th class="num"&gt;Сумма партии, ₽&lt;/th&gt;
            &lt;th class="num sortable" data-key="marginSum"&gt;Маржа партии, ₽&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody id="tbody"&gt;&lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;
    &lt;div class="footnote"&gt;
      &lt;div class="fn"&gt;Себестоимость 1 шт = Вес × (Себест. литья + Мех. обработка)&lt;/div&gt;
      &lt;div class="fn"&gt;Цена 1 шт = Вес × Цена продажи литья&lt;/div&gt;
      &lt;div class="fn"&gt;Маржа партии = (Цена − Себестоимость) × Количество&lt;/div&gt;
      &lt;div class="fn"&gt;Материал: Сталь 25Л / 35Л (стандарт ГОСТ 17424-72 для морских тумб)&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;div class="modal" id="modal"&gt;
  &lt;div class="modal-content"&gt;
    &lt;div class="modal-title" id="modalTitle"&gt;Запрос&lt;/div&gt;
    &lt;div class="modal-body" id="modalBody"&gt;&lt;/div&gt;
    &lt;div class="modal-actions"&gt;
      &lt;button class="btn primary" onclick="copyModal()"&gt;? Скопировать в буфер&lt;/button&gt;
      &lt;button class="btn" onclick="closeModal()"&gt;✕ Закрыть&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
/* ==================== ДАННЫЕ (ГОСТ 17424-72) ==================== */
var data=[
  {num:1, article:'ТСО-16', name:'Тумба швартовая ТСО-16', material:'Сталь 25Л', weight:200, qty:2},
  {num:2, article:'ТСО-25', name:'Тумба швартовая ТСО-25', material:'Сталь 25Л', weight:330, qty:2},
  {num:3, article:'ТСО-40', name:'Тумба швартовая ТСО-40', material:'Сталь 35Л', weight:630, qty:1},
  {num:4, article:'ТСО-63', name:'Тумба швартовая ТСО-63', material:'Сталь 35Л', weight:1050, qty:1},
  {num:5, article:'ТСО-80', name:'Тумба швартовая ТСО-80', material:'Сталь 35Л', weight:1350, qty:1},
  {num:6, article:'ТСО-100', name:'Тумба швартовая ТСО-100', material:'Сталь 35Л', weight:1600, qty:1},
  {num:7, article:'ТСО-125', name:'Тумба швартовая ТСО-125', material:'Сталь 35Л', weight:2100, qty:1},
  {num:8, article:'ТСО-160', name:'Тумба швартовая ТСО-160', material:'Сталь 35Л', weight:2800, qty:1}
];

/* ==================== СОСТОЯНИЕ ==================== */
var params={castCost:110, castSale:240, machining:25, vat:22};
var state={search:'', sort:{key:'num', dir:1}};
var qtyOverride={}; 

function fmt(n){return Math.round(n).toLocaleString('ru-RU');}

/* ==================== РАСЧЁТ ==================== */
function calc(r){
  var q=(qtyOverride[r.num]!==undefined) ? qtyOverride[r.num] : r.qty;
  var costPerKg = params.castCost + params.machining;
  var costPerUnit = r.weight * costPerKg;
  var salePerUnit = r.weight * params.castSale;
  var marginPerUnit = salePerUnit - costPerUnit;
  var marginPct = costPerUnit &gt; 0 ? (marginPerUnit / costPerUnit * 100) : 0;
  var sumParty = salePerUnit * q;
  var marginSum = marginPerUnit * q;
  return {
    qty: q,
    costPerUnit: costPerUnit,
    salePerUnit: salePerUnit,
    marginPerUnit: marginPerUnit,
    marginPct: marginPct,
    sumParty: sumParty,
    marginSum: marginSum
  };
}

/* ==================== ФИЛЬТРАЦИЯ И СОРТИРОВКА ==================== */
function getFiltered(){
  var rows=data.slice();
  if(state.search){
    var q=state.search.trim().toLowerCase();
    rows=rows.filter(function(r){
      return String(r.num).indexOf(q)!==-1 ||
             (r.article||'').toLowerCase().indexOf(q)!==-1 ||
             (r.name||'').toLowerCase().indexOf(q)!==-1 ||
             (r.material||'').toLowerCase().indexOf(q)!==-1 ||
             String(r.weight).indexOf(q)!==-1;
    });
  }
  if(state.sort.key){
    var k=state.sort.key, d=state.sort.dir;
    rows.sort(function(a,b){
      var va, vb;
      if(['margin','marginPct','marginSum'].indexOf(k)!==-1){
        va=calc(a)[k]; vb=calc(b)[k];
      } else {
        va=a[k]; vb=b[k];
      }
      if(typeof va==='number' &amp;&amp; typeof vb==='number') return (va-vb)*d;
      return String(va).localeCompare(String(vb),'ru')*d;
    });
  }
  return rows;
}

/* ==================== СТАТИСТИКА ==================== */
function updateStats(){
  var totalWeight=0, totalCost=0, totalSale=0, totalMargin=0;
  data.forEach(function(r){
    var c=calc(r);
    totalWeight += r.weight * c.qty;
    totalCost += c.costPerUnit * c.qty;
    totalSale += c.salePerUnit * c.qty;
    totalMargin += c.marginSum;
  });
  document.getElementById('statTotal').textContent = data.length;
  document.getElementById('statWeight').textContent = fmt(totalWeight) + ' кг';
  document.getElementById('statCost').textContent = fmt(totalCost) + ' ₽';
  document.getElementById('statSale').textContent = fmt(totalSale) + ' ₽';
  document.getElementById('statMargin').textContent = fmt(totalMargin) + ' ₽';
  document.getElementById('statMarginPct').textContent = (totalCost&gt;0 ? Math.round(totalMargin/totalCost*100)+'%' : '—');
}

/* ==================== РЕНДЕР ==================== */
function render(){
  var rows=getFiltered();
  var tbody=document.getElementById('tbody');
  tbody.innerHTML='';
  
  document.getElementById('counter').textContent = 'Найдено: ' + rows.length;
  
  if(!rows.length){
    tbody.innerHTML='&lt;tr&gt;&lt;td colspan="12" style="text-align:center;padding:40px;opacity:.6"&gt;Ничего не найдено&lt;/td&gt;&lt;/tr&gt;';
    updateStats(); return;
  }
  
  rows.forEach(function(r){
    var c=calc(r);
    var marginCls = c.marginPerUnit &gt; 0 ? 'margin-pos' : (c.marginPerUnit &lt; 0 ? 'margin-neg' : '');
    var tr=document.createElement('tr');
    if(c.marginPerUnit &lt; 0) tr.classList.add('loss');
    
    tr.innerHTML=
      '&lt;td class="num"&gt;'+r.num+'&lt;/td&gt;'+
      '&lt;td&gt;&lt;span class="badge"&gt;'+r.article+'&lt;/span&gt;&lt;/td&gt;'+
      '&lt;td&gt;&lt;b&gt;'+r.name+'&lt;/b&gt;&lt;/td&gt;'+
      '&lt;td&gt;'+r.material+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+r.weight+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;&lt;input type="number" class="qty" data-num="'+r.num+'" value="'+c.qty+'" min="1" max="9999"&gt;&lt;/td&gt;'+
      '&lt;td class="num" style="color:#ffb060;font-weight:700"&gt;'+fmt(c.costPerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num" style="color:#FFE55C;font-weight:700"&gt;'+fmt(c.salePerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num '+marginCls+'"&gt;'+fmt(c.marginPerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num '+marginCls+'"&gt;'+Math.round(c.marginPct)+'%&lt;/td&gt;'+
      '&lt;td class="num" style="color:#FFE55C;font-weight:700"&gt;'+fmt(c.sumParty)+'&lt;/td&gt;'+
      '&lt;td class="num '+marginCls+'"&gt;'+fmt(c.marginSum)+'&lt;/td&gt;';
    tbody.appendChild(tr);
  });
  
  updateStats();
  bindQty();
}

function bindQty(){
  document.querySelectorAll('input.qty').forEach(function(inp){
    inp.addEventListener('input',function(){
      var n=parseInt(this.dataset.num,10);
      var v=Math.max(1,parseInt(this.value,10)||1);
      qtyOverride[n]=v;
      render();
    });
  });
}

/* ==================== МОДАЛЬНЫЕ ОКНА ==================== */
function openModal(title,body){
  document.getElementById('modalTitle').textContent=title;
  document.getElementById('modalBody').innerHTML='&lt;pre&gt;'+body+'&lt;/pre&gt;';
  document.getElementById('modal').classList.add('active');
}
function closeModal(){document.getElementById('modal').classList.remove('active');}
function copyModal(){
  var txt=document.querySelector('#modalBody pre').textContent;
  if(navigator.clipboard &amp;&amp; navigator.clipboard.writeText){
    navigator.clipboard.writeText(txt).then(function(){alert('✅ Текст скопирован в буфер обмена!');});
  } else {alert('Не удалось скопировать');}
}

/* ==================== ЗАПРОС ЗАВОДУ ==================== */
function openRequestModal(){
  var text='Добрый день!\n\n'+
    'ООО «Арсенал-Деталь» занимается комплектацией портовой инфраструктуры и кранового оборудования.\n'+
    'Просим дать актуальное коммерческое предложение на изготовление швартовых тумб по ГОСТ 17424-72.\n\n'+
    'СПЕЦИФИКАЦИЯ:\n'+
    '№  |  Типоразмер  |  Наименование  |  Материал  |  Вес, кг  |  Кол-во, шт\n'+
    '────────────────────────────────────────────────────────────────────────\n';
  data.forEach(function(r){
    var c=calc(r);
    text+=(r.num+'  |  '+r.article+'  |  '+r.name+'  |  '+r.material+'  |  '+r.weight+'  |  '+c.qty+'\n');
  });
  text+='\nПРОШУ УКАЗАТЬ:\n'+
    '1. Цену за 1 шт (или за кг готового литья с мех. обработкой).\n'+
    '2. Срок изготовления данной партии.\n'+
    '3. Условия оплаты (возможность отсрочки или размер предоплаты).\n'+
    '4. Условия доставки до нашего склада (или до ТК в вашем городе).\n'+
    '5. Наличие сертификатов качества на материал (Сталь 25Л/35Л).\n\n'+
    'С уважением,\nОтдел снабжения ООО «Арсенал-Деталь»\nТел: 8 (902) 873-36-90\nE-mail: arsenal-detal@list.ru';
  openModal('? ЗАПРОС ЛИТЕЙНОМУ ЗАВОДУ', text);
}

/* ==================== СЧЁТ КЛИЕНТУ ==================== */
function openInvoiceModal(){
  var text='КОММЕРЧЕСКОЕ ПРЕДЛОЖЕНИЕ / СЧЁТ\n'+
    'ООО «Арсенал-Деталь» · Тел: 8 (902) 873-36-90\n'+
    'Дата: '+new Date().toLocaleDateString('ru-RU')+'\n\n'+
    'Швартовые тумбы морские (ТСО) · ГОСТ 17424-72\n\n'+
    '№  |  Типоразмер  |  Наименование  |  Цена/шт, ₽  |  Кол-во  |  Сумма, ₽\n'+
    '─────────────────────────────────────────────────────────────────────────\n';
  var total=0;
  data.forEach(function(r){
    var c=calc(r);
    if(c.qty &gt; 0){
      total += c.sumParty;
      text+=(r.num+'  |  '+r.article+'  |  '+r.name+'  |  '+fmt(c.salePerUnit)+'  |  '+c.qty+'  |  '+fmt(c.sumParty)+'\n');
    }
  });
  text+='\n═════════════════════════════════════════════════════════════════════\n';
  text+='ИТОГО: '+fmt(total)+' ₽ (в т.ч. НДС '+params.vat+'%)\n';
  text+='═════════════════════════════════════════════════════════════════════\n\n'+
    'Условия поставки:\n'+
    '• Оплата: 50% предоплата, 50% перед отгрузкой.\n'+
    '• Срок изготовления: 15–25 рабочих дней с момента оплаты.\n'+
    '• Доставка: до терминала транспортной компании в г. Екатеринбург (бесплатно).\n'+
    '• Гарантия: 12 месяцев на отсутствие литейных дефектов.\n\n'+
    'Менеджер проекта: Ольга · 8 (902) 873-36-90';
  openModal('? СЧЁТ / КП КЛИЕНТУ', text);
}

/* ==================== СОХРАНЕНИЕ / ЗАГРУЗКА ==================== */
function saveLocal(){
  try{
    localStorage.setItem('tso_params', JSON.stringify(params));
    localStorage.setItem('tso_qty', JSON.stringify(qtyOverride));
    alert('✅ Параметры и количества успешно сохранены в браузере.');
  }catch(e){alert('Ошибка сохранения: '+e.message);}
}
function loadLocal(){
  try{
    var p=localStorage.getItem('tso_params');
    var q=localStorage.getItem('tso_qty');
    if(p) params=JSON.parse(p);
    if(q) qtyOverride=JSON.parse(q);
    
    document.getElementById('pCastCost').value = params.castCost;
    document.getElementById('pCastSale').value = params.castSale;
    document.getElementById('pMachining').value = params.machining;
    document.getElementById('pVAT').value = params.vat;
    render();
    alert('✅ Данные успешно загружены.');
  }catch(e){alert('Ошибка загрузки: '+e.message);}
}
function resetAll(){
  if(!confirm('Сбросить все параметры и количества к значениям по умолчанию?')) return;
  params={castCost:110, castSale:240, machining:25, vat:22};
  qtyOverride={};
  document.getElementById('pCastCost').value=110;
  document.getElementById('pCastSale').value=240;
  document.getElementById('pMachining').value=25;
  document.getElementById('pVAT').value=22;
  document.getElementById('search').value='';
  state.search='';
  render();
}

/* ==================== ЭКСПОРТ ==================== */
function dlCSV(){
  var rows=getFiltered();
  var header=['№','Типоразмер','Наименование','Материал','Вес,кг','Кол-во','Себест/шт,₽','Цена/шт,₽','Маржа/шт,₽','Маржа,%','Сумма партии,₽','Маржа партии,₽'];
  var body=rows.map(function(r){
    var c=calc(r);
    return [r.num, r.article, r.name, r.material, r.weight, c.qty,
            Math.round(c.costPerUnit), Math.round(c.salePerUnit),
            Math.round(c.marginPerUnit), Math.round(c.marginPct),
            Math.round(c.sumParty), Math.round(c.marginSum)];
  });
  var esc=function(v){return '"'+String(v==null?'':v).replace(/"/g,'""')+'"';};
  var csv=[header].concat(body).map(function(r){return r.map(esc).join(';');}).join('\r\n');
  dlBlob(new Blob(['\uFEFF'+csv],{type:'text/csv;charset=utf-8;'}),'TSO_Marine_Calc_'+today()+'.csv');
}
function dlExcel(){
  var rows=getFiltered();
  var header=['№','Типоразмер','Наименование','Материал','Вес,кг','Кол-во','Себест/шт,₽','Цена/шт,₽','Маржа/шт,₽','Маржа,%','Сумма партии,₽','Маржа партии,₽'];
  var body=rows.map(function(r){
    var c=calc(r);
    return [r.num, r.article, r.name, r.material, r.weight, c.qty,
            Math.round(c.costPerUnit), Math.round(c.salePerUnit),
            Math.round(c.marginPerUnit), Math.round(c.marginPct),
            Math.round(c.sumParty), Math.round(c.marginSum)];
  });
  var esc=function(v){return String(v==null?'':v).replace(/&amp;/g,'&amp;amp;').replace(/&lt;/g,'&amp;lt;').replace(/&gt;/g,'&amp;gt;');};
  var table='&lt;table border="1" cellspacing="0" cellpadding="4"&gt;&lt;thead style="background:#000;color:#FFD700;"&gt;&lt;tr&gt;'+header.map(function(h){return '&lt;th&gt;'+esc(h)+'&lt;/th&gt;';}).join('')+'&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;'+body.map(function(r){return '&lt;tr&gt;'+r.map(function(c){return '&lt;td&gt;'+esc(c)+'&lt;/td&gt;';}).join('')+'&lt;/tr&gt;';}).join('')+'&lt;/tbody&gt;&lt;/table&gt;';
  var html='&lt;html xmlns:x="urn:schemas-microsoft-com:office:excel"&gt;&lt;head&gt;&lt;meta charset="UTF-8"&gt;&lt;style&gt;td,th{font-family:Arial;font-size:12px;}&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;h2&gt;Расчёт ТСО (Морские тумбы) · '+today()+'&lt;/h2&gt;'+table+'&lt;p&gt;&lt;i&gt;ООО «Арсенал Деталь» · 8 (902) 873-36-90&lt;/i&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;';
  dlBlob(new Blob(['\uFEFF'+html],{type:'application/vnd.ms-excel;charset=utf-8;'}),'TSO_Marine_Calc_'+today()+'.xls');
}
function dlBlob(blob,filename){
  var url=URL.createObjectURL(blob);
  var a=document.createElement('a');a.href=url;a.download=filename;
  document.body.appendChild(a);a.click();a.remove();
  setTimeout(function(){URL.revokeObjectURL(url);},500);
}
function today(){var d=new Date(),p=function(n){return String(n).padStart(2,'0');};return d.getFullYear()+'-'+p(d.getMonth()+1)+'-'+p(d.getDate());}

/* ==================== СОБЫТИЯ ==================== */
document.addEventListener('DOMContentLoaded',function(){
  document.getElementById('search').addEventListener('input',function(e){
    state.search=e.target.value;
    render();
  });
  document.querySelectorAll('#tbl th.sortable').forEach(function(th){
    th.addEventListener('click',function(){
      var k=th.dataset.key;
      if(state.sort.key===k) state.sort.dir*=-1;
      else state.sort={key:k, dir:-1};
      render();
    });
  });
  document.getElementById('modal').addEventListener('click',function(e){
    if(e.target===this) closeModal();
  });
  
  // Автозагрузка при старте, если есть данные
  if(localStorage.getItem('tso_params')) loadLocal();
  else render();
});
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</subtitle>
  <updated>2026-10-12T05:31:23Z</updated>
  <logo>https://pandoraburo.ru/ssl/u/pic/07/26436cc3c611f19d9efcd8c4a10874/-/0c723f71762611f1b3964aaba1665535.jpg</logo>
  <author>
    <name>1592962</name>
    <uri>https://www.pandoraburo.ru/</uri>
  </author>
</feed>
