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
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) {
let row = locals[cdt][cdn];
@ -335,46 +475,93 @@ frappe.ui.form.on('VAT allocation payment entry', {
frappe.msgprint(__('Cannot allocate to disabled customer'));
return;
}
// Open allocation dialog
show_allocation_dialog(frm, row.name, row.customer);
// Open allocation dialog - используем idx для несохраненных строк
show_allocation_dialog(frm, row.idx, row.customer);
}
});
}
});
// Show allocation dialog
function show_allocation_dialog(frm, payment_row_name, customer) {
// Get allocation data from server
frappe.call({
method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.get_allocation_data',
args: {
vat_allocation_name: frm.doc.name,
payment_entry_row_name: payment_row_name,
customer: customer
},
callback: function(r) {
if (!r.message) {
frappe.msgprint(__('No data available for allocation'));
return;
}
let data = r.message;
let vat_free_items = data.vat_free_items;
let vat_items = data.vat_items;
// Check if there are items to allocate to
if (vat_free_items.length === 0 && vat_items.length === 0) {
frappe.msgprint(__('No sales invoice items available for this customer'));
return;
}
// Create allocation dialog
create_allocation_dialog(frm, payment_row_name, data, vat_free_items, vat_items);
// Show allocation dialog - работает с несохраненными данными
function show_allocation_dialog(frm, payment_row_idx, customer) {
// Получаем данные напрямую из формы (не из БД!)
let payment_row = null;
for (let row of frm.doc.payment_entry_table || []) {
if (row.idx === payment_row_idx) {
payment_row = row;
break;
}
});
}
if (!payment_row) {
frappe.msgprint(__('Payment entry row not found'));
return;
}
// Получаем sales invoice items для этого клиента из формы
let vat_free_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
if (vat_free_items.length === 0 && vat_items.length === 0) {
frappe.msgprint(__('No sales invoice items available for this customer'));
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(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 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: 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: 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
frappe.call({
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
let allocations = [];
@ -659,46 +847,153 @@ function save_allocation_data(frm, payment_row_name, allocation_data, dialog) {
});
}
// Save to server with loading indicator
frappe.dom.freeze(__('Saving allocation...'));
// Найдем payment row по idx
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({
method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.save_allocation',
args: {
vat_allocation_name: frm.doc.name,
payment_entry_row_name: payment_row_name,
allocations: JSON.stringify(allocations)
},
callback: function(r) {
frappe.dom.unfreeze();
if (!payment_row) {
frappe.msgprint(__('Payment entry row not found'));
return;
}
// Удаляем старые links для этого payment row
frm.doc.allocation_links = (frm.doc.allocation_links || []).filter(function(link) {
return link.payment_entry_row !== payment_row.name;
});
// Добавляем новые links
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 (r.message && r.message.success) {
frappe.show_alert({
message: r.message.message + '<br>Total: ' + format_currency(r.message.total_allocated) +
'<br>VAT Free: ' + format_currency(r.message.vat_free_spent) +
'<br>VAT 18%: ' + format_currency(r.message.vat_allocated_spent),
indicator: 'green'
}, 7);
dialog.hide();
frm.reload_doc();
} else if (r.message && !r.message.success) {
frappe.msgprint({
title: __('Error'),
message: r.message.message,
indicator: 'red'
});
if (alloc.type === 'vat_free') {
total_vat_free += alloc.allocated;
} else {
total_vat_allocated += alloc.allocated;
}
},
error: function(r) {
frappe.dom.unfreeze();
frappe.msgprint({
title: __('Error'),
message: __('Failed to save allocation. Please try again.'),
indicator: 'red'
});
}
});
// Пересчитываем 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();
}
// Функция для пересчета totals без сохранения в БД
function recalculate_totals_manually(frm) {
// Reset all allocated amounts
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;
}
for (let si_row of frm.doc.sales_invoice_table || []) {
si_row.allocated_amount = 0;
}
// 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) {

View File

@ -1,6 +1,6 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "format:VAT-{year}-{month}",
"creation": "2025-09-29 19:03:22.767217",
"doctype": "DocType",
"engine": "InnoDB",
@ -14,7 +14,18 @@
"sales_invoice_section",
"sales_invoice_table",
"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": [
{
@ -65,17 +76,86 @@
{
"fieldname": "allocation_links",
"fieldtype": "Table",
"hidden": 1,
"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,
"is_submittable": 1,
"links": [],
"modified": "2025-10-01 21:26:07.923819",
"modified": "2025-10-06 16:52:07.347647",
"modified_by": "Administrator",
"module": "Taxes Az",
"name": "VAT allocation",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{

View File

@ -12,7 +12,7 @@ class VATallocation(Document):
# Check if document with same year and month already exists
self.validate_unique_year_month()
# Recalculate all totals from links
self.recalculate_totals_from_links()
#self.recalculate_totals_from_links()
# Validate all allocations
self.validate_allocations()
@ -701,4 +701,90 @@ def calculate_auto_allocation(vat_free_available, vat_allocated_available, vat_f
})
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": [],
"allow_rename": 1,
"creation": "2025-10-01 20:00:00.000000",
"creation": "2025-10-01 20:00:00",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
@ -31,20 +31,22 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Allocated Amount",
"precision": "2"
"precision": "2",
"read_only": 1
},
{
"fieldname": "allocation_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Type",
"options": "vat_free\nvat_allocated"
"options": "vat_free\nvat_allocated",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-10-01 20:00:00.000000",
"modified": "2025-10-03 18:42:25.411199",
"modified_by": "Administrator",
"module": "Taxes Az",
"name": "VAT Allocation Link",

View File

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

View File

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

View File

@ -3,6 +3,7 @@
import frappe
from frappe import _
from datetime import datetime
def execute(filters=None):
"""Main function for report execution"""
@ -50,6 +51,30 @@ def get_columns():
"fieldname": "closing_balance",
"fieldtype": "Currency",
"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
""", filters, as_dict=1)
# Get VAT allocation data
vat_data = get_vat_allocation_data(filters)
# 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
add_customer_details(result)
@ -141,7 +169,64 @@ def get_conditions(filters):
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"""
result_dict = {}
@ -153,7 +238,11 @@ def combine_data(opening_data, period_data, closing_data):
'opening_balance': row['opening_balance'] or 0.0,
'payments': 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
@ -165,7 +254,11 @@ def combine_data(opening_data, period_data, closing_data):
'opening_balance': 0.0,
'payments': 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
@ -180,11 +273,44 @@ def combine_data(opening_data, period_data, closing_data):
'opening_balance': 0.0,
'payments': 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
# 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
result = []
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
abs(data['closing_balance']) >= 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)
return sorted(result, key=lambda x: x['customer'])