Введи PIN-код для входу
Налаштуйте підключення до Google Sheets
Ще немає продажів.
Додай перший!
Реклама та контент не впливають на валову маржу. Вони враховуються лише у чистому прибутку.
Ще немає контент-витрат.
Ще немає записів реклами.
Той файл BloomDeLuxe_Tracker.xlsx який завантажив раніше — імпортуй його в Google Drive і відкрий як Google Sheets.
В Google Sheets: Extensions → Apps Script
Видали весь код і встав цей (скопіюй кнопкою нижче):
function doGet(e) {
try {
var p = e.parameter || {};
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (p.type === 'sale') {
var sheet = ss.getSheetByName('📦 Продажі');
if (!sheet.getRange(2, 18).getValue()) {
sheet.getRange(2, 18).setValue('Оплата карткою (AUD)');
sheet.getRange(2, 18).setBackground('#1A1A2E');
sheet.getRange(2, 18).setFontColor('#FFFFFF');
sheet.getRange(2, 18).setFontWeight('bold');
sheet.getRange(2, 19).setValue('Оплата кешем (AUD)');
sheet.getRange(2, 19).setBackground('#1A1A2E');
sheet.getRange(2, 19).setFontColor('#FFFFFF');
sheet.getRange(2, 19).setFontWeight('bold');
sheet.getRange(2, 18, 1, 2).setHorizontalAlignment('center');
sheet.getRange(2, 18, 1, 2).setVerticalAlignment('middle');
sheet.getRange(2, 18, 1, 2).setWrap(true);
sheet.setColumnWidth(18, 130);
sheet.setColumnWidth(19, 130);
}
var nextRow = -1;
for (var i = 3; i <= 102; i++) {
if (!sheet.getRange(i, 3).getValue()) {
nextRow = i;
break;
}
}
if (nextRow === -1) nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1, 1, 19).setValues([[
nextRow - 2,
p.date,
p.name,
p.composition,
p.channel,
Number(p.flowers) || 0,
Number(p.packaging) || 0,
Number(p.price) || 0,
Number(p.delivery) || 0,
Number(p.tips) || 0,
Number(p.revenue) || 0,
Number(p.profit) || 0,
'',
p.clientName || '',
p.phone || '',
p.email || '',
p.notes || '',
Number(p.paymentCard) || 0,
Number(p.paymentCash) || 0
]]);
sheet.getRange(nextRow, 13).setFormula('=IFERROR(L' + nextRow + '/H' + nextRow + ',"")');
sheet.getRange(nextRow, 13).setNumberFormat('0.0%');
}
if (p.type === 'ad') {
var sheet = ss.getSheetByName('📣 Реклама');
var nextRow = -1;
for (var i = 3; i <= 52; i++) {
if (!sheet.getRange(i, 2).getValue()) {
nextRow = i;
break;
}
}
if (nextRow === -1) nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1, 1, 5).setValues([[
p.date || '',
p.platform || '',
p.adType || '',
Number(p.amount) || 0,
p.notes || ''
]]);
}
if (p.type === 'content') {
var sheet = ss.getSheetByName('📸 Контент');
if (!sheet) {
sheet = ss.insertSheet('📸 Контент');
var headers = [
'№', 'Дата', 'Назва букету', 'Тип контенту',
'Квіти (AUD)', 'Упакування (AUD)', 'Інші витрати (AUD)',
'Загальна вартість (AUD)', 'Статус', 'Посилання', 'Нотатки'
];
sheet.getRange(2, 1, 1, headers.length).setValues([headers]);
sheet.getRange(2, 1, 1, headers.length)
.setBackground('#1A1A2E')
.setFontColor('#FFFFFF')
.setFontWeight('bold')
.setHorizontalAlignment('center')
.setVerticalAlignment('middle')
.setWrap(true);
sheet.setFrozenRows(2);
sheet.setColumnWidth(1, 55);
sheet.setColumnWidth(2, 105);
sheet.setColumnWidth(3, 190);
sheet.setColumnWidth(4, 140);
sheet.setColumnWidth(5, 115);
sheet.setColumnWidth(6, 125);
sheet.setColumnWidth(7, 125);
sheet.setColumnWidth(8, 145);
sheet.setColumnWidth(9, 130);
sheet.setColumnWidth(10, 220);
sheet.setColumnWidth(11, 260);
}
var nextRow = -1;
for (var i = 3; i <= 102; i++) {
if (!sheet.getRange(i, 3).getValue()) {
nextRow = i;
break;
}
}
if (nextRow === -1) nextRow = sheet.getLastRow() + 1;
sheet.getRange(nextRow, 1, 1, 11).setValues([[
nextRow - 2,
p.date || '',
p.name || '',
p.contentType || '',
Number(p.flowers) || 0,
Number(p.packaging) || 0,
Number(p.other) || 0,
Number(p.total) || 0,
p.status || '',
p.link || '',
p.notes || ''
]]);
sheet.getRange(nextRow, 5, 1, 4).setNumberFormat('$0.00');
}
// Create or refresh the read-only analytics output after any request.
rebuildAnalyticsSheet_(ss);
return ContentService
.createTextOutput(JSON.stringify({status:'ok'}))
.setMimeType(ContentService.MimeType.JSON);
} catch(err) {
return ContentService
.createTextOutput(JSON.stringify({status:'error', msg: err.toString()}))
.setMimeType(ContentService.MimeType.JSON);
}
}
function rebuildAnalyticsSheet_(ss) {
var name = '📈 Аналітика';
var sheet = ss.getSheetByName(name);
if (!sheet) {
sheet = ss.insertSheet(name);
}
// This sheet is generated analytics only. Source sheets are never cleared.
sheet.clear();
sheet.getCharts().forEach(function(chart) {
sheet.removeChart(chart);
});
var dark = '#1A1A2E';
var dark2 = '#24243A';
var gold = '#C9A84C';
var ivory = '#F5F0E8';
var green = '#2D6A4F';
var red = '#C1121F';
var border = '#D8CFC0';
sheet.setHiddenGridlines(true);
sheet.setFrozenRows(3);
sheet.getRange('A1:N40').setBackground(dark).setFontColor('#FFFFFF');
sheet.getRange('A1:N2').merge()
.setValue('🌸 BLOOM DE LUXE — БІЗНЕС-АНАЛІТИКА')
.setBackground(dark)
.setFontColor(gold)
.setFontSize(20)
.setFontWeight('bold')
.setHorizontalAlignment('center')
.setVerticalAlignment('middle');
sheet.getRange('A3:N3').merge()
.setValue('Автоматично оновлюється з Продажів, Реклами та Контенту')
.setFontColor('#E8D18A')
.setFontStyle('italic')
.setHorizontalAlignment('center');
var cards = [
['A4:C4','A5:C6','💰 ЗАГАЛЬНА ВИРУЧКА',"=SUMIF('📦 Продажі'!A3:A1000,\">0\",'📦 Продажі'!K3:K1000)",'$#,##0.00','#1A1A2E'],
['D4:F4','D5:F6','🌸 ВАЛОВИЙ ПРИБУТОК',"=SUMIF('📦 Продажі'!A3:A1000,\">0\",'📦 Продажі'!L3:L1000)",'$#,##0.00',green],
['G4:I4','G5:I6','📣 ВИТРАТИ НА РЕКЛАМУ',"=IFERROR(SUM(FILTER('📣 Реклама'!D3:D1000,ISNUMBER('📣 Реклама'!A3:A1000))),0)",'$#,##0.00',red],
['J4:L4','J5:L6','📸 ВИТРАТИ НА КОНТЕНТ',"=SUMIF('📸 Контент'!A3:A1000,\">0\",'📸 Контент'!H3:H1000)",'$#,##0.00',red],
['A8:C8','A9:C10','💎 ЧИСТИЙ ПРИБУТОК','=D5-G5-J5','$#,##0.00',green],
['D8:F8','D9:F10','📈 ВАЛОВА МАРЖА','=IF(A5=0,"",D5/A5)','0.0%','#1A1A2E'],
['G8:I8','G9:I10','🛍 КІЛЬКІСТЬ ПРОДАЖІВ',"=COUNT('📦 Продажі'!A3:A1000)",'0','#1A1A2E'],
['J8:L8','J9:L10','🧾 СЕРЕДНІЙ ЧЕК','=IF(G9=0,"",A5/G9)','$#,##0.00','#1A1A2E']
];
cards.forEach(function(c) {
sheet.getRange(c[0]).merge()
.setValue(c[2])
.setBackground(dark2)
.setFontColor(gold)
.setFontWeight('bold')
.setHorizontalAlignment('center')
.setVerticalAlignment('middle')
.setBorder(true,true,false,true,false,false,gold,SpreadsheetApp.BorderStyle.SOLID);
sheet.getRange(c[1]).merge();
var cell = sheet.getRange(c[1].split(':')[0]);
cell.setFormula(c[3]);
sheet.getRange(c[1])
.setBackground(ivory)
.setFontColor(c[5])
.setFontSize(17)
.setFontWeight('bold')
.setHorizontalAlignment('center')
.setVerticalAlignment('middle')
.setNumberFormat(c[4])
.setBorder(false,true,true,true,false,false,gold,SpreadsheetApp.BorderStyle.SOLID);
});
sheet.getRange('N4').setValue('Рік').setBackground(dark2).setFontColor(gold).setFontWeight('bold');
sheet.getRange('N5').setValue(new Date().getFullYear()).setBackground(dark2).setFontColor('#FFFFFF').setFontWeight('bold');
sheet.getRange('N4:N5').setHorizontalAlignment('center').setBorder(true,true,true,true,true,true,gold,SpreadsheetApp.BorderStyle.SOLID);
sectionHeader_(sheet, 'A12:F12', '📍 ПРОДАЖІ ЗА КАНАЛАМИ', gold, dark);
sectionHeader_(sheet, 'H12:N12', '🏆 ТОП-5 БУКЕТІВ ЗА ПРИБУТКОМ', gold, dark);
sectionHeader_(sheet, 'A24:F24', '📅 ПОМІСЯЧНА ДИНАМІКА', gold, dark);
sectionHeader_(sheet, 'H24:N24', '📊 СТРУКТУРА ВИТРАТ', gold, dark);
sheet.getRange('A13:D13').setValues([['Канал','Кількість','Виручка (AUD)','% від виручки']]);
tableHeader_(sheet, 'A13:D13', dark2, gold, border);
var channels = ['Instagram','Website','Google','Ринок','Рекомендація','Інше','Tik Tok'];
sheet.getRange(14,1,channels.length,1).setValues(channels.map(function(x){ return [x]; }));
channels.forEach(function(_, idx) {
var row = 14 + idx;
sheet.getRange(row,2).setFormula("=COUNTIF('📦 Продажі'!E3:E1000,A" + row + ")");
sheet.getRange(row,3).setFormula("=SUMIF('📦 Продажі'!E3:E1000,A" + row + ",'📦 Продажі'!K3:K1000)");
sheet.getRange(row,4).setFormula("=IF($A$5=0,0,C" + row + "/$A$5)");
});
tableBody_(sheet, 'A14:D20', ivory, dark, border);
sheet.getRange('C14:C20').setNumberFormat('$#,##0.00');
sheet.getRange('D14:D20').setNumberFormat('0.0%');
sheet.getRange('H13:L13').setValues([['#','Назва букету','Виручка','Прибуток','Маржа']]);
tableHeader_(sheet, 'H13:L13', dark2, gold, border);
for (var r = 14; r <= 18; r++) {
var rank = r - 13;
sheet.getRange(r,8).setValue(rank);
sheet.getRange(r,11).setFormula("=LARGE(FILTER('📦 Продажі'!L3:L1000,ISNUMBER('📦 Продажі'!A3:A1000)),H" + r + ")");
sheet.getRange(r,9).setFormula("=INDEX('📦 Продажі'!C3:C1000,MATCH(K" + r + ",'📦 Продажі'!L3:L1000,0))");
sheet.getRange(r,10).setFormula("=INDEX('📦 Продажі'!K3:K1000,MATCH(K" + r + ",'📦 Продажі'!L3:L1000,0))");
sheet.getRange(r,12).setFormula("=IF(J" + r + "=0,\"\",K" + r + "/J" + r + ")");
}
tableBody_(sheet, 'H14:L18', ivory, dark, border);
sheet.getRange('J14:K18').setNumberFormat('$#,##0.00');
sheet.getRange('L14:L18').setNumberFormat('0.0%');
sheet.getRange('A25:F25').setValues([['Місяць','Виручка','Валовий прибуток','Реклама','Контент','Чистий прибуток']]);
tableHeader_(sheet, 'A25:F25', dark2, gold, border);
var months = ['Січень','Лютий','Березень','Квітень','Травень','Червень','Липень','Серпень','Вересень','Жовтень','Листопад','Грудень'];
sheet.getRange(26,1,12,1).setValues(months.map(function(x){ return [x]; }));
for (var m = 1; m <= 12; m++) {
var row = 25 + m;
var nextMonth = m === 12 ? 'DATE($N$5+1,1,1)' : 'DATE($N$5,' + (m + 1) + ',1)';
var startMonth = 'DATE($N$5,' + m + ',1)';
sheet.getRange(row,2).setFormula("=SUMIFS('📦 Продажі'!K3:K1000,'📦 Продажі'!B3:B1000,\">=\"&" + startMonth + ",'📦 Продажі'!B3:B1000,\"<\"&" + nextMonth + ")");
sheet.getRange(row,3).setFormula("=SUMIFS('📦 Продажі'!L3:L1000,'📦 Продажі'!B3:B1000,\">=\"&" + startMonth + ",'📦 Продажі'!B3:B1000,\"<\"&" + nextMonth + ")");
sheet.getRange(row,4).setFormula("=SUMIFS('📣 Реклама'!D3:D1000,'📣 Реклама'!A3:A1000,\">=\"&" + startMonth + ",'📣 Реклама'!A3:A1000,\"<\"&" + nextMonth + ")");
sheet.getRange(row,5).setFormula("=SUMIFS('📸 Контент'!H3:H1000,'📸 Контент'!B3:B1000,\">=\"&" + startMonth + ",'📸 Контент'!B3:B1000,\"<\"&" + nextMonth + ")");
sheet.getRange(row,6).setFormula('=C' + row + '-D' + row + '-E' + row);
}
tableBody_(sheet, 'A26:F37', ivory, dark, border);
sheet.getRange('B26:F37').setNumberFormat('$#,##0.00');
sheet.getRange('H25:J25').setValues([['Категорія','Сума','% від витрат']]);
tableHeader_(sheet, 'H25:J25', dark2, gold, border);
sheet.getRange('H26:H28').setValues([['Реклама'],['Контент'],['Разом маркетинг']]);
sheet.getRange('I26').setFormula('=G5');
sheet.getRange('I27').setFormula('=J5');
sheet.getRange('I28').setFormula('=I26+I27');
sheet.getRange('J26').setFormula('=IF(I28=0,0,I26/I28)');
sheet.getRange('J27').setFormula('=IF(I28=0,0,I27/I28)');
sheet.getRange('J28').setValue(1);
tableBody_(sheet, 'H26:J28', ivory, dark, border);
sheet.getRange('I26:I28').setNumberFormat('$#,##0.00');
sheet.getRange('J26:J28').setNumberFormat('0.0%');
sheet.getRange('H30:N30').merge().setValue('ШВИДКА ОЦІНКА БІЗНЕСУ')
.setBackground(gold).setFontColor(dark).setFontWeight('bold').setHorizontalAlignment('center');
sheet.getRange('H31:N35').merge()
.setFormula('="Валова маржа: "&TEXT(D9,"0.0%")&CHAR(10)&"Чистий прибуток: "&TEXT(A9,"$#,##0.00")&CHAR(10)&"Маркетингові витрати: "&TEXT(G5+J5,"$#,##0.00")&CHAR(10)&CHAR(10)&"Реклама та контент не впливають на валову маржу. Вони враховуються у чистому прибутку."')
.setBackground(dark2).setFontColor('#E8D18A').setWrap(true).setVerticalAlignment('top');
var widths = [145,105,120,115,115,125,24,55,200,115,115,95,24,75];
widths.forEach(function(width, idx) {
sheet.setColumnWidth(idx + 1, width);
});
sheet.setRowHeights(5, 2, 28);
sheet.setRowHeights(9, 2, 28);
var monthlyChart = sheet.newChart()
.setChartType(Charts.ChartType.LINE)
.addRange(sheet.getRange('A25:F37'))
.setPosition(39,1,0,0)
.setOption('title','Фінансова динаміка за місяцями')
.setOption('legend',{position:'bottom'})
.setOption('backgroundColor',ivory)
.build();
sheet.insertChart(monthlyChart);
var expenseChart = sheet.newChart()
.setChartType(Charts.ChartType.PIE)
.addRange(sheet.getRange('H25:I27'))
.setPosition(39,8,0,0)
.setOption('title','Маркетингові витрати')
.setOption('legend',{position:'bottom'})
.setOption('backgroundColor',ivory)
.build();
sheet.insertChart(expenseChart);
}
function sectionHeader_(sheet, range, text, bg, color) {
sheet.getRange(range).merge()
.setValue(text)
.setBackground(bg)
.setFontColor(color)
.setFontWeight('bold')
.setHorizontalAlignment('center');
}
function tableHeader_(sheet, range, bg, color, border) {
sheet.getRange(range)
.setBackground(bg)
.setFontColor(color)
.setFontWeight('bold')
.setHorizontalAlignment('center')
.setWrap(true)
.setBorder(true,true,true,true,true,true,border,SpreadsheetApp.BorderStyle.SOLID);
}
function tableBody_(sheet, range, bg, color, border) {
sheet.getRange(range)
.setBackground(bg)
.setFontColor(color)
.setBorder(true,true,true,true,true,true,border,SpreadsheetApp.BorderStyle.SOLID);
}
Натисни Deploy → New deployment
Type: Web app
Execute as: Me
Who has access: Anyone
Натисни Deploy і скопіюй URL.
1. Відкрий Google Sheets
2. Натисни Share (кнопка вгорі праворуч)
3. Додай email людини → обери Editor
4. Поділись також посиланням на цю аппу