added vat allocation doctype and modifed cob report

This commit is contained in:
Ali 2025-10-06 17:32:06 +04:00
parent f121789432
commit c22dabf943
8 changed files with 721 additions and 100 deletions

View File

@ -251,6 +251,146 @@ function setup_journal_entry_query(frm) {
// Setup for VAT Journal Entry // Setup for VAT Journal Entry
frappe.ui.form.on('VAT allocation payment entry', { frappe.ui.form.on('VAT allocation payment entry', {
// При изменении Customer - очистить все поля и удалить связанные links
customer: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
let old_row_name = row.name;
// Очищаем все поля
frappe.model.set_value(cdt, cdn, 'payment_entry', '');
frappe.model.set_value(cdt, cdn, 'payment_entry_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_journal_entry', '');
frappe.model.set_value(cdt, cdn, 'vat_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_allocated_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_allocated_allocated', 0);
frappe.model.set_value(cdt, cdn, 'vat_free_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_free_allocated', 0);
frappe.model.set_value(cdt, cdn, 'allocated_amount', 0);
frappe.model.set_value(cdt, cdn, 'unallocated_amount', 0);
// Удаляем все allocation links связанные с этой строкой
frm.doc.allocation_links = (frm.doc.allocation_links || []).filter(function(link) {
return link.payment_entry_row !== old_row_name;
});
frm.refresh_field('allocation_links');
// Пересчитываем totals
recalculate_totals_manually(frm);
frm.refresh_field('payment_entry_table');
frm.refresh_field('sales_invoice_table');
},
// При изменении Payment Entry - обновить суммы
payment_entry: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
let old_row_name = row.name;
// Всегда удаляем allocation links связанные с этой строкой при изменении/очистке Payment Entry
frm.doc.allocation_links = (frm.doc.allocation_links || []).filter(function(link) {
return link.payment_entry_row !== old_row_name;
});
frm.refresh_field('allocation_links');
if (!row.payment_entry) {
// Если Payment Entry очищен, обнуляем все поля
frappe.model.set_value(cdt, cdn, 'payment_entry_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_allocated_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_free_amount', 0);
frappe.model.set_value(cdt, cdn, 'allocated_amount', 0);
frappe.model.set_value(cdt, cdn, 'unallocated_amount', 0);
// Пересчитываем totals
recalculate_totals_manually(frm);
frm.refresh_field('payment_entry_table');
frm.refresh_field('sales_invoice_table');
return;
}
// Получаем данные из Payment Entry
frappe.call({
method: 'frappe.client.get_value',
args: {
doctype: 'Payment Entry',
filters: { name: row.payment_entry },
fieldname: ['paid_amount', 'party', 'docstatus']
},
callback: function(r) {
if (r.message) {
// Проверяем что Payment Entry submitted
if (r.message.docstatus !== 1) {
frappe.msgprint(__('Warning: Selected Payment Entry is not submitted'));
frappe.model.set_value(cdt, cdn, 'payment_entry', '');
return;
}
// Проверяем что customer совпадает
if (row.customer && r.message.party !== row.customer) {
frappe.msgprint(__('Payment Entry customer does not match the selected customer'));
frappe.model.set_value(cdt, cdn, 'payment_entry', '');
return;
}
// Устанавливаем payment_entry_amount
let paid_amount = r.message.paid_amount || 0;
frappe.model.set_value(cdt, cdn, 'payment_entry_amount', paid_amount);
// Если есть vat_journal_entry, пересчитываем на его основе
if (row.vat_journal_entry && row.vat_amount) {
let vat_allocated = row.vat_amount / 0.18;
frappe.model.set_value(cdt, cdn, 'vat_allocated_amount', vat_allocated);
frappe.model.set_value(cdt, cdn, 'vat_free_amount', paid_amount - vat_allocated);
} else {
// Иначе вся сумма идет в vat_free
frappe.model.set_value(cdt, cdn, 'vat_allocated_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_free_amount', paid_amount);
}
// Обнуляем allocated amounts (так как links удалены)
frappe.model.set_value(cdt, cdn, 'allocated_amount', 0);
frappe.model.set_value(cdt, cdn, 'vat_free_allocated', 0);
frappe.model.set_value(cdt, cdn, 'vat_allocated_allocated', 0);
// Unallocated = payment_entry_amount (так как allocated = 0)
frappe.model.set_value(cdt, cdn, 'unallocated_amount', paid_amount);
// Автоматически заполняем customer, если он не был заполнен
if (!row.customer) {
frappe.model.set_value(cdt, cdn, 'customer', r.message.party);
}
// Пересчитываем totals
recalculate_totals_manually(frm);
frm.refresh_field('payment_entry_table');
frm.refresh_field('sales_invoice_table');
}
}
});
},
// При удалении строки - удалить связанные links
payment_entry_table_remove: function(frm, cdt, cdn) {
// Собираем список всех существующих row names из текущей таблицы
let existing_rows = {};
for (let pe_row of frm.doc.payment_entry_table || []) {
existing_rows[pe_row.name] = true;
}
// Удаляем все allocation links, которые ссылаются на несуществующие строки
frm.doc.allocation_links = (frm.doc.allocation_links || []).filter(function(link) {
return existing_rows[link.payment_entry_row];
});
frm.refresh_field('allocation_links');
// Пересчитываем totals для оставшихся строк
recalculate_totals_manually(frm);
frm.refresh_field('payment_entry_table');
frm.refresh_field('sales_invoice_table');
},
vat_journal_entry: function(frm, cdt, cdn) { vat_journal_entry: function(frm, cdt, cdn) {
let row = locals[cdt][cdn]; let row = locals[cdt][cdn];
@ -335,32 +475,67 @@ frappe.ui.form.on('VAT allocation payment entry', {
frappe.msgprint(__('Cannot allocate to disabled customer')); frappe.msgprint(__('Cannot allocate to disabled customer'));
return; return;
} }
// Open allocation dialog // Open allocation dialog - используем idx для несохраненных строк
show_allocation_dialog(frm, row.name, row.customer); show_allocation_dialog(frm, row.idx, row.customer);
} }
}); });
} }
}); });
// Show allocation dialog // Show allocation dialog - работает с несохраненными данными
function show_allocation_dialog(frm, payment_row_name, customer) { function show_allocation_dialog(frm, payment_row_idx, customer) {
// Get allocation data from server // Получаем данные напрямую из формы (не из БД!)
frappe.call({ let payment_row = null;
method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.get_allocation_data', for (let row of frm.doc.payment_entry_table || []) {
args: { if (row.idx === payment_row_idx) {
vat_allocation_name: frm.doc.name, payment_row = row;
payment_entry_row_name: payment_row_name, break;
customer: customer }
}, }
callback: function(r) {
if (!r.message) { if (!payment_row) {
frappe.msgprint(__('No data available for allocation')); frappe.msgprint(__('Payment entry row not found'));
return; return;
} }
let data = r.message; // Получаем sales invoice items для этого клиента из формы
let vat_free_items = data.vat_free_items; let vat_free_items = [];
let vat_items = data.vat_items; let vat_items = [];
for (let item of frm.doc.sales_invoice_table || []) {
if (item.customer !== customer) {
continue;
}
// Получаем существующие аллокации для этого item от текущего payment row
let current_allocation = 0;
for (let link of frm.doc.allocation_links || []) {
if (link.payment_entry_row === payment_row.name &&
link.sales_invoice_row === item.name) {
current_allocation = link.allocated_amount || 0;
break;
}
}
let item_data = {
name: item.name,
item_tax_template: item.item_tax_template || '',
tax_article: item.tax_article || '',
amount: item.amount || 0,
unallocated_amount: item.unallocated_amount || 0,
allocated_amount: item.allocated_amount || 0,
current_allocation: current_allocation
};
let template = (item.item_tax_template || '').toLowerCase();
// ИСПРАВЛЕНО: используем .includes() вместо 'in'
if (template.includes('ədv 0%') || template.includes('ədv-dən azadolma')) {
vat_free_items.push(item_data);
} else if (template.includes('ədv 18%') || template.includes('ədv daxil 18%')) {
vat_items.push(item_data);
}
}
// Check if there are items to allocate to // Check if there are items to allocate to
if (vat_free_items.length === 0 && vat_items.length === 0) { if (vat_free_items.length === 0 && vat_items.length === 0) {
@ -368,13 +543,25 @@ function show_allocation_dialog(frm, payment_row_name, customer) {
return; return;
} }
// Вычисляем available amounts из текущих данных формы
let vat_free_amount = payment_row.vat_free_amount || 0;
let vat_free_allocated = payment_row.vat_free_allocated || 0;
let vat_allocated_amount = payment_row.vat_allocated_amount || 0;
let vat_allocated_allocated = payment_row.vat_allocated_allocated || 0;
let data = {
payment_row: payment_row,
vat_free_available: vat_free_amount - vat_free_allocated,
vat_allocated_available: vat_allocated_amount - vat_allocated_allocated,
vat_free_items: vat_free_items,
vat_items: vat_items
};
// Create allocation dialog // Create allocation dialog
create_allocation_dialog(frm, payment_row_name, data, vat_free_items, vat_items); create_allocation_dialog(frm, payment_row_idx, data, vat_free_items, vat_items);
}
});
} }
function create_allocation_dialog(frm, payment_row_name, data, vat_free_items, vat_items) { function create_allocation_dialog(frm, payment_row_idx, data, vat_free_items, vat_items) {
let allocation_data = {}; let allocation_data = {};
let dialog = new frappe.ui.Dialog({ let dialog = new frappe.ui.Dialog({
@ -425,11 +612,11 @@ function create_allocation_dialog(frm, payment_row_name, data, vat_free_items, v
], ],
primary_action_label: __('Save Allocation'), primary_action_label: __('Save Allocation'),
primary_action: function() { primary_action: function() {
save_allocation_data(frm, payment_row_name, allocation_data, dialog); save_allocation_data(frm, payment_row_idx, allocation_data, dialog);
}, },
secondary_action_label: __('Auto Allocate'), secondary_action_label: __('Auto Allocate'),
secondary_action: function() { secondary_action: function() {
auto_allocate(frm, payment_row_name, data, vat_free_items, vat_items, allocation_data, dialog); auto_allocate(frm, payment_row_idx, data, vat_free_items, vat_items, allocation_data, dialog);
} }
}); });
@ -605,7 +792,7 @@ function setup_allocation_inputs(dialog, allocation_data, max_vat_free, max_vat_
}); });
} }
function auto_allocate(frm, payment_row_name, data, vat_free_items, vat_items, allocation_data, dialog) { function auto_allocate(frm, payment_row_idx, data, vat_free_items, vat_items, allocation_data, dialog) {
// Call server method to calculate auto allocation // Call server method to calculate auto allocation
frappe.call({ frappe.call({
method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.calculate_auto_allocation', method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.calculate_auto_allocation',
@ -646,7 +833,8 @@ function auto_allocate(frm, payment_row_name, data, vat_free_items, vat_items, a
}); });
} }
function save_allocation_data(frm, payment_row_name, allocation_data, dialog) { // Сохранение allocation - работает с несохраненным документом
function save_allocation_data(frm, payment_row_idx, allocation_data, dialog) {
// Prepare allocations array // Prepare allocations array
let allocations = []; let allocations = [];
@ -659,46 +847,153 @@ function save_allocation_data(frm, payment_row_name, allocation_data, dialog) {
}); });
} }
// Save to server with loading indicator // Найдем payment row по idx
frappe.dom.freeze(__('Saving allocation...')); let payment_row = null;
for (let row of frm.doc.payment_entry_table || []) {
if (row.idx === payment_row_idx) {
payment_row = row;
break;
}
}
frappe.call({ if (!payment_row) {
method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.save_allocation', frappe.msgprint(__('Payment entry row not found'));
args: { return;
vat_allocation_name: frm.doc.name, }
payment_entry_row_name: payment_row_name,
allocations: JSON.stringify(allocations)
},
callback: function(r) {
frappe.dom.unfreeze();
if (r.message && r.message.success) { // Удаляем старые links для этого payment row
frappe.show_alert({ frm.doc.allocation_links = (frm.doc.allocation_links || []).filter(function(link) {
message: r.message.message + '<br>Total: ' + format_currency(r.message.total_allocated) + return link.payment_entry_row !== payment_row.name;
'<br>VAT Free: ' + format_currency(r.message.vat_free_spent) + });
'<br>VAT 18%: ' + format_currency(r.message.vat_allocated_spent),
indicator: 'green' // Добавляем новые links
}, 7); let total_vat_free = 0;
let total_vat_allocated = 0;
allocations.forEach(function(alloc) {
if (alloc.allocated > 0) {
let link = frm.add_child('allocation_links');
link.payment_entry_row = payment_row.name;
link.sales_invoice_row = alloc.name;
link.allocated_amount = alloc.allocated;
link.allocation_type = alloc.type;
if (alloc.type === 'vat_free') {
total_vat_free += alloc.allocated;
} else {
total_vat_allocated += alloc.allocated;
}
}
});
// Пересчитываем totals вручную (так как validate еще не вызван)
recalculate_totals_manually(frm);
frm.refresh_field('allocation_links');
frm.refresh_field('payment_entry_table');
frm.refresh_field('sales_invoice_table');
// Помечаем документ как измененный
frm.dirty();
dialog.hide(); dialog.hide();
frm.reload_doc(); }
} else if (r.message && !r.message.success) {
frappe.msgprint({ // Функция для пересчета totals без сохранения в БД
title: __('Error'), function recalculate_totals_manually(frm) {
message: r.message.message, // Reset all allocated amounts
indicator: 'red' for (let pe_row of frm.doc.payment_entry_table || []) {
}); pe_row.vat_free_allocated = 0;
pe_row.vat_allocated_allocated = 0;
pe_row.allocated_amount = 0;
} }
},
error: function(r) { for (let si_row of frm.doc.sales_invoice_table || []) {
frappe.dom.unfreeze(); si_row.allocated_amount = 0;
frappe.msgprint({
title: __('Error'),
message: __('Failed to save allocation. Please try again.'),
indicator: 'red'
});
} }
});
// Calculate from links
for (let link of frm.doc.allocation_links || []) {
let allocated = link.allocated_amount || 0;
if (allocated <= 0) continue;
// Update payment entry row
for (let pe_row of frm.doc.payment_entry_table || []) {
if (pe_row.name === link.payment_entry_row) {
pe_row.allocated_amount = (pe_row.allocated_amount || 0) + allocated;
if (link.allocation_type === 'vat_free') {
pe_row.vat_free_allocated = (pe_row.vat_free_allocated || 0) + allocated;
} else {
pe_row.vat_allocated_allocated = (pe_row.vat_allocated_allocated || 0) + allocated;
}
break;
}
}
// Update sales invoice row
for (let si_row of frm.doc.sales_invoice_table || []) {
if (si_row.name === link.sales_invoice_row) {
si_row.allocated_amount = (si_row.allocated_amount || 0) + allocated;
break;
}
}
}
// Calculate unallocated amounts
for (let pe_row of frm.doc.payment_entry_table || []) {
let total = pe_row.payment_entry_amount || 0;
let allocated = pe_row.allocated_amount || 0;
pe_row.unallocated_amount = Math.max(0, total - allocated);
}
for (let si_row of frm.doc.sales_invoice_table || []) {
let total = si_row.amount || 0;
let allocated = si_row.allocated_amount || 0;
si_row.unallocated_amount = Math.max(0, total - allocated);
}
// Calculate tax template totals
calculate_tax_template_totals(frm);
}
// Новая функция для расчета итогов по tax templates
function calculate_tax_template_totals(frm) {
// Reset totals
frm.doc.total_edv_daxil_18 = 0;
frm.doc.total_edv_azadolma = 0;
frm.doc.total_edv_0 = 0;
frm.doc.total_edv_18 = 0;
frm.doc.total_allocated_sum = 0;
// Calculate totals from sales invoice table
for (let si_row of frm.doc.sales_invoice_table || []) {
let allocated = si_row.allocated_amount || 0;
if (allocated <= 0) continue;
let template = (si_row.item_tax_template || '').trim();
// Match by template name
if (template === 'ƏDV daxil 18%') {
frm.doc.total_edv_daxil_18 += allocated;
} else if (template === 'ƏDV-dən azadolma') {
frm.doc.total_edv_azadolma += allocated;
} else if (template === 'ƏDV 0%') {
frm.doc.total_edv_0 += allocated;
} else if (template === 'ƏDV 18%') {
frm.doc.total_edv_18 += allocated;
}
// Add to total sum
frm.doc.total_allocated_sum += allocated;
}
// Refresh the total fields
frm.refresh_field('total_edv_daxil_18');
frm.refresh_field('total_edv_azadolma');
frm.refresh_field('total_edv_0');
frm.refresh_field('total_edv_18');
frm.refresh_field('total_allocated_sum');
} }
function format_currency(value) { function format_currency(value) {

View File

@ -1,6 +1,6 @@
{ {
"actions": [], "actions": [],
"allow_rename": 1, "autoname": "format:VAT-{year}-{month}",
"creation": "2025-09-29 19:03:22.767217", "creation": "2025-09-29 19:03:22.767217",
"doctype": "DocType", "doctype": "DocType",
"engine": "InnoDB", "engine": "InnoDB",
@ -14,7 +14,18 @@
"sales_invoice_section", "sales_invoice_section",
"sales_invoice_table", "sales_invoice_table",
"allocation_links_section", "allocation_links_section",
"allocation_links" "allocation_links",
"total_edv_18",
"total_section_break",
"total_allocated_sum",
"totals_section",
"total_edv_daxil_18",
"column_break_totals_1",
"total_edv_azadolma",
"column_break_totals_2",
"total_edv_0",
"column_break_totals_3",
"amended_from"
], ],
"fields": [ "fields": [
{ {
@ -65,17 +76,86 @@
{ {
"fieldname": "allocation_links", "fieldname": "allocation_links",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 1,
"label": "Allocation Links", "label": "Allocation Links",
"options": "VAT Allocation Link" "options": "VAT Allocation Link",
"read_only": 1
},
{
"fieldname": "totals_section",
"fieldtype": "Section Break",
"label": "Sales Invoice Totals by Tax Template"
},
{
"fieldname": "total_edv_daxil_18",
"fieldtype": "Currency",
"label": "\u018fDV daxil 18%",
"precision": "2",
"read_only": 1
},
{
"fieldname": "column_break_totals_1",
"fieldtype": "Column Break"
},
{
"fieldname": "total_edv_azadolma",
"fieldtype": "Currency",
"label": "\u018fDV-d\u0259n azadolma",
"precision": "2",
"read_only": 1
},
{
"fieldname": "column_break_totals_2",
"fieldtype": "Column Break"
},
{
"fieldname": "total_edv_0",
"fieldtype": "Currency",
"label": "\u018fDV 0%",
"precision": "2",
"read_only": 1
},
{
"fieldname": "column_break_totals_3",
"fieldtype": "Column Break"
},
{
"fieldname": "total_edv_18",
"fieldtype": "Currency",
"label": "\u018fDV 18%",
"precision": "2",
"read_only": 1
},
{
"fieldname": "total_section_break",
"fieldtype": "Section Break"
},
{
"bold": 1,
"fieldname": "total_allocated_sum",
"fieldtype": "Currency",
"label": "Total Allocated Amount",
"precision": "2",
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "VAT allocation",
"print_hide": 1,
"read_only": 1,
"search_index": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [], "links": [],
"modified": "2025-10-01 21:26:07.923819", "modified": "2025-10-06 16:52:07.347647",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Taxes Az", "module": "Taxes Az",
"name": "VAT allocation", "name": "VAT allocation",
"naming_rule": "Expression",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -12,7 +12,7 @@ class VATallocation(Document):
# Check if document with same year and month already exists # Check if document with same year and month already exists
self.validate_unique_year_month() self.validate_unique_year_month()
# Recalculate all totals from links # Recalculate all totals from links
self.recalculate_totals_from_links() #self.recalculate_totals_from_links()
# Validate all allocations # Validate all allocations
self.validate_allocations() self.validate_allocations()
@ -702,3 +702,89 @@ def calculate_auto_allocation(vat_free_available, vat_allocated_available, vat_f
remaining -= allocated remaining -= allocated
return allocations return allocations
def recalculate_totals_from_links(self):
"""
Recalculate all allocated/unallocated amounts from allocation links
"""
# Reset all allocated amounts
for pe_row in self.payment_entry_table:
pe_row.vat_free_allocated = 0
pe_row.vat_allocated_allocated = 0
pe_row.allocated_amount = 0
for si_row in self.sales_invoice_table:
si_row.allocated_amount = 0
# Calculate from links
for link in self.allocation_links:
allocated = Decimal(str(link.allocated_amount or 0))
if allocated <= 0:
continue
# Update payment entry row
for pe_row in self.payment_entry_table:
if pe_row.name == link.payment_entry_row:
current_allocated = Decimal(str(pe_row.allocated_amount or 0))
pe_row.allocated_amount = float(current_allocated + allocated)
if link.allocation_type == 'vat_free':
current_vat_free = Decimal(str(pe_row.vat_free_allocated or 0))
pe_row.vat_free_allocated = float(current_vat_free + allocated)
else:
current_vat_allocated = Decimal(str(pe_row.vat_allocated_allocated or 0))
pe_row.vat_allocated_allocated = float(current_vat_allocated + allocated)
break
# Update sales invoice row
for si_row in self.sales_invoice_table:
if si_row.name == link.sales_invoice_row:
current_allocated = Decimal(str(si_row.allocated_amount or 0))
si_row.allocated_amount = float(current_allocated + allocated)
break
# Calculate unallocated amounts
for pe_row in self.payment_entry_table:
total = Decimal(str(pe_row.payment_entry_amount or 0))
allocated = Decimal(str(pe_row.allocated_amount or 0))
pe_row.unallocated_amount = float(max(Decimal('0'), total - allocated))
for si_row in self.sales_invoice_table:
total = Decimal(str(si_row.amount or 0))
allocated = Decimal(str(si_row.allocated_amount or 0))
si_row.unallocated_amount = float(max(Decimal('0'), total - allocated))
# Calculate totals by tax template
self.calculate_tax_template_totals()
def calculate_tax_template_totals(self):
"""
Calculate totals by tax template from sales invoice table
"""
# Reset totals
self.total_edv_daxil_18 = 0
self.total_edv_azadolma = 0
self.total_edv_0 = 0
self.total_edv_18 = 0
self.total_allocated_sum = 0
# Calculate totals
for si_row in self.sales_invoice_table:
allocated = Decimal(str(si_row.allocated_amount or 0))
if allocated <= 0:
continue
template = (si_row.item_tax_template or '').strip()
# Match by template name
if template == 'ƏDV daxil 18%':
self.total_edv_daxil_18 = float(Decimal(str(self.total_edv_daxil_18 or 0)) + allocated)
elif template == 'ƏDV-dən azadolma':
self.total_edv_azadolma = float(Decimal(str(self.total_edv_azadolma or 0)) + allocated)
elif template == 'ƏDV 0%':
self.total_edv_0 = float(Decimal(str(self.total_edv_0 or 0)) + allocated)
elif template == 'ƏDV 18%':
self.total_edv_18 = float(Decimal(str(self.total_edv_18 or 0)) + allocated)
# Add to total sum
self.total_allocated_sum = float(Decimal(str(self.total_allocated_sum or 0)) + allocated)

View File

@ -1,7 +1,7 @@
{ {
"actions": [], "actions": [],
"allow_rename": 1, "allow_rename": 1,
"creation": "2025-10-01 20:00:00.000000", "creation": "2025-10-01 20:00:00",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
@ -31,20 +31,22 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Allocated Amount", "label": "Allocated Amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "allocation_type", "fieldname": "allocation_type",
"fieldtype": "Select", "fieldtype": "Select",
"in_list_view": 1, "in_list_view": 1,
"label": "Type", "label": "Type",
"options": "vat_free\nvat_allocated" "options": "vat_free\nvat_allocated",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-10-01 20:00:00.000000", "modified": "2025-10-03 18:42:25.411199",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Taxes Az", "module": "Taxes Az",
"name": "VAT Allocation Link", "name": "VAT Allocation Link",

View File

@ -39,14 +39,16 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Payment Entry Amount", "label": "Payment Entry Amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "vat_amount", "fieldname": "vat_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "VAT Amount", "label": "VAT Amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "vat_journal_entry", "fieldname": "vat_journal_entry",
@ -91,14 +93,16 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Allocated amount", "label": "Allocated amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "unallocated_amount", "fieldname": "unallocated_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Unallocated amount", "label": "Unallocated amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "allocate_button", "fieldname": "allocate_button",
@ -110,7 +114,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-10-01 19:51:09.389265", "modified": "2025-10-03 17:09:35.593479",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Taxes Az", "module": "Taxes Az",
"name": "VAT allocation payment entry", "name": "VAT allocation payment entry",

View File

@ -20,11 +20,13 @@
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Customer", "label": "Customer",
"options": "Customer" "options": "Customer",
"read_only": 1
}, },
{ {
"fieldname": "payment_entry", "fieldname": "payment_entry",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Payment Entry", "label": "Payment Entry",
"options": "Payment Entry" "options": "Payment Entry"
@ -34,14 +36,16 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Allocated amount", "label": "Allocated amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "unallocated_amount", "fieldname": "unallocated_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Unallocated amount", "label": "Unallocated amount",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "item_tax_template", "fieldname": "item_tax_template",
@ -49,7 +53,8 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Item tax template", "label": "Item tax template",
"options": "Item Tax Template", "options": "Item Tax Template",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "tax_article", "fieldname": "tax_article",
@ -57,20 +62,22 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Tax Article", "label": "Tax Article",
"options": "Tax Article", "options": "Tax Article",
"precision": "2" "precision": "2",
"read_only": 1
}, },
{ {
"fieldname": "amount", "fieldname": "amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Amount", "label": "Amount",
"precision": "2" "precision": "2",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-09-29 20:36:21.708944", "modified": "2025-10-03 17:19:43.856209",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Taxes Az", "module": "Taxes Az",
"name": "VAT allocation sales invoice", "name": "VAT allocation sales invoice",

View File

@ -64,6 +64,23 @@ frappe.query_reports["Customer Outstanding Balance"] = {
value = "<span style='color: #d62728; font-weight: bold;'>" + value + "</span>"; value = "<span style='color: #d62728; font-weight: bold;'>" + value + "</span>";
} }
// Highlight VAT allocation columns
if (column.fieldname == "edv_daxil_18" && data && data.edv_daxil_18 > 0) {
value = "<span style='color: #17a2b8; font-weight: bold;'>" + value + "</span>";
}
if (column.fieldname == "edv_azadolma" && data && data.edv_azadolma > 0) {
value = "<span style='color: #28a745; font-weight: bold;'>" + value + "</span>";
}
if (column.fieldname == "edv_0" && data && data.edv_0 > 0) {
value = "<span style='color: #ffc107; font-weight: bold;'>" + value + "</span>";
}
if (column.fieldname == "edv_18" && data && data.edv_18 > 0) {
value = "<span style='color: #dc3545; font-weight: bold;'>" + value + "</span>";
}
return value; return value;
} }
}; };

View File

@ -3,6 +3,7 @@
import frappe import frappe
from frappe import _ from frappe import _
from datetime import datetime
def execute(filters=None): def execute(filters=None):
"""Main function for report execution""" """Main function for report execution"""
@ -50,6 +51,30 @@ def get_columns():
"fieldname": "closing_balance", "fieldname": "closing_balance",
"fieldtype": "Currency", "fieldtype": "Currency",
"width": 140 "width": 140
},
{
"label": _("ƏDV daxil 18%"),
"fieldname": "edv_daxil_18",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("ƏDV-dən azadolma"),
"fieldname": "edv_azadolma",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("ƏDV 0%"),
"fieldname": "edv_0",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("ƏDV 18%"),
"fieldname": "edv_18",
"fieldtype": "Currency",
"width": 140
} }
] ]
@ -120,8 +145,11 @@ def get_data(filters):
ABS(closing_balance) >= 0.01 ABS(closing_balance) >= 0.01
""", filters, as_dict=1) """, filters, as_dict=1)
# Get VAT allocation data
vat_data = get_vat_allocation_data(filters)
# Combine all data # Combine all data
result = combine_data(opening_balance_data, period_data, closing_balance_data) result = combine_data(opening_balance_data, period_data, closing_balance_data, vat_data)
# Get customer names and other details # Get customer names and other details
add_customer_details(result) add_customer_details(result)
@ -141,7 +169,64 @@ def get_conditions(filters):
return conditions return conditions
def combine_data(opening_data, period_data, closing_data): def get_vat_allocation_data(filters):
"""Get VAT allocation data for customers in the period"""
if not filters.get("from_date") or not filters.get("to_date"):
return []
# Determine year and month from the date range
from_date = datetime.strptime(str(filters.get("from_date")), "%Y-%m-%d")
to_date = datetime.strptime(str(filters.get("to_date")), "%Y-%m-%d")
# Get month name mapping
month_names = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
]
# Use the to_date for determining the period
year = to_date.year
month = month_names[to_date.month - 1]
# Query VAT allocation sales invoice data from submitted documents
vat_query = """
SELECT
vasi.customer,
vasi.item_tax_template,
SUM(vasi.allocated_amount) as allocated_amount
FROM
`tabVAT allocation sales invoice` vasi
INNER JOIN
`tabVAT allocation` va ON va.name = vasi.parent
WHERE
va.docstatus = 1
AND va.year = %(year)s
AND va.month = %(month)s
"""
# Add customer filter if specified
customer_condition = ""
if filters.get("customer"):
customer_list = "', '".join(filters.get("customer"))
customer_condition = f" AND vasi.customer IN ('{customer_list}')"
if filters.get("customer_group"):
customer_condition += " AND vasi.customer IN (SELECT name FROM `tabCustomer` WHERE customer_group = %(customer_group)s)"
vat_query += customer_condition + """
GROUP BY
vasi.customer, vasi.item_tax_template
"""
vat_data = frappe.db.sql(vat_query, {
"year": year,
"month": month,
"customer_group": filters.get("customer_group")
}, as_dict=1)
return vat_data
def combine_data(opening_data, period_data, closing_data, vat_data):
"""Combine all datasets into final result""" """Combine all datasets into final result"""
result_dict = {} result_dict = {}
@ -153,7 +238,11 @@ def combine_data(opening_data, period_data, closing_data):
'opening_balance': row['opening_balance'] or 0.0, 'opening_balance': row['opening_balance'] or 0.0,
'payments': 0.0, 'payments': 0.0,
'new_invoices': 0.0, 'new_invoices': 0.0,
'closing_balance': 0.0 'closing_balance': 0.0,
'edv_daxil_18': 0.0,
'edv_azadolma': 0.0,
'edv_0': 0.0,
'edv_18': 0.0
} }
# Add period movements # Add period movements
@ -165,7 +254,11 @@ def combine_data(opening_data, period_data, closing_data):
'opening_balance': 0.0, 'opening_balance': 0.0,
'payments': 0.0, 'payments': 0.0,
'new_invoices': 0.0, 'new_invoices': 0.0,
'closing_balance': 0.0 'closing_balance': 0.0,
'edv_daxil_18': 0.0,
'edv_azadolma': 0.0,
'edv_0': 0.0,
'edv_18': 0.0
} }
result_dict[customer]['payments'] = row['payments'] or 0.0 result_dict[customer]['payments'] = row['payments'] or 0.0
@ -180,11 +273,44 @@ def combine_data(opening_data, period_data, closing_data):
'opening_balance': 0.0, 'opening_balance': 0.0,
'payments': 0.0, 'payments': 0.0,
'new_invoices': 0.0, 'new_invoices': 0.0,
'closing_balance': 0.0 'closing_balance': 0.0,
'edv_daxil_18': 0.0,
'edv_azadolma': 0.0,
'edv_0': 0.0,
'edv_18': 0.0
} }
result_dict[customer]['closing_balance'] = row['closing_balance'] or 0.0 result_dict[customer]['closing_balance'] = row['closing_balance'] or 0.0
# Add VAT allocation data
for row in vat_data:
customer = row['customer']
if customer not in result_dict:
result_dict[customer] = {
'customer': customer,
'opening_balance': 0.0,
'payments': 0.0,
'new_invoices': 0.0,
'closing_balance': 0.0,
'edv_daxil_18': 0.0,
'edv_azadolma': 0.0,
'edv_0': 0.0,
'edv_18': 0.0
}
# Map item tax template to field names
template = row['item_tax_template']
allocated = row['allocated_amount'] or 0.0
if 'daxil 18' in template or 'daxil18' in template.lower().replace(' ', ''):
result_dict[customer]['edv_daxil_18'] += allocated
elif 'azadolma' in template.lower():
result_dict[customer]['edv_azadolma'] += allocated
elif '0%' in template or 'edv 0' in template.lower():
result_dict[customer]['edv_0'] += allocated
elif '18%' in template or 'edv 18' in template.lower():
result_dict[customer]['edv_18'] += allocated
# Convert to list and filter out zero balances # Convert to list and filter out zero balances
result = [] result = []
for customer, data in result_dict.items(): for customer, data in result_dict.items():
@ -192,7 +318,11 @@ def combine_data(opening_data, period_data, closing_data):
if (abs(data['opening_balance']) >= 0.01 or if (abs(data['opening_balance']) >= 0.01 or
abs(data['closing_balance']) >= 0.01 or abs(data['closing_balance']) >= 0.01 or
abs(data['new_invoices']) >= 0.01 or abs(data['new_invoices']) >= 0.01 or
abs(data['payments']) >= 0.01): abs(data['payments']) >= 0.01 or
abs(data['edv_daxil_18']) >= 0.01 or
abs(data['edv_azadolma']) >= 0.01 or
abs(data['edv_0']) >= 0.01 or
abs(data['edv_18']) >= 0.01):
result.append(data) result.append(data)
return sorted(result, key=lambda x: x['customer']) return sorted(result, key=lambda x: x['customer'])