';
// VAT Free items section
@@ -671,7 +906,7 @@ function build_allocation_html(vat_free_items, vat_items, allocation_data) {
html += '
';
html += '';
});
@@ -699,7 +934,7 @@ function build_allocation_html(vat_free_items, vat_items, allocation_data) {
html += '
';
html += '';
});
@@ -709,13 +944,13 @@ function build_allocation_html(vat_free_items, vat_items, allocation_data) {
// Summary section
let formatted_zero = $(frappe.format(0, {fieldtype: 'Currency'})).text();
- html += '
';
+ html += '
';
html += '
';
- html += '
Total VAT Free to Allocate: ' + formatted_zero + '
';
- html += '
Total VAT to Allocate: ' + formatted_zero + '
';
+ html += '
Total VAT Free to Allocate: ' + formatted_zero + '
';
+ html += '
Total VAT to Allocate: ' + formatted_zero + '
';
html += '
';
html += '
';
- html += '
Total: ' + formatted_zero + '
';
+ html += '
Total: ' + formatted_zero + '
';
html += '
';
html += '
';
@@ -724,15 +959,16 @@ function build_allocation_html(vat_free_items, vat_items, allocation_data) {
return html;
}
-function setup_allocation_inputs(dialog, allocation_data, max_vat_free, max_vat_allocated) {
- let $wrapper = dialog.fields_dict.allocation_html.$wrapper;
+function setup_allocation_inputs(dialog, allocation_data, max_vat_free, max_vat_allocated, tab_type) {
+ // Ищем wrapper по ID вкладки в общем wrapper диалога
+ let $wrapper = dialog.$wrapper.find('#tab-' + tab_type);
// Initial calculation
setTimeout(function() {
- $wrapper.find('.allocation-input').first().trigger('input');
+ $wrapper.find('.allocation-input-' + tab_type).first().trigger('input');
}, 100);
- $wrapper.find('.allocation-input').on('input', function() {
+ $wrapper.find('.allocation-input-' + tab_type).on('input', function() {
let $input = $(this);
let name = $input.data('name');
let type = $input.data('type');
@@ -755,11 +991,11 @@ function setup_allocation_inputs(dialog, allocation_data, max_vat_free, max_vat_
type: type
};
- // Calculate totals
+ // Calculate totals for this tab only
let total_vat_free = 0;
let total_vat_allocated = 0;
- $wrapper.find('.allocation-input').each(function() {
+ $wrapper.find('.allocation-input-' + tab_type).each(function() {
let item_type = $(this).data('type');
let item_value = parseFloat($(this).val()) || 0;
@@ -784,31 +1020,30 @@ function setup_allocation_inputs(dialog, allocation_data, max_vat_free, max_vat_
total_vat_free -= value;
}
-
- // Update summary
- $wrapper.find('#total_vat_free').text($(frappe.format(total_vat_free, {fieldtype: 'Currency'})).text());
- $wrapper.find('#total_vat_allocated').text($(frappe.format(total_vat_allocated, {fieldtype: 'Currency'})).text());
- $wrapper.find('#total_allocated').text($(frappe.format(total_vat_free + total_vat_allocated, {fieldtype: 'Currency'})).text());
+ // Update summary for this tab
+ $wrapper.find('#total_vat_free_' + tab_type).text($(frappe.format(total_vat_free, {fieldtype: 'Currency'})).text());
+ $wrapper.find('#total_vat_allocated_' + tab_type).text($(frappe.format(total_vat_allocated, {fieldtype: 'Currency'})).text());
+ $wrapper.find('#total_allocated_' + tab_type).text($(frappe.format(total_vat_free + total_vat_allocated, {fieldtype: 'Currency'})).text());
});
}
-function auto_allocate(frm, payment_row_idx, data, vat_free_items, vat_items, allocation_data, dialog) {
- // Call server method to calculate auto allocation
+function auto_allocate(frm, payment_row_idx, data, vat_free_items, vat_items, allocation_data, dialog, tab_type, available_amounts, other_allocation_data) {
+ // Use calculated available amounts
frappe.call({
method: 'taxes_az.taxes_az.doctype.vat_allocation.vat_allocation.calculate_auto_allocation',
args: {
- vat_free_available: data.vat_free_available,
- vat_allocated_available: data.vat_allocated_available,
+ vat_free_available: available_amounts.vat_free_available,
+ vat_allocated_available: available_amounts.vat_allocated_available,
vat_free_items: JSON.stringify(vat_free_items),
vat_items: JSON.stringify(vat_items)
},
callback: function(r) {
if (r.message) {
let allocations = r.message;
- let $wrapper = dialog.fields_dict.allocation_html.$wrapper;
+ let $wrapper = dialog.$wrapper.find('#tab-' + tab_type);
- // Reset all inputs
- $wrapper.find('.allocation-input').val(0);
+ // Reset all inputs for this tab
+ $wrapper.find('.allocation-input-' + tab_type).val(0);
// Update allocation data
for (let key in allocation_data) {
@@ -818,11 +1053,20 @@ function auto_allocate(frm, payment_row_idx, data, vat_free_items, vat_items, al
// Apply auto allocations
allocations.forEach(function(alloc) {
allocation_data[alloc.name].value = alloc.allocated;
- $wrapper.find('.allocation-input[data-name="' + alloc.name + '"]').val(alloc.allocated.toFixed(2));
+ $wrapper.find('.allocation-input-' + tab_type + '[data-name="' + alloc.name + '"]').val(alloc.allocated.toFixed(2));
});
// Trigger input event to update summary
- $wrapper.find('.allocation-input').first().trigger('input');
+ $wrapper.find('.allocation-input-' + tab_type).first().trigger('input');
+
+ // Update available amounts in dialog fields
+ let new_available = calculate_available_amounts(data,
+ tab_type === 'current' ? allocation_data : other_allocation_data,
+ tab_type === 'previous' ? allocation_data : other_allocation_data
+ );
+
+ dialog.set_value('vat_free_available', new_available.vat_free_available);
+ dialog.set_value('vat_allocated_available', new_available.vat_allocated_available);
frappe.show_alert({
message: __('Auto allocation completed'),
@@ -834,12 +1078,23 @@ function auto_allocate(frm, payment_row_idx, data, vat_free_items, vat_items, al
}
// Сохранение allocation - работает с несохраненным документом
-function save_allocation_data(frm, payment_row_idx, allocation_data, dialog) {
- // Prepare allocations array
+function save_allocation_data(frm, payment_row_idx, allocation_data_current, allocation_data_previous, dialog) {
+ // Prepare allocations array - combine both tabs
let allocations = [];
- for (let name in allocation_data) {
- let data = allocation_data[name];
+ // Add allocations from current month tab
+ for (let name in allocation_data_current) {
+ let data = allocation_data_current[name];
+ allocations.push({
+ name: name,
+ allocated: data.value,
+ type: data.type
+ });
+ }
+
+ // Add allocations from previous months tab
+ for (let name in allocation_data_previous) {
+ let data = allocation_data_previous[name];
allocations.push({
name: name,
allocated: data.value,
@@ -892,6 +1147,7 @@ function save_allocation_data(frm, payment_row_idx, allocation_data, dialog) {
frm.refresh_field('allocation_links');
frm.refresh_field('payment_entry_table');
frm.refresh_field('sales_invoice_table');
+ frm.refresh_field('sales_invoice_previous_table');
// Помечаем документ как измененный
frm.dirty();
@@ -899,7 +1155,7 @@ function save_allocation_data(frm, payment_row_idx, allocation_data, dialog) {
dialog.hide();
}
-// Функция для пересчета totals без сохранения в БД
+// Функция для пересчета totals без сохранения в БД - UPDATED
function recalculate_totals_manually(frm) {
// Reset all allocated amounts
for (let pe_row of frm.doc.payment_entry_table || []) {
@@ -912,6 +1168,10 @@ function recalculate_totals_manually(frm) {
si_row.allocated_amount = 0;
}
+ for (let si_row of frm.doc.sales_invoice_previous_table || []) {
+ si_row.allocated_amount = 0;
+ }
+
// Calculate from links
for (let link of frm.doc.allocation_links || []) {
let allocated = link.allocated_amount || 0;
@@ -931,13 +1191,24 @@ function recalculate_totals_manually(frm) {
}
}
- // Update sales invoice row
+ // Update sales invoice row - check both tables
+ let found = false;
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;
+ found = true;
break;
}
}
+
+ if (!found) {
+ for (let si_row of frm.doc.sales_invoice_previous_table || []) {
+ if (si_row.name === link.sales_invoice_row) {
+ si_row.allocated_amount = (si_row.allocated_amount || 0) + allocated;
+ break;
+ }
+ }
+ }
}
// Calculate unallocated amounts
@@ -953,6 +1224,12 @@ function recalculate_totals_manually(frm) {
si_row.unallocated_amount = Math.max(0, total - allocated);
}
+ for (let si_row of frm.doc.sales_invoice_previous_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);
}
@@ -966,7 +1243,7 @@ function calculate_tax_template_totals(frm) {
frm.doc.total_edv_18 = 0;
frm.doc.total_allocated_sum = 0;
- // Calculate totals from sales invoice table
+ // Calculate totals from sales invoice table (only current month)
for (let si_row of frm.doc.sales_invoice_table || []) {
let allocated = si_row.allocated_amount || 0;
if (allocated <= 0) continue;
diff --git a/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.json b/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.json
index f7f35cc..b65c43c 100644
--- a/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.json
+++ b/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.json
@@ -13,6 +13,8 @@
"payment_entry_table",
"sales_invoice_section",
"sales_invoice_table",
+ "section_break_fhfs",
+ "sales_invoice_previous_table",
"allocation_links_section",
"allocation_links",
"total_edv_18",
@@ -146,12 +148,22 @@
"print_hide": 1,
"read_only": 1,
"search_index": 1
+ },
+ {
+ "fieldname": "section_break_fhfs",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "sales_invoice_previous_table",
+ "fieldtype": "Table",
+ "label": "Sales Invoice Previous Months",
+ "options": "VAT allocation sales invoice"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2025-10-06 16:52:07.347647",
+ "modified": "2025-10-07 14:10:17.385408",
"modified_by": "Administrator",
"module": "Taxes Az",
"name": "VAT allocation",
diff --git a/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.py b/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.py
index 67ad2c3..31dcd00 100644
--- a/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.py
+++ b/taxes_az/taxes_az/doctype/vat_allocation/vat_allocation.py
@@ -46,6 +46,9 @@ class VATallocation(Document):
for si_row in self.sales_invoice_table:
si_row.allocated_amount = 0
+ for si_row in self.sales_invoice_previous_table:
+ si_row.allocated_amount = 0
+
# Calculate from links
for link in self.allocation_links:
allocated = Decimal(str(link.allocated_amount or 0))
@@ -66,12 +69,21 @@ class VATallocation(Document):
pe_row.vat_allocated_allocated = float(current_vat_allocated + allocated)
break
- # Update sales invoice row
+ # Update sales invoice row - check both tables
+ found = False
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)
+ found = True
break
+
+ if not found:
+ for si_row in self.sales_invoice_previous_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:
@@ -83,6 +95,11 @@ class VATallocation(Document):
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))
+
+ for si_row in self.sales_invoice_previous_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))
def validate_allocations(self):
"""
@@ -106,7 +123,7 @@ class VATallocation(Document):
if vat_allocated_spent > vat_allocated_total + ROUNDING_TOLERANCE:
frappe.throw(_(f"VAT Allocated spent exceeds total for payment entry {pe_row.payment_entry}"))
- # Validate sales invoice items
+ # Validate sales invoice items - both tables
for si_row in self.sales_invoice_table:
amount = Decimal(str(si_row.amount or 0))
allocated = Decimal(str(si_row.allocated_amount or 0))
@@ -120,6 +137,20 @@ class VATallocation(Document):
total_check = allocated + unallocated
if abs(total_check - amount) > ROUNDING_TOLERANCE:
frappe.throw(_(f"Allocated + Unallocated != Amount for {si_row.item_tax_template}"))
+
+ for si_row in self.sales_invoice_previous_table:
+ amount = Decimal(str(si_row.amount or 0))
+ allocated = Decimal(str(si_row.allocated_amount or 0))
+ unallocated = Decimal(str(si_row.unallocated_amount or 0))
+
+ # Check non-negative
+ if allocated < 0 or unallocated < -ROUNDING_TOLERANCE:
+ frappe.throw(_(f"Allocated amounts cannot be negative for previous month {si_row.item_tax_template}"))
+
+ # Check sum equals total (with tolerance)
+ total_check = allocated + unallocated
+ if abs(total_check - amount) > ROUNDING_TOLERANCE:
+ frappe.throw(_(f"Allocated + Unallocated != Amount for previous month {si_row.item_tax_template}"))
@frappe.whitelist()
def validate_all_allocations(vat_allocation_name):
@@ -351,6 +382,115 @@ def get_sales_invoice_items(year, month, customers=None, min_amount=None, max_am
return invoice_items
+@frappe.whitelist()
+def get_sales_invoice_items_previous_months(year, month, customers=None, min_amount=None, max_amount=None):
+ """
+ Get Sales Invoice items from previous VAT Allocation documents where unallocated_amount > 0
+ Grouped by customer, item_tax_template, and tax_article
+ """
+ # Convert month name to number
+ months = {
+ "January": 1, "February": 2, "March": 3, "April": 4,
+ "May": 5, "June": 6, "July": 7, "August": 8,
+ "September": 9, "October": 10, "November": 11, "December": 12
+ }
+
+ month_number = months.get(month)
+ if not month_number:
+ frappe.throw(_("Invalid month"))
+
+ # Parse customers filter
+ customer_list = None
+ if customers:
+ if isinstance(customers, str):
+ customers = json.loads(customers)
+ if customers and len(customers) > 0:
+ customer_list = [c.get('customer') for c in customers if c.get('customer')]
+
+ # Find all previous VAT Allocation documents
+ # We need to find documents where (year < current_year) OR (year == current_year AND month < current_month)
+ vat_allocations = frappe.db.sql("""
+ SELECT name, year, month
+ FROM `tabVAT allocation`
+ WHERE docstatus = 1
+ AND (
+ year < %(year)s
+ OR (year = %(year)s AND month < %(month)s)
+ )
+ ORDER BY year DESC, month DESC
+ """, {
+ 'year': year,
+ 'month': month_number
+ }, as_dict=1)
+
+ if not vat_allocations:
+ return []
+
+ # Collect all items with unallocated_amount > 0 from previous VAT Allocations
+ all_items = []
+
+ for vat_alloc in vat_allocations:
+ # Get sales invoice items from this VAT Allocation
+ items = frappe.db.sql("""
+ SELECT
+ customer,
+ item_tax_template,
+ tax_article,
+ unallocated_amount
+ FROM `tabVAT allocation sales invoice`
+ WHERE parent = %(parent)s
+ AND unallocated_amount > 0
+ """, {
+ 'parent': vat_alloc.name
+ }, as_dict=1)
+
+ all_items.extend(items)
+
+ # Group by customer, item_tax_template, tax_article and sum unallocated amounts
+ grouped_items = {}
+
+ for item in all_items:
+ # Apply customer filter
+ if customer_list and item.customer not in customer_list:
+ continue
+
+ key = (item.customer, item.item_tax_template or '', item.tax_article or '')
+
+ if key not in grouped_items:
+ grouped_items[key] = {
+ 'customer': item.customer,
+ 'item_tax_template': item.item_tax_template,
+ 'tax_article': item.tax_article,
+ 'amount': Decimal('0')
+ }
+
+ grouped_items[key]['amount'] += Decimal(str(item.unallocated_amount))
+
+ # Convert to list and apply amount filters
+ result = []
+ for key, item in grouped_items.items():
+ amount = float(item['amount'])
+
+ # Apply min/max amount filters
+ if min_amount and amount < float(min_amount):
+ continue
+ if max_amount and amount > float(max_amount):
+ continue
+
+ result.append({
+ 'customer': item['customer'],
+ 'item_tax_template': item['item_tax_template'],
+ 'tax_article': item['tax_article'],
+ 'amount': amount,
+ 'unallocated_amount': amount, # Initially all is unallocated
+ 'allocated_amount': 0
+ })
+
+ # Sort by customer, item_tax_template, tax_article
+ result.sort(key=lambda x: (x['customer'], x['item_tax_template'] or '', x['tax_article'] or ''))
+
+ return result
+
@frappe.whitelist()
def get_allocation_data(vat_allocation_name, payment_entry_row_name, customer):
"""
@@ -405,6 +545,10 @@ def get_allocation_data(vat_allocation_name, payment_entry_row_name, customer):
# VAT templates (ƏDV 18% and ƏDV daxil 18%)
vat_items = []
+ vat_free_items_previous = []
+ vat_items_previous = []
+
+ # Current month items
for item in doc.sales_invoice_table:
if item.customer == customer:
# Get existing allocation for this item
@@ -429,12 +573,39 @@ def get_allocation_data(vat_allocation_name, payment_entry_row_name, customer):
elif 'ədv 18%' in template or 'ədv daxil 18%' in template:
vat_items.append(item_data)
+ # Previous months items
+ for item in doc.sales_invoice_previous_table:
+ if item.customer == customer:
+ # Get existing allocation for this item
+ existing_alloc = existing_allocations.get(item.name, {'amount': 0, 'type': None})
+
+ item_data = {
+ 'name': item.name,
+ 'item_tax_template': item.item_tax_template or '',
+ 'tax_article': item.tax_article or '',
+ 'amount': item.amount,
+ 'unallocated_amount': item.unallocated_amount,
+ 'allocated_amount': item.allocated_amount or 0,
+ 'current_allocation': existing_alloc['amount'] # Allocation from THIS payment row
+ }
+
+ template = (item.item_tax_template or '').lower()
+
+ # Check if it's VAT free template
+ if 'ədv 0%' in template or 'ədv-dən azadolma' in template:
+ vat_free_items_previous.append(item_data)
+ # Check if it's VAT 18% template
+ elif 'ədv 18%' in template or 'ədv daxil 18%' in template:
+ vat_items_previous.append(item_data)
+
return {
'payment_row': payment_row,
'vat_free_available': vat_free_available,
'vat_allocated_available': vat_allocated_available,
'vat_free_items': vat_free_items,
- 'vat_items': vat_items
+ 'vat_items': vat_items,
+ 'vat_free_items_previous': vat_free_items_previous,
+ 'vat_items_previous': vat_items_previous
}
def round_decimal(value, precision=2):
@@ -513,8 +684,9 @@ def save_allocation(vat_allocation_name, payment_entry_row_name, allocations):
"Total VAT Allocated ({0}) exceeds total amount ({1})"
).format(total_vat_allocated_new, vat_allocated_amount))
- # Validate each SI item
+ # Validate each SI item - check both tables
si_items_map = {item.name: item for item in doc.sales_invoice_table}
+ si_items_map.update({item.name: item for item in doc.sales_invoice_previous_table})
for allocation in allocations:
si_row_name = allocation['name']
@@ -716,6 +888,9 @@ def recalculate_totals_from_links(self):
for si_row in self.sales_invoice_table:
si_row.allocated_amount = 0
+ for si_row in self.sales_invoice_previous_table:
+ si_row.allocated_amount = 0
+
# Calculate from links
for link in self.allocation_links:
allocated = Decimal(str(link.allocated_amount or 0))
@@ -736,12 +911,21 @@ def recalculate_totals_from_links(self):
pe_row.vat_allocated_allocated = float(current_vat_allocated + allocated)
break
- # Update sales invoice row
+ # Update sales invoice row - check both tables
+ found = False
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)
+ found = True
break
+
+ if not found:
+ for si_row in self.sales_invoice_previous_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:
@@ -754,6 +938,11 @@ def recalculate_totals_from_links(self):
allocated = Decimal(str(si_row.allocated_amount or 0))
si_row.unallocated_amount = float(max(Decimal('0'), total - allocated))
+ for si_row in self.sales_invoice_previous_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()
@@ -768,7 +957,7 @@ def calculate_tax_template_totals(self):
self.total_edv_18 = 0
self.total_allocated_sum = 0
- # Calculate totals
+ # Calculate totals - only from current month table
for si_row in self.sales_invoice_table:
allocated = Decimal(str(si_row.allocated_amount or 0))
if allocated <= 0:
@@ -787,4 +976,188 @@ def calculate_tax_template_totals(self):
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)
\ No newline at end of file
+ self.total_allocated_sum = float(Decimal(str(self.total_allocated_sum or 0)) + allocated)
+
+@frappe.whitelist()
+def get_payment_entries_previous_months(year, month, customers=None, min_amount=None, max_amount=None):
+ """
+ Get Payment Entries from previous VAT Allocation documents where unallocated_amount > 0
+ Returns the most recent state of each Payment Entry
+ """
+ # Convert month name to number
+ months = {
+ "January": 1, "February": 2, "March": 3, "April": 4,
+ "May": 5, "June": 6, "July": 7, "August": 8,
+ "September": 9, "October": 10, "November": 11, "December": 12
+ }
+
+ month_number = months.get(month)
+ if not month_number:
+ frappe.throw(_("Invalid month"))
+
+ # Parse customers filter
+ customer_list = None
+ if customers:
+ if isinstance(customers, str):
+ customers = json.loads(customers)
+ if customers and len(customers) > 0:
+ customer_list = [c.get('customer') for c in customers if c.get('customer')]
+
+ # Find all previous VAT Allocation documents
+ vat_allocations = frappe.db.sql("""
+ SELECT name, year, month
+ FROM `tabVAT allocation`
+ WHERE docstatus = 1
+ AND (
+ year < %(year)s
+ OR (year = %(year)s AND month < %(month)s)
+ )
+ ORDER BY year DESC, month DESC
+ """, {
+ 'year': year,
+ 'month': month_number
+ }, as_dict=1)
+
+ if not vat_allocations:
+ return []
+
+ # Collect all payment entries with unallocated_amount > 0
+ # Keep only the most recent entry for each payment_entry
+ payment_entries_map = {}
+
+ for vat_alloc in vat_allocations:
+ # Get payment entries from this VAT Allocation
+ entries = frappe.db.sql("""
+ SELECT
+ payment_entry,
+ customer,
+ payment_entry_amount,
+ vat_journal_entry,
+ vat_amount,
+ vat_allocated_amount,
+ vat_free_amount,
+ vat_allocated_allocated,
+ vat_free_allocated,
+ unallocated_amount
+ FROM `tabVAT allocation payment entry`
+ WHERE parent = %(parent)s
+ AND unallocated_amount > 0
+ """, {
+ 'parent': vat_alloc.name
+ }, as_dict=1)
+
+ for entry in entries:
+ # Apply customer filter
+ if customer_list and entry.customer not in customer_list:
+ continue
+
+ # Apply min/max amount filters to payment_entry_amount
+ if min_amount and (entry.payment_entry_amount or 0) < float(min_amount):
+ continue
+ if max_amount and (entry.payment_entry_amount or 0) > float(max_amount):
+ continue
+
+ # Keep only the most recent (first encountered due to ORDER BY DESC)
+ if entry.payment_entry not in payment_entries_map:
+ # Calculate remaining amounts
+ vat_allocated_remaining = Decimal(str(entry.vat_allocated_amount or 0)) - Decimal(str(entry.vat_allocated_allocated or 0))
+ vat_free_remaining = Decimal(str(entry.vat_free_amount or 0)) - Decimal(str(entry.vat_free_allocated or 0))
+
+ payment_entries_map[entry.payment_entry] = {
+ 'payment_entry': entry.payment_entry,
+ 'customer': entry.customer,
+ 'payment_entry_amount': entry.payment_entry_amount,
+ 'vat_journal_entry': entry.vat_journal_entry,
+ 'vat_amount': entry.vat_amount,
+ 'vat_allocated_amount': float(vat_allocated_remaining),
+ 'vat_free_amount': float(vat_free_remaining),
+ 'unallocated_amount': 0, # Will be calculated
+ 'allocated_amount': 0,
+ 'vat_allocated_allocated': 0,
+ 'vat_free_allocated': 0
+ }
+
+ # Convert to list
+ result = list(payment_entries_map.values())
+
+ # Sort by payment_entry
+ result.sort(key=lambda x: x['payment_entry'])
+
+ return result
+
+@frappe.whitelist()
+def get_payment_entries_with_previous(year, month, customers=None, min_amount=None, max_amount=None):
+ """
+ Get Payment Entries - both current month and previous months with unallocated amounts
+ """
+ # Get current month payment entries
+ current_entries = get_payment_entries(year, month, customers, min_amount, max_amount)
+
+ # Get previous months payment entries
+ previous_entries = get_payment_entries_previous_months(year, month, customers, min_amount, max_amount)
+
+ # Create a set of payment_entry names from current month
+ current_pe_names = {entry['payment_entry'] for entry in current_entries}
+
+ # Filter out previous entries that are already in current month
+ filtered_previous = [entry for entry in previous_entries if entry['payment_entry'] not in current_pe_names]
+
+ # Combine and return
+ return current_entries + filtered_previous
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_payment_entry_query(doctype, txt, searchfield, start, page_len, filters):
+ """
+ Custom query for filtering Payment Entries - includes both current month and those with remaining balance
+ """
+ year = filters.get('year')
+ month = filters.get('month')
+
+ if not all([year, month]):
+ return []
+
+ # Convert month name to number
+ months = {
+ "January": 1, "February": 2, "March": 3, "April": 4,
+ "May": 5, "June": 6, "July": 7, "August": 8,
+ "September": 9, "October": 10, "November": 11, "December": 12
+ }
+
+ month_number = months.get(month)
+ if not month_number:
+ return []
+
+ # Get Payment Entries from current month OR from previous VAT Allocations with balance
+ return frappe.db.sql("""
+ SELECT DISTINCT pe.name, pe.posting_date, pe.paid_amount
+ FROM `tabPayment Entry` pe
+ WHERE pe.docstatus = 1
+ AND pe.payment_type = 'Receive'
+ AND pe.party_type = 'Customer'
+ AND pe.name LIKE %(txt)s
+ AND (
+ (YEAR(pe.posting_date) = %(year)s AND MONTH(pe.posting_date) = %(month)s)
+ OR
+ EXISTS (
+ SELECT 1
+ FROM `tabVAT allocation payment entry` vape
+ INNER JOIN `tabVAT allocation` va ON va.name = vape.parent
+ WHERE vape.payment_entry = pe.name
+ AND va.docstatus = 1
+ AND vape.unallocated_amount > 0
+ AND (
+ va.year < %(year)s
+ OR (va.year = %(year)s AND va.month < %(month)s)
+ )
+ )
+ )
+ ORDER BY pe.posting_date DESC
+ LIMIT %(start)s, %(page_len)s
+ """, {
+ 'year': year,
+ 'month': month_number,
+ 'txt': f"%{txt}%",
+ 'start': start,
+ 'page_len': page_len
+ })
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/__init__.py b/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/vat_allocation_sales_invoice_previous_months.json b/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/vat_allocation_sales_invoice_previous_months.json
new file mode 100644
index 0000000..599da76
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/vat_allocation_sales_invoice_previous_months.json
@@ -0,0 +1,89 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-10-07 14:07:17.398766",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "customer",
+ "payment_entry",
+ "item_tax_template",
+ "tax_article",
+ "amount",
+ "allocated_amount",
+ "unallocated_amount"
+ ],
+ "fields": [
+ {
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Customer",
+ "options": "Customer",
+ "read_only": 1
+ },
+ {
+ "fieldname": "payment_entry",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "Payment Entry",
+ "options": "Payment Entry"
+ },
+ {
+ "fieldname": "item_tax_template",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item tax template",
+ "options": "Item Tax Template",
+ "precision": "2",
+ "read_only": 1
+ },
+ {
+ "fieldname": "tax_article",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Tax Article",
+ "options": "Tax Article",
+ "precision": "2",
+ "read_only": 1
+ },
+ {
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount",
+ "precision": "2",
+ "read_only": 1
+ },
+ {
+ "fieldname": "allocated_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Allocated amount",
+ "precision": "2",
+ "read_only": 1
+ },
+ {
+ "fieldname": "unallocated_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Unallocated amount",
+ "precision": "2",
+ "read_only": 1
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-10-07 14:07:17.398766",
+ "modified_by": "Administrator",
+ "module": "Taxes Az",
+ "name": "VAT allocation sales invoice previous months",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "creation",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/vat_allocation_sales_invoice_previous_months.py b/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/vat_allocation_sales_invoice_previous_months.py
new file mode 100644
index 0000000..2223ae9
--- /dev/null
+++ b/taxes_az/taxes_az/doctype/vat_allocation_sales_invoice_previous_months/vat_allocation_sales_invoice_previous_months.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Jey Soft and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class VATallocationsalesinvoicepreviousmonths(Document):
+ pass