diff --git a/AMOUNT_FIX_GUIDE.md b/AMOUNT_FIX_GUIDE.md
new file mode 100644
index 0000000..aa66c58
--- /dev/null
+++ b/AMOUNT_FIX_GUIDE.md
@@ -0,0 +1,145 @@
+# Amount Calculation Fix Guide
+
+## Problem Description
+
+Some payments with deductions may show incorrect amounts in the UI due to synchronization issues between the payment record and the journal entry. This typically happens when:
+
+1. A payment is created with deductions
+2. The payment is posted (journal entry created)
+3. Odoo's synchronization mechanism tries to update the payment amount based on the journal entry
+4. The amount gets incorrectly set to the final payment amount instead of the original amount
+
+## Symptoms
+
+- **Amount field** shows the final payment amount (after deductions) instead of the original amount
+- **Final Payment Amount field** shows an incorrect value
+- The journal entry is correct, but the payment record shows wrong amounts
+
+## Example
+
+**Expected:**
+- Amount: 8,557,500.00 (original amount)
+- Total Deductions: 92,000.00
+- Final Payment Amount: 8,465,500.00 (8,557,500 - 92,000)
+
+**Actual (incorrect):**
+- Amount: 6,465,500.00 (wrong - this is some other calculation)
+- Final Payment Amount: 8,373,500.00 (wrong)
+
+## Solutions
+
+### Solution 1: Use the Fix Button (Recommended)
+
+1. Open the payment with incorrect amounts
+2. Look for the "Fix Amount" button next to the Final Payment Amount field
+3. Click the button to automatically fix the calculation
+4. The amounts should now be correct
+
+### Solution 2: Use the Fix Wizard
+
+1. Go to **Accounting > Configuration > Fix Payment Amounts**
+2. The wizard will automatically detect payments with incorrect amounts
+3. Review the list of payments to fix
+4. Click "Fix Amounts" to correct all selected payments
+
+### Solution 3: Manual Fix via Code
+
+If you have access to Odoo shell, you can run the fix script:
+
+```python
+# Open Odoo shell
+python odoo-bin shell -d your_database
+
+# Run the fix for all payments
+env['account.payment'].fix_all_payment_amounts()
+
+# Or fix a specific payment
+payment = env['account.payment'].search([('name', '=', 'PBCA5858/2025/00061')])
+payment.action_fix_amount_calculation()
+```
+
+### Solution 4: Use the Python Script
+
+Run the provided fix script:
+
+```bash
+# Copy the fix script to your Odoo directory
+cp customaddons/vendor_payment_diff_amount/fix_amount_issue.py /path/to/odoo/
+
+# Open Odoo shell
+python odoo-bin shell -d your_database
+
+# Run the script
+exec(open('fix_amount_issue.py').read())
+
+# Check a specific payment
+check_specific_payment('PBCA5858/2025/00061')
+
+# Fix all payments
+fix_payment_amounts()
+```
+
+## How the Fix Works
+
+The fix works by:
+
+1. **Finding the correct amount** from the journal entry's counterpart line (payable/expense line with debit)
+2. **Updating the payment amount** to match the journal entry
+3. **Recalculating the final payment amount** using the formula: `final_payment_amount = amount - amount_substract`
+
+## Prevention
+
+To prevent this issue in the future, the module has been updated with:
+
+1. **Improved synchronization logic** that prevents incorrect amount updates
+2. **Write method override** that blocks amount changes for posted payments with deductions
+3. **Better error handling** for synchronization edge cases
+
+## Verification
+
+After applying the fix, verify that:
+
+1. **Amount field** shows the original payment amount (before deductions)
+2. **Total Deductions field** shows the sum of all deduction lines
+3. **Final Payment Amount field** shows `Amount - Total Deductions`
+4. **Journal entry** remains unchanged and balanced
+5. **Bank account** is credited with the Final Payment Amount
+6. **Payable/Expense account** is debited with the original Amount
+7. **Deduction accounts** are credited with their respective amounts
+
+## Example Verification
+
+For payment PBCA5858/2025/00061:
+
+**Payment Record:**
+- Amount: 8,557,500.00 ✓
+- Total Deductions: 92,000.00 ✓
+- Final Payment Amount: 8,465,500.00 ✓
+
+**Journal Entry:**
+- Payable/Expense (Debit): 8,557,500.00 ✓
+- Deduction accounts (Credit): 92,000.00 total ✓
+- Bank (Credit): 8,465,500.00 ✓
+- **Total:** Debit 8,557,500.00 = Credit 8,557,500.00 ✓
+
+## Support
+
+If you continue to experience issues:
+
+1. Check the Odoo logs for any error messages
+2. Verify that the deduction accounts are not payable/receivable accounts
+3. Ensure the payment has a valid partner and journal
+4. Contact support with the payment name/ID and error details
+
+## Technical Details
+
+The issue occurs because Odoo's `_synchronize_from_moves` method tries to keep the payment amount in sync with the journal entry. However, when we have deductions:
+
+- The **journal entry** has the original amount on the payable/expense line
+- The **bank line** has the reduced amount (final payment amount)
+- Odoo's sync logic sometimes picks the wrong line to sync from
+
+The fix ensures that:
+- The payment amount always reflects the original amount (from payable/expense line)
+- The final payment amount is always calculated as `amount - deductions`
+- Synchronization doesn't overwrite correct amounts for posted payments
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0b9ff69
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,56 @@
+# Changelog
+
+## Version 2.1.0 (2025-12-12)
+
+### Fixed
+- **Amount Calculation Issue**: Fixed issue where payment amounts were incorrectly synchronized from journal entries
+ - Amount field now correctly shows the original payment amount (before deductions)
+ - Final Payment Amount field now correctly shows the amount after deductions
+ - Added protection against incorrect amount synchronization for posted payments
+
+### Added
+- **Fix Amount Button**: Added "Fix Amount" button on payment form for quick correction of incorrect amounts
+- **Fix Wizard**: Added wizard accessible from Accounting > Configuration > Fix Payment Amounts
+- **Automatic Detection**: System automatically detects payments with incorrect amounts
+- **Debug Logging**: Added logging to help troubleshoot synchronization issues
+- **Fix Script**: Added standalone Python script for batch fixing of payment amounts
+
+### Improved
+- **Synchronization Logic**: Enhanced `_synchronize_from_moves` method to handle deductions correctly
+- **Write Method**: Added write method override to prevent incorrect amount changes
+- **Error Handling**: Better error handling for edge cases with expense accounts
+
+### Technical Changes
+- Added `write()` method override to prevent amount field changes for posted payments with deductions
+- Enhanced `_synchronize_from_moves()` method with better error handling and amount restoration
+- Added `action_fix_amount_calculation()` method for individual payment fixes
+- Added `fix_all_payment_amounts()` model method for batch fixes
+- Added debug logging throughout the synchronization process
+
+### Files Added
+- `wizard/payment_amount_fix_wizard.py` - Wizard for fixing payment amounts
+- `wizard/payment_amount_fix_wizard_views.xml` - Wizard UI
+- `fix_amount_issue.py` - Standalone fix script
+- `AMOUNT_FIX_GUIDE.md` - Comprehensive fix guide
+- `CHANGELOG.md` - This changelog
+
+### Files Modified
+- `models/account_payment.py` - Enhanced synchronization and added fix methods
+- `views/account_payment_views.xml` - Added fix button
+- `security/ir.model.access.csv` - Added wizard access rights
+- `__manifest__.py` - Updated version and added wizard views
+- `__init__.py` - Added wizard import
+
+## Version 2.0.0 (Previous)
+
+### Fixed
+- **Partner ID Issue**: Fixed "Missing required account on accountable line" error
+- **Journal Entry Structure**: Corrected partner_id assignment on journal entry lines
+
+### Added
+- **Account Domain Filtering**: Prevented selection of wrong account types for deductions
+- **Validation Rules**: Added comprehensive validation for deduction accounts and amounts
+
+### Improved
+- **Documentation**: Added comprehensive scenarios and troubleshooting guides
+- **Error Messages**: Better error messages and validation feedback
\ No newline at end of file
diff --git a/FINAL_FIX.md b/FINAL_FIX.md
deleted file mode 100644
index 22f35b5..0000000
--- a/FINAL_FIX.md
+++ /dev/null
@@ -1,263 +0,0 @@
-# Final Fix: "Missing required account on accountable line"
-
-## Problem Solved
-
-The error "Missing required account on accountable line" has been resolved by ensuring proper `partner_id` handling on journal entry lines.
-
-## Root Cause Analysis
-
-Odoo's validation requires that:
-1. Lines with **payable/receivable accounts** MUST have `partner_id`
-2. Lines with **other account types** should NOT have `partner_id` (or it's optional)
-
-The error occurred because the journal entry had inconsistent `partner_id` assignments.
-
-## Complete Solution
-
-### 1. Counterpart Line (Payable/Expense)
-
-**Always set `partner_id`** on the counterpart line:
-
-```python
-# CRITICAL: Always ensure partner_id is set on counterpart line
-counterpart_line['partner_id'] = self.partner_id.id
-
-# Also ensure the account_id is set
-if not counterpart_line.get('account_id'):
- counterpart_line['account_id'] = self.destination_account_id.id
-```
-
-### 2. Deduction Lines (Tax/Expense)
-
-**Never set `partner_id`** on deduction lines:
-
-```python
-deduction_line = {
- 'name': deduction_line_name,
- 'date_maturity': self.date,
- 'amount_currency': -deduction.amount_substract,
- 'currency_id': self.currency_id.id,
- 'debit': 0.0,
- 'credit': deduction_balance,
- 'account_id': deduction.substract_account_id.id,
- # No partner_id - deduction accounts are tax/expense accounts
-}
-```
-
-### 3. Account Domain Restriction
-
-**Prevent wrong account selection** by updating the domain:
-
-```python
-domain="[('account_type', 'not in', ['asset_cash', 'asset_cash_bank', 'asset_receivable', 'liability_payable']), ('deprecated', '=', False)]"
-```
-
-This prevents users from selecting:
-- ❌ Cash/Bank accounts
-- ❌ Accounts Receivable
-- ❌ Accounts Payable
-
-And allows only:
-- ✅ Tax Payable accounts (liability_current)
-- ✅ Expense accounts
-- ✅ Other liability accounts
-- ✅ Income accounts (if needed)
-
-## Correct Journal Entry Structure
-
-### Example: Payment Rp 2,000,000 with deductions
-
-**Scenario:**
-- Vendor: PT Telkom Indonesia
-- Amount: Rp 2,000,000
-- PPh 21: Rp 100,000 (Tax Payable account)
-- PPh 29: Rp 50,000 (Tax Payable account)
-- Final Payment: Rp 1,850,000
-
-**Journal Entry:**
-
-| Account | Type | Debit | Credit | Partner | Valid? |
-|---------|------|-------|--------|---------|--------|
-| Accounts Payable | liability_payable | 2,000,000 | | PT Telkom | ✅ Required |
-| PPh 21 | liability_current | | 100,000 | (none) | ✅ Correct |
-| PPh 29 | liability_current | | 50,000 | (none) | ✅ Correct |
-| Bank | asset_cash | | 1,850,000 | (none) | ✅ Correct |
-
-**Total:** Debit 2,000,000 = Credit 2,000,000 ✅ Balanced
-
-## Why This Works
-
-### Odoo's Validation Logic
-
-Odoo checks each journal entry line:
-
-```python
-# Pseudo-code of Odoo's validation
-for line in journal_entry.lines:
- if line.account.account_type in ('asset_receivable', 'liability_payable'):
- if not line.partner_id:
- raise ValidationError("Missing required account on accountable line")
-```
-
-### Our Solution
-
-1. **Counterpart line** (Payable): Has `partner_id` ✅
-2. **Deduction lines** (Tax): No `partner_id`, and account type is NOT payable/receivable ✅
-3. **Bank line** (Cash): No `partner_id`, and account type is NOT payable/receivable ✅
-
-All lines pass validation!
-
-## Account Type Reference
-
-### Account Types in Odoo 17
-
-| Account Type | Code | Requires Partner? | Use for Deductions? |
-|--------------|------|-------------------|---------------------|
-| Accounts Receivable | asset_receivable | ✅ Yes | ❌ No |
-| Accounts Payable | liability_payable | ✅ Yes | ❌ No |
-| Bank/Cash | asset_cash | ❌ No | ❌ No |
-| Current Liabilities | liability_current | ❌ No | ✅ Yes (Tax Payable) |
-| Expenses | expense | ❌ No | ✅ Yes |
-| Other Liabilities | liability_non_current | ❌ No | ✅ Yes |
-| Income | income | ❌ No | ⚠️ Rare |
-
-## Testing Checklist
-
-After applying this fix, test:
-
-### ✅ Test 1: Payment without expense account
-```
-- Create vendor payment
-- Amount: 1,000
-- Add deduction: PPh 21 - 100 (use tax payable account)
-- Post payment
-- Expected: Success, no errors
-```
-
-### ✅ Test 2: Payment with expense account
-```
-- Create vendor payment
-- Amount: 2,000
-- Expense Account: Telepon & Internet
-- Add deduction 1: PPh 21 - 100
-- Add deduction 2: PPh 29 - 50
-- Post payment
-- Expected: Success, no errors
-```
-
-### ✅ Test 3: Verify journal entry
-```
-- Open posted payment
-- View journal entry
-- Check:
- - Payable/Expense line has partner ✅
- - Tax lines don't have partner ✅
- - Entry is balanced ✅
-```
-
-### ❌ Test 4: Try wrong account (should fail gracefully)
-```
-- Create vendor payment
-- Try to add deduction with Accounts Payable account
-- Expected: Account not available in dropdown (domain restriction)
-```
-
-## Files Modified
-
-1. **`models/account_payment.py`**
- - Always set `partner_id` on counterpart line
- - Never set `partner_id` on deduction lines
- - Ensure `account_id` is set on counterpart line
-
-2. **`models/payment_deduction_line.py`**
- - Updated domain to exclude payable/receivable accounts
- - Updated help text to clarify account selection
-
-3. **Documentation files**
- - `FIX_SUMMARY.md` - Initial fix documentation
- - `FINAL_FIX.md` - This comprehensive guide
- - `SCENARIOS.md` - Updated validation rules
-
-## Common Mistakes to Avoid
-
-### ❌ Don't Do This:
-
-1. **Using Accounts Payable for deductions**
- ```
- Wrong: PPh 21 → Accounts Payable (vendor account)
- Right: PPh 21 → Tax Payable (liability account)
- ```
-
-2. **Adding partner to all lines**
- ```python
- # Wrong
- for line in all_lines:
- line['partner_id'] = partner.id # ❌
-
- # Right
- if line.account.account_type in ('asset_receivable', 'liability_payable'):
- line['partner_id'] = partner.id # ✅
- ```
-
-3. **Using expense accounts for tax withholding**
- ```
- Wrong: PPh 21 → Expense account (reduces expense)
- Right: PPh 21 → Tax Payable (creates liability)
- ```
-
-## Accounting Best Practices
-
-### Withholding Tax Treatment
-
-When you withhold tax from a vendor payment:
-
-1. **Record full expense/payable** (Debit)
-2. **Record tax liability** (Credit) - you owe this to tax office
-3. **Record reduced bank payment** (Credit) - actual cash out
-
-This correctly represents:
-- Full expense incurred
-- Tax liability created
-- Reduced cash payment
-
-### Example Accounts Setup
-
-Create these accounts for Indonesian tax:
-
-```
-217101 - PPh 21 (Tax Payable)
- Type: Current Liabilities
- Code: 217101
-
-217102 - PPh 23 (Tax Payable)
- Type: Current Liabilities
- Code: 217102
-
-117104 - PPh 29 (Tax Payable)
- Type: Current Liabilities
- Code: 117104
-```
-
-## Version
-
-This fix is included in version 2.0.0 of the `vendor_payment_diff_amount` module.
-
-## Support
-
-If you still encounter issues:
-
-1. Check that deduction accounts are NOT payable/receivable types
-2. Verify the partner is set on the payment
-3. Check Odoo logs for detailed error messages
-4. Ensure you're using the latest version of the module
-
-## Success Criteria
-
-✅ Payments post without errors
-✅ Journal entries are balanced
-✅ Payable line has partner
-✅ Tax lines don't have partner
-✅ Correct accounting treatment
-✅ Easy to use and understand
-
-The module is now production-ready!
diff --git a/FIX_SUMMARY.md b/FIX_SUMMARY.md
deleted file mode 100644
index 706e6dd..0000000
--- a/FIX_SUMMARY.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# Fix Summary: "Missing required account on accountable line" Error
-
-## Problem
-
-When creating a vendor payment with deductions, the system threw the error:
-```
-The operation cannot be completed: Missing required account on accountable line.
-```
-
-This occurred because Odoo validates that lines with payable/receivable accounts must have a `partner_id` set.
-
-## Root Cause
-
-The issue had two parts:
-
-1. **Deduction lines were incorrectly getting `partner_id`**: All deduction lines were being created with `partner_id`, but deduction accounts (like tax accounts) are typically NOT payable/receivable accounts and should NOT have a partner.
-
-2. **Odoo's validation**: When a line has a payable/receivable account type, Odoo requires the `partner_id` field. When a line has other account types (expense, liability, etc.), the `partner_id` should be optional or omitted.
-
-## Solution
-
-Modified the `_prepare_move_line_default_vals` method to:
-
-1. **Ensure counterpart line has partner**: The payable/expense line (counterpart) always gets `partner_id` set
-2. **Conditional partner on deduction lines**: Only add `partner_id` to deduction lines if the account type requires it
-
-### Code Change
-
-**Before:**
-```python
-deduction_line = {
- 'name': deduction_line_name,
- 'date_maturity': self.date,
- 'amount_currency': -deduction.amount_substract,
- 'currency_id': self.currency_id.id,
- 'debit': 0.0,
- 'credit': deduction_balance,
- 'partner_id': self.partner_id.id, # ❌ Always added
- 'account_id': deduction.substract_account_id.id,
-}
-```
-
-**After:**
-```python
-deduction_line = {
- 'name': deduction_line_name,
- 'date_maturity': self.date,
- 'amount_currency': -deduction.amount_substract,
- 'currency_id': self.currency_id.id,
- 'debit': 0.0,
- 'credit': deduction_balance,
- 'account_id': deduction.substract_account_id.id,
-}
-
-# Only add partner_id if the account requires it
-if deduction.substract_account_id.account_type in ('asset_receivable', 'liability_payable'):
- deduction_line['partner_id'] = self.partner_id.id # ✅ Conditionally added
-```
-
-## Account Types
-
-### Accounts that REQUIRE partner_id:
-- `asset_receivable` (Customer accounts)
-- `liability_payable` (Vendor accounts)
-
-### Accounts that DON'T need partner_id:
-- `liability_current` (Tax payable accounts like PPh 21, PPh 29)
-- `expense` (Expense accounts)
-- `income` (Income accounts)
-- `asset_cash` (Bank accounts)
-- All other account types
-
-## Result
-
-Now the journal entry is created correctly:
-
-### Example: Payment Rp 2,000,000 with PPh 21 (Rp 100,000) and PPh 29 (Rp 50,000)
-
-```
-Account | Debit | Credit | Partner
--------------------------------------|-----------------|-----------------|------------------
-Accounts Payable | Rp 2,000,000.00 | | PT Telkom ✅
-PPh 21 (Tax Payable) | | Rp 100,000.00 | (none) ✅
-PPh 29 (Tax Payable) | | Rp 50,000.00 | (none) ✅
-Bank Account | | Rp 1,850,000.00 | (none) ✅
--------------------------------------|-----------------|-----------------|------------------
-TOTAL | Rp 2,000,000.00 | Rp 2,000,000.00 |
-```
-
-### Key Points:
-- ✅ Payable account has partner (required)
-- ✅ Tax accounts don't have partner (correct)
-- ✅ Bank account doesn't have partner (correct)
-- ✅ Entry is balanced
-- ✅ No validation errors
-
-## Testing
-
-To test the fix:
-
-1. **Create a payment without expense account:**
- ```
- - Partner: Any vendor
- - Amount: 1000
- - Deduction: PPh 21 - 100 (use a tax account)
- - Result: Should post successfully
- ```
-
-2. **Create a payment with expense account:**
- ```
- - Partner: Any vendor
- - Amount: 2000
- - Expense Account: Telepon & Internet
- - Deduction 1: PPh 21 - 100
- - Deduction 2: PPh 29 - 50
- - Result: Should post successfully
- ```
-
-3. **Verify journal entries:**
- - Check that payable/expense line has partner
- - Check that tax lines don't have partner
- - Check that entry is balanced
-
-## Important Notes
-
-### Deduction Account Selection
-
-When adding deductions, make sure to use the correct account types:
-
-✅ **Correct accounts for deductions:**
-- Tax payable accounts (PPh 21, PPh 23, PPh 29, etc.)
-- Expense accounts (if recording as expense)
-- Liability accounts (for other withholdings)
-
-❌ **Don't use these for deductions:**
-- Accounts Payable (vendor accounts)
-- Accounts Receivable (customer accounts)
-
-### Why This Matters
-
-Using payable/receivable accounts for deductions would create confusion:
-- It would require a partner on the deduction line
-- It would mix vendor payables with tax payables
-- It would complicate reconciliation
-- It's not the correct accounting treatment
-
-## Files Modified
-
-1. **`models/account_payment.py`**
- - Added conditional `partner_id` logic for deduction lines
- - Ensured counterpart line always has `partner_id`
-
-2. **`SCENARIOS.md`**
- - Updated validation rules
- - Added explanation about partner_id handling
- - Updated troubleshooting section
-
-3. **`FIX_SUMMARY.md`** (this file)
- - Documented the fix and reasoning
-
-## Version
-
-This fix is included in version 2.0.0 of the module.
-
-## Related Issues
-
-- "Missing required account on accountable line" error
-- Partner validation on journal entry lines
-- Deduction account configuration
-
-## Credits
-
-Fixed based on user feedback and testing with real-world scenarios.
diff --git a/JOURNAL_ENTRY_STRUCTURE.md b/JOURNAL_ENTRY_STRUCTURE.md
deleted file mode 100644
index fc917c9..0000000
--- a/JOURNAL_ENTRY_STRUCTURE.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# Journal Entry Structure
-
-## Overview
-
-This document explains how journal entries are created when using payment deductions.
-
-## Standard Payment (Without Deductions)
-
-For a standard vendor payment of Rp 2,000,000:
-
-```
-Account | Debit | Credit
----------------------------|---------------|---------------
-Accounts Payable | | Rp 2,000,000
-Bank Account | | Rp 2,000,000
----------------------------|---------------|---------------
-TOTAL | Rp 2,000,000 | Rp 2,000,000
-```
-
-Wait, that's not right. Let me correct:
-
-```
-Account | Debit | Credit
----------------------------|---------------|---------------
-Accounts Payable | Rp 2,000,000 |
-Bank Account | | Rp 2,000,000
----------------------------|---------------|---------------
-TOTAL | Rp 2,000,000 | Rp 2,000,000
-```
-
-## Payment with Deductions (New Structure)
-
-For a vendor payment of Rp 2,000,000 with deductions:
-- PPh 21: Rp 100,000
-- PPh 29: Rp 50,000
-- Final payment to bank: Rp 1,850,000
-
-### Journal Entry:
-
-```
-Account | Debit | Credit
----------------------------|---------------|---------------
-Expense Account | Rp 2,000,000 |
-PPh 21 (Tax Payable) | | Rp 100,000
-PPh 29 (Tax Payable) | | Rp 50,000
-Bank Account | | Rp 1,850,000
----------------------------|---------------|---------------
-TOTAL | Rp 2,000,000 | Rp 2,000,000
-```
-
-### Explanation:
-
-1. **Expense Account (Debit Rp 2,000,000)**
- - Records the full expense amount
- - This is the original payment amount
-
-2. **PPh 21 (Credit Rp 100,000)**
- - Withholding tax deduction
- - Creates a liability (you owe this to the tax office)
- - Reduces the amount paid to vendor
-
-3. **PPh 29 (Credit Rp 50,000)**
- - Another withholding tax deduction
- - Also creates a liability
- - Further reduces the amount paid to vendor
-
-4. **Bank Account (Credit Rp 1,850,000)**
- - The actual amount paid from bank
- - Equals: Original Amount - Total Deductions
- - Equals: Rp 2,000,000 - Rp 150,000 = Rp 1,850,000
-
-### Why This Structure?
-
-This structure correctly represents the business transaction:
-- You incurred an expense of Rp 2,000,000
-- You withheld Rp 150,000 in taxes (which you'll pay to the government)
-- You paid Rp 1,850,000 to the vendor
-
-The deductions are **credits** because:
-- They represent liabilities (amounts you owe to the tax office)
-- They reduce the cash outflow
-- They offset part of the expense
-
-## Example from Screenshot
-
-Based on your payment PB5858/2025/00105:
-
-```
-Account | Debit | Credit
--------------------------------------|-----------------|------------------
-218401 AR Clearing | | Rp 2,000,000.00
-611505 Telepon & Internet | Rp 1,850,000.00 |
-217101 PPh 21 | | Rp 100,000.00
-117104 PPh 29 | | Rp 50,000.00
--------------------------------------|-----------------|------------------
-TOTAL | Rp 1,850,000.00 | Rp 2,150,000.00
-```
-
-Wait, this doesn't balance! Let me check the correct structure...
-
-Actually, looking at your screenshot again, the correct structure should be:
-
-```
-Account | Debit | Credit
--------------------------------------|-----------------|------------------
-611505 Telepon & Internet (Expense) | Rp 2,000,000.00 |
-217101 PPh 21 (Tax Payable) | | Rp 100,000.00
-117104 PPh 29 (Tax Payable) | | Rp 50,000.00
-218401 AR Clearing (Bank) | | Rp 1,850,000.00
--------------------------------------|-----------------|------------------
-TOTAL | Rp 2,000,000.00 | Rp 2,000,000.00
-```
-
-This is the correct balanced entry that the module will now create!
-
-## Multiple Deductions
-
-You can add as many deduction lines as needed. For example:
-
-Payment Amount: Rp 5,000,000
-- PPh 21: Rp 200,000
-- PPh 23: Rp 100,000
-- PPh 29: Rp 50,000
-- Admin Fee: Rp 25,000
-
-```
-Account | Debit | Credit
----------------------------|---------------|---------------
-Expense Account | Rp 5,000,000 |
-PPh 21 | | Rp 200,000
-PPh 23 | | Rp 100,000
-PPh 29 | | Rp 50,000
-Admin Fee | | Rp 25,000
-Bank Account | | Rp 4,625,000
----------------------------|---------------|---------------
-TOTAL | Rp 5,000,000 | Rp 5,000,000
-```
-
-Final Payment: Rp 4,625,000 (= Rp 5,000,000 - Rp 375,000)
diff --git a/REQUIREMENT_EXPENSE_ACCOUNT.md b/REQUIREMENT_EXPENSE_ACCOUNT.md
deleted file mode 100644
index 87e86eb..0000000
--- a/REQUIREMENT_EXPENSE_ACCOUNT.md
+++ /dev/null
@@ -1,269 +0,0 @@
-# Requirement: Expense Account for Deductions
-
-## Summary
-
-**Payment deductions require the Expense Account field to be set.**
-
-This is a mandatory requirement enforced by validation.
-
-## Why This Requirement?
-
-### Technical Reason
-
-When creating a payment with deductions, the journal entry structure is:
-
-```
-Expense Account (Debit) ← Must be specified
-Tax Accounts (Credit) ← Deductions
-Bank Account (Credit) ← Net payment
-```
-
-Without the Expense Account, the system cannot determine which account should receive the debit entry.
-
-### Accounting Reason
-
-The Expense Account represents:
-- The full cost/expense being incurred
-- The account that should be debited for the total amount
-- The proper classification of the expense
-
-Example:
-- Telepon & Internet expense: Rp 2,000,000
-- Less: PPh 21 withheld: Rp 100,000
-- Less: PPh 29 withheld: Rp 50,000
-- Net payment to vendor: Rp 1,850,000
-
-The Expense Account (Telepon & Internet) gets the full Rp 2,000,000 debit.
-
-## Validation
-
-The module enforces this requirement with a validation constraint:
-
-```python
-@api.constrains('amount', 'amount_substract', 'expense_account_id', 'deduction_line_ids')
-def _check_amount_substract(self):
- for payment in self:
- # ... other validations ...
-
- # Require expense account when using deductions
- if payment.deduction_line_ids and not payment.expense_account_id:
- raise ValidationError(_(
- "Expense Account is required when using payment deductions.\n\n"
- "Please set the Expense Account field before adding deductions."
- ))
-```
-
-### When Validation Triggers
-
-The validation error appears when:
-1. You add deduction lines
-2. But haven't set the Expense Account field
-3. And try to save or post the payment
-
-### Error Message
-
-```
-Expense Account is required when using payment deductions.
-
-Please set the Expense Account field before adding deductions.
-```
-
-## How to Use
-
-### Correct Workflow
-
-```
-1. Create Payment
- ↓
-2. Set Expense Account ✅ (e.g., "Telepon & Internet")
- ↓
-3. Add Deductions (e.g., PPh 21, PPh 29)
- ↓
-4. Post Payment ✅
-```
-
-### Incorrect Workflow (Will Fail)
-
-```
-1. Create Payment
- ↓
-2. Add Deductions ❌
- ↓
-3. Try to Post ❌
- ↓
-Error: "Expense Account is required..."
-```
-
-## Alternative: Without Deductions
-
-If you don't need deductions, you can use the standard payment flow:
-
-```
-1. Create Payment
- ↓
-2. Don't set Expense Account (optional)
- ↓
-3. Don't add Deductions
- ↓
-4. Post Payment ✅
-```
-
-In this case, the system uses the standard Accounts Payable account.
-
-## Comparison
-
-### With Expense Account + Deductions
-
-**Journal Entry:**
-```
-Expense Account Debit: 2,000,000
-PPh 21 Credit: 100,000
-PPh 29 Credit: 50,000
-Bank Credit: 1,850,000
-```
-
-**Use Case:** Direct expense recording with tax withholding
-
-### Without Expense Account (Standard)
-
-**Journal Entry:**
-```
-Accounts Payable Debit: 2,000,000
-Bank Credit: 2,000,000
-```
-
-**Use Case:** Standard vendor payment without deductions
-
-## Benefits of This Requirement
-
-### 1. Clear Accounting
-
-Forces users to specify exactly which expense account should be used, ensuring:
-- Proper expense classification
-- Accurate financial reporting
-- Clear audit trail
-
-### 2. Prevents Errors
-
-Prevents common mistakes like:
-- Missing expense account
-- Unclear journal entries
-- Unbalanced entries
-
-### 3. Consistent Behavior
-
-Ensures all payments with deductions follow the same pattern:
-- Always have an expense account
-- Always have proper journal entries
-- Always have correct tax treatment
-
-## Configuration
-
-### Required Accounts
-
-Before using deductions, set up:
-
-#### 1. Expense Accounts
-```
-Chart of Accounts > Create Account
-- Name: Telepon & Internet
-- Code: 611505
-- Type: Expenses
-```
-
-#### 2. Tax Payable Accounts
-```
-Chart of Accounts > Create Account
-- Name: PPh 21
-- Code: 217101
-- Type: Current Liabilities
-```
-
-### Account Selection
-
-When creating a payment with deductions:
-
-**Expense Account:** Choose from expense accounts
-- ✅ Telepon & Internet
-- ✅ Office Supplies
-- ✅ Professional Fees
-- ❌ Accounts Payable (not allowed)
-
-**Deduction Accounts:** Choose from tax/liability accounts
-- ✅ PPh 21 (Tax Payable)
-- ✅ PPh 23 (Tax Payable)
-- ✅ PPh 29 (Tax Payable)
-- ❌ Accounts Payable (not allowed)
-- ❌ Bank accounts (not allowed)
-
-## User Training
-
-### Key Points to Teach Users
-
-1. **Always set Expense Account first** when using deductions
-2. **Choose the right expense account** for the type of expense
-3. **Use tax accounts** for deductions, not payable accounts
-4. **Verify amounts** before posting
-
-### Common Questions
-
-**Q: Why can't I just use Accounts Payable?**
-
-A: When using deductions, you're recording the expense directly. Accounts Payable is for tracking vendor balances, not expenses.
-
-**Q: What if I forget to set Expense Account?**
-
-A: You'll get a validation error. Just set the Expense Account field and try again.
-
-**Q: Can I change the Expense Account after adding deductions?**
-
-A: Yes, as long as the payment hasn't been posted yet.
-
-## Technical Details
-
-### Module Integration
-
-This requirement is part of the `vendor_payment_diff_amount` module v2.0.0.
-
-It works with:
-- `vendor_batch_payment_merge` (provides expense_account_id field)
-- Standard Odoo accounting (account.payment)
-
-### Field Definition
-
-The `expense_account_id` field is defined in `vendor_batch_payment_merge`:
-
-```python
-expense_account_id = fields.Many2one(
- 'account.account',
- string='Expense Account',
- domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable'))]",
- help="Account used for expense instead of the default payable/receivable account"
-)
-```
-
-### Validation Logic
-
-The validation is in `vendor_payment_diff_amount`:
-
-```python
-if payment.deduction_line_ids and not payment.expense_account_id:
- raise ValidationError("Expense Account is required...")
-```
-
-## Summary
-
-✅ **DO:** Set Expense Account before adding deductions
-✅ **DO:** Use expense accounts for Expense Account field
-✅ **DO:** Use tax accounts for deduction accounts
-❌ **DON'T:** Try to add deductions without Expense Account
-❌ **DON'T:** Use Accounts Payable for deductions
-
----
-
-**Remember: Expense Account is REQUIRED when using deductions!**
-
-For more information, see:
-- USER_GUIDE.md - Step-by-step instructions
-- README.md - Module overview
-- FINAL_FIX.md - Technical details
diff --git a/SCENARIOS.md b/SCENARIOS.md
deleted file mode 100644
index d22f9e4..0000000
--- a/SCENARIOS.md
+++ /dev/null
@@ -1,236 +0,0 @@
-# Payment Scenarios with Deductions
-
-This document explains how the module handles different payment scenarios.
-
-## Scenario 1: Payment WITHOUT Expense Account
-
-When you create a vendor payment **without** setting an expense account, the system uses the standard vendor payable account.
-
-### Example:
-- Vendor: PT Telkom Indonesia
-- Amount: Rp 2,000,000
-- Deductions:
- - PPh 21: Rp 100,000
- - PPh 29: Rp 50,000
-- Final Payment: Rp 1,850,000
-
-### Journal Entry:
-```
-Account | Debit | Credit
--------------------------------------|-----------------|------------------
-Accounts Payable - PT Telkom | Rp 2,000,000.00 |
-PPh 21 (Tax Payable) | | Rp 100,000.00
-PPh 29 (Tax Payable) | | Rp 50,000.00
-Bank Account | | Rp 1,850,000.00
--------------------------------------|-----------------|------------------
-TOTAL | Rp 2,000,000.00 | Rp 2,000,000.00
-```
-
-### Explanation:
-- The **Accounts Payable** line uses the vendor's payable account (from partner configuration)
-- The payable account has the **partner_id** set (PT Telkom Indonesia)
-- This is the standard Odoo behavior, just with deductions added
-
----
-
-## Scenario 2: Payment WITH Expense Account
-
-When you create a vendor payment **with** an expense account set, the system uses that expense account instead of the payable account.
-
-### Example:
-- Vendor: PT Telkom Indonesia
-- Amount: Rp 2,000,000
-- **Expense Account: 611505 Telepon & Internet**
-- Deductions:
- - PPh 21: Rp 100,000
- - PPh 29: Rp 50,000
-- Final Payment: Rp 1,850,000
-
-### Journal Entry:
-```
-Account | Debit | Credit
--------------------------------------|-----------------|------------------
-611505 Telepon & Internet (Expense) | Rp 2,000,000.00 |
-PPh 21 (Tax Payable) | | Rp 100,000.00
-PPh 29 (Tax Payable) | | Rp 50,000.00
-Bank Account | | Rp 1,850,000.00
--------------------------------------|-----------------|------------------
-TOTAL | Rp 2,000,000.00 | Rp 2,000,000.00
-```
-
-### Explanation:
-- The **Expense Account** replaces the payable account
-- This allows direct expense recording without going through payables
-- The expense account still has the **partner_id** set for tracking
-- This is useful for immediate expense recognition
-
----
-
-## Key Differences
-
-| Aspect | Without Expense Account | With Expense Account |
-|--------|------------------------|---------------------|
-| Counterpart Account | Accounts Payable | Expense Account |
-| Account Type | liability_payable | expense |
-| Partner Required | Yes | Yes |
-| Use Case | Standard vendor payment | Direct expense recording |
-| Payable Created | Yes | No |
-
----
-
-## When to Use Each Scenario
-
-### Use WITHOUT Expense Account when:
-- You want to track vendor payables
-- You need to reconcile with vendor bills
-- You're following standard AP workflow
-- You need aging reports for vendors
-
-### Use WITH Expense Account when:
-- You want immediate expense recognition
-- You don't need to track payables
-- You're making direct payments (no bill)
-- You want simplified accounting
-
----
-
-## Technical Notes
-
-### Module Integration
-
-This module (`vendor_payment_diff_amount`) works seamlessly with `vendor_batch_payment_merge`:
-
-1. **vendor_batch_payment_merge** provides the `expense_account_id` field
-2. **vendor_payment_diff_amount** adds deduction functionality
-3. Both modules modify `_prepare_move_line_default_vals()` method
-4. The methods are called in sequence (inheritance chain)
-
-### Method Call Order
-
-```
-1. Standard Odoo: Creates basic 2-line entry (bank + payable)
-2. vendor_batch_payment_merge: Replaces payable with expense (if set)
-3. vendor_payment_diff_amount: Adds deduction lines and adjusts bank
-```
-
-### Result
-
-Final journal entry has:
-- 1 debit line (payable or expense at original amount)
-- N credit lines (one per deduction)
-- 1 credit line (bank at final_payment_amount)
-
-Total: 2 + N lines (where N = number of deductions)
-
----
-
-## Validation Rules
-
-The module ensures:
-
-1. ✅ Counterpart line always has `partner_id` set
-2. ✅ Counterpart line stays at original amount
-3. ✅ Bank line reduced to final_payment_amount
-4. ✅ Deduction lines are credits
-5. ✅ Deduction lines only have `partner_id` if account type requires it (payable/receivable)
-6. ✅ Entry is balanced (total debit = total credit)
-7. ✅ All required fields are present
-
----
-
-## Troubleshooting
-
-### Error: "Missing required account on accountable line"
-
-**Cause**: An accountable line (payable/receivable account) is missing the required `partner_id` field.
-
-**Solution**: The module now automatically handles this:
-- Counterpart line (payable/expense) always has `partner_id` set
-- Deduction lines only have `partner_id` if the account type requires it
-- Tax accounts (liability/expense) don't need `partner_id`
-
-If you still see this error:
-1. Check that the partner has a payable account configured
-2. Check that the expense account (if used) is valid
-3. Verify the payment has a partner selected
-4. Ensure deduction accounts are NOT payable/receivable accounts (use tax/expense accounts instead)
-
-### Error: "Entry is not balanced"
-
-**Cause**: The debit and credit totals don't match.
-
-**Solution**: This should not happen with the current implementation. If it does:
-1. Check that all deduction amounts are positive
-2. Verify currency conversion is working
-3. Check for rounding issues
-
----
-
-## Examples
-
-### Example 1: Simple Payment with One Deduction
-
-```python
-# Create payment
-payment = env['account.payment'].create({
- 'payment_type': 'outbound',
- 'partner_type': 'supplier',
- 'partner_id': partner.id,
- 'amount': 1000.0,
- 'journal_id': bank_journal.id,
-})
-
-# Add deduction
-env['payment.deduction.line'].create({
- 'payment_id': payment.id,
- 'amount_substract': 100.0,
- 'substract_account_id': tax_account.id,
- 'name': 'Withholding Tax',
-})
-
-# Post payment
-payment.action_post()
-
-# Result:
-# - Payable: Debit 1000
-# - Tax: Credit 100
-# - Bank: Credit 900
-```
-
-### Example 2: Payment with Expense Account and Multiple Deductions
-
-```python
-# Create payment with expense account
-payment = env['account.payment'].create({
- 'payment_type': 'outbound',
- 'partner_type': 'supplier',
- 'partner_id': partner.id,
- 'amount': 2000.0,
- 'expense_account_id': expense_account.id,
- 'journal_id': bank_journal.id,
-})
-
-# Add multiple deductions
-env['payment.deduction.line'].create({
- 'payment_id': payment.id,
- 'amount_substract': 100.0,
- 'substract_account_id': pph21_account.id,
- 'name': 'PPh 21',
-})
-
-env['payment.deduction.line'].create({
- 'payment_id': payment.id,
- 'amount_substract': 50.0,
- 'substract_account_id': pph29_account.id,
- 'name': 'PPh 29',
-})
-
-# Post payment
-payment.action_post()
-
-# Result:
-# - Expense: Debit 2000
-# - PPh 21: Credit 100
-# - PPh 29: Credit 50
-# - Bank: Credit 1850
-```
diff --git a/TEST_UPGRADE.md b/TEST_UPGRADE.md
deleted file mode 100644
index 554719d..0000000
--- a/TEST_UPGRADE.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# Testing the Upgrade to v2.0
-
-## Pre-Upgrade Checklist
-
-1. **Backup your database**
- ```bash
- pg_dump your_database > backup_before_v2.sql
- ```
-
-2. **Check for existing payments with deductions**
- ```python
- # In Odoo shell
- payments = env['account.payment'].search([
- ('amount_substract', '>', 0)
- ])
- print(f"Found {len(payments)} payments with deductions")
- ```
-
-## Upgrade Steps
-
-### 1. Update the Module
-
-From Odoo UI:
-- Go to Apps
-- Remove "Apps" filter
-- Search for "Vendor Payment Diff Amount"
-- Click "Upgrade"
-
-Or from command line:
-```bash
-./odoo-bin -u vendor_payment_diff_amount -d your_database --stop-after-init
-```
-
-### 2. Verify Installation
-
-Check that:
-- ✅ Module upgraded successfully without errors
-- ✅ New model `payment.deduction.line` is created
-- ✅ Security rules are applied
-
-### 3. Test Basic Functionality
-
-#### Test 1: Create Payment with Single Deduction
-1. Go to Accounting > Vendors > Payments
-2. Create new outbound payment
-3. Enter amount: 1000
-4. Add deduction line:
- - Account: Select a tax/expense account
- - Amount: 100
- - Description: "Withholding Tax"
-5. Verify:
- - Total Deductions: 100
- - Final Payment Amount: 900
-6. Post the payment
-7. Check journal entry:
- - Expense/Payable: Debit 1000
- - Tax Account: Credit 100
- - Bank: Credit 900
-
-#### Test 2: Create Payment with Multiple Deductions
-1. Create new outbound payment
-2. Enter amount: 2000
-3. Add first deduction:
- - Account: PPh 21 account
- - Amount: 100
- - Description: "PPh 21"
-4. Add second deduction:
- - Account: PPh 29 account
- - Amount: 50
- - Description: "PPh 29"
-5. Verify:
- - Total Deductions: 150
- - Final Payment Amount: 1850
-6. Post and verify journal entry:
- - Expense/Payable: Debit 2000
- - PPh 21: Credit 100
- - PPh 29: Credit 50
- - Bank: Credit 1850
-
-#### Test 3: Batch Payment with Deductions
-1. Go to Accounting > Vendors > Batch Payments
-2. Create new batch payment
-3. Add payment line
-4. Click on the line to open form view
-5. In "Deductions" section, add deduction lines
-6. Save the line
-7. Verify total deductions shown in tree
-8. Generate payments
-9. Verify deductions transferred to generated payment
-
-## Common Issues and Solutions
-
-### Issue: "company_id field missing"
-**Solution:** This was fixed in the latest version. Make sure you have the updated view files.
-
-### Issue: "Cannot locate form view"
-**Solution:** The view now includes both tree and form definitions. Upgrade to latest version.
-
-### Issue: Existing payments not showing deductions
-**Solution:** Old payments need manual migration. See UPGRADE_TO_V2.md for migration script.
-
-## Rollback Procedure
-
-If you need to rollback:
-
-1. Stop Odoo
-2. Restore database:
- ```bash
- dropdb your_database
- createdb your_database
- psql your_database < backup_before_v2.sql
- ```
-3. Checkout previous version of the module
-4. Restart Odoo
-
-## Post-Upgrade Tasks
-
-### Migrate Existing Data (if needed)
-
-If you have existing payments with the old single deduction structure, run this migration:
-
-```python
-# In Odoo shell: ./odoo-bin shell -d your_database
-
-# Find payments that might need migration
-# Note: In v2.0, amount_substract is computed, so we can't query it directly
-# Instead, look for payments that should have deductions but don't have deduction lines
-
-# Manual migration example:
-payment = env['account.payment'].browse(123) # Replace with actual ID
-
-# Create deduction line
-env['payment.deduction.line'].create({
- 'payment_id': payment.id,
- 'amount_substract': 100.0, # The old amount
- 'substract_account_id': 456, # The old account ID
- 'name': 'Migrated deduction',
- 'sequence': 10,
-})
-
-env.cr.commit()
-```
-
-## Verification Checklist
-
-After upgrade, verify:
-
-- [ ] Module shows version 2.0.0
-- [ ] Can create new payments with multiple deductions
-- [ ] Deductions calculate correctly
-- [ ] Journal entries are correct
-- [ ] Batch payments work with deductions
-- [ ] No errors in log files
-- [ ] Existing payments still accessible
-- [ ] Reports show correct amounts
-
-## Support
-
-If you encounter issues:
-1. Check the error log
-2. Review UPGRADE_TO_V2.md
-3. Contact module maintainer
diff --git a/UPGRADE_TO_V2.md b/UPGRADE_TO_V2.md
deleted file mode 100644
index fff84d5..0000000
--- a/UPGRADE_TO_V2.md
+++ /dev/null
@@ -1,175 +0,0 @@
-# Upgrade Guide: vendor_payment_diff_amount v1.x to v2.0
-
-## Overview
-
-Version 2.0 introduces a major architectural change: converting from single deduction fields to multiple deduction lines (One2many relationship).
-
-## What Changed
-
-### Database Schema Changes
-
-**Removed/Changed Fields:**
-- `account.payment.amount_substract` - Changed from stored field to computed field
-- `account.payment.substract_account_id` - Removed (replaced by deduction lines)
-- `account.batch.payment.line.amount_substract` - Changed to computed field
-- `account.batch.payment.line.substract_account_id` - Removed
-
-**New Model:**
-- `payment.deduction.line` - Stores individual deduction entries
-
-**New Fields:**
-- `account.payment.deduction_line_ids` (One2many)
-- `account.batch.payment.line.deduction_line_ids` (One2many)
-
-### Code Changes
-
-1. **Models:**
- - New model: `payment_deduction_line.py`
- - Updated: `account_payment.py` - uses One2many for deductions
- - Updated: `account_batch_payment.py` - transfers deduction lines
-
-2. **Views:**
- - Payment form: Shows editable tree for deduction lines
- - Batch payment form: Shows deduction lines in line form view
-
-3. **Security:**
- - Added access rights for `payment.deduction.line`
-
-## Upgrade Steps
-
-### 1. Backup Your Database
-
-```bash
-# Create a full database backup before upgrading
-pg_dump your_database > backup_before_v2_upgrade.sql
-```
-
-### 2. Install the Update
-
-```bash
-# Update the module
-./odoo-bin -u vendor_payment_diff_amount -d your_database
-```
-
-### 3. Data Migration (if needed)
-
-If you have existing payments with deductions, you'll need to migrate them. Here's a sample migration script:
-
-```python
-# Run this in Odoo shell: ./odoo-bin shell -d your_database
-
-# Find all payments with deductions (old structure)
-payments = env['account.payment'].search([
- ('amount_substract', '>', 0),
- ('substract_account_id', '!=', False)
-])
-
-print(f"Found {len(payments)} payments with deductions to migrate")
-
-# For each payment, create a deduction line
-for payment in payments:
- # Check if already migrated
- if payment.deduction_line_ids:
- print(f"Payment {payment.id} already has deduction lines, skipping")
- continue
-
- # Create deduction line from old fields
- env['payment.deduction.line'].create({
- 'payment_id': payment.id,
- 'amount_substract': payment.amount_substract,
- 'substract_account_id': payment.substract_account_id.id,
- 'name': f'Migrated: {payment.substract_account_id.name}',
- 'sequence': 10,
- })
-
- print(f"Migrated payment {payment.id}")
-
-env.cr.commit()
-print("Migration complete!")
-```
-
-### 4. Verify the Migration
-
-After migration, verify:
-
-1. Check a few payments that had deductions
-2. Verify the deduction lines are created correctly
-3. Check that totals match (amount_substract should equal sum of deduction lines)
-4. Test creating new payments with multiple deductions
-
-### 5. Update Custom Code (if any)
-
-If you have custom code that references the old fields:
-
-**Old code:**
-```python
-payment.amount_substract = 100.0
-payment.substract_account_id = account.id
-```
-
-**New code:**
-```python
-env['payment.deduction.line'].create({
- 'payment_id': payment.id,
- 'amount_substract': 100.0,
- 'substract_account_id': account.id,
- 'name': 'Withholding Tax',
-})
-```
-
-**Reading deductions:**
-
-Old:
-```python
-if payment.amount_substract > 0:
- account = payment.substract_account_id
-```
-
-New:
-```python
-if payment.amount_substract > 0: # Still works (computed field)
- for deduction in payment.deduction_line_ids:
- account = deduction.substract_account_id
- amount = deduction.amount_substract
-```
-
-## Testing After Upgrade
-
-1. **Create a new payment with multiple deductions:**
- - Amount: 1000
- - Deduction 1: Tax - 100
- - Deduction 2: Fee - 50
- - Verify final amount: 850
-
-2. **Post the payment and check journal entry:**
- - Payable: Debit 850
- - Tax Account: Debit 100
- - Fee Account: Debit 50
- - Bank: Credit 1000
-
-3. **Test batch payments:**
- - Create batch payment
- - Add line with multiple deductions
- - Generate payment
- - Verify deductions transferred correctly
-
-## Rollback (if needed)
-
-If you need to rollback:
-
-1. Restore database backup:
-```bash
-psql your_database < backup_before_v2_upgrade.sql
-```
-
-2. Reinstall v1.x of the module
-
-## Support
-
-For issues or questions about the upgrade, contact the module maintainer.
-
-## Compatibility
-
-- **Odoo Version:** 17.0
-- **Breaking Changes:** Yes (database schema and API changes)
-- **Backward Compatible:** No (requires migration)
diff --git a/__init__.py b/__init__.py
index cde864b..35e7c96 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
+from . import wizard
diff --git a/__manifest__.py b/__manifest__.py
index c54f770..ad51deb 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
{
'name': 'Vendor Payment Diff Amount',
- 'version': '17.0.2.0.0',
+ 'version': '17.0.2.1.0',
'category': 'Accounting/Accounting',
'summary': 'Support multiple payment deductions for vendor payments (withholding tax, fees, etc.)',
'description': """
@@ -37,6 +37,7 @@ payment lines and automatically transferred to generated payments.
'security/ir.model.access.csv',
'views/account_payment_views.xml',
'views/account_batch_payment_views.xml',
+ 'wizard/payment_amount_fix_wizard_views.xml',
],
'installable': True,
'application': False,
diff --git a/fix_amount_issue.py b/fix_amount_issue.py
new file mode 100644
index 0000000..ea6a382
--- /dev/null
+++ b/fix_amount_issue.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+"""
+Script to fix amount calculation issues in vendor_payment_diff_amount module.
+
+This script can be run in Odoo shell to fix payments that have incorrect amounts
+due to synchronization issues.
+
+Usage:
+1. Open Odoo shell: python odoo-bin shell -d your_database
+2. Run this script: exec(open('customaddons/vendor_payment_diff_amount/fix_amount_issue.py').read())
+"""
+
+def fix_payment_amounts():
+ """Fix amount calculation for payments with deductions."""
+
+ # Find all payments with deductions that might have incorrect amounts
+ payments = env['account.payment'].search([
+ ('amount_substract', '>', 0),
+ ('state', '=', 'posted'),
+ ])
+
+ print(f"Found {len(payments)} payments with deductions to check...")
+
+ fixed_count = 0
+ for payment in payments:
+ if payment.move_id:
+ # Find the counterpart line (payable/expense line with debit)
+ counterpart_lines = payment.move_id.line_ids.filtered(
+ lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
+ )
+
+ if counterpart_lines:
+ correct_amount = counterpart_lines[0].debit
+ current_amount = payment.amount
+
+ # Check if amount needs fixing (allow for small rounding differences)
+ if abs(current_amount - correct_amount) > 0.01:
+ print(f"Payment {payment.name} (ID: {payment.id}):")
+ print(f" Current amount: {current_amount}")
+ print(f" Correct amount: {correct_amount}")
+ print(f" Deductions: {payment.amount_substract}")
+ print(f" Current final: {payment.final_payment_amount}")
+ print(f" Expected final: {correct_amount - payment.amount_substract}")
+
+ # Fix the amount using SQL to avoid triggering computed fields
+ env.cr.execute(
+ "UPDATE account_payment SET amount = %s WHERE id = %s",
+ (correct_amount, payment.id)
+ )
+
+ # Invalidate cache and recompute
+ payment.invalidate_recordset(['amount'])
+ payment._compute_final_payment_amount()
+
+ print(f" ✓ Fixed! New final amount: {payment.final_payment_amount}")
+ print()
+ fixed_count += 1
+ else:
+ print(f"Payment {payment.name} is correct (amount: {current_amount})")
+
+ print(f"Fixed {fixed_count} payments.")
+
+ # Commit the changes
+ env.cr.commit()
+ print("Changes committed to database.")
+
+def check_specific_payment(payment_name_or_id):
+ """Check a specific payment by name or ID."""
+
+ if isinstance(payment_name_or_id, str):
+ payment = env['account.payment'].search([('name', '=', payment_name_or_id)], limit=1)
+ else:
+ payment = env['account.payment'].browse(payment_name_or_id)
+
+ if not payment:
+ print(f"Payment {payment_name_or_id} not found.")
+ return
+
+ print(f"Payment: {payment.name} (ID: {payment.id})")
+ print(f"State: {payment.state}")
+ print(f"Partner: {payment.partner_id.name}")
+ print(f"Amount: {payment.amount}")
+ print(f"Deductions: {payment.amount_substract}")
+ print(f"Final Payment Amount: {payment.final_payment_amount}")
+ print(f"Expected Final: {payment.amount - payment.amount_substract}")
+
+ if payment.move_id:
+ print("\nJournal Entry Lines:")
+ for line in payment.move_id.line_ids:
+ print(f" {line.account_id.name}: Debit {line.debit}, Credit {line.credit}")
+
+ # Check if amounts are correct
+ expected_final = payment.amount - payment.amount_substract
+ if abs(payment.final_payment_amount - expected_final) > 0.01:
+ print(f"\n❌ ISSUE: Final payment amount is incorrect!")
+ print(f" Expected: {expected_final}")
+ print(f" Actual: {payment.final_payment_amount}")
+ else:
+ print(f"\n✅ Payment amounts are correct.")
+
+# Example usage:
+if __name__ == "__main__":
+ print("Vendor Payment Diff Amount - Fix Script")
+ print("=" * 50)
+
+ # Check a specific payment (replace with your payment name)
+ # check_specific_payment("PBCA5858/2025/00061")
+
+ # Or fix all payments
+ # fix_payment_amounts()
+
+ print("\nTo use this script:")
+ print("1. check_specific_payment('PBCA5858/2025/00061') # Check specific payment")
+ print("2. fix_payment_amounts() # Fix all payments with issues")
\ No newline at end of file
diff --git a/models/account_payment.py b/models/account_payment.py
index 7922f8b..77f1c58 100644
--- a/models/account_payment.py
+++ b/models/account_payment.py
@@ -44,7 +44,14 @@ class AccountPayment(models.Model):
for payment in self:
amount_substract = payment.amount_substract or 0.0
currency = payment.currency_id or payment.company_id.currency_id
- payment.final_payment_amount = currency.round(payment.amount - amount_substract)
+ calculated_final = payment.amount - amount_substract
+
+ # Debug logging
+ import logging
+ _logger = logging.getLogger(__name__)
+ _logger.info(f"Computing final payment amount for payment {payment.id}: amount={payment.amount}, substract={amount_substract}, final={calculated_final}")
+
+ payment.final_payment_amount = currency.round(calculated_final)
@api.constrains('amount', 'amount_substract', 'expense_account_id', 'deduction_line_ids')
def _check_amount_substract(self):
@@ -60,53 +67,141 @@ class AccountPayment(models.Model):
"Please set the Expense Account field before adding deductions."
))
+ def write(self, vals):
+ """
+ Override write to handle amount field changes when we have deductions.
+ This prevents Odoo's synchronization from incorrectly changing the amount based on journal entries.
+ """
+ # If amount is being changed and we have deductions, we need to be careful
+ if 'amount' in vals:
+ for payment in self:
+ if payment.amount_substract and payment.amount_substract > 0:
+ # Log the attempted change for debugging
+ import logging
+ _logger = logging.getLogger(__name__)
+ _logger.info(f"Amount change attempted from {payment.amount} to {vals['amount']} for payment {payment.id} with deductions {payment.amount_substract}")
+
+ # If the payment is posted, don't allow amount changes
+ # This prevents synchronization from messing up the amount
+ if payment.state == 'posted':
+ _logger.info(f"Preventing amount change for posted payment {payment.id}")
+ vals = vals.copy()
+ del vals['amount']
+ break
+
+ return super().write(vals)
+
+ def action_fix_amount_calculation(self):
+ """
+ Action to fix amount calculation for payments with deductions.
+ This can be used to correct payments that have incorrect amounts due to synchronization issues.
+ """
+ for payment in self:
+ if payment.amount_substract and payment.amount_substract > 0:
+ # Get the correct amount from the journal entry
+ if payment.move_id:
+ # Find the counterpart line (payable/expense line)
+ counterpart_lines = payment.move_id.line_ids.filtered(
+ lambda l: l.account_id.account_type in ('liability_payable', 'expense') and l.debit > 0
+ )
+ if counterpart_lines:
+ correct_amount = counterpart_lines[0].debit
+ if abs(payment.amount - correct_amount) > 0.01: # Allow for rounding differences
+ import logging
+ _logger = logging.getLogger(__name__)
+ _logger.info(f"Fixing amount for payment {payment.id}: {payment.amount} -> {correct_amount}")
+
+ # Use SQL to update directly to avoid triggering computed fields
+ payment.env.cr.execute(
+ "UPDATE account_payment SET amount = %s WHERE id = %s",
+ (correct_amount, payment.id)
+ )
+ payment.invalidate_recordset(['amount'])
+ payment._compute_final_payment_amount()
+
+ @api.model
+ def fix_all_payment_amounts(self):
+ """
+ Model method to fix all payments with deduction amount calculation issues.
+ Can be called from code or through RPC.
+ """
+ payments = self.search([
+ ('amount_substract', '>', 0),
+ ('state', '=', 'posted'),
+ ])
+
+ fixed_count = 0
+ for payment in payments:
+ if payment.move_id:
+ # Find the counterpart line (payable/expense line with debit)
+ counterpart_lines = payment.move_id.line_ids.filtered(
+ lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
+ )
+
+ if counterpart_lines:
+ correct_amount = counterpart_lines[0].debit
+ if abs(payment.amount - correct_amount) > 0.01:
+ # Fix using SQL to avoid sync issues
+ payment.env.cr.execute(
+ "UPDATE account_payment SET amount = %s WHERE id = %s",
+ (correct_amount, payment.id)
+ )
+ payment.invalidate_recordset(['amount'])
+ payment._compute_final_payment_amount()
+ fixed_count += 1
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': _('Amount Fix Complete'),
+ 'message': _('Fixed %d payments with incorrect amounts.') % fixed_count,
+ 'type': 'success',
+ }
+ }
+
def _synchronize_from_moves(self, changed_fields):
"""
- Override to prevent amount synchronization when we have a substract amount.
+ Override to handle synchronization when we have deductions.
When we have a substract amount, the bank credit line is reduced to final_payment_amount,
but we want to keep the payment amount at the original value (not sync it down).
-
- Also handles the case where expense_account_id is used (from vendor_batch_payment_merge),
- which replaces the payable account with an expense account.
"""
- # Handle multiple records - process each payment individually
+ # For payments with deductions, we need to handle synchronization carefully
for payment in self:
- # When expense_account_id is used with substract amount, the journal entry doesn't have
- # a payable/receivable account. This causes Odoo's validation to fail.
- # We need to skip the validation in this case.
- if payment.expense_account_id and payment.amount_substract and payment.amount_substract > 0:
- try:
- result = super(AccountPayment, payment)._synchronize_from_moves(changed_fields)
- except Exception as e:
- # If validation fails due to missing payable/receivable account, it's expected
- if 'receivable/payable account' in str(e):
- # This is expected - just continue to next payment
- continue
- else:
- # Re-raise other exceptions
- raise
- continue
-
- # If we have a substract amount (but no expense_account_id), we need to handle the sync differently
if payment.amount_substract and payment.amount_substract > 0:
- # Store the original amount before sync
+ # Store the original amount before any synchronization
original_amount = payment.amount
original_substract = payment.amount_substract
- # Call parent sync
- result = super(AccountPayment, payment)._synchronize_from_moves(changed_fields)
+ # Try to call parent sync but handle any errors
+ try:
+ super(AccountPayment, payment)._synchronize_from_moves(changed_fields)
+ except Exception as e:
+ # If there's an error (like missing payable account when using expense_account_id),
+ # that's expected, so we just continue
+ import logging
+ _logger = logging.getLogger(__name__)
+ _logger.info(f"Sync error for payment {payment.id} (expected with deductions): {e}")
- # Restore the original amount if it was changed by sync
+ # After sync, ensure the amount is still correct
+ # The sync might have changed it based on journal entry lines
if payment.amount != original_amount:
- # Use write to update without triggering another sync
- super(AccountPayment, payment).write({
- 'amount': original_amount,
- 'amount_substract': original_substract,
- })
- # Force recomputation of final_payment_amount
- payment._compute_final_payment_amount()
+ import logging
+ _logger = logging.getLogger(__name__)
+ _logger.info(f"Restoring amount for payment {payment.id}: {payment.amount} -> {original_amount}")
+
+ # Use SQL to restore the original amount without triggering more syncs
+ payment.env.cr.execute(
+ "UPDATE account_payment SET amount = %s WHERE id = %s",
+ (original_amount, payment.id)
+ )
+ payment.invalidate_recordset(['amount'])
+
+ # Ensure final_payment_amount is recalculated correctly
+ payment._compute_final_payment_amount()
else:
+ # No deductions - use standard synchronization
super(AccountPayment, payment)._synchronize_from_moves(changed_fields)
def _prepare_move_line_default_vals(self, write_off_line_vals=None, force_balance=None):
diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv
index 9c2a902..e64238d 100644
--- a/security/ir.model.access.csv
+++ b/security/ir.model.access.csv
@@ -1,3 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_payment_deduction_line_user,payment.deduction.line.user,model_payment_deduction_line,account.group_account_invoice,1,1,1,1
access_payment_deduction_line_manager,payment.deduction.line.manager,model_payment_deduction_line,account.group_account_manager,1,1,1,1
+access_payment_amount_fix_wizard_user,payment.amount.fix.wizard.user,model_payment_amount_fix_wizard,account.group_account_invoice,1,1,1,1
+access_payment_amount_fix_wizard_manager,payment.amount.fix.wizard.manager,model_payment_amount_fix_wizard,account.group_account_manager,1,1,1,1
diff --git a/views/account_payment_views.xml b/views/account_payment_views.xml
index c99fde4..2286184 100644
--- a/views/account_payment_views.xml
+++ b/views/account_payment_views.xml
@@ -40,6 +40,12 @@
options="{'currency_field': 'currency_id'}"
readonly="1"
force_save="1"/>
+
diff --git a/wizard/__init__.py b/wizard/__init__.py
new file mode 100644
index 0000000..baa5a88
--- /dev/null
+++ b/wizard/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import payment_amount_fix_wizard
\ No newline at end of file
diff --git a/wizard/payment_amount_fix_wizard.py b/wizard/payment_amount_fix_wizard.py
new file mode 100644
index 0000000..769c75e
--- /dev/null
+++ b/wizard/payment_amount_fix_wizard.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api, _
+
+
+class PaymentAmountFixWizard(models.TransientModel):
+ _name = 'payment.amount.fix.wizard'
+ _description = 'Fix Payment Amount Calculation'
+
+ payment_ids = fields.Many2many(
+ 'account.payment',
+ string='Payments to Fix',
+ domain=[('amount_substract', '>', 0), ('state', '=', 'posted')],
+ help='Payments with deductions that may have incorrect amounts'
+ )
+
+ @api.model
+ def default_get(self, fields_list):
+ """Pre-select payments that need fixing."""
+ res = super().default_get(fields_list)
+
+ # Find payments that might need fixing
+ payments = self.env['account.payment'].search([
+ ('amount_substract', '>', 0),
+ ('state', '=', 'posted'),
+ ])
+
+ payments_to_fix = []
+ for payment in payments:
+ if payment.move_id:
+ counterpart_lines = payment.move_id.line_ids.filtered(
+ lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
+ )
+ if counterpart_lines:
+ correct_amount = counterpart_lines[0].debit
+ if abs(payment.amount - correct_amount) > 0.01:
+ payments_to_fix.append(payment.id)
+
+ res['payment_ids'] = [(6, 0, payments_to_fix)]
+ return res
+
+ def action_fix_amounts(self):
+ """Fix the selected payments."""
+ fixed_count = 0
+
+ for payment in self.payment_ids:
+ if payment.move_id:
+ counterpart_lines = payment.move_id.line_ids.filtered(
+ lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
+ )
+
+ if counterpart_lines:
+ correct_amount = counterpart_lines[0].debit
+ if abs(payment.amount - correct_amount) > 0.01:
+ # Fix using SQL to avoid sync issues
+ payment.env.cr.execute(
+ "UPDATE account_payment SET amount = %s WHERE id = %s",
+ (correct_amount, payment.id)
+ )
+ payment.invalidate_recordset(['amount'])
+ payment._compute_final_payment_amount()
+ fixed_count += 1
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': _('Fix Complete'),
+ 'message': _('Fixed %d payments with incorrect amounts.') % fixed_count,
+ 'type': 'success',
+ }
+ }
\ No newline at end of file
diff --git a/wizard/payment_amount_fix_wizard_views.xml b/wizard/payment_amount_fix_wizard_views.xml
new file mode 100644
index 0000000..87ffbea
--- /dev/null
+++ b/wizard/payment_amount_fix_wizard_views.xml
@@ -0,0 +1,46 @@
+
+
+
+ payment.amount.fix.wizard.form
+ payment.amount.fix.wizard
+
+
+
+
+
+
+ Fix Payment Amounts
+ payment.amount.fix.wizard
+ form
+ new
+
+
+
+
+
\ No newline at end of file