fixed default vat table
This commit is contained in:
parent
3863754449
commit
add68b8ae3
|
|
@ -94,3 +94,115 @@ def recalculate_tax_free_amounts(doc):
|
|||
taxes_data.append(tax_dict)
|
||||
|
||||
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)
|
||||
|
|
@ -231,12 +231,17 @@ class CustomSalesOrder(SalesOrder):
|
|||
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
|
||||
|
||||
|
|
@ -246,6 +251,8 @@ def patch_sales_documents():
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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" еще нет в списке, добавляем его
|
||||
|
|
|
|||
|
|
@ -164,3 +164,65 @@ function update_html_checkboxes(frm) {
|
|||
}
|
||||
}, 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);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,2 +1,64 @@
|
|||
// Запускаем логику калькулятора НДС для 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);
|
||||
}
|
||||
});
|
||||
Loading…
Reference in New Issue