bug fix
This commit is contained in:
parent
44bed2e82f
commit
061a71f93d
|
|
@ -305,8 +305,8 @@ bench --site site1 clear-cache
|
|||
# 4. Restart bench (if needed)
|
||||
bench restart
|
||||
|
||||
# 5. (Optional) Run data migration script
|
||||
# bench --site site1 execute invoice_az.patches.migrate_tax_templates
|
||||
# 5. (Optional) Run data migration script (if needed)
|
||||
# Create and execute migration function directly
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1405,7 +1405,9 @@ VATETaxes.import = {
|
|||
|
||||
// Journal Entry List
|
||||
frappe.listview_settings['Journal Entry'] = {
|
||||
add_fields: ['posting_date', 'voucher_type', 'total_debit', 'total_credit', 'docstatus', 'expense_income_type'],
|
||||
add_fields: ['posting_date', 'voucher_type', 'total_debit', 'total_credit', 'docstatus', 'expense_income_type', 'etaxes_document_type', 'loaded_from_etaxes'],
|
||||
|
||||
hide_name_column: false,
|
||||
|
||||
get_indicator: function(doc) {
|
||||
if (doc.docstatus == 1) {
|
||||
|
|
@ -1425,6 +1427,14 @@ frappe.listview_settings['Journal Entry'] = {
|
|||
return '<span class="indicator-pill red">' + value + '</span>';
|
||||
}
|
||||
return value || '';
|
||||
},
|
||||
|
||||
etaxes_document_type: function(value, field, doc) {
|
||||
// Show blue badge "ƏDV" only for documents loaded from e-taxes
|
||||
if (value === 'ədv' && (doc.loaded_from_etaxes === 1 || doc.loaded_from_etaxes === '1')) {
|
||||
return '<span class="indicator-pill blue" title="' + __('VAT Operation from E-Taxes') + '">ƏDV</span>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -75,10 +75,11 @@ def after_install():
|
|||
def after_migrate():
|
||||
"""Run migration tasks"""
|
||||
from invoice_az.auth import setup_token_renewal
|
||||
from invoice_az.install import create_journal_entry_custom_fields
|
||||
from invoice_az.install import create_journal_entry_custom_fields, create_supplier_custom_fields
|
||||
|
||||
setup_token_renewal()
|
||||
create_journal_entry_custom_fields()
|
||||
create_supplier_custom_fields()
|
||||
|
||||
# Fixtures for master data
|
||||
# ------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
"""Installation hooks for Invoice Az app"""
|
||||
|
||||
import frappe
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
|
||||
|
||||
def after_install():
|
||||
"""Run after app installation"""
|
||||
create_journal_entry_custom_fields()
|
||||
create_supplier_custom_fields()
|
||||
|
||||
|
||||
def create_journal_entry_custom_fields():
|
||||
"""Create custom fields for Journal Entry doctype"""
|
||||
|
||||
# Always define all fields - create_custom_fields will handle updates
|
||||
custom_fields = {
|
||||
'Journal Entry': [
|
||||
{
|
||||
'fieldname': 'expense_income_type',
|
||||
'label': 'Expense/Income Type',
|
||||
'fieldtype': 'Select',
|
||||
'options': '\nExpense\nIncome',
|
||||
'hidden': 1,
|
||||
'in_list_view': 0,
|
||||
'insert_after': 'user_remark'
|
||||
},
|
||||
{
|
||||
'fieldname': 'customer_name_etaxes',
|
||||
'label': 'Customer Name (E-Taxes)',
|
||||
'fieldtype': 'Data',
|
||||
'hidden': 1,
|
||||
'in_list_view': 0,
|
||||
'insert_after': 'expense_income_type'
|
||||
},
|
||||
{
|
||||
'fieldname': 'etaxes_document_type',
|
||||
'label': 'E-Taxes Document Type',
|
||||
'fieldtype': 'Select',
|
||||
'options': '\nədv',
|
||||
'hidden': 1,
|
||||
'in_list_view': 1,
|
||||
'insert_after': 'customer_name_etaxes'
|
||||
},
|
||||
{
|
||||
'fieldname': 'loaded_from_etaxes',
|
||||
'label': 'Loaded from E-Taxes',
|
||||
'fieldtype': 'Check',
|
||||
'default': '0',
|
||||
'hidden': 1,
|
||||
'in_list_view': 0,
|
||||
'insert_after': 'etaxes_document_type'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
frappe.logger().info("Creating/updating custom fields for Journal Entry")
|
||||
create_custom_fields(custom_fields, update=True)
|
||||
|
||||
# Clear cache for Journal Entry
|
||||
frappe.clear_cache(doctype='Journal Entry')
|
||||
frappe.logger().info("Custom fields created/updated successfully for Journal Entry")
|
||||
|
||||
|
||||
def create_supplier_custom_fields():
|
||||
"""Create custom fields for Supplier doctype"""
|
||||
|
||||
custom_fields = {
|
||||
'Supplier': [
|
||||
{
|
||||
'fieldname': 'patronimyc',
|
||||
'label': 'Patronymic (Father\'s Name)',
|
||||
'fieldtype': 'Data',
|
||||
'insert_after': 'first_name_individual',
|
||||
'depends_on': 'eval:doc.supplier_type=="Individual"'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
frappe.logger().info("Creating/updating custom fields for Supplier")
|
||||
create_custom_fields(custom_fields, update=True)
|
||||
|
||||
# Clear cache for Supplier
|
||||
frappe.clear_cache(doctype='Supplier')
|
||||
frappe.logger().info("Custom fields created/updated successfully for Supplier")
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
[pre_model_sync]
|
||||
# Patches added in this section will be executed before doctypes are migrated
|
||||
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
|
||||
|
||||
[post_model_sync]
|
||||
# Patches added in this section will be executed after doctypes are migrated
|
||||
invoice_az.patches.v1_0.rename_agricultural_to_purchase_type
|
||||
# TEMPORARY: cleanup_agricultural_fields is now executed via hooks.py after_migrate()
|
||||
# invoice_az.patches.cleanup_agricultural_fields
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
"""Remove old agricultural fields and hide E-Taxes Purchase Act ID field"""
|
||||
|
||||
# List of old fields to delete (already migrated to new fields)
|
||||
# These fields were migrated by rename_agricultural_to_purchase_type patch:
|
||||
# - agricultural_goods → purchase_type
|
||||
# - purchase_confirmation_doc_type → act_type
|
||||
# - agricultural_country was never used and should be removed
|
||||
old_fields_to_delete = [
|
||||
"agricultural_goods",
|
||||
"agricultural_country",
|
||||
"purchase_confirmation_doc_type"
|
||||
]
|
||||
|
||||
# Doctypes to clean up
|
||||
doctypes = ["Purchase Invoice", "Purchase Order"]
|
||||
|
||||
# Delete old custom fields
|
||||
deleted_count = 0
|
||||
for doctype in doctypes:
|
||||
for fieldname in old_fields_to_delete:
|
||||
try:
|
||||
field_name = f"{doctype}-{fieldname}"
|
||||
if frappe.db.exists("Custom Field", field_name):
|
||||
frappe.delete_doc("Custom Field", field_name, force=True, ignore_permissions=True)
|
||||
deleted_count += 1
|
||||
print(f"Deleted custom field: {field_name}")
|
||||
except Exception as e:
|
||||
print(f"Error deleting {field_name}: {str(e)}")
|
||||
|
||||
if deleted_count > 0:
|
||||
frappe.db.commit()
|
||||
print(f"Committed deletion of {deleted_count} fields")
|
||||
|
||||
# Hide E-Taxes Purchase Act ID field in Purchase Invoice
|
||||
try:
|
||||
field_name = "Purchase Invoice-etaxes_purchase_id"
|
||||
if frappe.db.exists("Custom Field", field_name):
|
||||
field_doc = frappe.get_doc("Custom Field", field_name)
|
||||
if not field_doc.hidden:
|
||||
field_doc.hidden = 1
|
||||
field_doc.flags.ignore_validate = True
|
||||
field_doc.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
print(f"Hidden field: {field_name}")
|
||||
else:
|
||||
print(f"Field already hidden: {field_name}")
|
||||
except Exception as e:
|
||||
print(f"Error hiding etaxes_purchase_id: {str(e)}")
|
||||
|
||||
# Fix section placement to prevent standard ERPNext fields (is_return, apply_tds, amended_from)
|
||||
# from appearing in the E-Taxes section
|
||||
# Correct order:
|
||||
# amended_from (standard)
|
||||
# is_taxes_doc (custom)
|
||||
# purchase_type (custom)
|
||||
# act_type (custom)
|
||||
# etaxes_agricultural_section (Section Break)
|
||||
# act_kind, etaxes_purchase_id, etc.
|
||||
|
||||
# Move is_taxes_doc to after amended_from
|
||||
try:
|
||||
is_taxes_field_name = "Purchase Invoice-is_taxes_doc"
|
||||
if frappe.db.exists("Custom Field", is_taxes_field_name):
|
||||
is_taxes_doc = frappe.get_doc("Custom Field", is_taxes_field_name)
|
||||
if is_taxes_doc.insert_after != "amended_from":
|
||||
is_taxes_doc.insert_after = "amended_from"
|
||||
is_taxes_doc.flags.ignore_validate = True
|
||||
is_taxes_doc.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
print(f"Moved is_taxes_doc to after 'amended_from': {is_taxes_field_name}")
|
||||
else:
|
||||
print(f"is_taxes_doc already in correct position: {is_taxes_field_name}")
|
||||
except Exception as e:
|
||||
print(f"Error moving is_taxes_doc: {str(e)}")
|
||||
|
||||
# Move etaxes_agricultural_section to after act_type (not amended_from!)
|
||||
try:
|
||||
section_field_name = "Purchase Invoice-etaxes_agricultural_section"
|
||||
if frappe.db.exists("Custom Field", section_field_name):
|
||||
section_doc = frappe.get_doc("Custom Field", section_field_name)
|
||||
if section_doc.insert_after != "act_type":
|
||||
section_doc.insert_after = "act_type"
|
||||
section_doc.flags.ignore_validate = True
|
||||
section_doc.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
print(f"Moved section to after 'act_type': {section_field_name}")
|
||||
else:
|
||||
print(f"Section already in correct position: {section_field_name}")
|
||||
except Exception as e:
|
||||
print(f"Error moving section: {str(e)}")
|
||||
|
||||
# Clear cache to apply changes
|
||||
frappe.clear_cache()
|
||||
print("Cache cleared. Old agricultural fields removed, E-Taxes Purchase Act ID hidden, and section placement fixed.")
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import frappe
|
||||
|
||||
def execute():
|
||||
"""
|
||||
Migrate agricultural fields to purchase_type fields
|
||||
|
||||
This migration renames:
|
||||
- agricultural_goods → purchase_type (Purchase Order, Purchase Invoice)
|
||||
- purchase_confirmation_doc_type → act_type (Purchase Order, Purchase Invoice)
|
||||
- agricultural_tax_amount → purchase_tax_amount (Purchase Invoice Item)
|
||||
"""
|
||||
|
||||
frappe.logger().info("Starting migration: rename_agricultural_to_purchase_type")
|
||||
|
||||
# Purchase Order - migrate agricultural_goods to purchase_type
|
||||
if frappe.db.has_column("Purchase Order", "agricultural_goods"):
|
||||
frappe.logger().info("Migrating Purchase Order: agricultural_goods → purchase_type")
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabPurchase Order`
|
||||
SET purchase_type = agricultural_goods
|
||||
WHERE agricultural_goods IS NOT NULL
|
||||
""")
|
||||
frappe.logger().info("Purchase Order: agricultural_goods migration complete")
|
||||
|
||||
# Purchase Order - migrate purchase_confirmation_doc_type to act_type
|
||||
if frappe.db.has_column("Purchase Order", "purchase_confirmation_doc_type"):
|
||||
frappe.logger().info("Migrating Purchase Order: purchase_confirmation_doc_type → act_type")
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabPurchase Order`
|
||||
SET act_type = purchase_confirmation_doc_type
|
||||
WHERE purchase_confirmation_doc_type IS NOT NULL
|
||||
""")
|
||||
frappe.logger().info("Purchase Order: purchase_confirmation_doc_type migration complete")
|
||||
|
||||
# Purchase Invoice - migrate agricultural_goods to purchase_type
|
||||
if frappe.db.has_column("Purchase Invoice", "agricultural_goods"):
|
||||
frappe.logger().info("Migrating Purchase Invoice: agricultural_goods → purchase_type")
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabPurchase Invoice`
|
||||
SET purchase_type = agricultural_goods
|
||||
WHERE agricultural_goods IS NOT NULL
|
||||
""")
|
||||
frappe.logger().info("Purchase Invoice: agricultural_goods migration complete")
|
||||
|
||||
# Purchase Invoice - migrate purchase_confirmation_doc_type to act_type
|
||||
if frappe.db.has_column("Purchase Invoice", "purchase_confirmation_doc_type"):
|
||||
frappe.logger().info("Migrating Purchase Invoice: purchase_confirmation_doc_type → act_type")
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabPurchase Invoice`
|
||||
SET act_type = purchase_confirmation_doc_type
|
||||
WHERE purchase_confirmation_doc_type IS NOT NULL
|
||||
""")
|
||||
frappe.logger().info("Purchase Invoice: purchase_confirmation_doc_type migration complete")
|
||||
|
||||
# Purchase Invoice Item - migrate agricultural_tax_amount to purchase_tax_amount
|
||||
if frappe.db.has_column("Purchase Invoice Item", "agricultural_tax_amount"):
|
||||
frappe.logger().info("Migrating Purchase Invoice Item: agricultural_tax_amount → purchase_tax_amount")
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabPurchase Invoice Item`
|
||||
SET purchase_tax_amount = agricultural_tax_amount
|
||||
WHERE agricultural_tax_amount IS NOT NULL
|
||||
""")
|
||||
frappe.logger().info("Purchase Invoice Item: agricultural_tax_amount migration complete")
|
||||
|
||||
# Commit changes
|
||||
frappe.db.commit()
|
||||
|
||||
frappe.logger().info("Migration completed successfully: rename_agricultural_to_purchase_type")
|
||||
print("Migration completed: agricultural fields renamed to purchase_type")
|
||||
|
|
@ -934,13 +934,25 @@ def build_act_payload(doc, serial_number):
|
|||
# Get supplier details
|
||||
supplier = frappe.get_doc("Supplier", doc.supplier)
|
||||
|
||||
# Build full name from individual fields (first name + patronymic + last name)
|
||||
# E-Taxes API requires exact match with their database
|
||||
full_name_parts = []
|
||||
if supplier.first_name_individual:
|
||||
full_name_parts.append(supplier.first_name_individual)
|
||||
if supplier.patronimyc:
|
||||
full_name_parts.append(supplier.patronimyc)
|
||||
if supplier.last_name_individual:
|
||||
full_name_parts.append(supplier.last_name_individual)
|
||||
|
||||
full_name = " ".join(full_name_parts) if full_name_parts else supplier.supplier_name
|
||||
|
||||
# Build seller object (physical person)
|
||||
# IMPORTANT: Only include required fields (fin, passportSerialNumber, fullName, address)
|
||||
# Extra fields (dateOfBirth, firstname, lastname, phoneNumber) cause API rejection
|
||||
seller = {
|
||||
"fin": supplier.fin,
|
||||
"passportSerialNumber": supplier.passport_serial_number,
|
||||
"fullName": supplier.supplier_name,
|
||||
"fullName": full_name,
|
||||
"address": "" # Empty string instead of None
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,16 +211,17 @@ def update_supplier_from_etaxes(supplier_name):
|
|||
supplier.last_name_individual = data.get("lastname")
|
||||
updated_fields.append("Last Name")
|
||||
|
||||
# Patronymic (middle name / father's name)
|
||||
if data.get("patronimyc"):
|
||||
supplier.patronimyc = data.get("patronimyc")
|
||||
updated_fields.append("Patronymic")
|
||||
|
||||
# Phone number (optional)
|
||||
if data.get("phone") and not supplier.phone_number_individual:
|
||||
supplier.phone_number_individual = data.get("phone")
|
||||
updated_fields.append("Phone Number")
|
||||
|
||||
# Update supplier name if needed (use fullName from E-Taxes)
|
||||
if data.get("fullName") and supplier.supplier_name != data.get("fullName"):
|
||||
old_name = supplier.supplier_name
|
||||
supplier.supplier_name = data.get("fullName")
|
||||
updated_fields.append(f"Supplier Name (from '{old_name}' to '{data.get('fullName')}')")
|
||||
# Note: Supplier Name is not updated to preserve user's custom name
|
||||
|
||||
# Save supplier
|
||||
supplier.save()
|
||||
|
|
|
|||
|
|
@ -749,6 +749,8 @@ def create_journal_entry_from_vat_operation(operation_data, company, create_as_d
|
|||
je.user_remark = f"VAT Operation: {operation_data.get('explanation', '')}"
|
||||
je.expense_income_type = expense_income_type
|
||||
je.customer_name_etaxes = operation_data.get('name', '')
|
||||
je.etaxes_document_type = 'ədv' # Always ədv for VAT operations
|
||||
je.loaded_from_etaxes = 1 # Flag that this was loaded from e-taxes
|
||||
|
||||
# Строка 1: Дебет
|
||||
debit_entry = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue