From 10415fd539d24313610be8e39a888cf66c5da8f9 Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Tue, 22 Sep 2026 16:05:38 +0800 Subject: [PATCH] Fix order deletion, line removal and Telegram provenance in documents --- public/app-runtime.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/public/app-runtime.js b/public/app-runtime.js index 4af2845..23f849d 100644 --- a/public/app-runtime.js +++ b/public/app-runtime.js @@ -252,7 +252,7 @@ window.SUN_LEGACY_CATALOG_V175=[]; .sun-discount-panel label>span{display:block;color:#68717a;font-size:12px;margin-bottom:5px} .sun-order-summary-discount{color:#8a5b00} .sun-order-summary-net{font-weight:800;color:#15364c} - .sun-order-summary-promo small{color:#7b858d;margin-left:5px} + .sun-order-summary-promo small{color:#7b858d;margin-left:5px}.sun-order-line-sum{display:flex;align-items:center;justify-content:flex-end;gap:8px}.sun-order-line-delete{width:28px;height:28px;flex:0 0 28px;border:1px solid #e2b8b8;border-radius:8px;background:#fff7f7;color:#a83535;font-size:19px;line-height:1;cursor:pointer}.sun-order-line-delete:hover{background:#ffeaea;border-color:#d98d8d} .sun-doc-qr{width:92px;height:92px;object-fit:contain;border:1px solid #e0e3e5;border-radius:7px;padding:4px;background:#fff} .sun-doc-title-with-qr{display:flex;align-items:flex-start;gap:14px;justify-content:flex-end} .sun-doc-title-with-qr .sun-doc-title{text-align:right} @@ -429,10 +429,11 @@ window.SUN_LEGACY_CATALOG_V175=[]; function renderOrderLines(){ if(!$('lines'))return; if(!(draft.lines||[]).length){$('lines').innerHTML='

Выберите позиции из каталога.

';return} - const rows=draft.lines.map((line,index)=>{const item=boxes.find(x=>String(x.id)===String(line.id));if(!item)return'';const qty=Math.max(1,Number(line.qty||1)),price=lineUnitPrice(line),isBox=[0,5,6].includes(Number(item.category||0));return `
${esc(String(item.name||'').replace(/^(?:Фуршетный\s+)?бокс\s*№\s*(\d+)\s*(?:[—–-]\s*)?/i,'№ $1 '))}${isBox&&(item.weight||inferBoxPieces(item))?`${item.weight?`Вес: ${esc(item.weight)}`:''}${item.weight&&inferBoxPieces(item)?' · ':''}${inferBoxPieces(item)?`Количество: ${inferBoxPieces(item)} шт.`:''}`:''}
${money(price*qty)}
`}).join(''); + const rows=draft.lines.map((line,index)=>{const item=boxes.find(x=>String(x.id)===String(line.id));if(!item)return'';const qty=Math.max(1,Number(line.qty||1)),price=lineUnitPrice(line),isBox=[0,5,6].includes(Number(item.category||0));return `
${esc(String(item.name||'').replace(/^(?:Фуршетный\s+)?бокс\s*№\s*(\d+)\s*(?:[—–-]\s*)?/i,'№ $1 '))}${isBox&&(item.weight||inferBoxPieces(item))?`${item.weight?`Вес: ${esc(item.weight)}`:''}${item.weight&&inferBoxPieces(item)?' · ':''}${inferBoxPieces(item)?`Количество: ${inferBoxPieces(item)} шт.`:''}`:''}
${money(price*qty)}
`}).join(''); $('lines').innerHTML=`
НаименованиеКол.ЦенаСтоимость
${rows}
Стоимость позиций${money(baseTotal(draft))}
`; } window.sunOrderLinePriceChanged=(index,input)=>{const line=draft.lines?.[index];if(!line)return;const value=Number(String(input.value||0).replace(',','.'));if(!Number.isFinite(value)){input.value=String(lineUnitPrice(line));return;}line.price=Math.max(0,Math.round(value*100)/100);const row=input.closest('.sun-order-line-row'),sum=row?.querySelector('.sun-order-line-sum b');if(sum)sum.textContent=money(line.price*Math.max(0,Number(line.qty||0)));const total=$('lines')?.querySelector('.sun-order-line-total b');if(total)total.textContent=money(baseTotal(draft));updateOrderSummary();}; + window.sunRemoveOrderLine=(index)=>{if(!Array.isArray(draft.lines))return false;const i=Number(index);if(!Number.isInteger(i)||i<0||i>=draft.lines.length)return false;draft.lines.splice(i,1);renderCatalogV5();updateOrderSummary();return true;}; function boxNumber(item){const explicit=Number(item?.boxNumber);if(Number.isInteger(explicit)&&explicit>=0)return explicit;const name=String(item?.name||'');const match=name.match(/(?:бокс\s*)?№\s*0*(\d+)/i)||name.match(/^\s*№\s*0*(\d+)/i);return match?Number(match[1]):null} function wantedBoxNumber(query){const raw=String(query||'').trim();if(!raw)return null;const match=raw.match(/^(?:бокс\s*)?(?:№\s*)?0*(\d+)\s*$/i);return match?Number(match[1]):null} function itemMatches(item,query){if(!query)return true;if(Number(activeCat)===0){const wanted=wantedBoxNumber(query);return wanted!==null&&boxNumber(item)===wanted}const q=query.trim().toLowerCase();const parts=[item.name,item.catalogSection,item.weight,...(Array.isArray(item.composition)?item.composition:[]),...(Array.isArray(item.ingredients)?item.ingredients.map(x=>x?.[0]):[])];return parts.join(' ').toLowerCase().includes(q)} @@ -659,16 +660,17 @@ window.SUN_LEGACY_CATALOG_V175=[]; function party(title,rows){const list=rows.filter(([,v])=>String(v||'').trim());if(!list.length)return'';return `

${esc(title)}

${list.map(([k,v])=>`
${esc(k)}${esc(v)}
`).join('')}
`} // ---------------- Order blank + compact receipt ---------------- + function customerVisibleOrderNote(value){return String(value||'').split(/\r?\n/).filter(line=>{const text=line.trim();if(!text)return false;return !/^(?:заказ\s+из\s+telegram|импорт(?:ирован)?(?:о|ный)?\s+из\s+telegram|источник\s*[:—-]?\s*telegram|source\s*[:—-]?\s*telegram)\b/i.test(text)}).join('\n').trim()} function currentRows(){return (draft.lines||[]).map((line,index)=>{const item=boxes.find(x=>String(x.id)===String(line.id)),price=lineUnitPrice(line),qty=Math.max(0,Number(line.qty||0)),categoryId=Number(item?.category??line.category??0);return{index:index+1,name:item?.name||line.name||`Позиция ${line.id||''}`,category:catName(categoryId),categoryId,weight:[0,5,6].includes(categoryId)?String(item?.weight||''):'',pieces:[0,5].includes(categoryId)?inferBoxPieces(item):0,price,qty,sum:price*qty}}).filter(r=>r.qty>0)} const docDate=raw=>{if(!raw)return'—';const d=new Date(`${raw}T00:00:00`);return Number.isNaN(d.getTime())?raw:d.toLocaleDateString('ru-RU')}; const docActions=()=>`
`; function showOrderBlank(){ - captureBuyerFromFields();captureDiscounts();const rows=currentRows(),p=pricing(draft),qr=defaultQr(),b=draft.buyerRequisites||buyerDefaults(),dialog=document.querySelector('#preview .dialog');if(!dialog)return;const number=draft.id||'черновик',qrNote=String(draft.blankQrText||'').trim(); - dialog.innerHTML=`
${window.CateriumBranding.nameHTML()}
${window.CateriumBranding.nameHTML()}${window.CateriumBranding.cityHTML()}

Заказ № ${esc(number)}

${esc($('event')?.value||'Новое мероприятие')}

${draft.status?`${esc(draft.status)}`:''}
${qr?`
QR-код
${esc(qrNote)}
`:''}
Клиент${esc($('contact')?.value||'—')}
Телефон${esc($('phone')?.value||'—')}
Дата и время выдачи${esc(docDate($('date')?.value))} · ${esc($('time')?.value||'—')}
Адрес доставки${esc($('address')?.value||'—')}
${b.name?`
Компания покупателя${esc(b.name)}
`:''}
${rows.length?rows.map(r=>``).join(''):''}
ПозицияЦенаКол.Сумма
${r.index}${esc(r.name)}${esc(r.category)}${(r.weight||r.pieces)?`${r.weight?`${r.categoryId===6?'Вес порции':'Вес бокса'}: ${esc(r.weight)}`:''}${r.weight&&r.pieces?' · ':''}${r.pieces?`Количество: ${esc(r.pieces)} шт.`:''}`:''}${money(r.price)}${r.qty}${money(r.sum)}
В заказе пока нет позиций.

Стоимость позиций${money(p.base)}

${p.manual?`

Скидка− ${money(p.manual)}

`:''}${p.promo?`

Промокод ${esc(draft.promoCode||'')}− ${money(p.promo)}

`:''}

Доставка${money(p.delivery)}

Итого${money(p.total)}

Предоплата${money(p.paid)}

Остаток к оплате${money(p.balance)}

${$('note')?.value?`
Комментарий к заказу${esc($('note').value)}
`:''}${docActions()}
`;window.modal?.('preview');const noteEl=$('sunBlankQrText');if(noteEl){const save=()=>{const text=String(noteEl.innerText||noteEl.textContent||'').replace(/\u00a0/g,' ').trim();draft.blankQrText=text;if(draft.id){const saved=(orders||[]).find(o=>String(o.id)===String(draft.id));if(saved){saved.blankQrText=text;try{persist()}catch(_){}}}};noteEl.addEventListener('input',save);noteEl.addEventListener('blur',save);} + captureBuyerFromFields();captureDiscounts();const rows=currentRows(),p=pricing(draft),qr=defaultQr(),b=draft.buyerRequisites||buyerDefaults(),dialog=document.querySelector('#preview .dialog');if(!dialog)return;const number=draft.id||'черновик',qrNote=String(draft.blankQrText||'').trim(),visibleNote=customerVisibleOrderNote($('note')?.value); + dialog.innerHTML=`
${window.CateriumBranding.nameHTML()}
${window.CateriumBranding.nameHTML()}${window.CateriumBranding.cityHTML()}

Заказ № ${esc(number)}

${esc($('event')?.value||'Новое мероприятие')}

${draft.status?`${esc(draft.status)}`:''}
${qr?`
QR-код
${esc(qrNote)}
`:''}
Клиент${esc($('contact')?.value||'—')}
Телефон${esc($('phone')?.value||'—')}
Дата и время выдачи${esc(docDate($('date')?.value))} · ${esc($('time')?.value||'—')}
Адрес доставки${esc($('address')?.value||'—')}
${b.name?`
Компания покупателя${esc(b.name)}
`:''}
${rows.length?rows.map(r=>``).join(''):''}
ПозицияЦенаКол.Сумма
${r.index}${esc(r.name)}${esc(r.category)}${(r.weight||r.pieces)?`${r.weight?`${r.categoryId===6?'Вес порции':'Вес бокса'}: ${esc(r.weight)}`:''}${r.weight&&r.pieces?' · ':''}${r.pieces?`Количество: ${esc(r.pieces)} шт.`:''}`:''}${money(r.price)}${r.qty}${money(r.sum)}
В заказе пока нет позиций.

Стоимость позиций${money(p.base)}

${p.manual?`

Скидка− ${money(p.manual)}

`:''}${p.promo?`

Промокод ${esc(draft.promoCode||'')}− ${money(p.promo)}

`:''}

Доставка${money(p.delivery)}

Итого${money(p.total)}

Предоплата${money(p.paid)}

Остаток к оплате${money(p.balance)}

${visibleNote?`
Комментарий к заказу${esc(visibleNote)}
`:''}${docActions()}
`;window.modal?.('preview');const noteEl=$('sunBlankQrText');if(noteEl){const save=()=>{const text=String(noteEl.innerText||noteEl.textContent||'').replace(/\u00a0/g,' ').trim();draft.blankQrText=text;if(draft.id){const saved=(orders||[]).find(o=>String(o.id)===String(draft.id));if(saved){saved.blankQrText=text;try{persist()}catch(_){}}}};noteEl.addEventListener('input',save);noteEl.addEventListener('blur',save);} } function showReceipt(){ - captureBuyerFromFields();captureDiscounts();const rows=currentRows(),p=pricing(draft),s=readSeller(),b=draft.buyerRequisites||buyerDefaults(),dialog=document.querySelector('#preview .dialog');if(!dialog)return;const seller=s.includeSeller!==false?party('Продавец',[['Наименование',s.sellerLegalName||s.sellerName],['ИНН',s.sellerInn],['КПП',s.sellerKpp],['ОГРН / ОГРНИП',s.sellerOgrn],['Адрес',s.sellerLegalAddress],['Телефон',s.sellerPhone],['E-mail',s.sellerEmail],['Банк',s.sellerBank],['БИК',s.sellerBik],['Расч. счёт',s.sellerAccount],['Корр. счёт',s.sellerCorrAccount]]):'';const buyer=party('Покупатель',[['Компания / ИП',b.name],['ИНН',b.inn],['КПП',b.kpp],['ОГРН / ОГРНИП',b.ogrn],['Адрес',b.legalAddress],['Контакт',b.contact],['Телефон',b.phone],['E-mail',b.email],['Банк',b.bank],['БИК',b.bik],['Расч. счёт',b.account],['Корр. счёт',b.corrAccount]]); - dialog.innerHTML=`
${window.CateriumBranding.nameHTML()}
${esc(s.sellerName||(window.CateriumBranding.documentName()))}${esc(s.sellerLegalName||'')}

Товарный чек № ${esc(draft.id||'—')}

${esc(docDate($('date')?.value||draft.date))} · ${esc($('time')?.value||draft.time||'')}

${seller}${buyer}${rows.length?rows.map(r=>``).join(''):''}
НаименованиеЦенаКол.Сумма
${r.index}${esc(r.name)}${(r.weight||r.pieces)?`${r.weight?`${r.categoryId===6?'Вес порции':'Вес бокса'}: ${esc(r.weight)}`:''}${r.weight&&r.pieces?' · ':''}${r.pieces?`Количество: ${esc(r.pieces)} шт.`:''}`:''}${money(r.price)}${r.qty}${money(r.sum)}
В заказе нет позиций.

Стоимость позиций${money(p.base)}

${p.manual?`

Скидка− ${money(p.manual)}

`:''}${p.promo?`

Промокод ${esc(draft.promoCode||'')}− ${money(p.promo)}

`:''}

Доставка${money(p.delivery)}

Итого${money(p.total)}

Оплачено${money(p.paid)}

К оплате${money(p.balance)}

${$('note')?.value?`
Примечание${esc($('note').value)}
`:''}${docActions()}
`;window.modal?.('preview'); + captureBuyerFromFields();captureDiscounts();const rows=currentRows(),p=pricing(draft),s=readSeller(),b=draft.buyerRequisites||buyerDefaults(),dialog=document.querySelector('#preview .dialog'),visibleNote=customerVisibleOrderNote($('note')?.value);if(!dialog)return;const seller=s.includeSeller!==false?party('Продавец',[['Наименование',s.sellerLegalName||s.sellerName],['ИНН',s.sellerInn],['КПП',s.sellerKpp],['ОГРН / ОГРНИП',s.sellerOgrn],['Адрес',s.sellerLegalAddress],['Телефон',s.sellerPhone],['E-mail',s.sellerEmail],['Банк',s.sellerBank],['БИК',s.sellerBik],['Расч. счёт',s.sellerAccount],['Корр. счёт',s.sellerCorrAccount]]):'';const buyer=party('Покупатель',[['Компания / ИП',b.name],['ИНН',b.inn],['КПП',b.kpp],['ОГРН / ОГРНИП',b.ogrn],['Адрес',b.legalAddress],['Контакт',b.contact],['Телефон',b.phone],['E-mail',b.email],['Банк',b.bank],['БИК',b.bik],['Расч. счёт',b.account],['Корр. счёт',b.corrAccount]]); + dialog.innerHTML=`
${window.CateriumBranding.nameHTML()}
${esc(s.sellerName||(window.CateriumBranding.documentName()))}${esc(s.sellerLegalName||'')}

Товарный чек № ${esc(draft.id||'—')}

${esc(docDate($('date')?.value||draft.date))} · ${esc($('time')?.value||draft.time||'')}

${seller}${buyer}${rows.length?rows.map(r=>``).join(''):''}
НаименованиеЦенаКол.Сумма
${r.index}${esc(r.name)}${(r.weight||r.pieces)?`${r.weight?`${r.categoryId===6?'Вес порции':'Вес бокса'}: ${esc(r.weight)}`:''}${r.weight&&r.pieces?' · ':''}${r.pieces?`Количество: ${esc(r.pieces)} шт.`:''}`:''}${money(r.price)}${r.qty}${money(r.sum)}
В заказе нет позиций.

Стоимость позиций${money(p.base)}

${p.manual?`

Скидка− ${money(p.manual)}

`:''}${p.promo?`

Промокод ${esc(draft.promoCode||'')}− ${money(p.promo)}

`:''}

Доставка${money(p.delivery)}

Итого${money(p.total)}

Оплачено${money(p.paid)}

К оплате${money(p.balance)}

${visibleNote?`
Примечание${esc(visibleNote)}
`:''}${docActions()}
`;window.modal?.('preview'); } window.sunShowConfiguredReceipt=showReceipt;window.sunShowOrderBlankV5=showOrderBlank; function patchDocButtons(){qa('#new .actions button').forEach(b=>{const text=(b.textContent||'').trim();if(text==='Товарный чек')b.onclick=showReceipt;if(text==='Бланк заказа')b.onclick=showOrderBlank;});} @@ -4069,6 +4071,7 @@ window.SUN_LEGACY_CATALOG_V175=[]; try{window.renderStats?.()}catch(_){} try{window.sunRenderOrderCards?.()}catch(_){} window.dispatchEvent(new CustomEvent('sun:order-deleted',{detail:{id:order.id}})); + try{const sync=window.SunCloudV2?.syncNow?.();if(sync&&typeof sync.catch==='function')sync.catch(error=>console.warn('Order delete cloud sync failed',error));}catch(error){console.warn('Order delete cloud sync failed',error)} window.SunEnterprise?.toast?.(`Заказ №${order.id} удалён.`,'success'); return true; };