added 4 columns in sales order and vat calculations
This commit is contained in:
parent
c515dcf83f
commit
722da8a097
|
|
@ -36,53 +36,28 @@ def set_item_vat_permissions(item):
|
|||
# Default state - all fields disabled
|
||||
vat_permissions = {
|
||||
'vat_18_percent_with_amount': False,
|
||||
'vat_amount': False,
|
||||
'vat_0_percent_with_amount': False,
|
||||
'vat_exempt_amount': False,
|
||||
'not_subject_to_vat': False
|
||||
'amount_without_vat': False,
|
||||
'vat_free_amount': False
|
||||
}
|
||||
|
||||
if item_tax_template:
|
||||
try:
|
||||
tax_template = frappe.get_doc("Item Tax Template", item_tax_template)
|
||||
|
||||
# Look for tax line with account code 521.3
|
||||
vat_tax_found = False
|
||||
vat_rate = None
|
||||
|
||||
for template_tax in tax_template.get("taxes", []):
|
||||
# Get account document to check account code
|
||||
try:
|
||||
account_doc = frappe.get_doc("Account", template_tax.tax_type)
|
||||
account_number = account_doc.get("account_number") or ""
|
||||
|
||||
# Check if this is VAT account (521.3)
|
||||
if account_number == "521.3":
|
||||
vat_tax_found = True
|
||||
vat_rate = flt(template_tax.tax_rate)
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if vat_tax_found:
|
||||
if vat_rate == 18.0:
|
||||
# Rule 1: Only 18% field enabled
|
||||
vat_permissions['vat_18_percent_with_amount'] = True
|
||||
elif vat_rate == 0.0:
|
||||
# Rule 2: Only 0% field enabled
|
||||
vat_permissions['vat_0_percent_with_amount'] = True
|
||||
else:
|
||||
# Rule 3: No VAT line found - enable only exempt field
|
||||
vat_permissions['vat_exempt_amount'] = True
|
||||
# not_subject_to_vat remains False (disabled)
|
||||
|
||||
except Exception:
|
||||
# Rule 3: Error loading template - enable only exempt field
|
||||
vat_permissions['vat_exempt_amount'] = True
|
||||
# not_subject_to_vat remains False (disabled)
|
||||
|
||||
if item_tax_template == "ƏDV 18%":
|
||||
# 18% VAT template
|
||||
vat_permissions['vat_18_percent_with_amount'] = True
|
||||
vat_permissions['vat_amount'] = True # Показывать VAT Amount только для 18%
|
||||
elif item_tax_template == "ƏDV 0%":
|
||||
# 0% VAT template
|
||||
vat_permissions['vat_0_percent_with_amount'] = True
|
||||
elif item_tax_template == "ƏDV-dən azadolma":
|
||||
# VAT exempt template
|
||||
vat_permissions['vat_free_amount'] = True
|
||||
elif not item_tax_template:
|
||||
# No tax template - not subject to VAT
|
||||
vat_permissions['amount_without_vat'] = True
|
||||
else:
|
||||
# Rule 3: No tax template - enable only exempt field
|
||||
vat_permissions['vat_exempt_amount'] = True
|
||||
# not_subject_to_vat remains False (disabled)
|
||||
# Any other template - default to amount_without_vat
|
||||
vat_permissions['amount_without_vat'] = True
|
||||
|
||||
# Apply permissions to item
|
||||
apply_vat_permissions_to_item(item, vat_permissions)
|
||||
|
|
@ -101,24 +76,50 @@ def apply_vat_permissions_to_item(item, permissions):
|
|||
|
||||
for field, enabled in permissions.items():
|
||||
if enabled and hasattr(item, field):
|
||||
# Only auto-fill if field is empty
|
||||
# Only auto-fill if field is empty (except vat_amount which is always calculated)
|
||||
current_value = getattr(item, field, 0)
|
||||
if not current_value or current_value == 0:
|
||||
if not current_value or current_value == 0 or field == 'vat_amount':
|
||||
if field == 'vat_18_percent_with_amount':
|
||||
# For 18% VAT: Amount + 18%
|
||||
vat_amount = item_amount * 1.18
|
||||
setattr(item, field, vat_amount)
|
||||
elif field == 'vat_amount':
|
||||
# Calculate VAT Amount: vat_18_percent_with_amount - amount
|
||||
vat_18_total = flt(item.get('vat_18_percent_with_amount', 0))
|
||||
if vat_18_total == 0:
|
||||
# If vat_18_percent_with_amount is not set, calculate it first
|
||||
vat_18_total = item_amount * 1.18
|
||||
setattr(item, 'vat_18_percent_with_amount', vat_18_total)
|
||||
vat_only = vat_18_total - item_amount
|
||||
setattr(item, field, vat_only)
|
||||
elif field == 'vat_0_percent_with_amount':
|
||||
# For 0% VAT: just the amount
|
||||
setattr(item, field, item_amount)
|
||||
elif field == 'vat_exempt_amount':
|
||||
elif field == 'amount_without_vat':
|
||||
# For VAT exempt: auto-fill with item amount
|
||||
setattr(item, field, item_amount)
|
||||
# not_subject_to_vat is never auto-filled (always disabled)
|
||||
elif field == 'vat_free_amount':
|
||||
# For "not subject to VAT": auto-fill with item amount
|
||||
setattr(item, field, item_amount)
|
||||
|
||||
# Store permissions in item for client-side use
|
||||
item.vat_permissions = json.dumps(permissions)
|
||||
|
||||
def calculate_vat_amount(item):
|
||||
"""
|
||||
Calculate VAT Amount whenever vat_18_percent_with_amount or amount changes
|
||||
This function can be called from hooks or client-side triggers
|
||||
"""
|
||||
if hasattr(item, 'vat_18_percent_with_amount') and hasattr(item, 'amount'):
|
||||
vat_18_total = flt(item.get('vat_18_percent_with_amount', 0))
|
||||
item_amount = flt(item.get('amount', 0))
|
||||
|
||||
if vat_18_total > 0 and item_amount > 0:
|
||||
vat_only = vat_18_total - item_amount
|
||||
setattr(item, 'vat_amount', vat_only)
|
||||
else:
|
||||
setattr(item, 'vat_amount', 0.0)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_vat_permissions_for_item(item_code, item_tax_template=None):
|
||||
"""
|
||||
|
|
@ -141,7 +142,55 @@ def get_vat_permissions_for_item(item_code, item_tax_template=None):
|
|||
# Default fallback
|
||||
return {
|
||||
'vat_18_percent_with_amount': False,
|
||||
'vat_amount': False,
|
||||
'vat_0_percent_with_amount': False,
|
||||
'vat_exempt_amount': True,
|
||||
'not_subject_to_vat': False # Always disabled
|
||||
'amount_without_vat': True,
|
||||
'vat_free_amount': False
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def calculate_vat_amount_api(item_data):
|
||||
"""
|
||||
API method to calculate VAT amount from client side
|
||||
"""
|
||||
try:
|
||||
if isinstance(item_data, str):
|
||||
item_data = json.loads(item_data)
|
||||
|
||||
vat_18_total = flt(item_data.get('vat_18_percent_with_amount', 0))
|
||||
item_amount = flt(item_data.get('amount', 0))
|
||||
|
||||
if vat_18_total > 0 and item_amount > 0:
|
||||
vat_only = vat_18_total - item_amount
|
||||
return vat_only
|
||||
else:
|
||||
return 0.0
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error calculating VAT amount: {str(e)}")
|
||||
return 0.0
|
||||
|
||||
# Hook functions for automatic calculation
|
||||
def on_sales_invoice_item_validate(doc, method=None):
|
||||
"""
|
||||
Hook to be called on Sales Invoice Item validate
|
||||
Automatically calculates VAT amount when item is saved
|
||||
"""
|
||||
if hasattr(doc, 'vat_18_percent_with_amount') and hasattr(doc, 'amount'):
|
||||
calculate_vat_amount(doc)
|
||||
|
||||
def on_sales_invoice_validate(doc, method=None):
|
||||
"""
|
||||
Hook to be called on Sales Invoice validate
|
||||
Ensures all VAT calculations are correct
|
||||
"""
|
||||
if doc.doctype != "Sales Invoice":
|
||||
return
|
||||
|
||||
for item in doc.get("items", []):
|
||||
# Set permissions first
|
||||
set_item_vat_permissions(item)
|
||||
|
||||
# Then calculate VAT amount if needed
|
||||
if hasattr(item, 'vat_18_percent_with_amount') and hasattr(item, 'amount'):
|
||||
calculate_vat_amount(item)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
# Файл: jey_erp/custom/sales_order_vat.py
|
||||
|
||||
import frappe
|
||||
import json
|
||||
from frappe.utils import flt
|
||||
|
||||
def set_vat_fields_permissions(doc, method=None):
|
||||
"""
|
||||
Set VAT fields permissions based on Item Tax Template
|
||||
Called on Sales Order validate/before_save
|
||||
"""
|
||||
if doc.doctype != "Sales Order":
|
||||
return
|
||||
|
||||
for item in doc.get("items", []):
|
||||
set_item_vat_permissions(item)
|
||||
|
||||
def set_item_vat_permissions(item):
|
||||
"""
|
||||
Set VAT field permissions for individual item based on tax template
|
||||
"""
|
||||
if not item.get("item_code"):
|
||||
return
|
||||
|
||||
# Get item tax template
|
||||
item_tax_template = item.get("item_tax_template")
|
||||
|
||||
# If no tax template in item row, get from Item master
|
||||
if not item_tax_template:
|
||||
try:
|
||||
item_doc = frappe.get_doc("Item", item.item_code)
|
||||
item_tax_template = item_doc.get("item_tax_template")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Default state - all fields disabled
|
||||
vat_permissions = {
|
||||
'vat_18_percent_with_amount': False,
|
||||
'vat_amount': False,
|
||||
'vat_0_percent_with_amount': False,
|
||||
'amount_without_vat': False,
|
||||
'vat_free_amount': False
|
||||
}
|
||||
|
||||
if item_tax_template == "ƏDV 18%":
|
||||
# 18% VAT template
|
||||
vat_permissions['vat_18_percent_with_amount'] = True
|
||||
vat_permissions['vat_amount'] = True # Показывать VAT Amount только для 18%
|
||||
elif item_tax_template == "ƏDV 0%":
|
||||
# 0% VAT template
|
||||
vat_permissions['vat_0_percent_with_amount'] = True
|
||||
elif item_tax_template == "ƏDV-dən azadolma":
|
||||
# VAT exempt template
|
||||
vat_permissions['vat_free_amount'] = True
|
||||
elif not item_tax_template:
|
||||
# No tax template - not subject to VAT
|
||||
vat_permissions['amount_without_vat'] = True
|
||||
else:
|
||||
# Any other template - default to amount_without_vat
|
||||
vat_permissions['amount_without_vat'] = True
|
||||
|
||||
# Apply permissions to item
|
||||
apply_vat_permissions_to_item(item, vat_permissions)
|
||||
|
||||
def apply_vat_permissions_to_item(item, permissions):
|
||||
"""
|
||||
Apply VAT field permissions to item and clear disabled fields
|
||||
"""
|
||||
# Clear values for disabled fields
|
||||
for field, enabled in permissions.items():
|
||||
if not enabled and hasattr(item, field):
|
||||
setattr(item, field, 0.0)
|
||||
|
||||
# Calculate amounts for enabled fields
|
||||
item_amount = flt(item.get("amount", 0))
|
||||
|
||||
for field, enabled in permissions.items():
|
||||
if enabled and hasattr(item, field):
|
||||
# Only auto-fill if field is empty (except vat_amount which is always calculated)
|
||||
current_value = getattr(item, field, 0)
|
||||
if not current_value or current_value == 0 or field == 'vat_amount':
|
||||
if field == 'vat_18_percent_with_amount':
|
||||
# For 18% VAT: Amount + 18%
|
||||
vat_amount = item_amount * 1.18
|
||||
setattr(item, field, vat_amount)
|
||||
elif field == 'vat_amount':
|
||||
# Calculate VAT Amount: vat_18_percent_with_amount - amount
|
||||
vat_18_total = flt(item.get('vat_18_percent_with_amount', 0))
|
||||
if vat_18_total == 0:
|
||||
# If vat_18_percent_with_amount is not set, calculate it first
|
||||
vat_18_total = item_amount * 1.18
|
||||
setattr(item, 'vat_18_percent_with_amount', vat_18_total)
|
||||
vat_only = vat_18_total - item_amount
|
||||
setattr(item, field, vat_only)
|
||||
elif field == 'vat_0_percent_with_amount':
|
||||
# For 0% VAT: just the amount
|
||||
setattr(item, field, item_amount)
|
||||
elif field == 'amount_without_vat':
|
||||
# For VAT exempt: auto-fill with item amount
|
||||
setattr(item, field, item_amount)
|
||||
elif field == 'vat_free_amount':
|
||||
# For "not subject to VAT": auto-fill with item amount
|
||||
setattr(item, field, item_amount)
|
||||
|
||||
# Store permissions in item for client-side use
|
||||
item.vat_permissions = json.dumps(permissions)
|
||||
|
||||
def calculate_vat_amount(item):
|
||||
"""
|
||||
Calculate VAT Amount whenever vat_18_percent_with_amount or amount changes
|
||||
This function can be called from hooks or client-side triggers
|
||||
"""
|
||||
if hasattr(item, 'vat_18_percent_with_amount') and hasattr(item, 'amount'):
|
||||
vat_18_total = flt(item.get('vat_18_percent_with_amount', 0))
|
||||
item_amount = flt(item.get('amount', 0))
|
||||
|
||||
if vat_18_total > 0 and item_amount > 0:
|
||||
vat_only = vat_18_total - item_amount
|
||||
setattr(item, 'vat_amount', vat_only)
|
||||
else:
|
||||
setattr(item, 'vat_amount', 0.0)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_vat_permissions_for_item(item_code, item_tax_template=None):
|
||||
"""
|
||||
API method to get VAT permissions for an item
|
||||
Called from client side when item changes
|
||||
"""
|
||||
# Create temporary item object
|
||||
temp_item = frappe._dict({
|
||||
'item_code': item_code,
|
||||
'item_tax_template': item_tax_template
|
||||
})
|
||||
|
||||
# Calculate permissions
|
||||
set_item_vat_permissions(temp_item)
|
||||
|
||||
# Return permissions
|
||||
if hasattr(temp_item, 'vat_permissions'):
|
||||
return json.loads(temp_item.vat_permissions)
|
||||
else:
|
||||
# Default fallback
|
||||
return {
|
||||
'vat_18_percent_with_amount': False,
|
||||
'vat_amount': False,
|
||||
'vat_0_percent_with_amount': False,
|
||||
'amount_without_vat': True,
|
||||
'vat_free_amount': False
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def calculate_vat_amount_api(item_data):
|
||||
"""
|
||||
API method to calculate VAT amount from client side
|
||||
"""
|
||||
try:
|
||||
if isinstance(item_data, str):
|
||||
item_data = json.loads(item_data)
|
||||
|
||||
vat_18_total = flt(item_data.get('vat_18_percent_with_amount', 0))
|
||||
item_amount = flt(item_data.get('amount', 0))
|
||||
|
||||
if vat_18_total > 0 and item_amount > 0:
|
||||
vat_only = vat_18_total - item_amount
|
||||
return vat_only
|
||||
else:
|
||||
return 0.0
|
||||
except Exception as e:
|
||||
frappe.log_error(f"Error calculating VAT amount: {str(e)}")
|
||||
return 0.0
|
||||
|
||||
# Hook functions for automatic calculation
|
||||
def on_sales_order_item_validate(doc, method=None):
|
||||
"""
|
||||
Hook to be called on Sales Order Item validate
|
||||
Automatically calculates VAT amount when item is saved
|
||||
"""
|
||||
if hasattr(doc, 'vat_18_percent_with_amount') and hasattr(doc, 'amount'):
|
||||
calculate_vat_amount(doc)
|
||||
|
||||
def on_sales_order_validate(doc, method=None):
|
||||
"""
|
||||
Hook to be called on Sales Order validate
|
||||
Ensures all VAT calculations are correct
|
||||
"""
|
||||
if doc.doctype != "Sales Order":
|
||||
return
|
||||
|
||||
for item in doc.get("items", []):
|
||||
# Set permissions first
|
||||
set_item_vat_permissions(item)
|
||||
|
||||
# Then calculate VAT amount if needed
|
||||
if hasattr(item, 'vat_18_percent_with_amount') and hasattr(item, 'amount'):
|
||||
calculate_vat_amount(item)
|
||||
|
|
@ -58,24 +58,35 @@ def create_custom_fields():
|
|||
dict(
|
||||
fieldname='vat_18_percent_with_amount',
|
||||
label='VAT 18% with amount',
|
||||
fieldtype='Currency',
|
||||
fieldtype='Currency',
|
||||
insert_after='amount',
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2
|
||||
),
|
||||
dict(
|
||||
fieldname='vat_0_percent_with_amount',
|
||||
label='VAT 0% with amount',
|
||||
fieldname='vat_amount',
|
||||
label='VAT Amount',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_18_percent_with_amount',
|
||||
in_list_view=1,
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2,
|
||||
read_only=1,
|
||||
description='Calculated as: VAT 18% with amount - Amount'
|
||||
),
|
||||
dict(
|
||||
fieldname='vat_0_percent_with_amount',
|
||||
label='VAT 0% with amount',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_amount',
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2
|
||||
),
|
||||
dict(
|
||||
fieldname='vat_exempt_amount',
|
||||
label='VAT free amount',
|
||||
fieldname='amount_without_vat',
|
||||
label='Amount without VAT',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_0_percent_with_amount',
|
||||
in_list_view=1,
|
||||
|
|
@ -83,10 +94,10 @@ def create_custom_fields():
|
|||
precision=2
|
||||
),
|
||||
dict(
|
||||
fieldname='not_subject_to_vat',
|
||||
label='Not subject to VAT',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_exempt_amount',
|
||||
fieldname='vat_free_amount',
|
||||
label='VAT free amount',
|
||||
fieldtype='Currency',
|
||||
insert_after='amount_without_vat',
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2
|
||||
|
|
@ -139,18 +150,29 @@ def create_custom_fields():
|
|||
columns=2,
|
||||
precision=2
|
||||
),
|
||||
dict(
|
||||
fieldname='vat_amount',
|
||||
label='VAT Amount',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_18_percent_with_amount',
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2,
|
||||
read_only=1,
|
||||
description='Calculated as: VAT 18% with amount - Amount'
|
||||
),
|
||||
dict(
|
||||
fieldname='vat_0_percent_with_amount',
|
||||
label='VAT 0% with amount',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_18_percent_with_amount',
|
||||
insert_after='vat_amount',
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2
|
||||
),
|
||||
dict(
|
||||
fieldname='vat_exempt_amount',
|
||||
label='VAT free amount',
|
||||
fieldname='amount_without_vat',
|
||||
label='Amount without VAT',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_0_percent_with_amount',
|
||||
in_list_view=1,
|
||||
|
|
@ -158,10 +180,10 @@ def create_custom_fields():
|
|||
precision=2
|
||||
),
|
||||
dict(
|
||||
fieldname='not_subject_to_vat',
|
||||
label='Not subject to VAT',
|
||||
fieldname='vat_free_amount',
|
||||
label='VAT free amount',
|
||||
fieldtype='Currency',
|
||||
insert_after='vat_exempt_amount',
|
||||
insert_after='amount_without_vat',
|
||||
in_list_view=1,
|
||||
columns=2,
|
||||
precision=2
|
||||
|
|
@ -348,6 +370,22 @@ def create_custom_fields():
|
|||
insert_after='business_activities_section',
|
||||
read_only=1
|
||||
),
|
||||
dict(
|
||||
fieldname='organizer',
|
||||
label='Organizer',
|
||||
fieldtype='Check',
|
||||
insert_after='main_activity',
|
||||
hidden=1,
|
||||
depends_on='eval:["92000", "9200003", "9200004", "9200001", "9200002", "9200005"].some(id => doc.main_activity && doc.main_activity.includes(id))'
|
||||
),
|
||||
dict(
|
||||
fieldname='seller',
|
||||
label='Seller',
|
||||
fieldtype='Check',
|
||||
insert_after='organizer',
|
||||
hidden=1,
|
||||
depends_on='eval:["92000", "9200003", "9200004", "9200001", "9200002", "9200005"].some(id => doc.main_activity && doc.main_activity.includes(id))'
|
||||
),
|
||||
dict(
|
||||
fieldname='additional_activity_types',
|
||||
label='Additional Activity Types',
|
||||
|
|
@ -741,6 +779,6 @@ def create_custom_fields():
|
|||
print(f"Failed to create field {field['fieldname']} in {doctype}: {e}")
|
||||
|
||||
# Register the calculation hook for existing Sales Invoices
|
||||
print("Setting up Tax Free Amount calculation hooks...")
|
||||
print("Setting up VAT calculation hooks...")
|
||||
|
||||
create_custom_fields()
|
||||
|
|
@ -11,7 +11,8 @@ boot_session = "jey_erp.extend_party_types.add_company_to_party_account_types"
|
|||
app_include_js = [
|
||||
"/assets/jey_erp/js/account_tree_custom.js",
|
||||
"/assets/jey_erp/js/sales_invoice.js",
|
||||
"/assets/jey_erp/js/sales_invoice_vat.js"
|
||||
"/assets/jey_erp/js/sales_invoice_vat.js",
|
||||
"/assets/jey_erp/js/sales_order_vat.js"
|
||||
]
|
||||
|
||||
doctype_js = {
|
||||
|
|
@ -38,7 +39,16 @@ doc_events = {
|
|||
"jey_erp.custom.sales_invoice_vat.set_vat_fields_permissions"
|
||||
],
|
||||
"before_save": "jey_erp.custom.sales_invoice_vat.set_vat_fields_permissions"
|
||||
}
|
||||
},
|
||||
"Sales Order": {
|
||||
"validate": [
|
||||
"jey_erp.custom.sales_order_vat.set_vat_fields_permissions"
|
||||
],
|
||||
"on_update": [
|
||||
"jey_erp.custom.sales_order_vat.set_vat_fields_permissions"
|
||||
],
|
||||
"before_save": "jey_erp.custom.sales_order_vat.set_vat_fields_permissions"
|
||||
}
|
||||
}
|
||||
|
||||
after_migrate = "jey_erp.hooks.after_migrate_combined"
|
||||
|
|
|
|||
|
|
@ -30,29 +30,53 @@ frappe.ui.form.on('Sales Invoice Item', {
|
|||
update_item_vat_permissions(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
// Пересчет при изменении rate
|
||||
rate: function(frm, cdt, cdn) {
|
||||
// Небольшая задержка, чтобы amount успел пересчитаться
|
||||
setTimeout(() => {
|
||||
recalculate_all_vat_fields(frm, cdt, cdn);
|
||||
}, 100);
|
||||
},
|
||||
|
||||
// Пересчет при изменении qty
|
||||
qty: function(frm, cdt, cdn) {
|
||||
// Небольшая задержка, чтобы amount успел пересчитаться
|
||||
setTimeout(() => {
|
||||
recalculate_all_vat_fields(frm, cdt, cdn);
|
||||
}, 100);
|
||||
},
|
||||
|
||||
amount: function(frm, cdt, cdn) {
|
||||
// Пересчитать все VAT поля при изменении основной суммы
|
||||
recalculate_all_vat_fields(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
vat_18_percent_with_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_18_percent_with_amount');
|
||||
// Пересчитать VAT Amount при изменении суммы с НДС
|
||||
calculate_vat_amount(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
vat_0_percent_with_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_0_percent_with_amount');
|
||||
},
|
||||
|
||||
vat_exempt_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_exempt_amount');
|
||||
amount_without_vat: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'amount_without_vat');
|
||||
},
|
||||
|
||||
not_subject_to_vat: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'not_subject_to_vat');
|
||||
vat_free_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_free_amount');
|
||||
}
|
||||
});
|
||||
|
||||
function setup_vat_fields_logic(frm) {
|
||||
frm.vat_field_names = [
|
||||
'vat_18_percent_with_amount',
|
||||
'vat_amount',
|
||||
'vat_0_percent_with_amount',
|
||||
'vat_exempt_amount',
|
||||
'not_subject_to_vat'
|
||||
'amount_without_vat',
|
||||
'vat_free_amount'
|
||||
];
|
||||
|
||||
frm.vat_permissions_cache = {};
|
||||
|
|
@ -190,7 +214,12 @@ function apply_field_permission(frm, cdt, cdn, row_index, fieldname, is_enabled,
|
|||
if (grid_row && grid_row.docfields) {
|
||||
let docfield = grid_row.docfields.find(df => df.fieldname === fieldname);
|
||||
if (docfield) {
|
||||
docfield.read_only = is_enabled ? 0 : 1;
|
||||
// VAT Amount всегда только для чтения
|
||||
if (fieldname === 'vat_amount') {
|
||||
docfield.read_only = 1;
|
||||
} else {
|
||||
docfield.read_only = is_enabled ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +227,11 @@ function apply_field_permission(frm, cdt, cdn, row_index, fieldname, is_enabled,
|
|||
let meta = frappe.get_meta("Sales Invoice Item");
|
||||
let field_meta = meta.fields.find(f => f.fieldname === fieldname);
|
||||
if (field_meta) {
|
||||
field_meta.read_only = is_enabled ? 0 : 1;
|
||||
if (fieldname === 'vat_amount') {
|
||||
field_meta.read_only = 1;
|
||||
} else {
|
||||
field_meta.read_only = is_enabled ? 0 : 1;
|
||||
}
|
||||
}
|
||||
} catch (meta_error) {
|
||||
// Ignore error
|
||||
|
|
@ -213,23 +246,112 @@ function apply_field_permission(frm, cdt, cdn, row_index, fieldname, is_enabled,
|
|||
}
|
||||
}
|
||||
|
||||
function auto_fill_vat_field(cdt, cdn, fieldname, item) {
|
||||
if (!item[fieldname] || item[fieldname] == 0) {
|
||||
function recalculate_all_vat_fields(frm, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
if (!item || !item.item_code) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Получаем текущие права доступа из кеша
|
||||
let cache_key = item.item_code + '|' + (item.item_tax_template || '');
|
||||
let permissions = frm.vat_permissions_cache ? frm.vat_permissions_cache[cache_key] : null;
|
||||
|
||||
if (!permissions) {
|
||||
// Если нет в кеше, получаем из API
|
||||
frappe.call({
|
||||
method: 'jey_erp.custom.sales_invoice_vat.get_vat_permissions_for_item',
|
||||
args: {
|
||||
item_code: item.item_code,
|
||||
item_tax_template: item.item_tax_template
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
if (!frm.vat_permissions_cache) {
|
||||
frm.vat_permissions_cache = {};
|
||||
}
|
||||
frm.vat_permissions_cache[cache_key] = r.message;
|
||||
recalculate_vat_fields_with_permissions(frm, cdt, cdn, r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
recalculate_vat_fields_with_permissions(frm, cdt, cdn, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate_vat_fields_with_permissions(frm, cdt, cdn, permissions) {
|
||||
let item = locals[cdt][cdn];
|
||||
let item_amount = flt(item.amount || 0);
|
||||
|
||||
if (item_amount <= 0) {
|
||||
// Если сумма 0 или меньше, очищаем все VAT поля
|
||||
frm.vat_field_names.forEach(function(fieldname) {
|
||||
if (item[fieldname] && item[fieldname] != 0) {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Пересчитываем только активные поля
|
||||
for (let fieldname in permissions) {
|
||||
if (permissions[fieldname]) {
|
||||
auto_fill_vat_field(cdt, cdn, fieldname, item, true); // true = force recalculate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function calculate_vat_amount(frm, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
// Вычислить VAT Amount только если есть vat_18_percent_with_amount и amount
|
||||
let vat_18_total = flt(item.vat_18_percent_with_amount || 0);
|
||||
let item_amount = flt(item.amount || 0);
|
||||
|
||||
if (vat_18_total > 0 && item_amount > 0) {
|
||||
let vat_only = vat_18_total - item_amount;
|
||||
frappe.model.set_value(cdt, cdn, 'vat_amount', vat_only);
|
||||
} else if (vat_18_total == 0 && item_amount > 0) {
|
||||
// Если vat_18_percent_with_amount пустое, но есть amount, очистить vat_amount
|
||||
frappe.model.set_value(cdt, cdn, 'vat_amount', 0);
|
||||
}
|
||||
}
|
||||
|
||||
function auto_fill_vat_field(cdt, cdn, fieldname, item, force_recalculate = false) {
|
||||
// Всегда пересчитываем vat_amount, для остальных полей проверяем есть ли значение
|
||||
if (!item[fieldname] || item[fieldname] == 0 || fieldname === 'vat_amount' || force_recalculate) {
|
||||
let item_amount = flt(item.amount || 0);
|
||||
|
||||
if (item_amount > 0) {
|
||||
let new_value = 0;
|
||||
|
||||
if (fieldname === 'vat_18_percent_with_amount') {
|
||||
new_value = item_amount * 1.18;
|
||||
} else if (fieldname === 'vat_amount') {
|
||||
// Вычислить VAT Amount
|
||||
let vat_18_total = flt(item.vat_18_percent_with_amount || 0);
|
||||
if (vat_18_total == 0) {
|
||||
vat_18_total = item_amount * 1.18;
|
||||
frappe.model.set_value(cdt, cdn, 'vat_18_percent_with_amount', vat_18_total);
|
||||
}
|
||||
new_value = vat_18_total - item_amount;
|
||||
} else if (fieldname === 'vat_0_percent_with_amount') {
|
||||
new_value = item_amount;
|
||||
} else if (fieldname === 'vat_exempt_amount') {
|
||||
} else if (fieldname === 'amount_without_vat') {
|
||||
new_value = item_amount;
|
||||
} else if (fieldname === 'vat_free_amount') {
|
||||
new_value = item_amount;
|
||||
}
|
||||
|
||||
if (new_value > 0) {
|
||||
if (new_value > 0 || fieldname === 'vat_amount') {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, new_value);
|
||||
}
|
||||
} else {
|
||||
// Если amount = 0, очищаем поле
|
||||
if (item[fieldname] && item[fieldname] != 0) {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -239,13 +361,49 @@ function clear_other_vat_fields(frm, cdt, cdn, current_field) {
|
|||
|
||||
if (item[current_field] && item[current_field] > 0) {
|
||||
frm.vat_field_names.forEach(function(fieldname) {
|
||||
if (fieldname !== current_field) {
|
||||
if (fieldname !== current_field && fieldname !== 'vat_amount') {
|
||||
// Не очищать vat_amount - оно вычисляется автоматически
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
});
|
||||
|
||||
// Если текущее поле не vat_18_percent_with_amount, очистить vat_amount
|
||||
if (current_field !== 'vat_18_percent_with_amount') {
|
||||
frappe.model.set_value(cdt, cdn, 'vat_amount', 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Дополнительная функция для ручного пересчета VAT Amount
|
||||
function recalculate_vat_amount_for_all_items(frm) {
|
||||
if (frm.doc.items) {
|
||||
frm.doc.items.forEach(function(item) {
|
||||
if (item.vat_18_percent_with_amount && item.amount) {
|
||||
let vat_only = flt(item.vat_18_percent_with_amount) - flt(item.amount);
|
||||
frappe.model.set_value("Sales Invoice Item", item.name, 'vat_amount', vat_only);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для добавления кнопки пересчета в форму (опционально)
|
||||
function add_recalculate_button(frm) {
|
||||
if (frm.doc.docstatus === 0) {
|
||||
frm.add_custom_button(__('Recalculate VAT Fields'), function() {
|
||||
if (frm.doc.items) {
|
||||
frm.doc.items.forEach(function(item) {
|
||||
recalculate_all_vat_fields(frm, "Sales Invoice Item", item.name);
|
||||
});
|
||||
}
|
||||
frappe.show_alert({
|
||||
message: __('VAT fields recalculated'),
|
||||
indicator: 'green'
|
||||
});
|
||||
}, __('VAT'));
|
||||
}
|
||||
}
|
||||
|
||||
// Интеграция с ERPNext SellingController
|
||||
$(document).ready(function() {
|
||||
function setup_erpnext_override() {
|
||||
if (erpnext && erpnext.selling && erpnext.selling.SellingController) {
|
||||
|
|
@ -272,6 +430,47 @@ $(document).ready(function() {
|
|||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Перехватываем calculate_taxes_and_totals для пересчета VAT полей
|
||||
if (!erpnext.selling.SellingController.prototype._original_calculate_taxes_and_totals) {
|
||||
let original_calculate = erpnext.selling.SellingController.prototype.calculate_taxes_and_totals;
|
||||
erpnext.selling.SellingController.prototype._original_calculate_taxes_and_totals = original_calculate;
|
||||
|
||||
erpnext.selling.SellingController.prototype.calculate_taxes_and_totals = function() {
|
||||
let result = this._original_calculate_taxes_and_totals.call(this);
|
||||
|
||||
if (this.frm.doc.doctype === 'Sales Invoice') {
|
||||
// Пересчитать все VAT поля для всех товаров после расчета налогов
|
||||
setTimeout(() => {
|
||||
if (this.frm.doc.items) {
|
||||
this.frm.doc.items.forEach((item) => {
|
||||
recalculate_all_vat_fields(this.frm, "Sales Invoice Item", item.name);
|
||||
});
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Перехватываем price_list_rate для пересчета при изменении цены
|
||||
if (!erpnext.selling.SellingController.prototype._original_price_list_rate) {
|
||||
let original_price_list_rate = erpnext.selling.SellingController.prototype.price_list_rate;
|
||||
erpnext.selling.SellingController.prototype._original_price_list_rate = original_price_list_rate;
|
||||
|
||||
erpnext.selling.SellingController.prototype.price_list_rate = function(doc, cdt, cdn) {
|
||||
let result = this._original_price_list_rate.call(this, doc, cdt, cdn);
|
||||
|
||||
if (this.frm.doc.doctype === 'Sales Invoice') {
|
||||
setTimeout(() => {
|
||||
recalculate_all_vat_fields(this.frm, cdt, cdn);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
} else {
|
||||
setTimeout(setup_erpnext_override, 500);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,480 @@
|
|||
// Файл: jey_erp/public/js/sales_order_vat.js
|
||||
|
||||
// Sales Order VAT fields logic
|
||||
frappe.ui.form.on('Sales Order', {
|
||||
onload: function(frm) {
|
||||
setup_vat_fields_logic(frm);
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.docstatus === 0) {
|
||||
wait_for_grid_ready(frm, function() {
|
||||
update_all_vat_permissions(frm);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Sales Order Item', {
|
||||
items_add: function(frm, cdt, cdn) {
|
||||
wait_for_grid_ready(frm, function() {
|
||||
update_item_vat_permissions(frm, cdt, cdn);
|
||||
});
|
||||
},
|
||||
|
||||
item_code: function(frm, cdt, cdn) {
|
||||
update_item_vat_permissions(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
item_tax_template: function(frm, cdt, cdn) {
|
||||
update_item_vat_permissions(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
// Пересчет при изменении rate
|
||||
rate: function(frm, cdt, cdn) {
|
||||
// Небольшая задержка, чтобы amount успел пересчитаться
|
||||
setTimeout(() => {
|
||||
recalculate_all_vat_fields(frm, cdt, cdn);
|
||||
}, 100);
|
||||
},
|
||||
|
||||
// Пересчет при изменении qty
|
||||
qty: function(frm, cdt, cdn) {
|
||||
// Небольшая задержка, чтобы amount успел пересчитаться
|
||||
setTimeout(() => {
|
||||
recalculate_all_vat_fields(frm, cdt, cdn);
|
||||
}, 100);
|
||||
},
|
||||
|
||||
amount: function(frm, cdt, cdn) {
|
||||
// Пересчитать все VAT поля при изменении основной суммы
|
||||
recalculate_all_vat_fields(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
vat_18_percent_with_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_18_percent_with_amount');
|
||||
// Пересчитать VAT Amount при изменении суммы с НДС
|
||||
calculate_vat_amount(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
vat_0_percent_with_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_0_percent_with_amount');
|
||||
},
|
||||
|
||||
amount_without_vat: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'amount_without_vat');
|
||||
},
|
||||
|
||||
vat_free_amount: function(frm, cdt, cdn) {
|
||||
clear_other_vat_fields(frm, cdt, cdn, 'vat_free_amount');
|
||||
}
|
||||
});
|
||||
|
||||
function setup_vat_fields_logic(frm) {
|
||||
frm.vat_field_names = [
|
||||
'vat_18_percent_with_amount',
|
||||
'vat_amount',
|
||||
'vat_0_percent_with_amount',
|
||||
'amount_without_vat',
|
||||
'vat_free_amount'
|
||||
];
|
||||
|
||||
frm.vat_permissions_cache = {};
|
||||
}
|
||||
|
||||
function wait_for_grid_ready(frm, callback, max_attempts = 20, attempt = 0) {
|
||||
if (attempt >= max_attempts) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
let grid = frm.fields_dict.items.grid;
|
||||
if (grid && grid.grid_rows && grid.grid_rows.length >= frm.doc.items.length) {
|
||||
let all_ready = true;
|
||||
for (let i = 0; i < frm.doc.items.length; i++) {
|
||||
if (!grid.grid_rows[i] || !grid.grid_rows[i].docfields) {
|
||||
all_ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_ready) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
wait_for_grid_ready(frm, callback, max_attempts, attempt + 1);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function update_all_vat_permissions(frm) {
|
||||
if (frm.doc.items) {
|
||||
frm.doc.items.forEach(function(item, idx) {
|
||||
if (item.item_code) {
|
||||
update_item_vat_permissions(frm, frm.fields_dict.items.grid.doctype, item.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function update_item_vat_permissions(frm, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
if (!item.item_code) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cache_key = item.item_code + '|' + (item.item_tax_template || '');
|
||||
|
||||
if (frm.vat_permissions_cache && frm.vat_permissions_cache[cache_key]) {
|
||||
apply_vat_permissions(frm, cdt, cdn, frm.vat_permissions_cache[cache_key]);
|
||||
return;
|
||||
}
|
||||
|
||||
let row_index = frm.doc.items.findIndex(i => i.name === cdn);
|
||||
if (row_index >= 0) {
|
||||
try {
|
||||
frm.fields_dict.items.grid.grid_rows[row_index].toggle_editable_row(false);
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: 'jey_erp.custom.sales_order_vat.get_vat_permissions_for_item',
|
||||
args: {
|
||||
item_code: item.item_code,
|
||||
item_tax_template: item.item_tax_template
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
if (!frm.vat_permissions_cache) {
|
||||
frm.vat_permissions_cache = {};
|
||||
}
|
||||
frm.vat_permissions_cache[cache_key] = r.message;
|
||||
|
||||
apply_vat_permissions(frm, cdt, cdn, r.message);
|
||||
}
|
||||
},
|
||||
always: function() {
|
||||
if (row_index >= 0) {
|
||||
try {
|
||||
frm.fields_dict.items.grid.grid_rows[row_index].toggle_editable_row(true);
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function apply_vat_permissions(frm, cdt, cdn, permissions) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
let row_index = frm.doc.items.findIndex(doc_item => doc_item.name === cdn);
|
||||
|
||||
if (row_index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
wait_for_row_ready(frm, row_index, function() {
|
||||
frm.vat_field_names.forEach(function(fieldname) {
|
||||
try {
|
||||
apply_field_permission(frm, cdt, cdn, row_index, fieldname, permissions[fieldname], item);
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
});
|
||||
|
||||
frm.fields_dict.items.grid.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
function wait_for_row_ready(frm, row_index, callback, max_attempts = 10, attempt = 0) {
|
||||
if (attempt >= max_attempts) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
let grid = frm.fields_dict.items.grid;
|
||||
if (grid && grid.grid_rows && grid.grid_rows[row_index] && grid.grid_rows[row_index].docfields) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
wait_for_row_ready(frm, row_index, callback, max_attempts, attempt + 1);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function apply_field_permission(frm, cdt, cdn, row_index, fieldname, is_enabled, item) {
|
||||
let grid_row = frm.fields_dict.items.grid.grid_rows[row_index];
|
||||
if (grid_row && grid_row.docfields) {
|
||||
let docfield = grid_row.docfields.find(df => df.fieldname === fieldname);
|
||||
if (docfield) {
|
||||
// VAT Amount всегда только для чтения
|
||||
if (fieldname === 'vat_amount') {
|
||||
docfield.read_only = 1;
|
||||
} else {
|
||||
docfield.read_only = is_enabled ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let meta = frappe.get_meta("Sales Order Item");
|
||||
let field_meta = meta.fields.find(f => f.fieldname === fieldname);
|
||||
if (field_meta) {
|
||||
if (fieldname === 'vat_amount') {
|
||||
field_meta.read_only = 1;
|
||||
} else {
|
||||
field_meta.read_only = is_enabled ? 0 : 1;
|
||||
}
|
||||
}
|
||||
} catch (meta_error) {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
if (is_enabled) {
|
||||
auto_fill_vat_field(cdt, cdn, fieldname, item);
|
||||
} else {
|
||||
if (item[fieldname] && item[fieldname] != 0) {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate_all_vat_fields(frm, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
if (!item || !item.item_code) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Получаем текущие права доступа из кеша
|
||||
let cache_key = item.item_code + '|' + (item.item_tax_template || '');
|
||||
let permissions = frm.vat_permissions_cache ? frm.vat_permissions_cache[cache_key] : null;
|
||||
|
||||
if (!permissions) {
|
||||
// Если нет в кеше, получаем из API
|
||||
frappe.call({
|
||||
method: 'jey_erp.custom.sales_order_vat.get_vat_permissions_for_item',
|
||||
args: {
|
||||
item_code: item.item_code,
|
||||
item_tax_template: item.item_tax_template
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
if (!frm.vat_permissions_cache) {
|
||||
frm.vat_permissions_cache = {};
|
||||
}
|
||||
frm.vat_permissions_cache[cache_key] = r.message;
|
||||
recalculate_vat_fields_with_permissions(frm, cdt, cdn, r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
recalculate_vat_fields_with_permissions(frm, cdt, cdn, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate_vat_fields_with_permissions(frm, cdt, cdn, permissions) {
|
||||
let item = locals[cdt][cdn];
|
||||
let item_amount = flt(item.amount || 0);
|
||||
|
||||
if (item_amount <= 0) {
|
||||
// Если сумма 0 или меньше, очищаем все VAT поля
|
||||
frm.vat_field_names.forEach(function(fieldname) {
|
||||
if (item[fieldname] && item[fieldname] != 0) {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Пересчитываем только активные поля
|
||||
for (let fieldname in permissions) {
|
||||
if (permissions[fieldname]) {
|
||||
auto_fill_vat_field(cdt, cdn, fieldname, item, true); // true = force recalculate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function calculate_vat_amount(frm, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
// Вычислить VAT Amount только если есть vat_18_percent_with_amount и amount
|
||||
let vat_18_total = flt(item.vat_18_percent_with_amount || 0);
|
||||
let item_amount = flt(item.amount || 0);
|
||||
|
||||
if (vat_18_total > 0 && item_amount > 0) {
|
||||
let vat_only = vat_18_total - item_amount;
|
||||
frappe.model.set_value(cdt, cdn, 'vat_amount', vat_only);
|
||||
} else if (vat_18_total == 0 && item_amount > 0) {
|
||||
// Если vat_18_percent_with_amount пустое, но есть amount, очистить vat_amount
|
||||
frappe.model.set_value(cdt, cdn, 'vat_amount', 0);
|
||||
}
|
||||
}
|
||||
|
||||
function auto_fill_vat_field(cdt, cdn, fieldname, item, force_recalculate = false) {
|
||||
// Всегда пересчитываем vat_amount, для остальных полей проверяем есть ли значение
|
||||
if (!item[fieldname] || item[fieldname] == 0 || fieldname === 'vat_amount' || force_recalculate) {
|
||||
let item_amount = flt(item.amount || 0);
|
||||
|
||||
if (item_amount > 0) {
|
||||
let new_value = 0;
|
||||
|
||||
if (fieldname === 'vat_18_percent_with_amount') {
|
||||
new_value = item_amount * 1.18;
|
||||
} else if (fieldname === 'vat_amount') {
|
||||
// Вычислить VAT Amount
|
||||
let vat_18_total = flt(item.vat_18_percent_with_amount || 0);
|
||||
if (vat_18_total == 0) {
|
||||
vat_18_total = item_amount * 1.18;
|
||||
frappe.model.set_value(cdt, cdn, 'vat_18_percent_with_amount', vat_18_total);
|
||||
}
|
||||
new_value = vat_18_total - item_amount;
|
||||
} else if (fieldname === 'vat_0_percent_with_amount') {
|
||||
new_value = item_amount;
|
||||
} else if (fieldname === 'amount_without_vat') {
|
||||
new_value = item_amount;
|
||||
} else if (fieldname === 'vat_free_amount') {
|
||||
new_value = item_amount;
|
||||
}
|
||||
|
||||
if (new_value > 0 || fieldname === 'vat_amount') {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, new_value);
|
||||
}
|
||||
} else {
|
||||
// Если amount = 0, очищаем поле
|
||||
if (item[fieldname] && item[fieldname] != 0) {
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clear_other_vat_fields(frm, cdt, cdn, current_field) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
if (item[current_field] && item[current_field] > 0) {
|
||||
frm.vat_field_names.forEach(function(fieldname) {
|
||||
if (fieldname !== current_field && fieldname !== 'vat_amount') {
|
||||
// Не очищать vat_amount - оно вычисляется автоматически
|
||||
frappe.model.set_value(cdt, cdn, fieldname, 0.0);
|
||||
}
|
||||
});
|
||||
|
||||
// Если текущее поле не vat_18_percent_with_amount, очистить vat_amount
|
||||
if (current_field !== 'vat_18_percent_with_amount') {
|
||||
frappe.model.set_value(cdt, cdn, 'vat_amount', 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Дополнительная функция для ручного пересчета VAT Amount
|
||||
function recalculate_vat_amount_for_all_items(frm) {
|
||||
if (frm.doc.items) {
|
||||
frm.doc.items.forEach(function(item) {
|
||||
if (item.vat_18_percent_with_amount && item.amount) {
|
||||
let vat_only = flt(item.vat_18_percent_with_amount) - flt(item.amount);
|
||||
frappe.model.set_value("Sales Order Item", item.name, 'vat_amount', vat_only);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для добавления кнопки пересчета в форму (опционально)
|
||||
function add_recalculate_button(frm) {
|
||||
if (frm.doc.docstatus === 0) {
|
||||
frm.add_custom_button(__('Recalculate VAT Fields'), function() {
|
||||
if (frm.doc.items) {
|
||||
frm.doc.items.forEach(function(item) {
|
||||
recalculate_all_vat_fields(frm, "Sales Order Item", item.name);
|
||||
});
|
||||
}
|
||||
frappe.show_alert({
|
||||
message: __('VAT fields recalculated'),
|
||||
indicator: 'green'
|
||||
});
|
||||
}, __('VAT'));
|
||||
}
|
||||
}
|
||||
|
||||
// Интеграция с ERPNext SellingController
|
||||
$(document).ready(function() {
|
||||
function setup_erpnext_override() {
|
||||
if (erpnext && erpnext.selling && erpnext.selling.SellingController) {
|
||||
if (!erpnext.selling.SellingController.prototype._original_item_code_so) {
|
||||
let original_item_code = erpnext.selling.SellingController.prototype.item_code;
|
||||
erpnext.selling.SellingController.prototype._original_item_code_so = original_item_code;
|
||||
|
||||
erpnext.selling.SellingController.prototype.item_code = function(doc, cdt, cdn) {
|
||||
let result = this._original_item_code_so.call(this, doc, cdt, cdn);
|
||||
|
||||
if (this.frm.doc.doctype === 'Sales Order') {
|
||||
let check_item_loaded = () => {
|
||||
let item = locals[cdt][cdn];
|
||||
if (item && item.item_code && (item.rate || item.price_list_rate)) {
|
||||
update_item_vat_permissions(this.frm, cdt, cdn);
|
||||
} else {
|
||||
setTimeout(check_item_loaded, 100);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(check_item_loaded, 200);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Перехватываем calculate_taxes_and_totals для пересчета VAT полей
|
||||
if (!erpnext.selling.SellingController.prototype._original_calculate_taxes_and_totals_so) {
|
||||
let original_calculate = erpnext.selling.SellingController.prototype.calculate_taxes_and_totals;
|
||||
erpnext.selling.SellingController.prototype._original_calculate_taxes_and_totals_so = original_calculate;
|
||||
|
||||
erpnext.selling.SellingController.prototype.calculate_taxes_and_totals = function() {
|
||||
let result = this._original_calculate_taxes_and_totals_so.call(this);
|
||||
|
||||
if (this.frm.doc.doctype === 'Sales Order') {
|
||||
// Пересчитать все VAT поля для всех товаров после расчета налогов
|
||||
setTimeout(() => {
|
||||
if (this.frm.doc.items) {
|
||||
this.frm.doc.items.forEach((item) => {
|
||||
recalculate_all_vat_fields(this.frm, "Sales Order Item", item.name);
|
||||
});
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Перехватываем price_list_rate для пересчета при изменении цены
|
||||
if (!erpnext.selling.SellingController.prototype._original_price_list_rate_so) {
|
||||
let original_price_list_rate = erpnext.selling.SellingController.prototype.price_list_rate;
|
||||
erpnext.selling.SellingController.prototype._original_price_list_rate_so = original_price_list_rate;
|
||||
|
||||
erpnext.selling.SellingController.prototype.price_list_rate = function(doc, cdt, cdn) {
|
||||
let result = this._original_price_list_rate_so.call(this, doc, cdt, cdn);
|
||||
|
||||
if (this.frm.doc.doctype === 'Sales Order') {
|
||||
setTimeout(() => {
|
||||
recalculate_all_vat_fields(this.frm, cdt, cdn);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
} else {
|
||||
setTimeout(setup_erpnext_override, 500);
|
||||
}
|
||||
}
|
||||
|
||||
setup_erpnext_override();
|
||||
});
|
||||
Loading…
Reference in New Issue