From 15e9c93dade2afc2ea120a2ce81208db18bc2838 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Thu, 30 Apr 2026 21:53:04 +0700 Subject: [PATCH] feat: prevent accidental journal reversals for expense payments by hiding the reverse button --- README.md | 31 +++++++++++++++++-------------- __manifest__.py | 1 + models/account_move.py | 13 ++++++++++++- views/account_move_views.xml | 17 +++++++++++++++++ 4 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 views/account_move_views.xml diff --git a/README.md b/README.md index da3b7e0..20b2d79 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HR Expense Account Split & Kiosk +# HR Expense Account Split & Kiosk (Odoo 19) This module enhances Odoo's standard Expense workflow by providing account-splitting logic, an **Anonymous Expense Kiosk** for employees, and automated realization accounting. @@ -6,25 +6,30 @@ This module enhances Odoo's standard Expense workflow by providing account-split ### 1. Dynamic Sequences - **Employee Reimbursement (RMBS)**: Expenses paid by the employee follow the prefix `RMBS/YYYY/MM/XXXXX`. -* **Company Advance (KSBN)**: Expenses paid by the company (Kasbon) follow the prefix `KSBN/YYYY/MM/XXXXX`. -* This ensures clear separation between standard reimbursements and company-issued advances at a glance. +- **Company Advance (KSBN)**: Expenses paid by the company (Kasbon) follow the prefix `KSBN/YYYY/MM/XXXXX`. +- This ensures clear separation between standard reimbursements and company-issued advances at a glance. -### 2. Enhanced Status Workflow +### 2. Enhanced Status Workflow (Odoo 19 Refined) - **Wait Post Status (Yellow)**: A new intermediate status for company-paid expense reports. - **Workflow**: `Approved` -> `Posted` -> **`Wait Post`** -> `Done`. - **Logic**: The report moves to **Wait Post** after the advance is paid. It stays here until the employee submits all receipts and the accountant posts the final realization journal. - **Done (Green)**: Only reached when all realization accounting is completed, ensuring the physical and financial cycles are fully synchronized. +- **Architectural Note**: In Odoo 19, the legacy `hr.expense.sheet` logic has been merged directly into the core `hr.expense` model for better performance and consistency. -### 3. Account Splitting +### 3. Account Splitting & Kasbon Logic - **Dynamic Selection**: Automatically routes expenses to different GL accounts based on the `Paid By` field. - **Configuration**: Set distinct `Expense Account (Employee)` and `Expense Account (Company)` directly on the Expense Category form. +- **Advance Account**: Company-paid advances (Kasbon) use account **115101** for the debit side of the initial payment journal entry. -### 4. Anonymous Expense Kiosk +### 4. Journal Entry Safeguards +- **Reversal Protection**: The **Reverse Entry** button is automatically hidden on journal entries linked to expenses. This prevents accidental reversals that would cause the accounting ledger to fall out of sync with the expense status. + +### 5. Anonymous Expense Kiosk - **PIN-Protected Access**: Secure employee login via a 4-digit PIN on a tablet interface. - **Real-Time Totaling**: Automatically summarizes multiple physical receipts into a single realization. - **Image Optimization**: Client-side JPEG compression (1024px, 70% quality) reduces server storage usage by ~90%. -### 5. Automated Realization Accounting +### 6. Automated Realization Accounting - **Balanced Journal Entries**: Automatically calculates discrepancies between the advance paid and actual spending. - **Discrepancy Accounts**: - **Over-spent (Spent > Paid)**: Balance is moved to `216109 Biaya Lain yang masih harus dibayar` (Liability/Payable). @@ -33,21 +38,19 @@ This module enhances Odoo's standard Expense workflow by providing account-split - **Create Vendor Payment**: One-click button on the realization form to pay the employee the difference. - **Create Customer Payment**: One-click button on the realization form to record the employee returning the excess funds. -### 6. Validation & Security -- **Mandatory Receipts**: Prevents submission of employee-paid expenses without attachments. -- **Overdue Tracking**: Highlights missing realization receipts in red/alerts for company-paid expenses. - ## 🛠 Configuration 1. **GL Accounts**: - **Expenses > Configuration > Expense Categories**. - Define the two accounts under the **Accounting** tab. -2. **Kiosk Token**: - - URL structure: `/hr_expense/kiosk/d56db48c463444c88b86f14980d7a185`. +2. **Kiosk Activation**: + - Go to **Expenses > Configuration > Settings**. + - The **Kiosk URL** is displayed in the "Expense Kiosk" block. You can regenerate the token here if needed. 3. **Employee PINs**: - Set 4-digit PINs on employee records for Kiosk access. ## 📋 Technical Notes +- **Odoo Version**: 19.0 - **Controller**: `/hr_expense/kiosk/` -- **Models**: `hr.expense`, `hr.expense.sheet`, `hr.expense.realization`, `account.payment` +- **Models**: `hr.expense`, `hr.expense.realization`, `account.move`, `account.payment` - **JS Framework**: Odoo 19 OWL diff --git a/__manifest__.py b/__manifest__.py index 0082fc0..2ca2245 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -14,6 +14,7 @@ 'views/hr_expense_realization_views.xml', 'views/hr_expense_kiosk_templates.xml', 'views/res_config_settings_views.xml', + 'views/account_move_views.xml', ], 'assets': { 'hr_expense_account_split.assets_public_kiosk': [ diff --git a/models/account_move.py b/models/account_move.py index d10e78b..82efbed 100644 --- a/models/account_move.py +++ b/models/account_move.py @@ -1,8 +1,19 @@ -from odoo import models, api +from odoo import models, fields, api class AccountMove(models.Model): _inherit = 'account.move' + is_expense_payment = fields.Boolean( + string="Is Expense Payment", + compute="_compute_is_expense_payment", + store=True, + ) + + @api.depends('expense_ids') + def _compute_is_expense_payment(self): + for move in self: + move.is_expense_payment = bool(move.sudo().expense_ids) + def _get_hr_expense_base_class(self): """ Returns the hr_expense class in the MRO to jump over it. """ mro = type(self).mro() diff --git a/views/account_move_views.xml b/views/account_move_views.xml new file mode 100644 index 0000000..f7af696 --- /dev/null +++ b/views/account_move_views.xml @@ -0,0 +1,17 @@ + + + + + account.move.form.inherit.expense.payment + account.move + + + + move_type != 'entry' or state != 'posted' or payment_state == 'reversed' or is_expense_payment + + + + + + +