added new gambling report

This commit is contained in:
Ali 2025-09-18 19:49:03 +04:00
parent aa22ffeef3
commit 9b3f6d7369
4 changed files with 549 additions and 0 deletions

View File

@ -0,0 +1,319 @@
// Copyright (c) 2024, Your Company and contributors
// For license information, please see license.txt
frappe.query_reports["Gaming Revenue Report"] = {
"filters": [
{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1,
},
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1,
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1,
}
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
// Подсветка колонок с суммами
if (column.fieldname && (column.fieldname.includes("sports_") || column.fieldname.includes("lottery_"))) {
if (value && parseFloat(value) > 0) {
let color = "#28a745"; // зеленый по умолчанию
let icon = "";
if (column.fieldname.includes("sports_organizer")) {
color = "#0d6efd"; // синий для спортивных игр организатор
icon = "🏆";
} else if (column.fieldname.includes("sports_seller")) {
color = "#17a2b8"; // голубой для спортивных игр продавец
icon = "⚽";
} else if (column.fieldname.includes("lottery_organizer")) {
color = "#dc3545"; // красный для лотереи организатор
icon = "🎰";
} else if (column.fieldname.includes("lottery_seller")) {
color = "#fd7e14"; // оранжевый для лотереи продавец
icon = "🎲";
}
value = `<span style="color: ${color}; font-weight: bold; background-color: ${color}15; padding: 2px 8px; border-radius: 4px; border-left: 3px solid ${color};">${icon} ${value}</span>`;
} else if (value == 0 || !value) {
value = `<span style="color: #ccc;">0.00</span>`;
}
}
// Подсветка Sales Invoice колонки
if (column.fieldname == "sales_invoice" && value) {
value = `<span style="color: #6f42c1; font-weight: bold;">📄 ${value}</span>`;
}
// Подсветка Date колонки
if (column.fieldname == "posting_date" && value) {
value = `<span style="color: #495057; font-weight: 500;">📅 ${value}</span>`;
}
return value;
},
"onload": function(report) {
// Добавляем кастомные кнопки
report.page.add_inner_button(__("Export Gaming Summary"), function() {
export_gaming_summary(report);
});
report.page.add_inner_button(__("Show Game Types"), function() {
show_game_types_info();
});
// Добавляем информационное сообщение
report.page.add_inner_button(__("About Gaming Activities"), function() {
show_gaming_activities_info();
});
},
"after_datatable_render": function(datatable_obj) {
// Добавляем итоговую статистику
setTimeout(() => {
add_gaming_statistics(datatable_obj);
}, 500);
}
};
// Функция показа информации о игровых активностях
function show_gaming_activities_info() {
let info_html = '<div class="gaming-activities-info">';
info_html += '<h4 style="color: #6f42c1; margin-bottom: 15px;">🎮 Oyun Fəaliyyətləri</h4>';
info_html += '<div class="row">';
info_html += '<div class="col-md-6">';
info_html += '<h5 style="color: #0d6efd; margin-bottom: 10px;">🏆 İdman Oyunları:</h5>';
info_html += '<ul style="font-size: 12px; color: #6c757d;">';
info_html += '<li>92000 - Həvəsləndirici oyunlar</li>';
info_html += '<li>9200003 - Oyun avtomatları</li>';
info_html += '<li>9200004 - Virtual oyunlar</li>';
info_html += '<li>9200002 - Totalizator</li>';
info_html += '<li>9200005 - İdman mərc oyunları</li>';
info_html += '</ul>';
info_html += '</div>';
info_html += '<div class="col-md-6">';
info_html += '<h5 style="color: #dc3545; margin-bottom: 10px;">🎰 Lotereya Oyunları:</h5>';
info_html += '<ul style="font-size: 12px; color: #6c757d;">';
info_html += '<li>9200001 Lotereya oyunları</li>';
info_html += '</ul>';
info_html += '<br>';
info_html += '<h5 style="color: #28a745; margin-bottom: 10px;">👥 Rollər:</h5>';
info_html += '<ul style="font-size: 12px; color: #6c757d;">';
info_html += '<li><strong>Təşkilatçı:</strong> Oyunu təşkil edən</li>';
info_html += '<li><strong>Satıcı:</strong> Oyunu satan</li>';
info_html += '</ul>';
info_html += '</div>';
info_html += '</div>';
info_html += '</div>';
frappe.msgprint({
title: __("Gaming Activities Information"),
message: info_html,
wide: true
});
}
// Функция показа типов игр
function show_game_types_info() {
let info_html = '<div class="game-types-info">';
info_html += '<h4>🎯 Hesabatda İstifadə Olunan Oyun Tipləri:</h4>';
info_html += '<div class="row">';
info_html += '<div class="col-md-6" style="margin-bottom: 15px;">';
info_html += '<div style="padding: 15px; border: 2px solid #0d6efd; border-radius: 8px; background: #0d6efd10;">';
info_html += '<h5 style="color: #0d6efd;">🏆 İdman Oyunları</h5>';
info_html += '<p style="color: #6c757d; font-size: 13px;">Təşkilatçı və Satıcı rollarında gəlir</p>';
info_html += '</div>';
info_html += '</div>';
info_html += '<div class="col-md-6" style="margin-bottom: 15px;">';
info_html += '<div style="padding: 15px; border: 2px solid #dc3545; border-radius: 8px; background: #dc354510;">';
info_html += '<h5 style="color: #dc3545;">🎰 Lotereya Oyunları</h5>';
info_html += '<p style="color: #6c757d; font-size: 13px;">Təşkilatçı və Satıcı rollarında gəlir</p>';
info_html += '</div>';
info_html += '</div>';
info_html += '</div>';
info_html += '</div>';
frappe.msgprint({
title: __("Game Types Information"),
message: info_html,
wide: true
});
}
// Функция экспорта сводки по игорному бизнесу
function export_gaming_summary(report) {
let data = report.data;
let summary = calculate_gaming_totals(data);
// Создаем CSV с итогами
let csv_content = "Category,Role,Revenue,Documents Count\n";
csv_content += `"İdman Oyunları","Təşkilatçı",${summary.sports_organizer_total},${summary.sports_organizer_count}\n`;
csv_content += `"İdman Oyunları","Satıcı",${summary.sports_seller_total},${summary.sports_seller_count}\n`;
csv_content += `"Lotereya Oyunları","Təşkilatçı",${summary.lottery_organizer_total},${summary.lottery_organizer_count}\n`;
csv_content += `"Lotereya Oyunları","Satıcı",${summary.lottery_seller_total},${summary.lottery_seller_count}\n`;
// Добавляем общие итоги
csv_content += `\n"CƏMİ","",${summary.grand_total},${summary.total_documents}\n`;
// Скачиваем файл
let blob = new Blob([csv_content], { type: 'text/csv;charset=utf-8' });
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = 'gaming_revenue_summary_' + frappe.datetime.get_today() + '.csv';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
frappe.show_alert({message: __("Gaming summary exported successfully"), indicator: "green"});
}
// Функция расчета итогов по игорному бизнесу
function calculate_gaming_totals(data) {
let summary = {
sports_organizer_total: 0,
sports_organizer_count: 0,
sports_seller_total: 0,
sports_seller_count: 0,
lottery_organizer_total: 0,
lottery_organizer_count: 0,
lottery_seller_total: 0,
lottery_seller_count: 0,
grand_total: 0,
total_documents: data.length
};
data.forEach(row => {
// Спортивные игры
if (row.sports_organizer && row.sports_organizer > 0) {
summary.sports_organizer_total += row.sports_organizer;
summary.sports_organizer_count++;
summary.grand_total += row.sports_organizer;
}
if (row.sports_seller && row.sports_seller > 0) {
summary.sports_seller_total += row.sports_seller;
summary.sports_seller_count++;
summary.grand_total += row.sports_seller;
}
// Лотерейные игры
if (row.lottery_organizer && row.lottery_organizer > 0) {
summary.lottery_organizer_total += row.lottery_organizer;
summary.lottery_organizer_count++;
summary.grand_total += row.lottery_organizer;
}
if (row.lottery_seller && row.lottery_seller > 0) {
summary.lottery_seller_total += row.lottery_seller;
summary.lottery_seller_count++;
summary.grand_total += row.lottery_seller;
}
});
return summary;
}
// Функция добавления статистики под таблицу
function add_gaming_statistics(datatable_obj) {
if (datatable_obj && datatable_obj.data) {
let summary = calculate_gaming_totals(datatable_obj.data);
let stats_html = '<div class="gaming-statistics" style="margin-top: 20px; padding: 20px; background: linear-gradient(135deg, #fff5f0 0%, #fff 100%); border-radius: 8px; border: 2px solid #fd7e14; box-shadow: 0 2px 8px rgba(253,126,20,0.1);">';
stats_html += '<h4 style="margin-bottom: 15px; color: #fd7e14; display: flex; align-items: center;"><span style="margin-right: 8px;">🎮</span> Oyun Gəlirləri Statistikası:</h4>';
// Статистика по спортивным играм
stats_html += '<div class="row" style="margin-bottom: 20px;">';
stats_html += '<div class="col-md-6">';
stats_html += '<h5 style="color: #0d6efd; margin-bottom: 15px; display: flex; align-items: center;"><span style="margin-right: 8px;">🏆</span> İdman Oyunları</h5>';
stats_html += '<div class="row">';
stats_html += '<div class="col-md-6" style="margin-bottom: 10px;">';
stats_html += '<div style="padding: 12px; border: 2px solid #0d6efd; border-radius: 6px; background: white; text-align: center;">';
stats_html += '<strong style="color: #0d6efd;">Təşkilatçı</strong><br>';
stats_html += `<span style="font-size: 18px; font-weight: bold; color: #0d6efd;">${summary.sports_organizer_total.toFixed(2)}</span><br>`;
stats_html += `<small style="color: #6c757d;">${summary.sports_organizer_count} sənəd</small>`;
stats_html += '</div>';
stats_html += '</div>';
stats_html += '<div class="col-md-6" style="margin-bottom: 10px;">';
stats_html += '<div style="padding: 12px; border: 2px solid #17a2b8; border-radius: 6px; background: white; text-align: center;">';
stats_html += '<strong style="color: #17a2b8;">Satıcı</strong><br>';
stats_html += `<span style="font-size: 18px; font-weight: bold; color: #17a2b8;">${summary.sports_seller_total.toFixed(2)}</span><br>`;
stats_html += `<small style="color: #6c757d;">${summary.sports_seller_count} sənəd</small>`;
stats_html += '</div>';
stats_html += '</div>';
stats_html += '</div>';
stats_html += '</div>';
// Статистика по лотерейным играм
stats_html += '<div class="col-md-6">';
stats_html += '<h5 style="color: #dc3545; margin-bottom: 15px; display: flex; align-items: center;"><span style="margin-right: 8px;">🎰</span> Lotereya Oyunları</h5>';
stats_html += '<div class="row">';
stats_html += '<div class="col-md-6" style="margin-bottom: 10px;">';
stats_html += '<div style="padding: 12px; border: 2px solid #dc3545; border-radius: 6px; background: white; text-align: center;">';
stats_html += '<strong style="color: #dc3545;">Təşkilatçı</strong><br>';
stats_html += `<span style="font-size: 18px; font-weight: bold; color: #dc3545;">${summary.lottery_organizer_total.toFixed(2)}</span><br>`;
stats_html += `<small style="color: #6c757d;">${summary.lottery_organizer_count} sənəd</small>`;
stats_html += '</div>';
stats_html += '</div>';
stats_html += '<div class="col-md-6" style="margin-bottom: 10px;">';
stats_html += '<div style="padding: 12px; border: 2px solid #fd7e14; border-radius: 6px; background: white; text-align: center;">';
stats_html += '<strong style="color: #fd7e14;">Satıcı</strong><br>';
stats_html += `<span style="font-size: 18px; font-weight: bold; color: #fd7e14;">${summary.lottery_seller_total.toFixed(2)}</span><br>`;
stats_html += `<small style="color: #6c757d;">${summary.lottery_seller_count} sənəd</small>`;
stats_html += '</div>';
stats_html += '</div>';
stats_html += '</div>';
stats_html += '</div>';
stats_html += '</div>';
// Общие итоги
let sports_total = summary.sports_organizer_total + summary.sports_seller_total;
let lottery_total = summary.lottery_organizer_total + summary.lottery_seller_total;
stats_html += '<div style="margin-top: 20px; padding: 15px; border-top: 3px solid #fd7e14; background: linear-gradient(135deg, #fd7e1410 0%, #fff 100%);">';
stats_html += '<div class="row text-center">';
stats_html += `<div class="col-md-3"><span style="color: #0d6efd; font-size: 14px; font-weight: bold;">🏆 İdman Cəmi<br><span style="font-size: 18px;">${sports_total.toFixed(2)}</span></span></div>`;
stats_html += `<div class="col-md-3"><span style="color: #dc3545; font-size: 14px; font-weight: bold;">🎰 Lotereya Cəmi<br><span style="font-size: 18px;">${lottery_total.toFixed(2)}</span></span></div>`;
stats_html += `<div class="col-md-3"><span style="color: #28a745; font-size: 14px; font-weight: bold;">💰 Ümumi Gəlir<br><span style="font-size: 18px;">${summary.grand_total.toFixed(2)}</span></span></div>`;
stats_html += `<div class="col-md-3"><span style="color: #6f42c1; font-size: 14px; font-weight: bold;">📄 Cəmi Sənədlər<br><span style="font-size: 18px;">${summary.total_documents}</span></span></div>`;
stats_html += '</div>';
stats_html += '</div>';
stats_html += '</div>';
// Удаляем предыдущую статистику если есть
$('.gaming-statistics').remove();
// Добавляем новую статистику
$(stats_html).insertAfter('.datatable');
}
}

View File

@ -0,0 +1,33 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2025-09-18 19:33:16.290750",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letterhead": null,
"modified": "2025-09-18 19:33:16.290750",
"modified_by": "Administrator",
"module": "Taxes Az",
"name": "Gaming Revenue Report",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Invoice",
"report_name": "Gaming Revenue Report",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Accounts Manager"
},
{
"role": "Employee Self Service"
}
],
"timeout": 0
}

View File

@ -0,0 +1,197 @@
# Copyright (c) 2024, Your Company and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import flt, formatdate
def execute(filters=None):
columns, data = get_columns(), get_data(filters)
return columns, data
def get_data(filters):
"""Получаем данные по Sales Invoice для игорного бизнеса"""
# Получаем Sales Invoice с учетом фильтров
sales_invoices = get_sales_invoices(filters)
if not sales_invoices:
return []
# Получаем информацию о компаниях для определения типа игры
companies_activity = get_companies_main_activity()
# Формируем итоговые данные
data = []
for invoice in sales_invoices:
row = frappe._dict()
# Основные данные
row.sales_invoice = invoice['name']
row.posting_date = invoice['posting_date']
row.company = invoice['company']
row.grand_total = flt(invoice['grand_total'])
# Определяем тип игры по main_activity компании
company_activity = companies_activity.get(invoice['company'], '')
game_type = determine_game_type(company_activity)
# Инициализируем все колонки нулями
row.sports_organizer = 0
row.sports_seller = 0
row.lottery_organizer = 0
row.lottery_seller = 0
# Распределяем сумму в зависимости от типа игры и роли
if game_type == 'sports':
if invoice.get('organizer'):
row.sports_organizer = row.grand_total
elif invoice.get('seller'):
row.sports_seller = row.grand_total
elif game_type == 'lottery':
if invoice.get('organizer'):
row.lottery_organizer = row.grand_total
elif invoice.get('seller'):
row.lottery_seller = row.grand_total
data.append(row)
return data
def get_sales_invoices(filters):
"""Получает Sales Invoice с учетом фильтров"""
conditions = []
values = []
# Фильтр по компании
if filters.get("company"):
conditions.append("si.company = %s")
values.append(filters.get("company"))
# Фильтр по датам
if filters.get("from_date"):
conditions.append("si.posting_date >= %s")
values.append(filters.get("from_date"))
if filters.get("to_date"):
conditions.append("si.posting_date <= %s")
values.append(filters.get("to_date"))
# Только подтвержденные документы
conditions.append("si.docstatus = 1")
# Только документы с отмеченными галочками organizer или seller
conditions.append("(si.organizer = 1 OR si.seller = 1)")
where_clause = ""
if conditions:
where_clause = "WHERE " + " AND ".join(conditions)
query = f"""
SELECT
si.name,
si.posting_date,
si.company,
si.grand_total,
si.organizer,
si.seller
FROM
`tabSales Invoice` si
{where_clause}
ORDER BY
si.posting_date DESC, si.name
"""
return frappe.db.sql(query, values, as_dict=True)
def get_companies_main_activity():
"""Получает main_activity для всех компаний"""
companies = frappe.db.sql("""
SELECT name, main_activity
FROM `tabCompany`
WHERE main_activity IS NOT NULL
""", as_dict=True)
return {company['name']: company['main_activity'] for company in companies}
def determine_game_type(main_activity):
"""Определяет тип игры на основе main_activity"""
if not main_activity:
return 'unknown'
# Коды для спортивных игр
sports_codes = [
'92000',
'9200003',
'9200004',
'9200002',
'9200005'
]
# Код для лотерейных игр
lottery_codes = [
'9200001'
]
# Проверяем на соответствие кодам
for code in sports_codes:
if code in main_activity:
return 'sports'
for code in lottery_codes:
if code in main_activity:
return 'lottery'
return 'unknown'
def get_columns():
"""Создает колонки отчета"""
return [
{
"label": "Sales Invoice",
"fieldname": "sales_invoice",
"fieldtype": "Link",
"options": "Sales Invoice",
"width": 150
},
{
"label": "Tarix",
"fieldname": "posting_date",
"fieldtype": "Date",
"width": 100
},
{
"label": "İdman oyunları - Təşkilatçı",
"fieldname": "sports_organizer",
"fieldtype": "Currency",
"width": 160
},
{
"label": "İdman oyunları - Satıcı",
"fieldname": "sports_seller",
"fieldtype": "Currency",
"width": 160
},
{
"label": "Lotereya oyunları - Təşkilatçı",
"fieldname": "lottery_organizer",
"fieldtype": "Currency",
"width": 170
},
{
"label": "Lotereya oyunları - Satıcı",
"fieldname": "lottery_seller",
"fieldtype": "Currency",
"width": 170
}
]