# 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 ### Option 1: Manual Migration (Recommended for Few Documents) 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: ```python 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:** ```bash 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:** ```python 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() ``` 2. **Check counts:** ```python 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") ``` 3. **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:** ```bash bench --site site1 restore /path/to/backup.sql.gz ``` 2. **Clear tax_type manually:** ```python 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 ```bash # 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!