diff --git a/FINAL_FIX.md b/FINAL_FIX.md new file mode 100644 index 0000000..22f35b5 --- /dev/null +++ b/FINAL_FIX.md @@ -0,0 +1,263 @@ +# 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 new file mode 100644 index 0000000..706e6dd --- /dev/null +++ b/FIX_SUMMARY.md @@ -0,0 +1,173 @@ +# 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 new file mode 100644 index 0000000..fc917c9 --- /dev/null +++ b/JOURNAL_ENTRY_STRUCTURE.md @@ -0,0 +1,139 @@ +# 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 new file mode 100644 index 0000000..87e86eb --- /dev/null +++ b/REQUIREMENT_EXPENSE_ACCOUNT.md @@ -0,0 +1,269 @@ +# 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 new file mode 100644 index 0000000..d22f9e4 --- /dev/null +++ b/SCENARIOS.md @@ -0,0 +1,236 @@ +# 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 new file mode 100644 index 0000000..554719d --- /dev/null +++ b/TEST_UPGRADE.md @@ -0,0 +1,162 @@ +# 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 new file mode 100644 index 0000000..fff84d5 --- /dev/null +++ b/UPGRADE_TO_V2.md @@ -0,0 +1,175 @@ +# 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)