fixed default vat table
This commit is contained in:
parent
3863754449
commit
add68b8ae3
|
|
@ -93,4 +93,116 @@ def recalculate_tax_free_amounts(doc):
|
||||||
tax_dict = tax.as_dict() if hasattr(tax, 'as_dict') else dict(tax)
|
tax_dict = tax.as_dict() if hasattr(tax, 'as_dict') else dict(tax)
|
||||||
taxes_data.append(tax_dict)
|
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)
|
||||||
|
|
@ -230,23 +230,30 @@ class CustomSalesOrder(SalesOrder):
|
||||||
# Альтернативный способ через monkey patching, если не хотите наследовать классы
|
# Альтернативный способ через monkey patching, если не хотите наследовать классы
|
||||||
def patch_sales_documents():
|
def patch_sales_documents():
|
||||||
"""Патчит существующие классы Sales Invoice и Sales Order"""
|
"""Патчит существующие классы Sales Invoice и Sales Order"""
|
||||||
|
|
||||||
|
# Импортируем функцию синхронизации VAT amounts
|
||||||
|
from jey_erp.custom.sales_invoice import sync_actual_vat_amounts
|
||||||
|
|
||||||
# Патчим Sales Invoice
|
# Патчим Sales Invoice
|
||||||
original_calc_taxes_invoice = SalesInvoice.calculate_taxes_and_totals
|
original_calc_taxes_invoice = SalesInvoice.calculate_taxes_and_totals
|
||||||
|
|
||||||
def new_calc_taxes_invoice(self):
|
def new_calc_taxes_invoice(self):
|
||||||
original_calc_taxes_invoice(self)
|
original_calc_taxes_invoice(self)
|
||||||
calculate_vat_fields_for_doc(self)
|
calculate_vat_fields_for_doc(self)
|
||||||
|
# Синхронизируем фактические суммы VAT в taxes таблицу
|
||||||
|
sync_actual_vat_amounts(self)
|
||||||
|
|
||||||
SalesInvoice.calculate_taxes_and_totals = new_calc_taxes_invoice
|
SalesInvoice.calculate_taxes_and_totals = new_calc_taxes_invoice
|
||||||
|
|
||||||
# Патчим Sales Order
|
# Патчим Sales Order
|
||||||
original_calc_taxes_order = SalesOrder.calculate_taxes_and_totals
|
original_calc_taxes_order = SalesOrder.calculate_taxes_and_totals
|
||||||
|
|
||||||
def new_calc_taxes_order(self):
|
def new_calc_taxes_order(self):
|
||||||
original_calc_taxes_order(self)
|
original_calc_taxes_order(self)
|
||||||
calculate_vat_fields_for_doc(self)
|
calculate_vat_fields_for_doc(self)
|
||||||
|
# Синхронизируем фактические суммы VAT в taxes таблицу
|
||||||
|
sync_actual_vat_amounts(self)
|
||||||
|
|
||||||
SalesOrder.calculate_taxes_and_totals = new_calc_taxes_order
|
SalesOrder.calculate_taxes_and_totals = new_calc_taxes_order
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
def add_company_to_party_account_types(bootinfo):
|
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
|
# Проверяем, существует ли объект party_account_types в bootinfo
|
||||||
if "party_account_types" in bootinfo:
|
if "party_account_types" in bootinfo:
|
||||||
# Если "Company" еще нет в списке, добавляем его
|
# Если "Company" еще нет в списке, добавляем его
|
||||||
|
|
|
||||||
|
|
@ -157,10 +157,72 @@ function update_html_checkboxes(frm) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
const wonCheckbox = document.querySelector('.won-checkbox');
|
const wonCheckbox = document.querySelector('.won-checkbox');
|
||||||
const lostCheckbox = document.querySelector('.lost-checkbox');
|
const lostCheckbox = document.querySelector('.lost-checkbox');
|
||||||
|
|
||||||
if (wonCheckbox && lostCheckbox) {
|
if (wonCheckbox && lostCheckbox) {
|
||||||
wonCheckbox.checked = frm.doc.won ? true : false;
|
wonCheckbox.checked = frm.doc.won ? true : false;
|
||||||
lostCheckbox.checked = frm.doc.lost ? true : false;
|
lostCheckbox.checked = frm.doc.lost ? true : false;
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 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
|
// Запускаем логику калькулятора НДС для 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue