/* Модуль «Оплаты и поставки» — вкладка Материалы с РОЛЕВОЙ видимостью цен: Прораб — без цен; Наблюдатель/заказчик — сметная; Снабженец/Сметчик/Админ — обе. Клик по поставщику → карточка позиции (счёт, условия, контакт, канал связи). */ window.RD = window.RD || { core: {}, modules: {} }; window.RD.modules.Payments = function Payments() { var store = RD.core.useStore(); var C = RD.core, money = C.money0, h = React.createElement; var perms = store.perms; var proc = store.raw.procurement || []; var SC = store.raw.suppliers || []; var drawerRef = React.useState(null); var drawer = drawerRef[0], setDrawer = drawerRef[1]; var tabRef = React.useState('materials'); var tab = tabRef[0], setTab = tabRef[1]; var selRef = React.useState({}); var sel = selRef[0], setSel = selRef[1]; function chosenIdx(pc) { var bi = pc.offers.findIndex(function (o) { return o.rec === 'best'; }); return sel[pc.id] != null ? sel[pc.id] : (bi >= 0 ? bi : 0); } function pickSupplier(pcId, i) { var n = Object.assign({}, sel); n[pcId] = i; setSel(n); store.showToast('Поставщик выбран · экономия пересчитана', 'ok'); } var mgrs = { 'ЭлектроОпт': 'Ольга Кравцова', 'КабельТорг': 'Сергей Линд', 'АкваОпт': 'Тимур Хаз', 'СтройБаза': 'Нина Бакш', 'КерамаМаркет': 'Алия Кер', 'ПолМаркет': 'Кирилл П' }; function chosen(pc) { var i = chosenIdx(pc); return pc.offers[i]; } function estUnit(pc) { return Math.max.apply(null, pc.offers.map(function (o) { return o.price; })); } // сметная (бюджетная) function priceCell(pc) { var o = chosen(pc), estSum = estUnit(pc) * pc.need, sum = o.price * pc.need; if (!perms.seeContractPrice) return { v: '—', sub: 'скрыто для роли' }; if (perms.seeSupplierPrice) return { v: money(estSum) + ' / ' + money(sum) + ' ₽', sub: 'сметная / закупочная' }; return { v: money(estSum) + ' ₽', sub: 'сметная' }; } var head = ['Материал', 'Этап', 'Поставщик · бренд', 'Кол-во', 'Цена/сумма', 'Статус']; var grid = 'minmax(150px,1.3fr) 110px minmax(130px,1fr) 80px 150px 100px'; return h('div', { className: 'rd-main-content', style: { background: 'var(--rd-surface)' } }, h('div', { style: { height: 46, flex: 'none', borderBottom: '1px solid var(--rd-border)', display: 'flex', alignItems: 'center', padding: '0 16px', gap: 12, background: 'var(--rd-surface-2)' } }, h('div', { style: { fontSize: 13, fontWeight: 600 } }, 'Оплаты и поставки'), h('div', { className: 'mono', style: { fontSize: 11.5, color: 'var(--rd-text-muted)' } }, proc.length + ' позиций · видимость цен по роли «' + perms.role + '»'), h('div', { style: { flex: 1 } }), [['materials', 'Материалы'], ['tender', 'Тендер'], ['raw', 'Все столбцы из БД']].map(function (tb) { return h('button', { key: tb[0], onClick: function () { setTab(tb[0]); }, style: { padding: '5px 11px', border: '1px solid var(--rd-border)', borderRadius: 7, background: tab === tb[0] ? 'var(--rd-accent-soft)' : 'var(--rd-surface)', color: tab === tb[0] ? 'var(--rd-accent)' : 'var(--rd-text-2)', fontSize: 11, fontWeight: tab === tb[0] ? 600 : 500, cursor: 'pointer' } }, tb[1]); })), tab === 'raw' ? (function () { var ds = [['payments', 'ОПЛАТЫ', store.raw.payments || []], ['deliveries', 'ПОСТАВКИ', store.raw.deliveries || []], ['procurement', 'ЗАКУПКИ/ТЕНДЕР', proc]]; var fmt = function (v) { if (v == null) return ''; if (Array.isArray(v) || typeof v === 'object') return JSON.stringify(v); return '' + v; }; var priceKeys = { price: 1, amount: 1, sum: 1, cost: 1 }; return h('div', { className: 'rd-gantt-wrap', style: { overflow: 'auto', padding: 12 } }, ds.map(function (d) { var ent = d[0], list = d[2]; if (!list.length) return null; var keys = []; list.forEach(function (x) { Object.keys(x).forEach(function (k) { if (keys.indexOf(k) < 0) keys.push(k); }); }); var grid = '40px ' + keys.map(function (k) { return k === 'offers' ? '240px' : '110px'; }).join(' '); return h('div', { key: ent, style: { marginBottom: 20 } }, h('div', { style: { fontSize: 11, fontWeight: 700, color: 'var(--rd-text-muted)', margin: '0 0 6px 2px' } }, d[1] + ' · ' + list.length + ' строк · ' + keys.length + ' столбцов'), h('div', { style: { border: '1px solid var(--rd-border)', borderRadius: 8, overflow: 'auto' } }, h('div', { style: { minWidth: 50 + keys.length * 112 } }, h('div', { style: { display: 'grid', gridTemplateColumns: grid, background: 'var(--rd-surface-3)', borderBottom: '1px solid var(--rd-border)', fontSize: 10, fontWeight: 600, color: 'var(--rd-text-muted)', position: 'sticky', top: 0 } }, [h('div', { key: 'n', style: { padding: '7px 8px' } }, '№')].concat(keys.map(function (k) { return h('div', { key: k, style: { padding: '7px 8px' } }, (priceKeys[k] && !perms.seeSupplierPrice) ? (k + ' 🔒') : k); }))), list.map(function (x, i) { return h('div', { key: i, style: { display: 'grid', gridTemplateColumns: grid, borderBottom: '1px solid var(--rd-border)', fontSize: 11, alignItems: 'center', background: i % 2 ? 'var(--rd-surface-2)' : 'var(--rd-surface)' } }, [h('div', { key: 'n', className: 'mono', style: { padding: '5px 8px', color: 'var(--rd-text-muted)', fontSize: 10 } }, i + 1)].concat(keys.map(function (k) { var hide = priceKeys[k] && !perms.seeSupplierPrice; return h('div', { key: k, className: (typeof x[k] === 'number') ? 'mono' : '', style: { padding: '5px 8px', color: 'var(--rd-text-2)', fontSize: 10.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, hide ? '—' : fmt(x[k])); }))); }) ))) }) ); })() : null, tab === 'tender' && perms.seeSupplierPrice ? h('div', { className: 'rd-gantt-wrap', style: { padding: 16 } }, h('div', { style: { display: 'flex', gap: 12, marginBottom: 14, flexWrap: 'wrap' } }, (function () { var chosenSum = 0, maxSum = 0; proc.forEach(function (pc) { chosenSum += chosen(pc).price * pc.need; maxSum += Math.max.apply(null, pc.offers.map(function (o) { return o.price; })) * pc.need; }); var sav = maxSum - chosenSum; return [['Выбрано на сумму', money(chosenSum) + ' ₽', 'var(--rd-accent)'], ['Сэкономлено', money(sav) + ' ₽', 'var(--rd-good)']].map(function (k, i) { return h('div', { key: i, style: { border: '1px solid var(--rd-border)', borderRadius: 10, padding: '11px 14px', background: 'var(--rd-surface)' } }, h('div', { style: { fontSize: 11, color: 'var(--rd-text-muted)' } }, k[0]), h('div', { className: 'mono', style: { fontSize: 16, fontWeight: 700, color: k[2] } }, k[1])); }); })()), proc.map(function (pc) { return h('div', { key: pc.id, style: { border: '1px solid var(--rd-border)', borderRadius: 11, overflow: 'hidden', marginBottom: 12 } }, h('div', { style: { padding: '10px 14px', borderBottom: '1px solid var(--rd-border)', background: 'var(--rd-surface-3)', display: 'flex', gap: 8, alignItems: 'center' } }, h('span', { style: { fontSize: 13, fontWeight: 600 } }, pc.material), h('span', { style: { fontSize: 11, color: 'var(--rd-text-muted)' } }, '· ' + pc.need + ' ' + pc.unit + ' · этап «' + pc.stage + '»')), pc.offers.map(function (o, oi) { var isCh = oi === chosenIdx(pc), minP = Math.min.apply(null, pc.offers.map(function (x) { return x.price; })); return h('div', { key: oi, onClick: function () { pickSupplier(pc.id, oi); }, style: { display: 'grid', gridTemplateColumns: '30px 150px 110px 100px 70px 100px 1fr', alignItems: 'center', fontSize: 12, borderTop: '1px solid var(--rd-border)', cursor: 'pointer', background: isCh ? 'var(--rd-accent-soft)' : 'transparent' } }, h('div', { style: { padding: '9px 0 9px 12px' } }, h('span', { style: { width: 15, height: 15, borderRadius: '50%', border: '2px solid ' + (isCh ? 'var(--rd-accent)' : 'var(--rd-border)'), display: 'inline-flex', alignItems: 'center', justifyContent: 'center' } }, isCh ? h('span', { style: { width: 7, height: 7, borderRadius: '50%', background: 'var(--rd-accent)' } }) : null)), h('div', { style: { padding: '9px 10px', fontWeight: isCh ? 700 : 500 } }, o.sup), h('div', { style: { padding: '9px 10px', color: 'var(--rd-text-2)' } }, o.brand), h('div', { className: 'mono', style: { padding: '9px 10px', fontWeight: 600, color: o.price === minP ? 'var(--rd-good)' : 'var(--rd-text)' } }, money(o.price) + ' ₽'), h('div', { className: 'mono', style: { padding: '9px 10px', color: 'var(--rd-text-2)' } }, (o.lead || '?') + ' дн'), h('div', { style: { padding: '9px 10px', color: 'var(--rd-text-2)' } }, '★ ' + (o.rating || 0).toFixed(1)), h('div', { style: { padding: '9px 10px', fontSize: 11, color: 'var(--rd-text-muted)' } }, o.terms || '')); })); })) : null, tab === 'tender' && !perms.seeSupplierPrice ? h('div', { style: { padding: 30, textAlign: 'center', color: 'var(--rd-text-muted)', fontSize: 13 } }, 'Тендер с закупочными ценами недоступен для роли «' + perms.role + '»') : null, tab === 'materials' ? h('div', { className: 'rd-gantt-wrap', style: { padding: 16 } }, h('div', { style: { border: '1px solid var(--rd-border)', borderRadius: 11, overflow: 'hidden', minWidth: 720 } }, h('div', { style: { display: 'grid', gridTemplateColumns: grid, background: 'var(--rd-surface-3)', borderBottom: '1px solid var(--rd-border)', fontSize: 10.5, fontWeight: 600, color: 'var(--rd-text-muted)' } }, head.map(function (t, i) { return h('div', { key: i, style: { padding: '9px 12px', textAlign: i === 4 ? 'right' : 'left' } }, t); })), proc.map(function (pc) { var o = chosen(pc), pCell = priceCell(pc), clickable = perms.seeSupplierPrice; return h('div', { key: pc.id, style: { display: 'grid', gridTemplateColumns: grid, borderBottom: '1px solid var(--rd-border)', fontSize: 12, alignItems: 'center' } }, h('div', { style: { padding: '10px 12px', fontWeight: 500 } }, pc.material), h('div', { style: { padding: '10px 12px', color: 'var(--rd-text-2)', fontSize: 11 } }, pc.stage), h('div', { onClick: clickable ? function () { setDrawer(pc.id); } : null, style: { padding: '10px 12px', color: clickable ? 'var(--rd-accent)' : 'var(--rd-text-2)', fontSize: 11.5, cursor: clickable ? 'pointer' : 'default', textDecoration: clickable ? 'underline' : 'none' } }, perms.seeSupplierPrice ? (o.sup + ' · ' + o.brand) : '—'), h('div', { className: 'mono', style: { padding: '10px 12px', color: 'var(--rd-text-2)' } }, pc.need + ' ' + pc.unit), h('div', { style: { padding: '8px 12px', textAlign: 'right' } }, h('div', { className: 'mono', style: { fontWeight: 600 } }, pCell.v), h('div', { style: { fontSize: 9, color: 'var(--rd-text-muted)' } }, pCell.sub)), h('div', { style: { padding: '10px 12px' } }, h('span', { style: { fontSize: 10.5, fontWeight: 600, color: o.stock === 'под заказ' ? 'var(--rd-warn,#c2790a)' : 'var(--rd-good)', background: 'var(--rd-surface-3)', padding: '2px 8px', borderRadius: 11 } }, o.stock || 'в наличии')) ); }) ) ) : null, // drawer карточки позиции drawer ? (function () { var pc = proc.find(function (x) { return x.id === drawer; }); if (!pc) return null; var o = chosen(pc), mgr = mgrs[o.sup] || 'Менеджер отдела продаж'; var invoice = 'Счёт №' + (1040 + proc.indexOf(pc)); return h('div', { onClick: function () { setDrawer(null); }, style: { position: 'fixed', inset: 0, background: 'rgba(0,0,0,.34)', zIndex: 60, display: 'flex', justifyContent: 'flex-end' } }, h('div', { onClick: function (e) { e.stopPropagation(); }, style: { width: 380, maxWidth: '92vw', height: '100%', background: 'var(--rd-surface)', boxShadow: '-12px 0 40px rgba(0,0,0,.2)', display: 'flex', flexDirection: 'column', overflow: 'auto' } }, h('div', { style: { padding: 18, borderBottom: '1px solid var(--rd-border)', display: 'flex', alignItems: 'center', gap: 13 } }, h('div', { style: { width: 46, height: 46, borderRadius: 12, background: 'var(--rd-accent)', color: '#fff', fontSize: 15, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 'none' } }, o.sup.slice(0, 2)), h('div', { style: { flex: 1 } }, h('div', { style: { fontSize: 15, fontWeight: 600 } }, o.sup + ' · ' + o.brand), h('div', { style: { fontSize: 11, color: 'var(--rd-text-muted)' } }, 'Закупка: ' + pc.material + ' · этап «' + pc.stage + '»')), h('button', { onClick: function () { setDrawer(null); }, style: { border: 'none', background: 'none', fontSize: 20, color: 'var(--rd-text-muted)', cursor: 'pointer' } }, '×')), h('div', { style: { padding: 16 } }, h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 9, marginBottom: 16 } }, [['Цена/ед.', money(o.price) + ' ₽'], ['Кол-во', pc.need + ' ' + pc.unit], ['Сумма', money(o.price * pc.need) + ' ₽'], ['Рейтинг', (o.rating || 0).toFixed(1) + ' ★']].map(function (s, i) { return h('div', { key: i, style: { border: '1px solid var(--rd-border)', borderRadius: 9, padding: '9px 11px', background: 'var(--rd-surface-2)' } }, h('div', { style: { fontSize: 10.5, color: 'var(--rd-text-muted)', marginBottom: 3 } }, s[0]), h('div', { className: 'mono', style: { fontSize: 14, fontWeight: 700 } }, s[1])); })), [['СЧЁТ И НАПОЛНЕНИЕ', invoice + ' · ' + pc.material + ' (' + pc.need + ' ' + pc.unit + ') · нужно к ' + (pc.needBy || '—') + ' · альтернатив в тендере: ' + pc.offers.length], ['УСЛОВИЯ', (o.terms || '—') + ' · срок ' + (o.lead || '?') + ' дн · мин. партия ' + (o.moq || '?') + ' ' + pc.unit + ' · ' + (o.stock || 'в наличии')], ['КОНТАКТНОЕ ЛИЦО', mgr + ' · тел. +7 900 ' + (100 + proc.indexOf(pc) * 7) + '-' + (10 + pc.need % 89) + '-' + (10 + (o.lead || 1) * 7 % 89)], ['КАНАЛ СВЯЗИ', 'e-mail: ' + o.sup.toLowerCase().replace(/[^a-zа-я]/gi, '') + '@postavshik.ru · Telegram / WhatsApp'] ].map(function (b, i) { return h('div', { key: i, style: { border: '1px solid var(--rd-border)', borderRadius: 9, padding: '11px 12px', marginBottom: 9 } }, h('div', { style: { fontSize: 10, fontWeight: 600, letterSpacing: .4, color: 'var(--rd-text-muted)', marginBottom: 4 } }, b[0]), h('div', { style: { fontSize: 12, color: 'var(--rd-text)', lineHeight: 1.4 } }, b[1])); }) ) ) ); })() : null ); };