invoice_az/MIGRATION_GUIDE.md

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_template set
  • Items with tax_type = None (empty)

Migration Options

For each Purchase Invoice:

  1. Open the document in edit mode
  2. Check if agricultural_goods is checked
  3. For each item in the items table:
    • Look at the old item_tax_template value
    • Set new tax_type based on mapping:
      • If template is "ƏDV 2% cəlb" → select "Taxable"
      • If template is anything else → select "Tax Free"
  4. Save the document
  5. agricultural_tax_amount will 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:

  1. 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()
  1. 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")
  1. 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:

  1. Restore from backup:
bench --site site1 restore /path/to/backup.sql.gz
  1. 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_type before sending to E-Taxes
  • Validation will fail if tax_type is 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:

  1. Check IMPLEMENTATION_SUMMARY.md for implementation details
  2. Check logs: bench --site site1 logs
  3. Review validation errors in the UI
  4. Test with a new Purchase Invoice first

Migration Timeline

Recommended approach:

  1. Week 1: Test migration script on staging/development
  2. Week 2: Migrate draft documents on production
  3. Week 3: Migrate submitted documents on production
  4. 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!