From 537289c23086746ef877bce7416b27774122c63a Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Fri, 15 Aug 2025 19:33:50 +0400 Subject: [PATCH] added new report Fuel Products Purchase Report --- .../fuel_products_purchase_report/__init__.py | 0 .../fuel_products_purchase_report.js | 190 ++++++++++++++++++ .../fuel_products_purchase_report.json | 36 ++++ .../fuel_products_purchase_report.py | 187 +++++++++++++++++ 4 files changed, 413 insertions(+) create mode 100644 taxes_az/taxes_az/report/fuel_products_purchase_report/__init__.py create mode 100644 taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.js create mode 100644 taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.json create mode 100644 taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.py diff --git a/taxes_az/taxes_az/report/fuel_products_purchase_report/__init__.py b/taxes_az/taxes_az/report/fuel_products_purchase_report/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.js b/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.js new file mode 100644 index 0000000..6e34156 --- /dev/null +++ b/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.js @@ -0,0 +1,190 @@ +// Copyright (c) 2024, Your Company and contributors +// For license information, please see license.txt + +frappe.query_reports["Fuel Products Purchase Report"] = { + "filters": [ + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + "width": "100px" + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, + "width": "100px" + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + // Подсветка колонок с топливом + if (["ai_92", "ai_95", "ai_80", "dizel", "maye_qaz"].includes(column.fieldname)) { + if (value && parseFloat(value) > 0) { + // Разные цвета для разных типов топлива + let color = "#28a745"; // зеленый по умолчанию + + switch(column.fieldname) { + case "ai_92": + color = "#28a745"; // зеленый + break; + case "ai_95": + color = "#007bff"; // синий + break; + case "ai_80": + color = "#6c757d"; // серый + break; + case "dizel": + color = "#fd7e14"; // оранжевый + break; + case "maye_qaz": + color = "#e83e8c"; // розовый + break; + } + + value = `${value}`; + } else if (value == 0 || !value) { + value = `0`; + } + } + + // Подсветка Total Qty + if (column.fieldname == "total_qty" && value) { + let qty = parseFloat(value); + if (qty > 50) { + value = `${value}`; + } else if (qty > 20) { + value = `${value}`; + } else if (qty > 0) { + value = `${value}`; + } + } + + return value; + }, + + "onload": function(report) { + // Добавляем кастомные кнопки + report.page.add_inner_button(__("Export Summary"), function() { + export_fuel_summary(report); + }); + + + }, + + "after_datatable_render": function(datatable_obj) { + // Добавляем итоговую статистику + setTimeout(() => { + add_fuel_statistics(datatable_obj); + }, 500); + } +}; + +// Функция экспорта сводки +function export_fuel_summary(report) { + let data = report.data; + let summary = calculate_totals(data); + + // Создаем CSV с итогами + let csv_content = "Metric,Aİ-92,Aİ-95,Aİ-80,Dizel yanacağı,Maye qaz,Total\n"; + csv_content += `Total Quantity,${summary.ai_92},${summary.ai_95},${summary.ai_80},${summary.dizel},${summary.maye_qaz},${summary.total}\n`; + csv_content += `Documents Count,${summary.docs_with_ai92},${summary.docs_with_ai95},${summary.docs_with_ai80},${summary.docs_with_dizel},${summary.docs_with_maye_qaz},${summary.total_docs}\n`; + + // Скачиваем файл + let blob = new Blob([csv_content], { type: 'text/csv' }); + let url = window.URL.createObjectURL(blob); + let a = document.createElement('a'); + a.href = url; + a.download = 'fuel_summary_' + frappe.datetime.get_today() + '.csv'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + + frappe.show_alert({message: __("Summary exported successfully"), indicator: "green"}); +} + +// Функция расчета итогов +function calculate_totals(data) { + let totals = { + ai_92: 0, + ai_95: 0, + ai_80: 0, + dizel: 0, + maye_qaz: 0, + total: 0, + docs_with_ai92: 0, + docs_with_ai95: 0, + docs_with_ai80: 0, + docs_with_dizel: 0, + docs_with_maye_qaz: 0, + total_docs: data.length + }; + + data.forEach(row => { + totals.ai_92 += row.ai_92 || 0; + totals.ai_95 += row.ai_95 || 0; + totals.ai_80 += row.ai_80 || 0; + totals.dizel += row.dizel || 0; + totals.maye_qaz += row.maye_qaz || 0; + totals.total += row.total_qty || 0; + + // Считаем документы с каждым типом топлива + if (row.ai_92 > 0) totals.docs_with_ai92++; + if (row.ai_95 > 0) totals.docs_with_ai95++; + if (row.ai_80 > 0) totals.docs_with_ai80++; + if (row.dizel > 0) totals.docs_with_dizel++; + if (row.maye_qaz > 0) totals.docs_with_maye_qaz++; + }); + + return totals; +} + +// Функция добавления статистики под таблицу +function add_fuel_statistics(datatable_obj) { + if (datatable_obj && datatable_obj.data) { + let totals = calculate_totals(datatable_obj.data); + + let stats_html = '
'; + stats_html += '

📈 Итоговая статистика по периоду:

'; + + stats_html += '
'; + stats_html += '
'; + stats_html += '
Общее количество по типам:
'; + stats_html += `
Aİ-92: ${totals.ai_92.toFixed(2)}
`; + stats_html += `
Aİ-95: ${totals.ai_95.toFixed(2)}
`; + stats_html += `
Aİ-80: ${totals.ai_80.toFixed(2)}
`; + stats_html += `
Dizel: ${totals.dizel.toFixed(2)}
`; + stats_html += `
Maye qaz: ${totals.maye_qaz.toFixed(2)}
`; + stats_html += '
'; + + stats_html += '
'; + stats_html += '
Количество документов:
'; + stats_html += `
Всего документов: ${totals.total_docs}
`; + stats_html += `
С Aİ-92: ${totals.docs_with_ai92}
`; + stats_html += `
С Aİ-95: ${totals.docs_with_ai95}
`; + stats_html += `
С Aİ-80: ${totals.docs_with_ai80}
`; + stats_html += `
С Dizel: ${totals.docs_with_dizel}
`; + stats_html += `
С Maye qaz: ${totals.docs_with_maye_qaz}
`; + stats_html += '
'; + stats_html += '
'; + + stats_html += `
`; + stats_html += `🔥 Общее количество топлива: ${totals.total.toFixed(2)}`; + stats_html += `
`; + + stats_html += '
'; + + // Удаляем предыдущую статистику если есть + $('.fuel-statistics').remove(); + + // Добавляем новую статистику + $(stats_html).insertAfter('.datatable'); + } +} \ No newline at end of file diff --git a/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.json b/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.json new file mode 100644 index 0000000..7c18ff4 --- /dev/null +++ b/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2025-08-15 17:22:57.350629", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2025-08-15 17:22:57.350629", + "modified_by": "Administrator", + "module": "Taxes Az", + "name": "Fuel Products Purchase Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Invoice", + "report_name": "Fuel Products Purchase Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Purchase User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ], + "timeout": 0 +} \ No newline at end of file diff --git a/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.py b/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.py new file mode 100644 index 0000000..2745617 --- /dev/null +++ b/taxes_az/taxes_az/report/fuel_products_purchase_report/fuel_products_purchase_report.py @@ -0,0 +1,187 @@ +# Copyright (c) 2024, Your Company and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ + + +def execute(filters=None): + columns = get_columns() + data = get_data(filters) + + return columns, data + + +def get_columns(): + return [ + { + "label": _("Purchase Invoice"), + "fieldname": "invoice", + "fieldtype": "Link", + "options": "Purchase Invoice", + "width": 150 + }, + { + "label": _("Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Supplier"), + "fieldname": "supplier", + "fieldtype": "Link", + "options": "Supplier", + "width": 150 + }, + { + "label": _("Aİ-92"), + "fieldname": "ai_92", + "fieldtype": "Float", + "width": 80 + }, + { + "label": _("Aİ-95"), + "fieldname": "ai_95", + "fieldtype": "Float", + "width": 80 + }, + { + "label": _("Aİ-80"), + "fieldname": "ai_80", + "fieldtype": "Float", + "width": 80 + }, + { + "label": _("Dizel yanacağı"), + "fieldname": "dizel", + "fieldtype": "Float", + "width": 100 + }, + { + "label": _("Maye qaz"), + "fieldname": "maye_qaz", + "fieldtype": "Float", + "width": 80 + }, + { + "label": _("Total Qty"), + "fieldname": "total_qty", + "fieldtype": "Float", + "width": 100 + } + ] + + +def get_data(filters): + # Хардкод фильтр на топливные продукты + fuel_items = [ + "Avtomobil benzini (Aİ-92)", + "Avtomobil benzini (Aİ-95)", + "Avtomobil benzini (Aİ-80)", + "Dizel yanacağı", + "Maye qaz" + ] + + conditions = [] + values = [] + + # Фильтр по датам + if filters.get("from_date"): + conditions.append("pi.posting_date >= %s") + values.append(filters.get("from_date")) + + if filters.get("to_date"): + conditions.append("pi.posting_date <= %s") + values.append(filters.get("to_date")) + + # Хардкод фильтр по товарам + item_condition = "pii.item_name IN ({})".format(", ".join(["%s"] * len(fuel_items))) + conditions.append(item_condition) + values.extend(fuel_items) + + # Только подтвержденные инвойсы + conditions.append("pi.docstatus = 1") + + where_clause = "" + if conditions: + where_clause = "WHERE " + " AND ".join(conditions) + + # Получаем все данные + query = """ + SELECT + pi.name as invoice, + pi.posting_date, + pi.supplier, + pii.item_name, + pii.qty + FROM + `tabPurchase Invoice Item` pii + INNER JOIN + `tabPurchase Invoice` pi ON pii.parent = pi.name + {where_clause} + ORDER BY + pi.posting_date DESC, pi.name + """.format(where_clause=where_clause) + + raw_data = frappe.db.sql(query, values, as_dict=True) + + # Группируем данные по документам + invoice_data = {} + + for row in raw_data: + invoice = row['invoice'] + + if invoice not in invoice_data: + invoice_data[invoice] = { + 'invoice': invoice, + 'posting_date': row['posting_date'], + 'supplier': row['supplier'], + 'ai_92': 0, + 'ai_95': 0, + 'ai_80': 0, + 'dizel': 0, + 'maye_qaz': 0, + 'total_qty': 0 + } + + # Распределяем количество по колонкам + item_name = row['item_name'] + qty = row['qty'] or 0 + + if 'Aİ-92' in item_name: + invoice_data[invoice]['ai_92'] += qty + elif 'Aİ-95' in item_name: + invoice_data[invoice]['ai_95'] += qty + elif 'Aİ-80' in item_name: + invoice_data[invoice]['ai_80'] += qty + elif 'Dizel' in item_name: + invoice_data[invoice]['dizel'] += qty + elif 'Maye qaz' in item_name: + invoice_data[invoice]['maye_qaz'] += qty + + invoice_data[invoice]['total_qty'] += qty + + # Конвертируем в список + data = list(invoice_data.values()) + + return data + + +def get_filters(): + return [ + { + "fieldname": "from_date", + "label": _("From Date"), + "fieldtype": "Date", + "default": frappe.utils.add_months(frappe.utils.today(), -1), + "reqd": 1 + }, + { + "fieldname": "to_date", + "label": _("To Date"), + "fieldtype": "Date", + "default": frappe.utils.today(), + "reqd": 1 + } + ] \ No newline at end of file