8.3 KiB
Migration Guide: Tax Template to Tax Type
Overview
This guide explains how to migrate existing Purchase Invoices from the old item_tax_template system to the new tax_type field.
Background
Before: Agricultural purchase invoices used item_tax_template field to determine tax rates:
- "ƏDV 0%" → 0% VAT
- "ƏDV-dən azadolma" → 0% VAT (exempt)
- "ƏDV 2% cəlb" → 5% tax
After: Agricultural purchase invoices now use tax_type field:
- "Tax Free" → 0% VAT
- "Taxable" → 5% tax
Who Needs to Migrate?
You need to migrate if you have:
- Purchase Invoices with
agricultural_goods = True - Items with
item_tax_templateset - Items with
tax_type= None (empty)
Migration Options
Option 1: Manual Migration (Recommended for Few Documents)
For each Purchase Invoice:
- Open the document in edit mode
- Check if
agricultural_goodsis checked - For each item in the items table:
- Look at the old
item_tax_templatevalue - Set new
tax_typebased on mapping:- If template is "ƏDV 2% cəlb" → select "Taxable"
- If template is anything else → select "Tax Free"
- Look at the old
- Save the document
agricultural_tax_amountwill calculate automatically
Option 2: Bulk Migration Script (For Many Documents)
Run this script in bench console:
import frappe
def migrate_tax_templates_to_tax_type():
"""
Migrate item_tax_template values to tax_type for agricultural invoices
Mapping:
- "ƏDV 2% cəlb" → "Taxable" (5% tax)
- All others → "Tax Free" (0% VAT)
"""
# Get all agricultural purchase invoices (exclude cancelled)
invoices = frappe.get_all(
"Purchase Invoice",
filters={
"agricultural_goods": 1,
"docstatus": ["<", 2] # Draft or Submitted
},
fields=["name", "docstatus"]
)
print(f"\nFound {len(invoices)} agricultural purchase invoices")
print("="*60)
migrated_count = 0
skipped_count = 0
for inv in invoices:
doc = frappe.get_doc("Purchase Invoice", inv.name)
modified = False
for item in doc.items:
# Skip if tax_type already set
if item.tax_type:
continue
# Only migrate if item_tax_template exists
if not item.item_tax_template:
continue
# Map template to tax_type
if item.item_tax_template == "ƏDV 2% cəlb":
item.tax_type = "Taxable"
# Calculate 5% tax
item.agricultural_tax_amount = item.amount * 0.05
else:
# ƏDV 0%, ƏDV-dən azadolma, etc.
item.tax_type = "Tax Free"
item.agricultural_tax_amount = 0
modified = True
if modified:
try:
# For submitted documents, allow update
if doc.docstatus == 1:
doc.flags.ignore_validate_update_after_submit = True
doc.save()
migrated_count += 1
print(f"✓ Migrated: {doc.name} ({len(doc.items)} items)")
except Exception as e:
print(f"✗ Error migrating {doc.name}: {str(e)}")
else:
skipped_count += 1
print(f"⊘ Skipped: {doc.name} (no items to migrate)")
print("="*60)
print(f"\nMigration Complete:")
print(f" - Migrated: {migrated_count}")
print(f" - Skipped: {skipped_count}")
print(f" - Total: {len(invoices)}")
print()
# Run the migration
migrate_tax_templates_to_tax_type()
How to run:
cd /home/frappe/frappe-bench
bench --site site1 console
# Paste the script above, then press Enter
Migration Mapping Table
| Old Item Tax Template | New Tax Type | Tax Amount Calculation |
|---|---|---|
| ƏDV 0% | Tax Free | 0% (no tax) |
| ƏDV-dən azadolma | Tax Free | 0% (no tax) |
| ƏDV 2% cəlb | Taxable | 5% of amount |
| (empty) | Tax Free | 0% (default) |
| Any other | Tax Free | 0% (safe default) |
Pre-Migration Checklist
- Backup your database:
bench --site site1 backup - Test the migration script on a single document first
- Verify the mapping is correct for your use case
- Note: Cancelled documents (docstatus = 2) are NOT migrated
Post-Migration Verification
After migration, verify:
- Check migrated documents:
import frappe
# Get sample migrated document
doc = frappe.get_doc("Purchase Invoice", "ACC-PINV-2026-XXXXX")
for item in doc.items:
print(f"Item: {item.item_code}")
print(f" Old: {item.item_tax_template}")
print(f" New: {item.tax_type}")
print(f" Tax: {item.agricultural_tax_amount}")
print()
- Check counts:
import frappe
# Count items with tax_type set
result = frappe.db.sql("""
SELECT
tax_type,
COUNT(*) as count
FROM `tabPurchase Invoice Item`
WHERE parent IN (
SELECT name
FROM `tabPurchase Invoice`
WHERE agricultural_goods = 1
AND docstatus < 2
)
GROUP BY tax_type
""", as_dict=True)
for row in result:
print(f"{row.tax_type or '(empty)'}: {row.count} items")
- Test E-Taxes sending:
- Open a migrated Purchase Invoice
- Verify buttons are visible (if agricultural_goods = True)
- Try sending to E-Taxes
- Confirm payload is correct
Rollback Plan
If migration causes issues, you can:
- Restore from backup:
bench --site site1 restore /path/to/backup.sql.gz
- Clear tax_type manually:
import frappe
frappe.db.sql("""
UPDATE `tabPurchase Invoice Item`
SET tax_type = NULL, agricultural_tax_amount = 0
WHERE parent IN (
SELECT name
FROM `tabPurchase Invoice`
WHERE agricultural_goods = 1
)
""")
frappe.db.commit()
FAQ
Q: Will old documents still work without migration?
A: Yes, but:
- Buttons will be hidden if
agricultural_goods = False - You must manually set
tax_typebefore sending to E-Taxes - Validation will fail if
tax_typeis empty
Q: Do I need to migrate cancelled documents?
A: No, cancelled documents (docstatus = 2) don't need migration.
Q: What happens to the old item_tax_template field?
A: It's still there but no longer used by E-Taxes integration. You can keep it for reference.
Q: Can I delete item_tax_template after migration?
A: No! Other parts of ERPNext may use it. Only the E-Taxes integration uses tax_type.
Q: What if I have custom tax templates?
A: The migration script treats all templates except "ƏDV 2% cəlb" as "Tax Free". If you have custom templates with different rates, adjust the script accordingly.
Support
If you encounter issues:
- Check
IMPLEMENTATION_SUMMARY.mdfor implementation details - Check logs:
bench --site site1 logs - Review validation errors in the UI
- Test with a new Purchase Invoice first
Migration Timeline
Recommended approach:
- Week 1: Test migration script on staging/development
- Week 2: Migrate draft documents on production
- Week 3: Migrate submitted documents on production
- Week 4: Verify all E-Taxes integrations work correctly
Note: No deadline - you can migrate documents as needed. New documents will use the new fields automatically.
Example Migration Session
# 1. Backup database
bench --site site1 backup
# 2. Run migration
bench --site site1 console
# 3. Paste migration script (see Option 2 above)
# 4. Verify results
# Sample output:
# Found 15 agricultural purchase invoices
# ============================================================
# ✓ Migrated: ACC-PINV-2026-00001 (3 items)
# ✓ Migrated: ACC-PINV-2026-00002 (5 items)
# ⊘ Skipped: ACC-PINV-2026-00003 (no items to migrate)
# ...
# ============================================================
# Migration Complete:
# - Migrated: 12
# - Skipped: 3
# - Total: 15
# 5. Clear cache
bench --site site1 clear-cache
# 6. Test in UI
# Open one of the migrated documents and verify tax_type is set
Conclusion
✅ Migration is optional but recommended ✅ Old and new systems coexist ✅ No data loss - both fields are preserved ✅ Easy rollback via database backup
For new documents: Just use the new tax_type field - no migration needed!