* feat: add editable DocTypes To Delete list with import/export
Add user control over transaction deletion with reviewable and reusable deletion templates.
- New "DocTypes To Delete" table allows users to review and customize what will be deleted before submission
- Import/Export CSV templates for reusability across environments
- Company field rule: only filter by company if field is specifically named "company", otherwise delete all records
- Child tables (istable=1) automatically excluded from selection
- "Remove Zero Counts" helper button to clean up list
- Backward compatible with existing deletion records
* refactor: improve Transaction Deletion Record code quality
- Remove unnecessary chatty comments from AI-generated code
- Add concise docstrings to all new methods
- Remove redundant @frappe.whitelist() decorators from internal methods
- Improve CSV import validation (header check, child table filtering)
- Add better error feedback with consolidated skip messages
- Reorder form fields: To Delete list now appears before Excluded list
- Add conditional visibility for Summary table (legacy records only)
- Improve architectural clarity: single API entry point per feature
Technical improvements:
- export_to_delete_template_method and import_to_delete_template_method
are now internal helpers without whitelist decorators
- CSV import now validates format and provides detailed skip reasons
- Summary table only shows for submitted records without To Delete list
- Maintains backward compatibility for existing deletion records
* fix: field order
* test: fix broken tests and add new ones
* fix: adapt create_transaction_deletion_request
* test: fix assertRaises trigger
* fix: conditionally execute Transaction Deletion pre-tasks based on selected DocTypes
* refactor: replace boolean task flags with status fields
* fix: remove UI comment
* fix: don't allow virtual doctype selection and improve protected Doctype List
* fix: replace outdated frappe.db.sql by frappe.qb
* feat: add support for multiple company fields
* fix: autofill comapny field, add docstrings, filter for company_field
* fix: add edge case handling for update_naming_series and add tests for prefix extraction
* fix: use redis for running deletion validation, check per doctype instead of company
(cherry picked from commit 0fb37ad792)
# Conflicts:
# erpnext/patches.txt
6.2 KiB
Transaction Deletion CSV Import Logic - Updated Behavior
Auto-Detection of Company Field
When importing a CSV without a company_field column or with empty values, the system uses smart auto-detection:
Priority Order:
-
"company" field (most common convention)
- Check if a field named
companyexists that links to Company DocType - ✅ Use "company" if found
- Check if a field named
-
First Company link field (custom fields)
- If no "company" field, get all fields linking to Company DocType
- ✅ Use the first one (sorted by field index)
-
No company field (DocTypes without company filtering)
- If no Company link fields exist at all
- ✅ Leave
company_fieldas None/empty - ✅ Delete ALL records (no company filtering)
Import CSV Format
Minimal Format (Auto-Detection)
doctype_name,child_doctypes
Sales Order,Sales Order Item
Note,
Task,
Result:
Sales Order: Auto-detects "company" field → Filters by companyNote: No company field → Deletes all Note recordsTask: Has "company" field → Filters by company
Explicit Format (Recommended)
doctype_name,company_field,child_doctypes
Sales Order,company,Sales Order Item
Sales Contract,primary_company,Sales Contract Item
Sales Contract,billing_company,Sales Contract Item
Note,,
Result:
Sales Order: Uses "company" field explicitlySales Contract(row 1): Uses "primary_company" fieldSales Contract(row 2): Uses "billing_company" field (separate row!)Note: No company field, deletes all records
Multiple Company Fields Example
doctype_name,company_field,child_doctypes
Customer Invoice,head_office,Customer Invoice Item
Customer Invoice,billing_company,Customer Invoice Item
Deletion Process:
- Row 1 deletes:
WHERE head_office = 'ABC Company' - Row 2 deletes:
WHERE billing_company = 'ABC Company' - Documents with both fields = ABC get deleted in first pass
- Documents with only billing_company = ABC get deleted in second pass
Validation Rules
✅ Accepted Cases
- DocType with "company" field - Auto-detected
- DocType with custom Company link field - Auto-detected (first field used)
- DocType with multiple Company fields - Auto-detected (first field used), but user can add multiple rows
- DocType with NO Company fields - Accepted! Deletes ALL records
- Explicit company_field provided - Validated and used
❌ Rejected Cases
- Protected DocTypes - User, Role, DocType, etc.
- Child tables - Auto-deleted with parent
- Virtual DocTypes - No database table
- Invalid company_field - Field doesn't exist or isn't a Company link
- DocType doesn't exist - Not found in system
Code Flow
# 1. Read company_field from CSV (may be empty)
company_field = row.get("company_field", "").strip()
# 2. Auto-detect if not provided
if not company_field:
# Try "company" first
if exists("company" field linking to Company):
company_field = "company"
else:
# Check for other Company link fields
company_fields = get_all_company_link_fields()
if company_fields:
company_field = company_fields[0] # Use first
# else: company_field stays empty
# 3. Validate if company_field was provided/detected
if company_field:
if not is_valid_company_link_field(company_field):
skip_with_error()
# 4. Count documents
if company_field:
count = count(WHERE company_field = self.company)
else:
count = count(all records)
# 5. Store in To Delete list
append({
"doctype_name": doctype_name,
"company_field": company_field or None, # Store None if empty
"document_count": count
})
Examples
Example 1: Standard DocType with "company" Field
CSV:
doctype_name,company_field,child_doctypes
Sales Order,,
Auto-Detection:
- Finds "company" field linking to Company
- Sets
company_field = "company" - Counts:
WHERE company = 'Test Company' - Result: Deletes only Test Company's Sales Orders
Example 2: Custom Company Field
CSV:
doctype_name,company_field,child_doctypes
Project Contract,,
Auto-Detection:
- No "company" field found
- Finds "contracting_company" field linking to Company
- Sets
company_field = "contracting_company" - Counts:
WHERE contracting_company = 'Test Company' - Result: Deletes only Test Company's Project Contracts
Example 3: No Company Field (Global DocType)
CSV:
doctype_name,company_field,child_doctypes
Note,,
Global Settings,,
Auto-Detection:
- No Company link fields found
- Sets
company_field = None - Counts: All records
- Result: Deletes ALL Note and Global Settings records
Example 4: Multiple Company Fields (Explicit)
CSV:
doctype_name,company_field,child_doctypes
Sales Contract,primary_company,Sales Contract Item
Sales Contract,billing_company,Sales Contract Item
No Auto-Detection:
- Row 1: Uses "primary_company" explicitly
- Row 2: Uses "billing_company" explicitly
- Both rows validated as valid Company link fields
- Result: Two separate deletion passes
Example 5: Mixed Approaches
CSV:
doctype_name,company_field,child_doctypes
Sales Order,,Sales Order Item
Sales Contract,billing_company,Sales Contract Item
Note,,
Result:
- Row 1: Auto-detects "company" field
- Row 2: Uses "billing_company" explicitly
- Row 3: No company field (deletes all)
User Benefits
✅ Flexible: Supports auto-detection and explicit specification ✅ Safe: Validates all fields before processing ✅ Clear: Empty company_field means "delete all" ✅ Powerful: Can target specific company fields in multi-company setups ✅ Backward Compatible: Old CSVs (without company_field column) still work
Migration from Old Format
Old CSV (without company_field):
doctype_name,child_doctypes
Sales Order,Sales Order Item
New System Behavior:
- Auto-detects "company" field
- Works identically to before
- ✅ Backward compatible
New CSV (with company_field):
doctype_name,company_field,child_doctypes
Sales Order,company,Sales Order Item
Benefits:
- Explicit and clear
- Supports multiple rows per DocType
- Can specify custom company fields
Generated for Transaction Deletion Record enhancement