Учет доходов и расходов фермы

Общий доход
0 ₽
Общий расход
0 ₽
Прибыль
0 ₽
Рентабельность
0%

Куриные яйца

0

Перепелиные яйца

0

Фильтры


Добавить запись

Доступно: 0

Финансовые операции

Дата Тип Категория Сумма Описание Действия
document.getElementById('totalIncome').textContent = formatCurrency(totalIncome); document.getElementById('totalExpense').textContent = formatCurrency(totalExpense); document.getElementById('totalProfit').textContent = formatCurrency(profit); document.getElementById('profitability').textContent = `${profitability}%`; } // Обновить графики function updateCharts(stats) { updateIncomeExpenseChart(stats); updateCategoriesChart(stats); } // График доходов и расходов function updateIncomeExpenseChart(stats) { const ctx = document.getElementById('incomeExpenseChart').getContext('2d'); if (incomeExpenseChart) { incomeExpenseChart.destroy(); } const totalIncome = stats.totals.total_income || 0; const totalExpense = stats.totals.total_expense || 0; incomeExpenseChart = new Chart(ctx, { type: 'bar', data: { labels: ['Доходы', 'Расходы'], datasets: [{ label: 'Сумма (₽)', data: [totalIncome, totalExpense], backgroundColor: [ 'rgba(46, 125, 50, 0.7)', 'rgba(198, 40, 40, 0.7)' ], borderColor: [ 'rgba(46, 125, 50, 1)', 'rgba(198, 40, 40, 1)' ], borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Доходы и расходы', font: { size: 16, weight: 'bold' }, padding: { top: 10, bottom: 15 } }, legend: { display: false } }, scales: { y: { beginAtZero: true, title: { display: true, text: 'Сумма (₽)', font: { weight: 'bold', size: 12 } }, grid: { drawBorder: false } }, x: { grid: { display: false } } } } }); } // График по категориям function updateCategoriesChart(stats) { const ctx = document.getElementById('categoriesChart').getContext('2d'); if (categoriesChart) { categoriesChart.destroy(); } // Подготовка данных для графика const incomeCategories = stats.income_by_category.map(item => item.category); const incomeData = stats.income_by_category.map(item => item.total); const expenseCategories = stats.expense_by_category.map(item => item.category); const expenseData = stats.expense_by_category.map(item => item.total); categoriesChart = new Chart(ctx, { type: 'doughnut', data: { labels: [...incomeCategories, ...expenseCategories], datasets: [ { label: 'Доходы по категориям', data: [...incomeData, ...Array(expenseCategories.length).fill(0)], backgroundColor: [ 'rgba(46, 125, 50, 0.7)', 'rgba(67, 160, 71, 0.7)', 'rgba(102, 187, 106, 0.7)', 'rgba(165, 214, 167, 0.7)' ], borderColor: [ 'rgba(46, 125, 50, 1)', 'rgba(67, 160, 71, 1)', 'rgba(102, 187, 106, 1)', 'rgba(165, 214, 167, 1)' ], borderWidth: 1 }, { label: 'Расходы по категориям', data: [...Array(incomeCategories.length).fill(0), ...expenseData], backgroundColor: [ 'rgba(198, 40, 40, 0.7)', 'rgba(229, 57, 53, 0.7)', 'rgba(239, 83, 80, 0.7)', 'rgba(255, 138, 128, 0.7)' ], borderColor: [ 'rgba(198, 40, 40, 1)', 'rgba(229, 57, 53, 1)', 'rgba(239, 83, 80, 1)', 'rgba(255, 138, 128, 1)' ], borderWidth: 1 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Распределение по категориям', font: { size: 16, weight: 'bold' }, padding: { top: 10, bottom: 15 } }, legend: { position: 'bottom', labels: { boxWidth: 15, padding: 10, font: { size: 11 }, usePointStyle: true } } }, cutout: '60%' } }); } // Вспомогательные функции function formatDate(dateString) { if (!dateString) return 'Нет данных'; try { const date = new Date(dateString); return date.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' }); } catch (e) { console.error('Ошибка форматирования даты:', dateString, e); return dateString; } } function formatCurrency(amount) { return new Intl.NumberFormat('ru-RU', { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(amount) + ' ₽'; } function showSuccess(message) { const alert = document.getElementById('successAlert'); alert.textContent = message; alert.style.display = 'block'; setTimeout(() => alert.style.display = 'none', 5000); } function showError(message) { const alert = document.getElementById('errorAlert'); alert.textContent = message; alert.style.display = 'block'; setTimeout(() => alert.style.display = 'none', 5000); }