fixed default vat table

This commit is contained in:
Ali 2026-01-16 21:06:33 +04:00
parent 3863754449
commit add68b8ae3
5 changed files with 260 additions and 10 deletions

View File

@ -93,4 +93,116 @@ def recalculate_tax_free_amounts(doc):
tax_dict = tax.as_dict() if hasattr(tax, 'as_dict') else dict(tax)
taxes_data.append(tax_dict)
return taxes_data
return taxes_data
@frappe.whitelist()
def sync_vat_to_taxes(doc):
"""
Синхронизирует фактические VAT amounts из items в taxes таблицу
Вызывается из клиентского JavaScript
"""
if isinstance(doc, str):
doc = frappe.parse_json(doc)
# Словарь для агрегации VAT amounts по account_head
vat_aggregates = {}
# Агрегируем vat_amount из items
for item in doc.get("items", []):
if not item.get("item_code"):
continue
# Округляем до 2 знаков при чтении
vat_amount = flt(item.get("vat_amount", 0), 2)
if vat_amount <= 0:
continue
item_tax_template = item.get("item_tax_template")
if not item_tax_template:
continue
# Получить account_head из налогового шаблона
try:
tax_template = frappe.get_doc("Item Tax Template", item_tax_template)
if tax_template.taxes and len(tax_template.taxes) > 0:
account_head = tax_template.taxes[0].tax_type
if account_head not in vat_aggregates:
vat_aggregates[account_head] = 0.0
vat_aggregates[account_head] += vat_amount
except Exception:
continue
# Обновляем taxes таблицу
updated_taxes = []
for tax in doc.get("taxes", []):
if tax.get("account_head") in vat_aggregates:
# Округляем итоговую сумму до 2 знаков
tax["tax_amount"] = flt(vat_aggregates[tax["account_head"]], 2)
tax["base_tax_amount"] = flt(vat_aggregates[tax["account_head"]], 2)
updated_taxes.append(tax)
return updated_taxes
def sync_actual_vat_amounts(doc, method=None):
"""
Перезаписывает tax_amount в taxes таблице фактическими суммами НДС из items.
Вызывается после стандартного calculate_taxes_and_totals.
"""
if doc.doctype not in ["Sales Invoice", "Sales Order"]:
return
# Словарь для агрегации сумм НДС по account_head
vat_aggregates = {}
# Агрегировать фактические суммы НДС из items
for item in doc.get("items", []):
if not item.get("item_code"):
continue
# Получить фактическую сумму НДС из строки товара
vat_amount = flt(item.get("vat_amount", 0), 2)
if vat_amount <= 0:
continue
# Получить налоговый шаблон товара
item_tax_template = item.get("item_tax_template")
if not item_tax_template:
# Если нет шаблона в строке, проверить Item master
try:
item_doc = frappe.get_doc("Item", item.item_code)
item_tax_template = item_doc.get("item_tax_template")
except Exception:
continue
if not item_tax_template:
continue
# Получить account_head из налогового шаблона
try:
tax_template = frappe.get_doc("Item Tax Template", item_tax_template)
# Взять первый tax_type (account_head) из шаблона
if tax_template.taxes and len(tax_template.taxes) > 0:
account_head = tax_template.taxes[0].tax_type
# Агрегировать сумму НДС с округлением
if account_head not in vat_aggregates:
vat_aggregates[account_head] = 0.0
vat_aggregates[account_head] += vat_amount
except Exception:
continue
# ПЕРЕЗАПИСАТЬ tax_amount в taxes таблице агрегированными суммами
for tax in doc.get("taxes", []):
account_head = tax.account_head
if account_head in vat_aggregates:
# Заменить стандартный расчет ERPNext нашей фактической суммой
# Округляем до 2 знаков
tax.tax_amount = flt(vat_aggregates[account_head], 2)
# Также обновить base_tax_amount для мультивалютных документов
tax.base_tax_amount = flt(vat_aggregates[account_head], 2)

View File

@ -230,23 +230,30 @@ class CustomSalesOrder(SalesOrder):
# Альтернативный способ через monkey patching, если не хотите наследовать классы
def patch_sales_documents():
"""Патчит существующие классы Sales Invoice и Sales Order"""
# Импортируем функцию синхронизации VAT amounts
from jey_erp.custom.sales_invoice import sync_actual_vat_amounts
# Патчим Sales Invoice
original_calc_taxes_invoice = SalesInvoice.calculate_taxes_and_totals
def new_calc_taxes_invoice(self):
original_calc_taxes_invoice(self)
calculate_vat_fields_for_doc(self)
# Синхронизируем фактические суммы VAT в taxes таблицу
sync_actual_vat_amounts(self)
SalesInvoice.calculate_taxes_and_totals = new_calc_taxes_invoice
# Патчим Sales Order
original_calc_taxes_order = SalesOrder.calculate_taxes_and_totals
def new_calc_taxes_order(self):
original_calc_taxes_order(self)
calculate_vat_fields_for_doc(self)
# Синхронизируем фактические суммы VAT в taxes таблицу
sync_actual_vat_amounts(self)
SalesOrder.calculate_taxes_and_totals = new_calc_taxes_order

View File

@ -1,6 +1,13 @@
import frappe
def add_company_to_party_account_types(bootinfo):
# Применяем VAT monkey patches при загрузке сессии
try:
from jey_erp.custom.vat_calculations import patch_sales_documents
patch_sales_documents()
except Exception:
pass
# Проверяем, существует ли объект party_account_types в bootinfo
if "party_account_types" in bootinfo:
# Если "Company" еще нет в списке, добавляем его

View File

@ -157,10 +157,72 @@ function update_html_checkboxes(frm) {
setTimeout(function() {
const wonCheckbox = document.querySelector('.won-checkbox');
const lostCheckbox = document.querySelector('.lost-checkbox');
if (wonCheckbox && lostCheckbox) {
wonCheckbox.checked = frm.doc.won ? true : false;
lostCheckbox.checked = frm.doc.lost ? true : false;
}
}, 100);
}
}
// Функция синхронизации фактических сумм НДС из items в taxes
function sync_vat_amounts_to_taxes(frm) {
if (!frm.doc.items || !frm.doc.taxes || frm.doc.items.length === 0) return;
// Вызываем серверный метод для синхронизации
frappe.call({
method: 'jey_erp.custom.sales_invoice.sync_vat_to_taxes',
args: {
doc: frm.doc
},
callback: function(r) {
if (r.message && r.message.length > 0) {
// Обновляем taxes таблицу результатами с сервера
r.message.forEach(function(updated_tax, idx) {
if (frm.doc.taxes[idx]) {
frm.doc.taxes[idx].tax_amount = updated_tax.tax_amount;
frm.doc.taxes[idx].base_tax_amount = updated_tax.base_tax_amount;
}
});
// Обновляем отображение
frm.refresh_field('taxes');
}
}
});
}
// Обработчики для Sales Invoice Item
frappe.ui.form.on('Sales Invoice Item', {
item_tax_template: function(frm, cdt, cdn) {
// Даем время на пересчет vat_amount, затем синхронизируем
setTimeout(function() {
sync_vat_amounts_to_taxes(frm);
}, 500);
},
qty: function(frm, cdt, cdn) {
setTimeout(function() {
sync_vat_amounts_to_taxes(frm);
}, 500);
},
rate: function(frm, cdt, cdn) {
setTimeout(function() {
sync_vat_amounts_to_taxes(frm);
}, 500);
},
amount: function(frm, cdt, cdn) {
setTimeout(function() {
sync_vat_amounts_to_taxes(frm);
}, 500);
},
vat_amount: function(frm, cdt, cdn) {
// Когда vat_amount изменяется, сразу синхронизируем
setTimeout(function() {
sync_vat_amounts_to_taxes(frm);
}, 300);
}
});

View File

@ -1,2 +1,64 @@
// Запускаем логику калькулятора НДС для Sales Order
jey_erp.vat_calculator.init('Sales Order');
jey_erp.vat_calculator.init('Sales Order');
// Функция синхронизации фактических сумм НДС из items в taxes
function sync_vat_amounts_to_taxes_so(frm) {
if (!frm.doc.items || !frm.doc.taxes || frm.doc.items.length === 0) return;
// Вызываем серверный метод для синхронизации
frappe.call({
method: 'jey_erp.custom.sales_invoice.sync_vat_to_taxes',
args: {
doc: frm.doc
},
callback: function(r) {
if (r.message && r.message.length > 0) {
// Обновляем taxes таблицу результатами с сервера
r.message.forEach(function(updated_tax, idx) {
if (frm.doc.taxes[idx]) {
frm.doc.taxes[idx].tax_amount = updated_tax.tax_amount;
frm.doc.taxes[idx].base_tax_amount = updated_tax.base_tax_amount;
}
});
// Обновляем отображение
frm.refresh_field('taxes');
}
}
});
}
// Обработчики для Sales Order Item
frappe.ui.form.on('Sales Order Item', {
item_tax_template: function(frm, cdt, cdn) {
// Даем время на пересчет vat_amount, затем синхронизируем
setTimeout(function() {
sync_vat_amounts_to_taxes_so(frm);
}, 500);
},
qty: function(frm, cdt, cdn) {
setTimeout(function() {
sync_vat_amounts_to_taxes_so(frm);
}, 500);
},
rate: function(frm, cdt, cdn) {
setTimeout(function() {
sync_vat_amounts_to_taxes_so(frm);
}, 500);
},
amount: function(frm, cdt, cdn) {
setTimeout(function() {
sync_vat_amounts_to_taxes_so(frm);
}, 500);
},
vat_amount: function(frm, cdt, cdn) {
// Когда vat_amount изменяется, сразу синхронизируем
setTimeout(function() {
sync_vat_amounts_to_taxes_so(frm);
}, 300);
}
});