Cleanup: remove orphan addon source at_master_order (no tags reference it)
This commit is contained in:
7
addons/at_master_order/.gitignore
vendored
7
addons/at_master_order/.gitignore
vendored
@@ -1,7 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.__pycache__
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
__pycache__/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
# Accounting & Journal Entries - How It Works
|
|
||||||
|
|
||||||
## Executive Summary
|
|
||||||
|
|
||||||
**There is NO duplicate journal entry creation.** The system is working correctly.
|
|
||||||
|
|
||||||
When you create and post invoices/bills, commission and expenses are added as **invoice lines**, not as separate journal entries. When the invoice is posted, Odoo automatically creates ONE journal entry that includes all the invoice lines (products, commission, expenses, shipping, etc.).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How Commission and Expense Accounting Works
|
|
||||||
|
|
||||||
### Modern Workflow (Current Implementation) ✅
|
|
||||||
|
|
||||||
#### 1. Creating Customer Invoices
|
|
||||||
|
|
||||||
When you create a customer invoice using the "Create Invoice" wizard (`wizard/create_invoice_wizard.py`):
|
|
||||||
|
|
||||||
1. **Product lines** are added from Master Order lines
|
|
||||||
2. **Commission line** is added based on commission rate (e.g., 10% of product sales)
|
|
||||||
3. **Expense lines** are added for expenses that should be invoiced to the customer
|
|
||||||
4. **Shipping charge line** is added if applicable
|
|
||||||
|
|
||||||
All of these are added as **invoice lines**, not as separate journal entries.
|
|
||||||
|
|
||||||
#### 2. Posting the Invoice
|
|
||||||
|
|
||||||
When you post the invoice using `action_post()`:
|
|
||||||
|
|
||||||
- Odoo automatically creates **ONE journal entry** with multiple lines:
|
|
||||||
- **Debit**: Customer Receivable Account (total amount owed by customer)
|
|
||||||
- **Credit**: Income accounts for each line:
|
|
||||||
- Product income account (for products sold)
|
|
||||||
- Commission income account (for commission)
|
|
||||||
- Expense income account (for reinvoiced expenses)
|
|
||||||
- Shipping income account (for shipping charges)
|
|
||||||
|
|
||||||
**Result**: ONE balanced journal entry that includes everything.
|
|
||||||
|
|
||||||
#### 3. Example
|
|
||||||
|
|
||||||
If you create an invoice with:
|
|
||||||
- Products: $1,000
|
|
||||||
- Commission (10%): $100
|
|
||||||
- Shipping: $50
|
|
||||||
- **Total**: $1,150
|
|
||||||
|
|
||||||
The posted journal entry will be:
|
|
||||||
```
|
|
||||||
Date Account Debit Credit
|
|
||||||
---------------------------------------------------------
|
|
||||||
2024-01-14 Customer Receivable $1,150
|
|
||||||
2024-01-14 Product Income $1,000
|
|
||||||
2024-01-14 Commission Income $100
|
|
||||||
2024-01-14 Shipping Income $50
|
|
||||||
---------------------------------------------------------
|
|
||||||
TOTAL $1,150 $1,150 ✅ BALANCED
|
|
||||||
```
|
|
||||||
|
|
||||||
**This is ONE journal entry, not multiple separate entries.**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Legacy Methods (DO NOT USE) ❌
|
|
||||||
|
|
||||||
The following methods exist in the code but are **NEVER called**:
|
|
||||||
|
|
||||||
1. `_create_commission_journal_entry()` in `models/master_order.py`
|
|
||||||
2. `_create_expense_customer_journal_entry()` in `models/master_order.py`
|
|
||||||
3. `_create_expenses_journal_entry()` in `models/master_order.py`
|
|
||||||
|
|
||||||
These were part of an old workflow where commission and expenses were recorded as **separate journal entries** apart from the invoice. This approach was:
|
|
||||||
- More complex to manage
|
|
||||||
- Required manual reconciliation
|
|
||||||
- Created confusion about whether items were "double-counted"
|
|
||||||
|
|
||||||
**The old workflow has been completely replaced.** These methods are kept only for backward compatibility and are clearly marked as "LEGACY - DO NOT USE".
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why You Might Think There Are "Duplicate" Entries
|
|
||||||
|
|
||||||
You may see what looks like duplicate entries in Odoo, but they're actually the same data viewed from different perspectives:
|
|
||||||
|
|
||||||
### 1. **Journal Items vs. Invoices**
|
|
||||||
|
|
||||||
- **Accounting > Journal Items**: Shows the raw journal entry with all debit/credit lines
|
|
||||||
- **Invoicing > Customer Invoices**: Shows the invoice document with line items
|
|
||||||
|
|
||||||
These are two views of the **SAME data**, not duplicates.
|
|
||||||
|
|
||||||
### 2. **Analytic Entries**
|
|
||||||
|
|
||||||
If you have analytic accounting enabled:
|
|
||||||
- Each invoice line creates an **analytic entry** on the analytic account
|
|
||||||
- This is for cost/revenue tracking by project or department
|
|
||||||
- These are **not journal entries**, they're analytic distribution records
|
|
||||||
|
|
||||||
### 3. **Partner Ledger**
|
|
||||||
|
|
||||||
When you view the partner ledger for a customer:
|
|
||||||
- You see the invoice amount as a single line
|
|
||||||
- If you drill down, you see the invoice line details
|
|
||||||
- Again, this is the **SAME data** viewed at different levels of detail
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Vendor Bills
|
|
||||||
|
|
||||||
The same logic applies to vendor bills:
|
|
||||||
|
|
||||||
### Creating a Vendor Bill
|
|
||||||
|
|
||||||
1. Products, shipping costs, and other expenses are added as **bill lines**
|
|
||||||
2. When posted, Odoo creates **ONE journal entry**:
|
|
||||||
- **Debit**: Expense accounts (for each bill line)
|
|
||||||
- **Credit**: Vendor Payable Account (total amount owed to vendor)
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
If you create a vendor bill for:
|
|
||||||
- Product purchase: $800
|
|
||||||
- Shipping cost: $50
|
|
||||||
- **Total**: $850
|
|
||||||
|
|
||||||
The posted journal entry will be:
|
|
||||||
```
|
|
||||||
Date Account Debit Credit
|
|
||||||
---------------------------------------------------------
|
|
||||||
2024-01-14 Product Expense $800
|
|
||||||
2024-01-14 Shipping Expense $50
|
|
||||||
2024-01-14 Vendor Payable $850
|
|
||||||
---------------------------------------------------------
|
|
||||||
TOTAL $850 $850 ✅ BALANCED
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What To Do If You See Unexpected Journal Entries
|
|
||||||
|
|
||||||
If you're seeing journal entries that you think are duplicates:
|
|
||||||
|
|
||||||
### Step 1: Check the Journal Entry Source
|
|
||||||
|
|
||||||
1. Go to **Accounting > Accounting > Journal Items**
|
|
||||||
2. Find the journal entry you think is a duplicate
|
|
||||||
3. Check the **Reference** field - it will show you where it came from:
|
|
||||||
- `INV/2024/00001` = Customer Invoice
|
|
||||||
- `BILL/2024/00001` = Vendor Bill
|
|
||||||
- `MISC/2024/00001` = Manual Journal Entry
|
|
||||||
- `PAYMENT/2024/00001` = Payment
|
|
||||||
|
|
||||||
### Step 2: Verify It's Not the Same Entry
|
|
||||||
|
|
||||||
1. Check the **Date** and **Amount**
|
|
||||||
2. Check the **Partner** (customer/vendor)
|
|
||||||
3. Check the **Journal** (Sales, Purchase, Miscellaneous, etc.)
|
|
||||||
|
|
||||||
If they match, they're likely the same entry viewed in different places.
|
|
||||||
|
|
||||||
### Step 3: Check for Manual Entries
|
|
||||||
|
|
||||||
If you find entries with the reference pattern `MISC/*` or similar:
|
|
||||||
- These might be manual journal entries created by mistake
|
|
||||||
- Check if they reference the Master Order
|
|
||||||
- These can be deleted if they're truly duplicates
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### ✅ DO:
|
|
||||||
|
|
||||||
1. **Create invoices/bills using the wizards** - they automatically include commission, expenses, and shipping as invoice lines
|
|
||||||
2. **Let Odoo create journal entries automatically** when you post invoices/bills
|
|
||||||
3. **Use the "Journal Items" report** to see the full accounting details
|
|
||||||
4. **Review the balance** - every journal entry should be balanced (Debit = Credit)
|
|
||||||
|
|
||||||
### ❌ DON'T:
|
|
||||||
|
|
||||||
1. **Don't manually create journal entries** for commission or expenses - they're already included in the invoice
|
|
||||||
2. **Don't use the legacy methods** (`_create_commission_journal_entry`, etc.) - they're deprecated
|
|
||||||
3. **Don't panic if you see "multiple" entries** - check if they're the same entry in different views
|
|
||||||
4. **Don't delete journal entries** without understanding their source - they might be legitimate
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technical Details
|
|
||||||
|
|
||||||
### Code References
|
|
||||||
|
|
||||||
#### Invoice Creation with Commission and Expenses
|
|
||||||
|
|
||||||
**File**: `wizard/create_invoice_wizard.py`
|
|
||||||
**Method**: `action_create_invoice()`
|
|
||||||
**Lines**: 222-255
|
|
||||||
|
|
||||||
This is where commission and expense lines are added to the invoice:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Add commission line (lines 222-239)
|
|
||||||
if has_commission and total_products > 0:
|
|
||||||
commission_amount = (total_products * self.commission_rate) / 100.0
|
|
||||||
if commission_amount > 0:
|
|
||||||
commission_product = self._get_or_create_commission_product(master)
|
|
||||||
invoice_vals['invoice_line_ids'].append((0, 0, {
|
|
||||||
'product_id': commission_product.id,
|
|
||||||
'name': f'Commission ({self.commission_rate}%)',
|
|
||||||
'quantity': 1.0,
|
|
||||||
'price_unit': commission_amount,
|
|
||||||
# ... (account_id set from product's income account)
|
|
||||||
}))
|
|
||||||
|
|
||||||
# Add expense lines (lines 241-255)
|
|
||||||
if has_expenses:
|
|
||||||
for expense in self.expense_ids:
|
|
||||||
expense_product = self._get_or_create_expense_product(expense, master)
|
|
||||||
invoice_vals['invoice_line_ids'].append((0, 0, {
|
|
||||||
'product_id': expense_product.id,
|
|
||||||
'name': f'{expense.expense_type}: {expense.description}',
|
|
||||||
'quantity': 1.0,
|
|
||||||
'price_unit': expense.amount,
|
|
||||||
# ... (account_id set from product's income account)
|
|
||||||
}))
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Invoice Posting (No Additional Journal Entries Created)
|
|
||||||
|
|
||||||
**File**: `models/account_move.py`
|
|
||||||
**Method**: `action_post()`
|
|
||||||
**Lines**: 947-978
|
|
||||||
|
|
||||||
The override only validates balance and syncs bill lines to Master Order. **No additional journal entries are created**.
|
|
||||||
|
|
||||||
```python
|
|
||||||
def action_post(self):
|
|
||||||
"""Override to validate invoice balance and sync vendor bill lines to Master Order.
|
|
||||||
|
|
||||||
NOTE: Commission and expenses are added as invoice lines (not separate journal entries).
|
|
||||||
When the invoice is posted, they become part of the invoice's journal entry automatically.
|
|
||||||
"""
|
|
||||||
# Validate balance before posting
|
|
||||||
if self.move_type in ['out_invoice', 'out_refund', 'in_invoice', 'in_refund']:
|
|
||||||
total_debit = sum(self.line_ids.mapped('debit'))
|
|
||||||
total_credit = sum(self.line_ids.mapped('credit'))
|
|
||||||
if abs(total_debit - total_credit) > 0.01:
|
|
||||||
raise ValidationError(_('The entry is not balanced...'))
|
|
||||||
|
|
||||||
res = super().action_post() # Standard Odoo posting - creates the journal entry
|
|
||||||
|
|
||||||
# Sync vendor bill lines to MO when bill is posted
|
|
||||||
if self.move_type == 'in_invoice' and self.master_order_id:
|
|
||||||
for line in self.invoice_line_ids.filtered(...):
|
|
||||||
line._sync_to_master_order()
|
|
||||||
|
|
||||||
# Create supplier debt record for tracking
|
|
||||||
if self.move_type == 'in_invoice' and self.master_order_customer_id:
|
|
||||||
self._create_supplier_debt_record() # This creates a vera.client.supplier record, NOT a journal entry
|
|
||||||
|
|
||||||
return res
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
**The system is working correctly.** Commission and expenses are included as invoice lines, and when the invoice is posted, Odoo creates ONE balanced journal entry that includes everything.
|
|
||||||
|
|
||||||
If you're seeing what looks like "duplicate" journal entries, they're likely:
|
|
||||||
1. The same entry viewed from different perspectives (Journal Items vs. Invoices)
|
|
||||||
2. Analytic entries (for project/department tracking)
|
|
||||||
3. Different entries for different transactions
|
|
||||||
|
|
||||||
If you have specific journal entries you think are duplicates, please provide:
|
|
||||||
- Journal Entry Reference numbers (e.g., `MISC/2024/00001`)
|
|
||||||
- Dates and amounts
|
|
||||||
- Partner names
|
|
||||||
|
|
||||||
This will help identify if there's an actual issue or just confusion about how the accounting views work.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated**: 2026-01-14
|
|
||||||
**Module**: at_master_order
|
|
||||||
**Version**: 18.0
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
# Brand Color Guideline - OWL Dashboard Components
|
|
||||||
|
|
||||||
## Primary Brand Color: Steel Blue
|
|
||||||
|
|
||||||
| Color Name | Hex Code | RGB | Usage |
|
|
||||||
|------------|----------|-----|-------|
|
|
||||||
| **Steel Blue (Primary)** | `#4682B4` | rgb(70, 130, 180) | Primary accent, buttons, links, icons |
|
|
||||||
| **Steel Blue Dark** | `#3A6D99` | rgb(58, 109, 153) | Hover states, darker accents |
|
|
||||||
| **Steel Blue Light** | `#5B9BD5` | rgb(91, 155, 213) | Lighter accents, highlights |
|
|
||||||
| **Steel Blue Pale** | `#E8F4FC` | rgb(232, 244, 252) | Background tints, selected states |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Design Principles
|
|
||||||
|
|
||||||
### 1. Clean White Header Bar (No Gradients)
|
|
||||||
```scss
|
|
||||||
.header {
|
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. No SCSS Variables
|
|
||||||
**❌ AVOID** - Can cause Odoo compilation issues:
|
|
||||||
```scss
|
|
||||||
$steel-blue: #4682B4;
|
|
||||||
$steel-blue-dark: lighten($steel-blue, 10%); // lighten() NOT supported
|
|
||||||
```
|
|
||||||
|
|
||||||
**✅ USE** - Hardcoded hex colors:
|
|
||||||
```scss
|
|
||||||
color: #4682B4; // Steel Blue
|
|
||||||
background: #3A6D99; // Steel Blue Dark
|
|
||||||
border-color: #5B9BD5; // Steel Blue Light
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Color Palette Reference
|
|
||||||
|
|
||||||
### Accent Colors (Steel Blue Family)
|
|
||||||
```scss
|
|
||||||
// Primary actions, links, icons
|
|
||||||
.primary-element {
|
|
||||||
color: #4682B4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hover/active states
|
|
||||||
.primary-element:hover {
|
|
||||||
color: #3A6D99;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Icon backgrounds with gradient
|
|
||||||
.icon-box {
|
|
||||||
background: linear-gradient(135deg, #4682B4, #3A6D99);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Status Colors (Keep Original)
|
|
||||||
| Status | Background | Text Color | Border |
|
|
||||||
|--------|------------|------------|--------|
|
|
||||||
| Success/Complete | `#f0fdf4` | `#22c55e` | `#22c55e` |
|
|
||||||
| Warning | `#fffbeb` | `#f59e0b` | `#f59e0b` |
|
|
||||||
| Danger/Critical | `#fef2f2` | `#ef4444` | `#ef4444` |
|
|
||||||
|
|
||||||
### Neutral Colors
|
|
||||||
| Usage | Hex Code |
|
|
||||||
|-------|----------|
|
|
||||||
| Primary text | `#1e293b` |
|
|
||||||
| Secondary text | `#64748b` |
|
|
||||||
| Muted text | `#94a3b8` |
|
|
||||||
| Border light | `#e2e8f0` |
|
|
||||||
| Background | `#f8f9fa` |
|
|
||||||
| Card background | `#ffffff` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Component Styling
|
|
||||||
|
|
||||||
### Cards with Status Borders
|
|
||||||
```scss
|
|
||||||
.customer-card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 16px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
||||||
border-left: 5px solid transparent;
|
|
||||||
|
|
||||||
&.card-complete {
|
|
||||||
border-left-color: #22c55e;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.card-warning {
|
|
||||||
border-left-color: #f59e0b;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.card-critical {
|
|
||||||
border-left-color: #ef4444;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Links & Interactive Elements
|
|
||||||
```scss
|
|
||||||
.link {
|
|
||||||
color: #4682B4;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: #3A6D99;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Buttons
|
|
||||||
```scss
|
|
||||||
.btn-primary {
|
|
||||||
background: #4682B4 !important;
|
|
||||||
border-color: #4682B4 !important;
|
|
||||||
color: white !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #3A6D99 !important;
|
|
||||||
border-color: #3A6D99 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Icons in Headers
|
|
||||||
```scss
|
|
||||||
.title-icon {
|
|
||||||
color: #4682B4;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Checkbox Accent
|
|
||||||
```scss
|
|
||||||
input[type="checkbox"] {
|
|
||||||
accent-color: #4682B4;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary Card Icons (Gradient Style)
|
|
||||||
```scss
|
|
||||||
// Steel Blue card
|
|
||||||
&.card-blue .card-icon {
|
|
||||||
background: linear-gradient(135deg, #4682B4, #3A6D99);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep other status gradients
|
|
||||||
&.card-green .card-icon {
|
|
||||||
background: linear-gradient(135deg, #22c55e, #16a34a);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.card-red .card-icon {
|
|
||||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Files Using This Guideline
|
|
||||||
|
|
||||||
| File | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `static/src/scss/inventory_tracking_dashboard.scss` | Inventory CTN tracking |
|
|
||||||
| `static/src/scss/invoice_tracking_dashboard.scss` | Invoice payment tracking |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Important Notes
|
|
||||||
|
|
||||||
1. **Never use SCSS functions** like `lighten()`, `darken()`, `mix()` - Odoo doesn't support them
|
|
||||||
2. **Always use hardcoded hex values** - More reliable compilation
|
|
||||||
3. **Keep status colors (green/yellow/red) unchanged** - They have semantic meaning
|
|
||||||
4. **Only update blue accent colors** to Steel Blue (#4682B4)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Last Updated: January 2026*
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
# Complete Customer & Supplier Tracking - Implementation Summary
|
|
||||||
|
|
||||||
## ✅ What Has Been Implemented
|
|
||||||
|
|
||||||
### Phase 1: Bill-to-Customer Linking ✅
|
|
||||||
|
|
||||||
**Problem**: Bills may not always have `master_order_customer_id` set correctly.
|
|
||||||
|
|
||||||
**Solution Implemented**:
|
|
||||||
|
|
||||||
1. **Auto-set customer in `create()` method** (`account_move.py`):
|
|
||||||
- When a bill is created with `master_order_id`, automatically sets `master_order_customer_id`
|
|
||||||
- Ensures customer is always set when bill is created
|
|
||||||
|
|
||||||
2. **Auto-update customer in `write()` method** (`account_move.py`):
|
|
||||||
- When `master_order_id` changes, automatically updates `master_order_customer_id`
|
|
||||||
- When `master_order_id` is removed, clears `master_order_customer_id`
|
|
||||||
|
|
||||||
3. **Enhanced `_link_master_order()` method** (`account_move.py`):
|
|
||||||
- Now always sets customer when linking to MO
|
|
||||||
- Ensures customer is never missing when MO is linked
|
|
||||||
|
|
||||||
4. **Validation constraint** (`account_move.py`):
|
|
||||||
- `_check_customer_consistency()` ensures customer matches MO customer
|
|
||||||
- Prevents data inconsistencies
|
|
||||||
|
|
||||||
5. **Auto-create supplier debt** (`account_move.py`):
|
|
||||||
- `_create_supplier_debt_record()` creates supplier debt when bill is posted
|
|
||||||
- Ensures supplier debts are tracked per customer automatically
|
|
||||||
|
|
||||||
### Phase 2: Supplier Payment Tracking ✅
|
|
||||||
|
|
||||||
**Problem**: Supplier payments may not be properly linked to customers.
|
|
||||||
|
|
||||||
**Solution Implemented**:
|
|
||||||
|
|
||||||
1. **Enhanced payment-to-customer linking** (`document_links.py`):
|
|
||||||
- Method 1: From Master Order (existing)
|
|
||||||
- Method 2: From `master_order_customer_id` on bills (NEW)
|
|
||||||
- Method 3: From reconciled bills directly (NEW)
|
|
||||||
- Ensures customer is always found when payment is made
|
|
||||||
|
|
||||||
2. **Supplier payment transaction creation** (`document_links.py`):
|
|
||||||
- Creates `paid_out` transaction for supplier payments
|
|
||||||
- Links transaction to customer cash flow
|
|
||||||
- Sets `counterparty_id` to supplier
|
|
||||||
|
|
||||||
3. **Auto-update supplier debt** (`document_links.py`):
|
|
||||||
- Calls `cashflow._update_supplier_debt()` when payment is made
|
|
||||||
- Refreshes supplier debt totals automatically
|
|
||||||
|
|
||||||
### Phase 3: Supplier Debt Tracking ✅
|
|
||||||
|
|
||||||
**Problem**: Supplier debts may not reflect all bills correctly.
|
|
||||||
|
|
||||||
**Solution Implemented**:
|
|
||||||
|
|
||||||
1. **Auto-create supplier debt on bill post** (`account_move.py`):
|
|
||||||
- `_create_supplier_debt_record()` called when bill is posted
|
|
||||||
- Creates `vera.client.supplier` record if doesn't exist
|
|
||||||
- Links supplier debt to customer cash flow
|
|
||||||
|
|
||||||
2. **Update supplier debt method** (`vera_client_cashflow.py`):
|
|
||||||
- `_update_supplier_debt()` refreshes supplier debt totals
|
|
||||||
- Called when payments are made
|
|
||||||
- Ensures supplier debts are always up-to-date
|
|
||||||
|
|
||||||
3. **Supplier debt computation** (`vera_client_supplier.py`):
|
|
||||||
- Uses `master_order_customer_id` to find bills (more reliable)
|
|
||||||
- Computes totals from bills and payments correctly
|
|
||||||
- Shows accurate outstanding amounts per customer
|
|
||||||
|
|
||||||
### Phase 4: Migration & Repair Tools ✅
|
|
||||||
|
|
||||||
**Implementation**:
|
|
||||||
|
|
||||||
1. **Migration script** (`account_move_migration.py`):
|
|
||||||
- `repair_all_bill_customers()` - Fixes all bills with missing customer links
|
|
||||||
- `sync_all_payments_to_cashflow()` - Syncs existing payments to cash flow
|
|
||||||
|
|
||||||
2. **Repair wizard** (`repair_customer_links_wizard.py`):
|
|
||||||
- UI for running repair operations
|
|
||||||
- Options to repair bills, create supplier debts, sync payments
|
|
||||||
- Shows summary of fixes
|
|
||||||
|
|
||||||
## Data Flow Diagram
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────┐
|
|
||||||
│ Master Order │
|
|
||||||
│ (at.master.order)│
|
|
||||||
└────────┬────────┘
|
|
||||||
│
|
|
||||||
├─── client_id → Customer
|
|
||||||
│
|
|
||||||
├─── vendor_bill_ids → Bills
|
|
||||||
│ ├─── master_order_id → MO ✅
|
|
||||||
│ ├─── master_order_customer_id → Customer ✅ (AUTO-SET)
|
|
||||||
│ └─── partner_id → Supplier
|
|
||||||
│ │
|
|
||||||
│ └─── When Posted:
|
|
||||||
│ ├─── Create vera.client.supplier ✅
|
|
||||||
│ └─── Update customer cash flow ✅
|
|
||||||
│
|
|
||||||
└─── customer_invoice_ids → Invoices
|
|
||||||
├─── master_order_id → MO ✅
|
|
||||||
└─── partner_id → Customer ✅
|
|
||||||
|
|
||||||
When Payment Made:
|
|
||||||
┌─────────────────┐
|
|
||||||
│ Payment │
|
|
||||||
│ (account.payment)│
|
|
||||||
└────────┬────────┘
|
|
||||||
│
|
|
||||||
├─── Find Customer:
|
|
||||||
│ ├─── From master_order_id ✅
|
|
||||||
│ ├─── From bill.master_order_customer_id ✅ (NEW)
|
|
||||||
│ └─── From reconciled bills ✅ (NEW)
|
|
||||||
│
|
|
||||||
├─── Create Transaction ✅
|
|
||||||
│ ├─── transaction_type: 'paid_out' (supplier)
|
|
||||||
│ └─── counterparty_id: Supplier
|
|
||||||
│
|
|
||||||
└─── Update Supplier Debt ✅
|
|
||||||
└─── vera.client.supplier.total_paid
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
### ✅ Automatic Customer Linking
|
|
||||||
- Bills automatically get customer from MO
|
|
||||||
- No manual linking required
|
|
||||||
- Customer always set when MO is linked
|
|
||||||
|
|
||||||
### ✅ Automatic Supplier Debt Tracking
|
|
||||||
- Supplier debts created automatically when bills are posted
|
|
||||||
- Updated automatically when payments are made
|
|
||||||
- Per-customer supplier tracking
|
|
||||||
|
|
||||||
### ✅ Complete Payment Tracking
|
|
||||||
- All payments linked to customers
|
|
||||||
- Transactions created automatically
|
|
||||||
- Supplier payments tracked per customer
|
|
||||||
|
|
||||||
### ✅ Data Integrity
|
|
||||||
- Validation ensures customer matches MO customer
|
|
||||||
- Repair tools fix existing data
|
|
||||||
- Migration scripts sync historical data
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Automatic (No Action Required)
|
|
||||||
- ✅ Bills created from MO → Customer auto-set
|
|
||||||
- ✅ Bills linked to MO → Customer auto-set
|
|
||||||
- ✅ Bills posted → Supplier debt auto-created
|
|
||||||
- ✅ Payments made → Transactions auto-created
|
|
||||||
- ✅ Payments made → Supplier debt auto-updated
|
|
||||||
|
|
||||||
### Manual Repair (If Needed)
|
|
||||||
1. Go to **Master Orders > Configuration**
|
|
||||||
2. Click **"Repair Customer Links"** button
|
|
||||||
3. Select options:
|
|
||||||
- ✅ Repair Bill Customer Links
|
|
||||||
- ✅ Create Supplier Debt Records
|
|
||||||
- ✅ Sync Payments to Cash Flow
|
|
||||||
4. Click **"Repair"**
|
|
||||||
|
|
||||||
### View Customer Tracking
|
|
||||||
- **Client Cash Flow** → Shows all transactions per customer
|
|
||||||
- **Supplier Debts** → Shows supplier debts per customer
|
|
||||||
- **Bill Tracking** → Filter by customer
|
|
||||||
- **Advanced Supplier** → Company-wide supplier tracking
|
|
||||||
|
|
||||||
## Files Modified
|
|
||||||
|
|
||||||
### Core Models:
|
|
||||||
- ✅ `models/account_move.py` - Auto-set customer, create supplier debt
|
|
||||||
- ✅ `models/document_links.py` - Enhanced payment-to-customer linking
|
|
||||||
- ✅ `models/vera_client_cashflow.py` - Supplier debt update method
|
|
||||||
- ✅ `models/vera_client_supplier.py` - Uses master_order_customer_id
|
|
||||||
|
|
||||||
### Migration & Repair:
|
|
||||||
- ✅ `models/account_move_migration.py` - Repair scripts
|
|
||||||
- ✅ `wizard/repair_customer_links_wizard.py` - Repair wizard
|
|
||||||
- ✅ `wizard/repair_customer_links_wizard_views.xml` - Wizard views
|
|
||||||
|
|
||||||
### Documentation:
|
|
||||||
- ✅ `plans/CUSTOMER_SUPPLIER_TRACKING_PLAN.md` - Full plan
|
|
||||||
- ✅ `CUSTOMER_SUPPLIER_TRACKING_IMPLEMENTED.md` - This summary
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Create bill from MO → Customer auto-set ✅
|
|
||||||
- [ ] Link bill to MO → Customer auto-set ✅
|
|
||||||
- [ ] Post bill → Supplier debt created ✅
|
|
||||||
- [ ] Pay vendor bill → Transaction created ✅
|
|
||||||
- [ ] Pay vendor bill → Supplier debt updated ✅
|
|
||||||
- [ ] Unlink bill from MO → Customer cleared ✅
|
|
||||||
- [ ] Run repair wizard → Fixes existing data ✅
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
1. **✅ Complete Traceability** - Every bill/payment linked to customer
|
|
||||||
2. **✅ Accurate Supplier Debts** - Per-customer supplier tracking
|
|
||||||
3. **✅ Automatic Sync** - No manual linking required
|
|
||||||
4. **✅ Data Integrity** - Validation ensures correctness
|
|
||||||
5. **✅ Easy Repair** - Wizard fixes existing data issues
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
1. **Deploy to server** and upgrade module
|
|
||||||
2. **Run repair wizard** to fix existing bills
|
|
||||||
3. **Test** automatic customer linking
|
|
||||||
4. **Verify** supplier debts are created correctly
|
|
||||||
5. **Monitor** payment transactions are created
|
|
||||||
|
|
||||||
## Example Scenarios
|
|
||||||
|
|
||||||
### Scenario 1: Create Bill from MO
|
|
||||||
```
|
|
||||||
1. User clicks "Sync Bills" in Master Order
|
|
||||||
2. System creates vendor bills for each supplier
|
|
||||||
3. ✅ master_order_customer_id automatically set from MO.client_id
|
|
||||||
4. ✅ Bill posted → Supplier debt record created
|
|
||||||
5. ✅ Customer can see supplier debt in Cash Flow page
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scenario 2: Pay Vendor Bill
|
|
||||||
```
|
|
||||||
1. User pays vendor bill from Bill Tracking page
|
|
||||||
2. System finds customer from bill.master_order_customer_id
|
|
||||||
3. ✅ Creates paid_out transaction in customer cash flow
|
|
||||||
4. ✅ Updates supplier debt total_paid
|
|
||||||
5. ✅ Customer can see payment in transactions
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scenario 3: Link Existing Bill to MO
|
|
||||||
```
|
|
||||||
1. User links existing bill to Master Order
|
|
||||||
2. ✅ master_order_customer_id automatically set
|
|
||||||
3. ✅ Supplier debt record created if bill is posted
|
|
||||||
4. ✅ Bill now appears in customer's supplier debts
|
|
||||||
```
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
# Odoo Module Deployment Workflow
|
|
||||||
|
|
||||||
## Server Details
|
|
||||||
|
|
||||||
| Parameter | Value |
|
|
||||||
|-----------|-------|
|
|
||||||
| **Host** | `106.75.152.117` |
|
|
||||||
| **SSH Port** | `22` |
|
|
||||||
| **User** | `ubuntu` |
|
|
||||||
| **Password** | `BtZS9w@LFVyWMc3` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Odoo Instances
|
|
||||||
|
|
||||||
| Instance | Port | Service Name | Addons Path | Database |
|
|
||||||
|----------|------|--------------|-------------|----------|
|
|
||||||
| **odoo18-new** | 10020 | `odoo18-new.service` | `/opt/odoo18-new/addons` | `mastertest` |
|
|
||||||
| **odoo18-main** | 10018 | `odoo18-main.service` | `/opt/odoo18-main/addons` | (multiple) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Access URLs
|
|
||||||
|
|
||||||
| Instance | URL |
|
|
||||||
|----------|-----|
|
|
||||||
| odoo18-new (Primary) | http://106.75.152.117:10020/ |
|
|
||||||
| odoo18-main | http://106.75.152.117:10018/ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Deployment Steps
|
|
||||||
|
|
||||||
### Step 1: Upload Module
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' scp -o StrictHostKeyChecking=no -r "<local_module_path>" ubuntu@106.75.152.117:/opt/odoo18-new/addons/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Set Permissions
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo chown -R ubuntu:ubuntu /opt/odoo18-new/addons/<module_name>"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Restart Odoo Service
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo systemctl restart odoo18-new.service"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 4: Upgrade Module
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "cd /opt/odoo18-new && source venv/bin/activate && python3 odoo/odoo-bin -c config/odoo.conf -d mastertest -u <module_name> --stop-after-init"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 5: Final Restart & Verify
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo systemctl restart odoo18-new.service && sudo systemctl status odoo18-new.service"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Deploy (One-Line Commands)
|
|
||||||
|
|
||||||
### For at_master_order module:
|
|
||||||
|
|
||||||
**Step 1: Upload**
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' scp -o StrictHostKeyChecking=no -r "/Users/hussein/Desktop/last verstion only /at_master_order" ubuntu@106.75.152.117:/opt/odoo18-new/addons/
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 2: Set Permissions & Restart**
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo chown -R ubuntu:ubuntu /opt/odoo18-new/addons/at_master_order && sudo systemctl restart odoo18-new.service"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 3: Upgrade Module**
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "cd /opt/odoo18-new && source venv/bin/activate && python3 odoo/odoo-bin -c config/odoo.conf -d mastertest -u at_master_order --stop-after-init"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 4: Final Restart**
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo systemctl restart odoo18-new.service"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Port Conflict Resolution
|
|
||||||
|
|
||||||
If port 10020 is already in use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo fuser -k 10020/tcp && sudo systemctl restart odoo18-new.service"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Module State
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo -u postgres psql -d mastertest -c \"SELECT name, state, latest_version FROM ir_module_module WHERE name = '<module_name>';\""
|
|
||||||
```
|
|
||||||
|
|
||||||
### View Logs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "tail -100 /opt/odoo18-new/logs/odoo.log"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Service Status
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117 "sudo systemctl status odoo18-new.service"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Quick Reference Commands
|
|
||||||
|
|
||||||
| Action | Command |
|
|
||||||
|--------|---------|
|
|
||||||
| SSH Connect | `sshpass -p 'BtZS9w@LFVyWMc3' ssh ubuntu@106.75.152.117` |
|
|
||||||
| Restart Service | `sudo systemctl restart odoo18-new.service` |
|
|
||||||
| Stop Service | `sudo systemctl stop odoo18-new.service` |
|
|
||||||
| View Logs | `tail -f /opt/odoo18-new/logs/odoo.log` |
|
|
||||||
| List Addons | `ls -la /opt/odoo18-new/addons/` |
|
|
||||||
| List Databases | `sudo -u postgres psql -c "SELECT datname FROM pg_database;"` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Config File Locations
|
|
||||||
|
|
||||||
| Instance | Config Path |
|
|
||||||
|----------|-------------|
|
|
||||||
| odoo18-new | `/opt/odoo18-new/config/odoo.conf` |
|
|
||||||
| odoo18-main | `/opt/odoo18-main/config/odoo.conf` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Always use `--stop-after-init` flag when upgrading modules via command line
|
|
||||||
- The `mastertest` database is the primary database for odoo18-new instance
|
|
||||||
- Service runs under `ubuntu` user
|
|
||||||
- Virtual environment located at `/opt/odoo18-new/venv/`
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
# Excel Image Paste Feature for Master Order Lines
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This feature enables users to copy images from Excel and paste them directly into Master Order line image cells using `Ctrl+V`, providing an Excel-like workflow for product image management.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Feature Summary
|
|
||||||
|
|
||||||
| Aspect | Details |
|
|
||||||
|--------|---------|
|
|
||||||
| **Target Users** | Purchasing and sourcing teams |
|
|
||||||
| **Use Case** | Paste product images from Excel directly into order lines |
|
|
||||||
| **Supported Formats** | PNG, JPEG, BMP, GIF, WEBP |
|
|
||||||
| **Max Image Size** | Auto-resizes if > 1024x1024 pixels |
|
|
||||||
| **Storage** | Odoo filestore via `ir.attachment` |
|
|
||||||
| **Browser Support** | Chrome, Edge, Firefox (desktop) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## User Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Open Master Order form
|
|
||||||
2. Click on image cell in Order Lines (cell gets focus ring)
|
|
||||||
3. Copy image from Excel (Ctrl+C on image)
|
|
||||||
4. Click target image cell
|
|
||||||
5. Press Ctrl+V
|
|
||||||
6. Image appears instantly with success notification
|
|
||||||
7. Save the form to persist
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technical Architecture
|
|
||||||
|
|
||||||
### Files Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
at_master_order/
|
|
||||||
├── static/src/
|
|
||||||
│ ├── js/
|
|
||||||
│ │ ├── image_paste_widget.js # Main OWL widget
|
|
||||||
│ │ └── master_order_form.js # Form controller enhancements
|
|
||||||
│ ├── xml/
|
|
||||||
│ │ └── image_paste_templates.xml # OWL templates
|
|
||||||
│ └── scss/
|
|
||||||
│ └── image_paste.scss # Styling for paste interaction
|
|
||||||
├── models/
|
|
||||||
│ └── master_order_line.py # Backend image processing
|
|
||||||
└── views/
|
|
||||||
└── master_order_views.xml # View with widget="image_paste"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Component Diagram
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Browser (Frontend) │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
|
|
||||||
│ │ ImagePasteField │ │ Clipboard API │ │
|
|
||||||
│ │ (OWL Widget) │◄───│ (DataTransfer/Clipboard) │ │
|
|
||||||
│ │ │ └─────────────────────────────┘ │
|
|
||||||
│ │ - onPaste() │ │
|
|
||||||
│ │ - _processImage() │ ┌─────────────────────────────┐ │
|
|
||||||
│ │ - _saveToRecord() │───►│ Canvas API │ │
|
|
||||||
│ └─────────────────────┘ │ (Image resize) │ │
|
|
||||||
│ │ └─────────────────────────────┘ │
|
|
||||||
│ ▼ │
|
|
||||||
│ ┌─────────────────────┐ │
|
|
||||||
│ │ record.update() │ │
|
|
||||||
│ │ (OWL Record) │ │
|
|
||||||
│ └─────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Server (Backend) │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
|
|
||||||
│ │ master_order_line │───►│ ir.attachment │ │
|
|
||||||
│ │ - image_1920 field │ │ (Image storage) │ │
|
|
||||||
│ │ - write() override │ └─────────────────────────────┘ │
|
|
||||||
│ │ - _process_image() │ │
|
|
||||||
│ └─────────────────────┘ ┌─────────────────────────────┐ │
|
|
||||||
│ │ Filestore │ │
|
|
||||||
│ │ (Binary data) │ │
|
|
||||||
│ └─────────────────────────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Implementation Details
|
|
||||||
|
|
||||||
### 1. OWL Widget (`image_paste_widget.js`)
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Extends Odoo's ImageField
|
|
||||||
export class ImagePasteField extends ImageField {
|
|
||||||
static template = "at_master_order.ImagePasteField";
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
super.setup();
|
|
||||||
// CRITICAL: Extend state, don't replace it!
|
|
||||||
this.state.isFocused = false;
|
|
||||||
this.state.isProcessing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onPaste(ev) {
|
|
||||||
// 1. Get image from clipboard
|
|
||||||
// 2. Process/resize if needed
|
|
||||||
// 3. Save via record.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Template (`image_paste_templates.xml`)
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<t t-name="at_master_order.ImagePasteField">
|
|
||||||
<div t-ref="imagePasteContainer" tabindex="0">
|
|
||||||
<!-- Uses parent's getUrl() method for image display -->
|
|
||||||
<img t-att-src="this.getUrl(props.previewImage || props.name)"/>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. View Configuration (`master_order_views.xml`)
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<field name="image_1920" widget="image_paste"
|
|
||||||
options="{'size': [50, 50]}"
|
|
||||||
optional="show"
|
|
||||||
help="Click to focus, then Ctrl+V to paste image from Excel"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Model Field (`master_order_line.py`)
|
|
||||||
|
|
||||||
```python
|
|
||||||
image_1920 = fields.Image('Product Image', attachment=True,
|
|
||||||
help='Upload product image for this line. Supports paste from Excel (Ctrl+V).')
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Critical Lessons Learned
|
|
||||||
|
|
||||||
### 1. OWL State Inheritance
|
|
||||||
|
|
||||||
**Problem**: Overwriting `this.state` in child widget destroyed parent's `isValid` property.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// WRONG - destroys parent state
|
|
||||||
this.state = useState({ isFocused: false });
|
|
||||||
|
|
||||||
// CORRECT - extends parent state
|
|
||||||
this.state.isFocused = false;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. ImageField URL Generation
|
|
||||||
|
|
||||||
The `getUrl()` method checks `!this.state.isValid` and returns placeholder if falsy. Parent's state must be preserved.
|
|
||||||
|
|
||||||
### 3. Attachment Storage
|
|
||||||
|
|
||||||
Image fields with `attachment=True` store data in `ir_attachment` table, not as a database column. Use ORM methods to access, not direct SQL.
|
|
||||||
|
|
||||||
### 4. Cache Invalidation
|
|
||||||
|
|
||||||
ImageField uses `write_date` for cache busting. Ensure `fieldDependencies` includes `write_date`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Database Schema
|
|
||||||
|
|
||||||
### ir_attachment (Image Storage)
|
|
||||||
|
|
||||||
| Column | Value |
|
|
||||||
|--------|-------|
|
|
||||||
| res_model | `at.master.order.line` |
|
|
||||||
| res_field | `image_1920` |
|
|
||||||
| res_id | Line ID |
|
|
||||||
| type | `binary` |
|
|
||||||
| store_fname | Filestore path |
|
|
||||||
| mimetype | `image/jpeg`, `image/png`, etc. |
|
|
||||||
|
|
||||||
### Query to Check Images
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT l.id, l.name, a.id as attachment_id, a.file_size
|
|
||||||
FROM at_master_order_line l
|
|
||||||
LEFT JOIN ir_attachment a
|
|
||||||
ON a.res_model = 'at.master.order.line'
|
|
||||||
AND a.res_field = 'image_1920'
|
|
||||||
AND a.res_id = l.id;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Image URL Format
|
|
||||||
|
|
||||||
```
|
|
||||||
/web/image/at.master.order.line/{line_id}/image_1920?unique={write_date}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
| Issue | Cause | Solution |
|
|
||||||
|-------|-------|----------|
|
|
||||||
| Placeholder shown instead of image | `state.isValid` is undefined | Don't overwrite parent's `this.state` |
|
|
||||||
| Image not saving | Clipboard API not returning data | Check browser console, ensure HTTPS |
|
|
||||||
| 404 on image URL | Attachment not linked correctly | Check `ir_attachment` table |
|
|
||||||
| No paste event firing | Cell not focused | Click cell first to focus |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Paste PNG image from Excel
|
|
||||||
- [ ] Paste JPEG image from Excel
|
|
||||||
- [ ] Paste large image (> 1024px) - should auto-resize
|
|
||||||
- [ ] Save form after paste
|
|
||||||
- [ ] Reload page - image should persist
|
|
||||||
- [ ] Delete image (Delete key when focused)
|
|
||||||
- [ ] Multiple pastes on different rows
|
|
||||||
- [ ] Paste on new unsaved line
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
- `@web/views/fields/image/image_field` (ImageField)
|
|
||||||
- `@web/core/utils/hooks` (useService)
|
|
||||||
- `@odoo/owl` (Component, useState, onMounted, useRef)
|
|
||||||
|
|
||||||
### Backend
|
|
||||||
- `PIL/Pillow` (Image processing)
|
|
||||||
- `base64` (Encoding)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Version History
|
|
||||||
|
|
||||||
| Version | Date | Changes |
|
|
||||||
|---------|------|---------|
|
|
||||||
| 1.0 | 2026-01-12 | Initial implementation |
|
|
||||||
| 1.1 | 2026-01-12 | Fixed state inheritance bug |
|
|
||||||
| 1.2 | 2026-01-12 | Fixed template to use getUrl() |
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
# Tag-Based Wallet & Supplier Management - Implementation Summary
|
|
||||||
|
|
||||||
## ✅ What Has Been Implemented
|
|
||||||
|
|
||||||
### Phase 1: Tag Category System ✅
|
|
||||||
|
|
||||||
1. **New Model**: `res_partner_category_cashflow.py`
|
|
||||||
- Extends `res.partner.category` with metadata fields:
|
|
||||||
- `is_cashflow_category` - Marks category for wallet management
|
|
||||||
- `is_supplier_category` - Marks category for supplier management
|
|
||||||
- `auto_manage` - Auto-add/remove tags based on wallet/supplier existence
|
|
||||||
- `category_type` - Computed field (cashflow/supplier/both/none)
|
|
||||||
- Helper methods: `get_wallet_tag()`, `get_supplier_tag()`
|
|
||||||
|
|
||||||
2. **Tag Data**: `data/partner_category_data.xml`
|
|
||||||
- **Cash Flow Management Category** with tags:
|
|
||||||
- "Has Wallet" (auto-managed) ✅
|
|
||||||
- "Wallet Active" (manual)
|
|
||||||
- "Wallet Blocked" (manual)
|
|
||||||
- "VIP Customer" (manual)
|
|
||||||
- "Regular Customer" (manual)
|
|
||||||
- "New Customer" (manual)
|
|
||||||
- "Payment Always On Time" (manual)
|
|
||||||
- "Payment Sometimes Late" (manual)
|
|
||||||
- "Payment Always Late" (manual)
|
|
||||||
|
|
||||||
- **Supplier Management Category** with tags:
|
|
||||||
- "Advanced Supplier" (auto-managed) ✅
|
|
||||||
- "Key Supplier" (manual)
|
|
||||||
- "Preferred Supplier" (manual)
|
|
||||||
- "Payment Priority" (manual)
|
|
||||||
|
|
||||||
### Phase 2: Partner Model Enhancements ✅
|
|
||||||
|
|
||||||
1. **Updated `res_partner_wallet.py`**:
|
|
||||||
- Added `_get_wallet_tag()` method (uses new category system)
|
|
||||||
- Updated `_update_wallet_tag()` to use new method
|
|
||||||
- All tag references now use the new system
|
|
||||||
|
|
||||||
2. **New Model**: `res_partner_supplier.py`
|
|
||||||
- Added `is_advanced_supplier` computed field
|
|
||||||
- Added `_get_supplier_tag()` method
|
|
||||||
- Added `_sync_supplier_tag()` method for auto-tag management
|
|
||||||
|
|
||||||
### Phase 3: Cash Flow Integration ✅
|
|
||||||
|
|
||||||
1. **Updated `vera_client_cashflow.py`**:
|
|
||||||
- Added `create()` override - syncs "Has Wallet" tag when wallet created
|
|
||||||
- Added `unlink()` override - syncs "Has Wallet" tag when wallet deleted
|
|
||||||
- Tags automatically added/removed when wallets are created/deleted
|
|
||||||
|
|
||||||
### Phase 4: Advanced Supplier Integration ✅
|
|
||||||
|
|
||||||
1. **Updated `vera_advanced_supplier.py`**:
|
|
||||||
- Updated `create()` override - syncs "Advanced Supplier" tag
|
|
||||||
- Added `unlink()` override - syncs "Advanced Supplier" tag
|
|
||||||
- Tags automatically added/removed when advanced suppliers are created/deleted
|
|
||||||
|
|
||||||
## 🎯 How It Works
|
|
||||||
|
|
||||||
### For Customer Wallets:
|
|
||||||
|
|
||||||
1. **When a wallet is created**:
|
|
||||||
- `vera.client.cashflow.create()` is called
|
|
||||||
- Automatically adds "Has Wallet" tag to the customer
|
|
||||||
- Customer can now be filtered by "Has Wallet" tag
|
|
||||||
|
|
||||||
2. **When a wallet is deleted**:
|
|
||||||
- `vera.client.cashflow.unlink()` is called
|
|
||||||
- Automatically removes "Has Wallet" tag from the customer
|
|
||||||
|
|
||||||
3. **Manual tag management**:
|
|
||||||
- Users can manually add/remove other tags (VIP, Payment behavior, etc.)
|
|
||||||
- These tags help with filtering and grouping
|
|
||||||
|
|
||||||
### For Advanced Suppliers:
|
|
||||||
|
|
||||||
1. **When an advanced supplier is created**:
|
|
||||||
- `vera.advanced.supplier.create()` is called
|
|
||||||
- Automatically adds "Advanced Supplier" tag to the supplier
|
|
||||||
- Supplier can now be filtered by "Advanced Supplier" tag
|
|
||||||
|
|
||||||
2. **When an advanced supplier is deleted**:
|
|
||||||
- `vera.advanced.supplier.unlink()` is called
|
|
||||||
- Automatically removes "Advanced Supplier" tag from the supplier
|
|
||||||
|
|
||||||
## 📋 Next Steps (To Complete Implementation)
|
|
||||||
|
|
||||||
### Phase 5: Views & UI (TODO)
|
|
||||||
|
|
||||||
1. **Partner Views**:
|
|
||||||
- Add tag columns to list view
|
|
||||||
- Add filters: "Has Wallet", "Advanced Supplier", "VIP Customer"
|
|
||||||
- Add group by: "Cash Flow Tags", "Supplier Tags"
|
|
||||||
|
|
||||||
2. **Cash Flow Views**:
|
|
||||||
- Show customer tags in list view
|
|
||||||
- Filter by customer tags
|
|
||||||
- Group by customer tags
|
|
||||||
|
|
||||||
3. **Advanced Supplier Views**:
|
|
||||||
- Show supplier tags in list view
|
|
||||||
- Filter by supplier tags
|
|
||||||
- Group by supplier tags
|
|
||||||
|
|
||||||
### Phase 6: Migration Script (TODO)
|
|
||||||
|
|
||||||
Create a migration script to:
|
|
||||||
- Add "Has Wallet" tag to all existing customers with wallets
|
|
||||||
- Add "Advanced Supplier" tag to all existing advanced suppliers
|
|
||||||
- Ensure all tags are properly linked
|
|
||||||
|
|
||||||
## 🔧 Files Created/Modified
|
|
||||||
|
|
||||||
### New Files:
|
|
||||||
- ✅ `models/res_partner_category_cashflow.py`
|
|
||||||
- ✅ `models/res_partner_supplier.py`
|
|
||||||
- ✅ `data/partner_category_data.xml`
|
|
||||||
- ✅ `plans/TAG_BASED_WALLET_SUPPLIER_PLAN.md`
|
|
||||||
|
|
||||||
### Modified Files:
|
|
||||||
- ✅ `models/__init__.py` - Added new model imports
|
|
||||||
- ✅ `models/res_partner_wallet.py` - Updated to use new tag system
|
|
||||||
- ✅ `models/vera_client_cashflow.py` - Added tag sync on create/unlink
|
|
||||||
- ✅ `models/vera_advanced_supplier.py` - Added tag sync on create/unlink
|
|
||||||
- ✅ `__manifest__.py` - Added partner_category_data.xml
|
|
||||||
|
|
||||||
## ✨ Benefits
|
|
||||||
|
|
||||||
1. **✅ No External Dependencies** - Uses standard Odoo `res.partner.category`
|
|
||||||
2. **✅ Automatic Tag Management** - Tags sync with wallet/supplier existence
|
|
||||||
3. **✅ Safe & Reliable** - Works in all Odoo versions (no missing models)
|
|
||||||
4. **✅ Easy Filtering** - Can filter customers by wallet status, suppliers by type
|
|
||||||
5. **✅ Visual Indicators** - Tags show in partner forms and lists
|
|
||||||
6. **✅ Flexible** - Can add custom tags for business needs
|
|
||||||
|
|
||||||
## 🚀 Ready to Deploy
|
|
||||||
|
|
||||||
The core functionality is implemented and ready to test. After deployment:
|
|
||||||
|
|
||||||
1. **Upgrade the module** to create tags
|
|
||||||
2. **Run migration** to sync existing wallets/suppliers with tags
|
|
||||||
3. **Test** that tags are automatically added/removed
|
|
||||||
4. **Add views** (Phase 5) for better UI experience
|
|
||||||
|
|
||||||
## 📝 Usage Examples
|
|
||||||
|
|
||||||
### Filter Customers with Wallets:
|
|
||||||
```
|
|
||||||
Search: tag_ids contains "Has Wallet"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filter Advanced Suppliers:
|
|
||||||
```
|
|
||||||
Search: tag_ids contains "Advanced Supplier"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Group by Customer Type:
|
|
||||||
```
|
|
||||||
Group By: Tags → Cash Flow Management
|
|
||||||
```
|
|
||||||
|
|
||||||
### Monitor VIP Customers:
|
|
||||||
```
|
|
||||||
Filter: tag_ids contains "VIP Customer"
|
|
||||||
```
|
|
||||||
@@ -1,760 +0,0 @@
|
|||||||
# Invoice Expense Tracking - Design Document
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Track expenses paid on behalf of customers directly on invoices, with automatic accounting settlement.
|
|
||||||
|
|
||||||
**Key Concept**: "Customer Fund" is conceptual - it means "expenses we'll charge to the customer". It's a monitoring/tracking mechanism, NOT a deposit account. Customer deposits are handled by a separate module.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Design Decisions
|
|
||||||
|
|
||||||
| Decision | Choice | Rationale |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| Invoice Line Account | Uses same Asset account (Customer Expenses Receivable) | Directly credits/settles the asset |
|
|
||||||
| Tax Treatment | No tax on expenses | Pass-through charges, not revenue |
|
|
||||||
| Currency | Single currency only | Simplified implementation |
|
|
||||||
| Partial Addition | Not allowed | One expense = One invoice line |
|
|
||||||
| Expense Splitting | Not allowed | Create multiple expenses instead |
|
|
||||||
| Payment Source | Always Bank | No need to track source (customer deposits handled separately) |
|
|
||||||
| Invoice-MO Link | Required | Invoice must be linked to Master Order |
|
|
||||||
| Settlement | Automatic when added to invoice | Asset account credited |
|
|
||||||
| Collection | Standard AR process | Sales team follows up with customer |
|
|
||||||
| Default Expense Type | Customer Expense | `invoice_to_customer = True` by default |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Business Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ EXPENSE TRACKING FLOW │
|
|
||||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ 1. PAY EXPENSE 2. TRACK ON INVOICE │
|
|
||||||
│ ─────────────── ──────────────────── │
|
|
||||||
│ │
|
|
||||||
│ Company pays on behalf Record expense on │
|
|
||||||
│ of customer: customer invoice: │
|
|
||||||
│ │
|
|
||||||
│ • Certificate Fee: $100 Invoice #INV/2026/001 │
|
|
||||||
│ • Customs: $200 ├── Products: $5,000 │
|
|
||||||
│ • Documentation: $50 ├── Certificate Fee: $100 │
|
|
||||||
│ ├── Customs: $200 │
|
|
||||||
│ Paid from: └── Documentation: $50 │
|
|
||||||
│ Bank / Customer Expenses Fund │
|
|
||||||
│ TOTAL: $5,350 │
|
|
||||||
│ │
|
|
||||||
│ │
|
|
||||||
│ 3. CUSTOMER PAYS 4. SETTLEMENT │
|
|
||||||
│ ──────────────── ─────────────── │
|
|
||||||
│ │
|
|
||||||
│ Customer pays invoice Expense account is │
|
|
||||||
│ total: $5,350 automatically settled │
|
|
||||||
│ │
|
|
||||||
│ ✅ Products paid ✅ No manual reconciliation │
|
|
||||||
│ ✅ Expenses reimbursed ✅ Clean accounting │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accounting Entries
|
|
||||||
|
|
||||||
### Step 1: Pay Expense (Manual or Bank Transaction)
|
|
||||||
|
|
||||||
When company pays expense on behalf of customer:
|
|
||||||
|
|
||||||
| Account | Debit | Credit |
|
|
||||||
|---------|-------|--------|
|
|
||||||
| Customer Expenses Receivable (Asset) | $100 | |
|
|
||||||
| Bank Account | | $100 |
|
|
||||||
|
|
||||||
### Step 2: Create Invoice with Expense
|
|
||||||
|
|
||||||
When expense is added to customer invoice:
|
|
||||||
|
|
||||||
| Account | Debit | Credit |
|
|
||||||
|---------|-------|--------|
|
|
||||||
| Accounts Receivable (Customer) | $5,100 | |
|
|
||||||
| Sales Revenue | | $5,000 |
|
|
||||||
| Customer Expenses Receivable (Asset) | | $100 |
|
|
||||||
|
|
||||||
**Result:** Expense receivable is credited (settled)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Expense Types - Customer vs Company
|
|
||||||
|
|
||||||
### Two Types of Expenses
|
|
||||||
|
|
||||||
| Type | `invoice_to_customer` | Account Used | Can Add to Invoice? |
|
|
||||||
|------|----------------------|--------------|---------------------|
|
|
||||||
| **Customer Expense** (Default) | ✅ True | Customer Expenses Receivable (Asset) | Yes |
|
|
||||||
| **Company Expense** | ☐ False | Company Expense Account (P&L) | No |
|
|
||||||
|
|
||||||
### Default Behavior
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ CREATE EXPENSE │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Expense Type: [Shipping ▼] │
|
|
||||||
│ Description: [Certificate Fee ] │
|
|
||||||
│ Amount: [$100 ] │
|
|
||||||
│ Date: [2026-01-10 ] │
|
|
||||||
│ │
|
|
||||||
│ ☑️ Invoice to Customer ← DEFAULT IS CHECKED │
|
|
||||||
│ └─ Checked: Customer pays (Asset account) │
|
|
||||||
│ └─ Unchecked: Company absorbs (P&L account) │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customer Expense Flow (Default)
|
|
||||||
|
|
||||||
```
|
|
||||||
Payment: Debit: Customer Expenses Receivable (Asset)
|
|
||||||
Credit: Bank
|
|
||||||
|
|
||||||
Settlement: Add to customer invoice
|
|
||||||
→ Asset credited (zeroed)
|
|
||||||
→ Customer owes via AR
|
|
||||||
```
|
|
||||||
|
|
||||||
### Company Expense Flow (Unchecked)
|
|
||||||
|
|
||||||
```
|
|
||||||
Payment: Debit: Company Expense Account (P&L)
|
|
||||||
Credit: Bank
|
|
||||||
|
|
||||||
No settlement needed - direct expense recognition
|
|
||||||
Cannot be added to customer invoice
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reclassification: Customer → Company (Company Absorbs)
|
|
||||||
|
|
||||||
If expense was created as customer expense but company decides to absorb:
|
|
||||||
|
|
||||||
| Expense Status | Action | Accounting |
|
|
||||||
|----------------|--------|------------|
|
|
||||||
| **Not on invoice** | Click [Company Absorbs] | Debit P&L, Credit Asset |
|
|
||||||
| **On draft invoice** | Remove from invoice first, then reclassify | Remove line + Debit P&L, Credit Asset |
|
|
||||||
| **On posted invoice** | Create Credit Note + Reclassify | Reverse AR + Move to P&L |
|
|
||||||
|
|
||||||
### Step 3: Customer Pays Invoice
|
|
||||||
|
|
||||||
When customer pays the full invoice:
|
|
||||||
|
|
||||||
| Account | Debit | Credit |
|
|
||||||
|---------|-------|--------|
|
|
||||||
| Bank Account | $5,100 | |
|
|
||||||
| Accounts Receivable (Customer) | | $5,100 |
|
|
||||||
|
|
||||||
**Result:** Full settlement complete
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Workflows for Adding Expenses to Invoice
|
|
||||||
|
|
||||||
### Workflow A: Select Existing Pending Expenses
|
|
||||||
|
|
||||||
User creates invoice FIRST, then adds expenses that were already paid earlier.
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ ADD EXISTING EXPENSE TO INVOICE │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ User is on DRAFT Invoice Form │
|
|
||||||
│ │
|
|
||||||
│ 1. Clicks [+ Add Existing Expense] button │
|
|
||||||
│ │
|
|
||||||
│ 2. System shows selection wizard: │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ SELECT PENDING EXPENSES │ │
|
|
||||||
│ │ Customer: ABC Trading Co. │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ☐ Certificate Fee $100 Paid: Jan 05 │ │
|
|
||||||
│ │ ☐ Customs Clearance $200 Paid: Jan 07 │ │
|
|
||||||
│ │ ☐ Documentation $50 Paid: Jan 08 │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Filter: Same customer + Status=Pending │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ [Cancel] [Add Selected to Invoice] │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ 3. User selects expenses → System adds invoice lines │
|
|
||||||
│ │
|
|
||||||
│ 4. Expenses marked as "Added to Invoice" │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workflow B: Create New Expense Directly on Invoice (Simplified)
|
|
||||||
|
|
||||||
User wants to record an expense and add to invoice. Payment entry is handled separately (manual or already exists).
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ CREATE & ADD NEW EXPENSE (Simplified) │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ User is on DRAFT Invoice Form │
|
|
||||||
│ │
|
|
||||||
│ 1. Clicks [+ Create New Expense] button │
|
|
||||||
│ │
|
|
||||||
│ 2. System shows creation wizard: │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ CREATE NEW EXPENSE │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Expense Account: [Customer Expenses Receivable ▼] │ │
|
|
||||||
│ │ (pre-filled from configuration) │ │
|
|
||||||
│ │ Expense Type: [Shipping / Customs / Other ▼] │ │
|
|
||||||
│ │ Description: [Certificate Fee ] │ │
|
|
||||||
│ │ Amount: [$100 ] │ │
|
|
||||||
│ │ Date: [2026-01-10 ] │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ℹ️ Payment entry should be created separately │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ [Cancel] [Create & Add to Invoice]│ │
|
|
||||||
│ └─────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ 3. System creates: │
|
|
||||||
│ a) Expense record (linked to Master Order from invoice) │
|
|
||||||
│ b) Invoice line (using Asset account = settlement) │
|
|
||||||
│ │
|
|
||||||
│ Note: Payment journal entry is NOT auto-created. │
|
|
||||||
│ User creates payment entry manually/separately. │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: In this simplified workflow, the expense is created and immediately added to invoice. The payment entry (Debit Asset, Credit Bank) must be handled separately by the user. This keeps the system simple and flexible.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Edge Cases & Business Rules
|
|
||||||
|
|
||||||
### State-Based Rules
|
|
||||||
|
|
||||||
| Invoice State | Add Existing Expense | Create New Expense | Remove Expense |
|
|
||||||
|---------------|---------------------|-------------------|----------------|
|
|
||||||
| **DRAFT** | ✅ Allowed | ✅ Allowed | ✅ Allowed |
|
|
||||||
| **POSTED** | ❌ Blocked | ❌ Blocked | ❌ Blocked |
|
|
||||||
| **CANCELLED** | ❌ Blocked | ❌ Blocked | N/A |
|
|
||||||
|
|
||||||
### Edge Case Solutions
|
|
||||||
|
|
||||||
| Edge Case | Decision | Solution |
|
|
||||||
|-----------|----------|----------|
|
|
||||||
| **Partial expense addition** | ❌ Not allowed | Create multiple expenses at start |
|
|
||||||
| **Remove from DRAFT invoice** | ✅ Allowed | Reset expense status to "Pending" |
|
|
||||||
| **Remove from POSTED invoice** | ❌ Not allowed | Must use Credit Note workflow |
|
|
||||||
| **Split expense across invoices** | ❌ Not allowed | Create multiple expense records |
|
|
||||||
| **Add to POSTED invoice** | ❌ Not allowed | Create new supplementary invoice |
|
|
||||||
| **Edit amount (not invoiced)** | ✅ Allowed | Direct edit on expense |
|
|
||||||
| **Edit amount (on draft invoice)** | Requires steps | Remove → Edit → Re-add |
|
|
||||||
| **Edit amount (on posted invoice)** | ❌ Not allowed | Credit Note required |
|
|
||||||
| **Invoice cancelled** | ✅ Auto-reset | All expenses return to "Pending" |
|
|
||||||
| **Customer mismatch** | ❌ Blocked | Validation error prevents action |
|
|
||||||
|
|
||||||
### Remove Expense from Draft Invoice
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ REMOVE EXPENSE FROM DRAFT INVOICE │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ 1. User clicks [Remove] on expense line │
|
|
||||||
│ │
|
|
||||||
│ 2. System actions: │
|
|
||||||
│ • Delete the corresponding invoice line │
|
|
||||||
│ • Reset expense: added_to_invoice = False │
|
|
||||||
│ • Clear links: invoice_id = False, invoice_line_id = False │
|
|
||||||
│ │
|
|
||||||
│ 3. Result: │
|
|
||||||
│ • Expense returns to "Pending" status │
|
|
||||||
│ • Can be added to different invoice │
|
|
||||||
│ • Invoice total recalculates automatically │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Correct Expense on Posted Invoice (Credit Note Flow)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ CORRECT EXPENSE ON POSTED INVOICE │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Cannot directly modify posted invoice. Steps: │
|
|
||||||
│ │
|
|
||||||
│ 1. Create Credit Note for the expense amount only │
|
|
||||||
│ • Debits: Customer Expenses Receivable (reverses credit) │
|
|
||||||
│ • Credits: Accounts Receivable (reduces customer balance) │
|
|
||||||
│ │
|
|
||||||
│ 2. Post the Credit Note │
|
|
||||||
│ │
|
|
||||||
│ 3. Create new invoice with expense on correct customer │
|
|
||||||
│ (if needed) │
|
|
||||||
│ │
|
|
||||||
│ Result: Original expense receivable is restored, can be │
|
|
||||||
│ properly re-invoiced │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Invoice Cancellation - Expense Handling
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ INVOICE CANCELLATION │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ When invoice is cancelled, system automatically: │
|
|
||||||
│ │
|
|
||||||
│ 1. Resets ALL linked expenses: │
|
|
||||||
│ • added_to_invoice = False │
|
|
||||||
│ • invoice_id = False │
|
|
||||||
│ • invoice_line_id = False │
|
|
||||||
│ │
|
|
||||||
│ 2. Expenses return to "Pending" status │
|
|
||||||
│ │
|
|
||||||
│ 3. Expenses can be added to new invoice later │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customer Validation Rule
|
|
||||||
|
|
||||||
```
|
|
||||||
Validation: Expense.master_order_id.partner_id == Invoice.partner_id
|
|
||||||
|
|
||||||
If mismatch → Block with error:
|
|
||||||
"Expense belongs to [Customer A] but invoice is for [Customer B]"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Company Absorbs Cost (Reclassification)
|
|
||||||
|
|
||||||
When expense should NOT be charged to customer:
|
|
||||||
|
|
||||||
```
|
|
||||||
Original Entry:
|
|
||||||
Debit: Customer Expenses Receivable $100
|
|
||||||
Credit: Bank $100
|
|
||||||
|
|
||||||
Reclassification Entry:
|
|
||||||
Debit: Company Expense Account $100
|
|
||||||
Credit: Customer Expenses Receivable $100
|
|
||||||
|
|
||||||
Result: Cost moved from asset to company expense
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Data Model
|
|
||||||
|
|
||||||
### Expense Model (Simplified)
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MasterOrderExpense(models.Model):
|
|
||||||
_name = 'at.master.order.expense'
|
|
||||||
|
|
||||||
# Links
|
|
||||||
master_order_id = fields.Many2one('at.master.order')
|
|
||||||
invoice_id = fields.Many2one('account.move') # NEW - link to invoice
|
|
||||||
|
|
||||||
# Expense Details
|
|
||||||
expense_account_id = fields.Many2one('account.account') # PRIMARY - select from COA
|
|
||||||
description = fields.Char('Description')
|
|
||||||
amount = fields.Float('Amount')
|
|
||||||
date = fields.Date('Date')
|
|
||||||
|
|
||||||
# Status
|
|
||||||
added_to_invoice = fields.Boolean('Added to Invoice') # Track if converted to invoice line
|
|
||||||
invoice_line_id = fields.Many2one('account.move.line') # Link to created invoice line
|
|
||||||
```
|
|
||||||
|
|
||||||
### Invoice Model (Extended)
|
|
||||||
|
|
||||||
```python
|
|
||||||
class AccountMove(models.Model):
|
|
||||||
_inherit = 'account.move'
|
|
||||||
|
|
||||||
# Expense tracking
|
|
||||||
expense_ids = fields.One2many('at.master.order.expense', 'invoice_id')
|
|
||||||
|
|
||||||
# Computed totals
|
|
||||||
invoice_total_expenses = fields.Float(compute='_compute_expense_totals')
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## UI Design
|
|
||||||
|
|
||||||
### 1. Invoice Form - Expenses Tab (DRAFT)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌───────────────────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ 📋 CUSTOMER INVOICE (DRAFT) INV/2026/00123 │
|
|
||||||
├───────────────────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ Customer: ABC Trading Co. │ Invoice Date: 2026-01-10 │
|
|
||||||
│ Master Order: MO/2026/00045 │ Due Date: 2026-02-10 │
|
|
||||||
├───────────────────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ [Invoice Lines] [Expenses] [Other Info] │
|
|
||||||
│ ════════════════════════════════════════════════════════════════════════════════ │
|
|
||||||
│ │
|
|
||||||
│ EXPENSES ON THIS INVOICE │
|
|
||||||
│ ┌────────────────────────┬─────────────────┬──────────┬──────────┬─────────────┐ │
|
|
||||||
│ │ Type │ Description │ Amount │ Date │ Action │ │
|
|
||||||
│ ├────────────────────────┼─────────────────┼──────────┼──────────┼─────────────┤ │
|
|
||||||
│ │ Customs │ Import Duty │ $200 │ Jan 05 │ [Remove] │ │
|
|
||||||
│ │ Documentation │ Certificate Fee │ $100 │ Jan 07 │ [Remove] │ │
|
|
||||||
│ │ Shipping │ Air Freight │ $350 │ Jan 08 │ [Remove] │ │
|
|
||||||
│ └────────────────────────┴─────────────────┴──────────┴──────────┴─────────────┘ │
|
|
||||||
│ Expenses Total: $650 │
|
|
||||||
│ │
|
|
||||||
│ [+ Add Existing Expense] [+ Create New Expense] │
|
|
||||||
│ │
|
|
||||||
│ ════════════════════════════════════════════════════════════════════════════════ │
|
|
||||||
│ │
|
|
||||||
│ PENDING EXPENSES (Not Yet Added) │
|
|
||||||
│ ┌────────────────────────┬─────────────────┬──────────┬──────────┬─────────────┐ │
|
|
||||||
│ │ Type │ Description │ Amount │ Date │ Action │ │
|
|
||||||
│ ├────────────────────────┼─────────────────┼──────────┼──────────┼─────────────┤ │
|
|
||||||
│ │ Handling │ Warehouse Fee │ $75 │ Jan 09 │ [+ Add] │ │
|
|
||||||
│ │ Other │ Inspection │ $50 │ Jan 10 │ [+ Add] │ │
|
|
||||||
│ └────────────────────────┴─────────────────┴──────────┴──────────┴─────────────┘ │
|
|
||||||
│ Pending Total: $125 │
|
|
||||||
│ │
|
|
||||||
│ ════════════════════════════════════════════════════════════════════════════════ │
|
|
||||||
│ │
|
|
||||||
│ SUMMARY │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ Product Lines: $5,000 │ │
|
|
||||||
│ │ Expenses on Invoice: $650 │ │
|
|
||||||
│ │ ───────────────────────────────── │ │
|
|
||||||
│ │ INVOICE TOTAL: $5,650 │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
└───────────────────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Invoice Form - Expenses Tab (POSTED - Read Only)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌───────────────────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ 📋 CUSTOMER INVOICE (POSTED) INV/2026/00123 │
|
|
||||||
├───────────────────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ Customer: ABC Trading Co. │ Invoice Date: 2026-01-10 │
|
|
||||||
│ Master Order: MO/2026/00045 │ Status: Posted ✓ │
|
|
||||||
├───────────────────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ [Invoice Lines] [Expenses] [Other Info] │
|
|
||||||
│ ════════════════════════════════════════════════════════════════════════════════ │
|
|
||||||
│ │
|
|
||||||
│ EXPENSES ON THIS INVOICE (Read-only - Invoice Posted) │
|
|
||||||
│ ┌────────────────────────┬─────────────────┬──────────┬──────────┬─────────────┐ │
|
|
||||||
│ │ Type │ Description │ Amount │ Date │ Status │ │
|
|
||||||
│ ├────────────────────────┼─────────────────┼──────────┼──────────┼─────────────┤ │
|
|
||||||
│ │ Customs │ Import Duty │ $200 │ Jan 05 │ ✓ Settled │ │
|
|
||||||
│ │ Documentation │ Certificate Fee │ $100 │ Jan 07 │ ✓ Settled │ │
|
|
||||||
│ │ Shipping │ Air Freight │ $350 │ Jan 08 │ ✓ Settled │ │
|
|
||||||
│ └────────────────────────┴─────────────────┴──────────┴──────────┴─────────────┘ │
|
|
||||||
│ Expenses Total: $650 │
|
|
||||||
│ │
|
|
||||||
│ ⚠️ Invoice is posted. To modify expenses, create a Credit Note. │
|
|
||||||
│ │
|
|
||||||
└───────────────────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Master Order - Expenses Section
|
|
||||||
|
|
||||||
```
|
|
||||||
┌───────────────────────────────────────────────────────────────────────────────────┐
|
|
||||||
│ 📦 MASTER ORDER MO/2026/00045 │
|
|
||||||
├───────────────────────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ [Order Info] [Lines] [Expenses] [Invoices] [Shipments] │
|
|
||||||
│ ════════════════════════════════════════════════════════════════════════════════ │
|
|
||||||
│ │
|
|
||||||
│ EXPENSES [+ Add Expense] │
|
|
||||||
│ ┌────────────┬─────────────────┬──────────┬──────────┬───────────┬─────────────┐ │
|
|
||||||
│ │ Type │ Description │ Amount │ Date │ Invoice │ Status │ │
|
|
||||||
│ ├────────────┼─────────────────┼──────────┼──────────┼───────────┼─────────────┤ │
|
|
||||||
│ │ Customs │ Import Duty │ $200 │ Jan 05 │ INV/00123 │ ✓ Invoiced │ │
|
|
||||||
│ │ Docs │ Certificate Fee │ $100 │ Jan 07 │ INV/00123 │ ✓ Invoiced │ │
|
|
||||||
│ │ Shipping │ Air Freight │ $350 │ Jan 08 │ INV/00123 │ ✓ Invoiced │ │
|
|
||||||
│ │ Handling │ Warehouse Fee │ $75 │ Jan 09 │ - │ ⏳ Pending │ │
|
|
||||||
│ │ Other │ Samples │ $50 │ Jan 10 │ - │ 🏢 Company │ │
|
|
||||||
│ └────────────┴─────────────────┴──────────┴──────────┴───────────┴─────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ════════════════════════════════════════════════════════════════════════════════ │
|
|
||||||
│ │
|
|
||||||
│ EXPENSE SUMMARY │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ Total Customer Expenses: $725 (charged to customer) │ │
|
|
||||||
│ │ - Invoiced: $650 ✓ │ │
|
|
||||||
│ │ - Pending: $75 ⏳ │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ Total Company Expenses: $50 (company absorbed) │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
└───────────────────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Create Expense Form (Popup/Wizard)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ CREATE NEW EXPENSE [X] │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Expense Type: [Customs ▼] │
|
|
||||||
│ │
|
|
||||||
│ Description: [Import duty for shipment ] │
|
|
||||||
│ │
|
|
||||||
│ Amount: [$200.00 ] │
|
|
||||||
│ │
|
|
||||||
│ Date: [2026-01-10 ] │
|
|
||||||
│ │
|
|
||||||
│ Expense Account: [1300 - Customer Exp. Receivable ▼] │
|
|
||||||
│ (pre-filled from settings) │
|
|
||||||
│ │
|
|
||||||
│ ───────────────────────────────────────────────────────────── │
|
|
||||||
│ │
|
|
||||||
│ ☑️ Invoice to Customer │
|
|
||||||
│ ├─ ✓ Checked: Customer will be charged (default) │
|
|
||||||
│ └─ ☐ Unchecked: Company absorbs cost │
|
|
||||||
│ │
|
|
||||||
│ ───────────────────────────────────────────────────────────── │
|
|
||||||
│ │
|
|
||||||
│ [Cancel] [Create Expense] │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Select Existing Expenses Wizard
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ SELECT EXPENSES TO ADD [X] │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Invoice: INV/2026/00123 │
|
|
||||||
│ Customer: ABC Trading Co. │
|
|
||||||
│ │
|
|
||||||
│ ───────────────────────────────────────────────────────────── │
|
|
||||||
│ │
|
|
||||||
│ AVAILABLE PENDING EXPENSES │
|
|
||||||
│ ┌────┬────────────┬─────────────────┬──────────┬────────────┐ │
|
|
||||||
│ │ ☐ │ Type │ Description │ Amount │ Date │ │
|
|
||||||
│ ├────┼────────────┼─────────────────┼──────────┼────────────┤ │
|
|
||||||
│ │ ☑️ │ Handling │ Warehouse Fee │ $75 │ Jan 09 │ │
|
|
||||||
│ │ ☑️ │ Other │ Inspection │ $50 │ Jan 10 │ │
|
|
||||||
│ │ ☐ │ Shipping │ Local Delivery │ $30 │ Jan 11 │ │
|
|
||||||
│ └────┴────────────┴─────────────────┴──────────┴────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ Selected Total: $125 │
|
|
||||||
│ │
|
|
||||||
│ ───────────────────────────────────────────────────────────── │
|
|
||||||
│ │
|
|
||||||
│ [Cancel] [Add Selected to Invoice] │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Company Absorbs Action (Reclassification)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ COMPANY ABSORBS EXPENSE [X] │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Expense: Samples for client │
|
|
||||||
│ Amount: $50 │
|
|
||||||
│ │
|
|
||||||
│ ───────────────────────────────────────────────────────────── │
|
|
||||||
│ │
|
|
||||||
│ ⚠️ This will: │
|
|
||||||
│ • Move expense from Customer to Company │
|
|
||||||
│ • Create reclassification journal entry │
|
|
||||||
│ • Expense will NOT be charged to customer │
|
|
||||||
│ │
|
|
||||||
│ Reclassification Entry: │
|
|
||||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ Debit: Company Expense (P&L) $50 │ │
|
|
||||||
│ │ Credit: Customer Expenses Receivable $50 │ │
|
|
||||||
│ └─────────────────────────────────────────────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ ───────────────────────────────────────────────────────────── │
|
|
||||||
│ │
|
|
||||||
│ [Cancel] [Confirm - Company Pays] │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Default Customer Expense Account Setting
|
|
||||||
|
|
||||||
Use existing settings pattern in `models/settings.py`:
|
|
||||||
|
|
||||||
**Parameter**: `at_master_order.customer_expense_account_id`
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ MASTER ORDER SETTINGS → Account Configuration │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ Commission Income Account: [4100 - Commission Income ▼] │
|
|
||||||
│ Shipping Revenue Account: [4200 - Shipping Revenue ▼] │
|
|
||||||
│ Shipping Expense Account: [5100 - Shipping Expense ▼] │
|
|
||||||
│ Other Expense Account: [5200 - Other Expenses ▼] │
|
|
||||||
│ │
|
|
||||||
│ ─── NEW ─── │
|
|
||||||
│ Customer Expense Account: [1300 - Cust. Exp. Receivable ▼]│
|
|
||||||
│ │
|
|
||||||
│ ℹ️ Used for expenses paid on behalf of customers. │
|
|
||||||
│ Debited when expense is paid, credited when added to │
|
|
||||||
│ customer invoice for settlement. │
|
|
||||||
│ │
|
|
||||||
│ Account Filter: Asset accounts only │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Implementation (Existing Pattern)
|
|
||||||
|
|
||||||
**File**: `models/settings.py`
|
|
||||||
|
|
||||||
| Section | Add |
|
|
||||||
|---------|-----|
|
|
||||||
| Field definition (line ~35) | `customer_expense_account_id = fields.Many2one('account.account', ...)` |
|
|
||||||
| `get_values()` (line ~145) | Read from `at_master_order.customer_expense_account_id` |
|
|
||||||
| `set_values()` (line ~190) | Write to `at_master_order.customer_expense_account_id` |
|
|
||||||
|
|
||||||
**File**: `models/expense_box.py`
|
|
||||||
|
|
||||||
| Section | Add |
|
|
||||||
|---------|-----|
|
|
||||||
| `get_expense_account()` | Add fallback to `at_master_order.customer_expense_account_id` |
|
|
||||||
|
|
||||||
| Setting Behavior | Description |
|
|
||||||
|------------------|-------------|
|
|
||||||
| Pre-fills expense account | When creating expense, this account is selected by default |
|
|
||||||
| User can override | Default is convenience, not mandatory |
|
|
||||||
| Follows existing pattern | Same as other account configurations |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Recommended Account Setup
|
|
||||||
|
|
||||||
### Chart of Accounts
|
|
||||||
|
|
||||||
| Account Code | Account Name | Type | Purpose |
|
|
||||||
|--------------|--------------|------|--------|
|
|
||||||
| 1300 | Customer Expenses Receivable | Asset (Receivable) | Track expenses paid on behalf of customers |
|
|
||||||
|
|
||||||
**Alternative names:**
|
|
||||||
- Customer Advances
|
|
||||||
- Customer Expense Fund
|
|
||||||
- Prepaid Customer Expenses
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Tasks
|
|
||||||
|
|
||||||
### Phase 0: Configuration
|
|
||||||
|
|
||||||
| # | Task | File | Status |
|
|
||||||
|---|------|------|--------|
|
|
||||||
| 0 | Add `customer_expense_account_id` field to settings | `models/settings.py` | ✅ |
|
|
||||||
| 0b | Add to `get_values()` method | `models/settings.py` | ✅ |
|
|
||||||
| 0c | Add to `set_values()` method | `models/settings.py` | ✅ |
|
|
||||||
| 0d | Add field to settings view (if exists) | `views/master_order_config_views.xml` | ✅ |
|
|
||||||
|
|
||||||
### Phase 1: Model Changes
|
|
||||||
|
|
||||||
| # | Task | File | Status |
|
|
||||||
|---|------|------|--------|
|
|
||||||
| 1 | Add `invoice_id` field to expense model | `models/expense_box.py` | ✅ |
|
|
||||||
| 2 | Add `added_to_invoice`, `invoice_line_id` fields | `models/expense_box.py` | ✅ |
|
|
||||||
| 3 | Add `expense_id` field to invoice line (reverse link) | `models/account_move.py` | ✅ |
|
|
||||||
| 4 | Add `expense_ids` One2many to account.move | `models/account_move.py` | ✅ |
|
|
||||||
| 5 | Add computed expense totals | `models/account_move.py` | ✅ |
|
|
||||||
| 6 | Add `action_add_to_invoice()` method | `models/expense_box.py` | ✅ |
|
|
||||||
| 7 | Add `action_remove_from_invoice()` method | `models/expense_box.py` | ✅ |
|
|
||||||
| 8 | Override invoice cancel to reset expenses | `models/account_move.py` | ✅ |
|
|
||||||
| 9 | Add customer validation on expense add | `models/expense_box.py` | ✅ |
|
|
||||||
| 10 | Use default account in expense creation | `models/expense_box.py` | ✅ |
|
|
||||||
|
|
||||||
### Phase 2: Wizard Development
|
|
||||||
|
|
||||||
| # | Task | File | Status |
|
|
||||||
|---|------|------|--------|
|
|
||||||
| 10 | Create "Select Existing Expense" wizard | `wizard/add_expense_to_invoice_wizard.py` | ✅ |
|
|
||||||
| 11 | Create "Create New Expense" wizard | `wizard/create_expense_wizard.py` | ✅ |
|
|
||||||
| 12 | Wizard views | `wizard/expense_wizard_views.xml` | ✅ |
|
|
||||||
|
|
||||||
### Phase 3: View Changes
|
|
||||||
|
|
||||||
| # | Task | File | Status |
|
|
||||||
|---|------|------|--------|
|
|
||||||
| 13 | Add Expenses section to invoice form | `views/invoice_expense_views.xml` | ✅ |
|
|
||||||
| 14 | Add wizard buttons (draft state only) | `views/invoice_expense_views.xml` | ✅ |
|
|
||||||
| 15 | Add pending expenses quick list | `views/invoice_expense_views.xml` | ✅ |
|
|
||||||
| 16 | State-based visibility (draft vs posted) | `views/invoice_expense_views.xml` | ✅ |
|
|
||||||
|
|
||||||
### Phase 4: Testing & Deployment
|
|
||||||
|
|
||||||
| # | Task | Status |
|
|
||||||
|---|------|--------|
|
|
||||||
| 17 | Test Workflow A: Select existing expense | ⬜ |
|
|
||||||
| 18 | Test Workflow B: Create new expense on invoice | ⬜ |
|
|
||||||
| 19 | Test remove expense from draft invoice | ⬜ |
|
|
||||||
| 20 | Test invoice cancellation resets expenses | ⬜ |
|
|
||||||
| 21 | Test customer validation | ⬜ |
|
|
||||||
| 22 | Test accounting entries (settlement) | ⬜ |
|
|
||||||
| 23 | Deploy to aspir database | ⬜ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
| Feature | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| **Expense Account Selection** | Choose any account from COA |
|
|
||||||
| **Track on Invoice** | Link expenses to specific customer invoice |
|
|
||||||
| **Add to Invoice Line** | Convert expense to invoice line for billing |
|
|
||||||
| **Auto Settlement** | Expense account settles when customer pays |
|
|
||||||
| **Aggregate to MO** | Expenses also visible at Master Order level |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
1. **No Vendor Bills** - This is for expenses paid directly, not through vendor bills
|
|
||||||
2. **Simple Tracking** - Just account + description + amount
|
|
||||||
3. **Flexible Accounts** - Use any expense/asset account from COA
|
|
||||||
4. **Automatic Settlement** - Adding to invoice creates the settlement entry
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Approval
|
|
||||||
|
|
||||||
- [x] Design approved by user
|
|
||||||
- [x] Option A: Invoice line uses Asset account (settlement)
|
|
||||||
- [x] No tax treatment
|
|
||||||
- [x] Single currency only
|
|
||||||
- [x] Workflow A + B confirmed
|
|
||||||
- [x] Edge cases reviewed and approved
|
|
||||||
- [x] Ready for implementation
|
|
||||||
- [x] **Implementation Complete** (2026-01-10)
|
|
||||||
@@ -1,284 +0,0 @@
|
|||||||
# Invoice-Centric Shipment Tracking
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document describes the implementation of shipment tracking integrated directly into customer invoices, replacing the separate Shipment tab in Master Order.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Business Requirements
|
|
||||||
|
|
||||||
### Core Principle
|
|
||||||
> **Invoice = Commercial + Shipment unit**
|
|
||||||
> Shipment does not exist without invoice. Customer tracks shipment by invoice.
|
|
||||||
|
|
||||||
### Key Decisions
|
|
||||||
|
|
||||||
| Decision | Value |
|
|
||||||
|----------|-------|
|
|
||||||
| Shipping Methods | Sea, Air (only) |
|
|
||||||
| Shipping Company Source | `res.partner` with `is_shipper` flag |
|
|
||||||
| Shipping Selection | Optional at invoice creation, editable on invoice form |
|
|
||||||
| Shipping Charge | Added later via button, creates vendor bill |
|
|
||||||
| Old Shipment Model | Delete `at.master.order.shipment` (testing phase) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
### Step 1: Create Invoice
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ CREATE INVOICE WIZARD │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ Products: [Product A, Product B...] │
|
|
||||||
│ │
|
|
||||||
│ ☐ Include Shipping (Optional) │
|
|
||||||
│ └── IF checked: │
|
|
||||||
│ Shipping Company: [MSC Shipping ▼] │
|
|
||||||
│ Shipping Method: [Sea ▼ / Air ▼] │
|
|
||||||
│ │
|
|
||||||
│ [Create Invoice] │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
Result: Invoice created with optional shipping context (no charge yet)
|
|
||||||
|
|
||||||
### Step 2: Add/Edit Shipping Details (On Invoice Form)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ INVOICE FORM - Shipment Tracking Section │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ Shipping Company: [MSC ▼] Method: [🚢 Sea ▼] │
|
|
||||||
│ Container #: [MSKU8821456] Status: [In Transit] │
|
|
||||||
│ ETD: [2026-01-15] Actual Ship: [2026-01-16] │
|
|
||||||
│ ETA: [2026-02-12] Delivered: [________] │
|
|
||||||
│ Notes: [Customs cleared...] │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Add Shipping Charge (Creates Bill)
|
|
||||||
|
|
||||||
User clicks **[Add Shipping Charge]** button on invoice:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ ADD SHIPPING CHARGE │
|
|
||||||
├─────────────────────────────────────────────────────────────┤
|
|
||||||
│ Shipping Company: MSC Shipping (from invoice) │
|
|
||||||
│ Shipping Cost: $800 ← Pay to shipper │
|
|
||||||
│ Shipping Charge: $1,000 ← Charge customer │
|
|
||||||
│ │
|
|
||||||
│ ☑ Add to Customer Invoice (as line) │
|
|
||||||
│ ☑ Create Vendor Bill for Shipping Cost │
|
|
||||||
│ │
|
|
||||||
│ [Confirm] │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
**Result:**
|
|
||||||
- Invoice line added: "Shipping Charge: $1,000"
|
|
||||||
- Vendor bill created: Partner=MSC, Amount=$800, auto-confirmed
|
|
||||||
- Shipping margin tracked: $200 profit
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Data Model Changes
|
|
||||||
|
|
||||||
### 1. New Field on `res.partner`
|
|
||||||
|
|
||||||
| Field | Type | Purpose |
|
|
||||||
|-------|------|---------|
|
|
||||||
| `is_shipper` | Boolean | Filter for shipping company dropdown |
|
|
||||||
|
|
||||||
### 2. New Fields on `account.move` (Customer Invoices)
|
|
||||||
|
|
||||||
| Field | Type | Required | Purpose |
|
|
||||||
|-------|------|----------|---------|
|
|
||||||
| `shipper_id` | Many2one (res.partner) | No | Shipping company |
|
|
||||||
| `shipment_method` | Selection | No | 'sea' / 'air' |
|
|
||||||
| `container_tracking` | Char | No | Container # or AWB |
|
|
||||||
| `etd` | Date | No | Estimated Time of Departure |
|
|
||||||
| `eta` | Date | No | Estimated Time of Arrival |
|
|
||||||
| `actual_ship_date` | Date | No | When goods shipped |
|
|
||||||
| `actual_delivery_date` | Date | No | When goods delivered |
|
|
||||||
| `shipment_status` | Selection (Computed) | Auto | Status based on dates |
|
|
||||||
| `shipment_notes` | Text | No | Notes about shipment |
|
|
||||||
| `shipping_cost` | Float | No | Cost paid to shipper |
|
|
||||||
| `shipping_charge` | Float | No | Amount charged to customer |
|
|
||||||
| `shipping_margin` | Float (Computed) | Auto | charge - cost |
|
|
||||||
| `shipping_bill_id` | Many2one (account.move) | No | Link to shipping vendor bill |
|
|
||||||
|
|
||||||
### 3. New Field on `at.master.order`
|
|
||||||
|
|
||||||
| Field | Type | Purpose |
|
|
||||||
|-------|------|---------|
|
|
||||||
| `overall_shipment_status` | Selection (Computed) | Aggregated from invoices |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Shipment Status Logic
|
|
||||||
|
|
||||||
### Invoice Level
|
|
||||||
|
|
||||||
```python
|
|
||||||
IF not shipper_id:
|
|
||||||
status = 'no_shipping'
|
|
||||||
ELIF actual_delivery_date:
|
|
||||||
status = 'delivered'
|
|
||||||
ELIF actual_ship_date:
|
|
||||||
IF eta AND today > eta:
|
|
||||||
status = 'delayed'
|
|
||||||
ELSE:
|
|
||||||
status = 'in_transit'
|
|
||||||
ELIF shipper_id:
|
|
||||||
status = 'pending'
|
|
||||||
ELSE:
|
|
||||||
status = 'draft'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Master Order Level
|
|
||||||
|
|
||||||
```python
|
|
||||||
invoice_statuses = [inv.shipment_status for inv in posted_invoices]
|
|
||||||
|
|
||||||
IF all(s == 'delivered' for s in invoice_statuses):
|
|
||||||
status = 'delivered'
|
|
||||||
ELIF any(s == 'delayed' for s in invoice_statuses):
|
|
||||||
status = 'delayed'
|
|
||||||
ELIF any(s == 'in_transit' for s in invoice_statuses):
|
|
||||||
status = 'in_transit'
|
|
||||||
ELIF any(s == 'pending' for s in invoice_statuses):
|
|
||||||
status = 'pending'
|
|
||||||
ELIF len(set(invoice_statuses)) > 1:
|
|
||||||
status = 'partial'
|
|
||||||
ELSE:
|
|
||||||
status = 'not_shipped'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## View Changes
|
|
||||||
|
|
||||||
### 1. Master Order Form
|
|
||||||
|
|
||||||
**REMOVE:**
|
|
||||||
- `<page string="Shipping" name="shipping">` entire section
|
|
||||||
|
|
||||||
**MODIFY - Invoice Tab:**
|
|
||||||
Add columns to invoice list:
|
|
||||||
- Shipment Status (badge)
|
|
||||||
- Ship Method (icon)
|
|
||||||
- Container/Tracking
|
|
||||||
- ETA
|
|
||||||
|
|
||||||
**ADD - Header:**
|
|
||||||
- Overall shipment status field
|
|
||||||
|
|
||||||
### 2. Invoice Form
|
|
||||||
|
|
||||||
**ADD - Before Notebook:**
|
|
||||||
- Shipment Tracking section (visible only for `out_invoice`)
|
|
||||||
|
|
||||||
**ADD - Button:**
|
|
||||||
- "Add Shipping Charge" button in header
|
|
||||||
|
|
||||||
### 3. Create Invoice Wizard
|
|
||||||
|
|
||||||
**ADD:**
|
|
||||||
- Optional shipping company selection
|
|
||||||
- Shipping method selection
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Files to Modify
|
|
||||||
|
|
||||||
| File | Action |
|
|
||||||
|------|--------|
|
|
||||||
| `models/shipment.py` | **DELETE** entire file |
|
|
||||||
| `models/__init__.py` | Remove shipment import |
|
|
||||||
| `views/shipment_views.xml` | **DELETE** entire file |
|
|
||||||
| `views/master_order_views.xml` | Remove Shipping tab, enhance Invoice tab |
|
|
||||||
| `security/ir.model.access.csv` | Remove shipment model access |
|
|
||||||
| `data/ir_sequence_data.xml` | Remove shipment sequence (if exists) |
|
|
||||||
| `__manifest__.py` | Remove shipment_views.xml reference |
|
|
||||||
| `models/account_move.py` | Add shipment tracking fields |
|
|
||||||
| `models/res_partner.py` | **CREATE** - Add is_shipper field |
|
|
||||||
| `models/master_order.py` | Add overall_shipment_status, remove shipment references |
|
|
||||||
| `wizard/create_invoice_wizard.py` | Add shipping company selection |
|
|
||||||
| `wizard/create_invoice_wizard_views.xml` | Add shipping fields to wizard |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Implementation Phases
|
|
||||||
|
|
||||||
### Phase 1: Cleanup
|
|
||||||
1. Delete `at.master.order.shipment` model and related files
|
|
||||||
2. Remove Shipping tab from Master Order views
|
|
||||||
3. Clean up manifest and security files
|
|
||||||
|
|
||||||
### Phase 2: Partner Enhancement
|
|
||||||
1. Add `is_shipper` field to `res.partner`
|
|
||||||
2. Create partner form extension view
|
|
||||||
|
|
||||||
### Phase 3: Invoice Enhancement
|
|
||||||
1. Add shipment tracking fields to `account.move`
|
|
||||||
2. Add computed `shipment_status` field
|
|
||||||
3. Add Shipment Tracking section to invoice form
|
|
||||||
4. Add shipment columns to invoice list in Master Order
|
|
||||||
|
|
||||||
### Phase 4: Shipping Charge Workflow
|
|
||||||
1. Create "Add Shipping Charge" wizard
|
|
||||||
2. Implement invoice line creation
|
|
||||||
3. Implement vendor bill creation (auto-confirmed)
|
|
||||||
|
|
||||||
### Phase 5: Master Order Integration
|
|
||||||
1. Add `overall_shipment_status` computed field
|
|
||||||
2. Update Invoice tab view with shipment columns
|
|
||||||
3. Add status display in header
|
|
||||||
|
|
||||||
### Phase 6: Create Invoice Wizard Update
|
|
||||||
1. Add optional shipping company field
|
|
||||||
2. Add shipping method selection
|
|
||||||
3. Pass values to created invoice
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Status Badge Colors
|
|
||||||
|
|
||||||
| Status | Color | Icon |
|
|
||||||
|--------|-------|------|
|
|
||||||
| No Shipping | Gray | ⚪ |
|
|
||||||
| Draft | Gray | ⚪ |
|
|
||||||
| Pending | Yellow | 🟡 |
|
|
||||||
| In Transit | Blue | 🚚 |
|
|
||||||
| Delayed | Red | 🔴 |
|
|
||||||
| Delivered | Green | ✅ |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Customer Portal (Future)
|
|
||||||
|
|
||||||
Portal enhancements will include:
|
|
||||||
- Shipment status visible on invoice list
|
|
||||||
- Shipment tracking section on invoice detail
|
|
||||||
- Carrier tracking link integration
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accounting Safety
|
|
||||||
|
|
||||||
- Shipment fields are **informational only**
|
|
||||||
- No impact on journal entries or reconciliation
|
|
||||||
- Shipping bill uses standard Odoo vendor bill workflow
|
|
||||||
- All changes tracked via Odoo tracking mechanism
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
- No data migration needed (shipment model was in testing)
|
|
||||||
- Existing invoices will have empty shipment fields
|
|
||||||
- Users can populate shipment data on existing invoices manually
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
# Payment Customer Validation - Implementation Summary
|
|
||||||
|
|
||||||
## ✅ Implementation Complete
|
|
||||||
|
|
||||||
### Phase 1: Payment Customer Validation Wizard ✅
|
|
||||||
|
|
||||||
**Files Created:**
|
|
||||||
- `wizard/payment_customer_validation_wizard.py` - Wizard model
|
|
||||||
- `wizard/payment_customer_validation_wizard_views.xml` - Wizard views
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- ✅ Shows payment details (amount, partner, date)
|
|
||||||
- ✅ Shows related bills (if found)
|
|
||||||
- ✅ Suggests customers based on bills/partner history
|
|
||||||
- ✅ Customer selection with search
|
|
||||||
- ✅ Master Order selection (optional)
|
|
||||||
- ✅ Two actions: "Confirm & Link" and "Skip for Now"
|
|
||||||
- ✅ Creates transaction when customer is linked
|
|
||||||
- ✅ Updates payment linkage status
|
|
||||||
|
|
||||||
### Phase 2: Payment Tracking Dashboard ✅
|
|
||||||
|
|
||||||
**Files Created:**
|
|
||||||
- `views/payment_tracking_views.xml` - Tracking views
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
- ✅ List view with linkage status
|
|
||||||
- ✅ Filters: Needs Review, Linked, Pending, Skipped, Failed
|
|
||||||
- ✅ Group by: Status, Customer, Partner, Date
|
|
||||||
- ✅ Color coding: Red (failed), Yellow (pending), Gray (skipped), Green (linked)
|
|
||||||
- ✅ Quick actions: "Link Customer" button
|
|
||||||
- ✅ Form view integration: Shows linkage fields
|
|
||||||
- ✅ Menu item: "Payment Tracking" under Client Cash Flow
|
|
||||||
|
|
||||||
### Phase 3: Enhanced Payment Model ✅
|
|
||||||
|
|
||||||
**Fields Added to `account.payment`:**
|
|
||||||
- ✅ `customer_linkage_status` - Status of customer linkage
|
|
||||||
- ✅ `linked_customer_id` - Customer payment is linked to
|
|
||||||
- ✅ `linked_transaction_id` - Transaction created from payment
|
|
||||||
- ✅ `linkage_date` - When payment was linked
|
|
||||||
- ✅ `linkage_user_id` - User who linked payment
|
|
||||||
- ✅ `needs_review` - Flag for payments needing review
|
|
||||||
|
|
||||||
**Methods Added:**
|
|
||||||
- ✅ `action_open_validation_wizard()` - Open wizard for payment
|
|
||||||
- ✅ `action_link_customer_manually()` - Manual linking action
|
|
||||||
- ✅ `action_view_transaction()` - View linked transaction
|
|
||||||
|
|
||||||
### Phase 4: Integration ✅
|
|
||||||
|
|
||||||
**Payment Creation Flow:**
|
|
||||||
1. Payment created → System tries to find customer automatically
|
|
||||||
2. If customer found → Transaction created automatically ✅
|
|
||||||
3. If customer NOT found → Payment marked as 'pending', needs review ⏳
|
|
||||||
4. User can link manually from Payment Tracking page ✅
|
|
||||||
|
|
||||||
**Payment Tracking Flow:**
|
|
||||||
1. User opens "Payment Tracking" menu
|
|
||||||
2. Sees list of all payments with linkage status
|
|
||||||
3. Filters by "Needs Review" to see unlinked payments
|
|
||||||
4. Clicks "Link Customer" button
|
|
||||||
5. Wizard opens → User selects customer
|
|
||||||
6. Transaction created → Payment marked as 'linked' ✅
|
|
||||||
|
|
||||||
## User Workflows
|
|
||||||
|
|
||||||
### Workflow 1: Automatic Linking (No Action Needed)
|
|
||||||
```
|
|
||||||
Payment Created → Customer Found Automatically → Transaction Created → Done ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workflow 2: Manual Linking (Wizard)
|
|
||||||
```
|
|
||||||
Payment Created → Customer NOT Found → Payment Marked 'Pending'
|
|
||||||
↓
|
|
||||||
User Opens Payment Tracking → Sees Unlinked Payment
|
|
||||||
↓
|
|
||||||
Clicks "Link Customer" → Wizard Opens
|
|
||||||
↓
|
|
||||||
User Selects Customer → Clicks "Confirm & Link"
|
|
||||||
↓
|
|
||||||
Transaction Created → Payment Marked 'Linked' ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workflow 3: Skip for Later
|
|
||||||
```
|
|
||||||
Payment Created → Customer NOT Found → Payment Marked 'Pending'
|
|
||||||
↓
|
|
||||||
User Opens Payment Tracking → Sees Unlinked Payment
|
|
||||||
↓
|
|
||||||
Clicks "Link Customer" → Wizard Opens
|
|
||||||
↓
|
|
||||||
User Clicks "Skip for Now"
|
|
||||||
↓
|
|
||||||
Payment Marked 'Skipped' → Appears in Unlinked List → Can Link Later ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
## Menu Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
Master Orders
|
|
||||||
└── Client Cash Flow
|
|
||||||
├── Cash Flow Overview
|
|
||||||
├── All Transactions
|
|
||||||
├── Supplier Debts
|
|
||||||
├── Advanced Suppliers
|
|
||||||
├── Payment Validation
|
|
||||||
└── Payment Tracking (NEW) ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
### 1. Validation Wizard
|
|
||||||
- ✅ Non-intrusive: Only shows when customer cannot be found
|
|
||||||
- ✅ Smart suggestions: Suggests customers based on bills/partner
|
|
||||||
- ✅ Flexible: User can search for any customer
|
|
||||||
- ✅ Clear messaging: Explains why validation is needed
|
|
||||||
|
|
||||||
### 2. Payment Tracking Dashboard
|
|
||||||
- ✅ Complete visibility: See all payments and their status
|
|
||||||
- ✅ Easy filtering: Filter by linkage status
|
|
||||||
- ✅ Quick actions: One-click linking
|
|
||||||
- ✅ Color coding: Visual status indicators
|
|
||||||
|
|
||||||
### 3. Data Integrity
|
|
||||||
- ✅ All payments tracked: Status field shows linkage state
|
|
||||||
- ✅ Audit trail: Tracks who linked and when
|
|
||||||
- ✅ Transaction linking: Links payment to transaction
|
|
||||||
- ✅ Automatic retry: Retries when payment is updated
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Create payment with clear customer link → Should work automatically ✅
|
|
||||||
- [ ] Create payment without customer link → Should appear in unlinked list ✅
|
|
||||||
- [ ] Open Payment Tracking → Should show all payments ✅
|
|
||||||
- [ ] Filter by "Needs Review" → Should show unlinked payments ✅
|
|
||||||
- [ ] Click "Link Customer" → Wizard should open ✅
|
|
||||||
- [ ] Select customer in wizard → Should create transaction ✅
|
|
||||||
- [ ] Skip validation → Payment should be marked 'skipped' ✅
|
|
||||||
- [ ] Link skipped payment later → Should work ✅
|
|
||||||
|
|
||||||
## Files Modified
|
|
||||||
|
|
||||||
1. ✅ `models/document_links.py` - Added fields and methods
|
|
||||||
2. ✅ `wizard/__init__.py` - Added wizard import
|
|
||||||
3. ✅ `__manifest__.py` - Added new files
|
|
||||||
4. ✅ `views/menu.xml` - Added menu item
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
1. **Deploy** to server
|
|
||||||
2. **Test** payment creation flow
|
|
||||||
3. **Test** manual linking from dashboard
|
|
||||||
4. **Verify** transactions are created correctly
|
|
||||||
5. **Check** wallet balances update correctly
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
✅ **Data Integrity** - All payments properly linked to customers
|
|
||||||
✅ **User Control** - Manual selection when automatic fails
|
|
||||||
✅ **Visibility** - Clear dashboard shows unlinked payments
|
|
||||||
✅ **Audit Trail** - Tracks who linked payments and when
|
|
||||||
✅ **Flexibility** - Skip for later review
|
|
||||||
✅ **Automation** - Still tries automatic detection first
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Implementation Status: COMPLETE ✅**
|
|
||||||
|
|
||||||
Ready for deployment and testing!
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Init for at_master_order
|
|
||||||
|
|
||||||
from . import models
|
|
||||||
from . import wizard
|
|
||||||
from . import controllers
|
|
||||||
from . import services
|
|
||||||
from . import reports
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "Vera OMS (Sourcing & Shipping)",
|
|
||||||
"summary": "One-stop module for sourcing, shipping, and financial tracking with advanced dashboard.",
|
|
||||||
"version": "18.0.10.0",
|
|
||||||
"author": "AccountTechs",
|
|
||||||
"license": "LGPL-3",
|
|
||||||
"category": "Operations",
|
|
||||||
"depends": [
|
|
||||||
"base",
|
|
||||||
"web",
|
|
||||||
"mail",
|
|
||||||
"product",
|
|
||||||
"sale_management",
|
|
||||||
"purchase",
|
|
||||||
"sale_purchase",
|
|
||||||
"stock",
|
|
||||||
"account",
|
|
||||||
"analytic",
|
|
||||||
"hr",
|
|
||||||
"project",
|
|
||||||
],
|
|
||||||
"data": [
|
|
||||||
"security/security.xml",
|
|
||||||
"security/ir.model.access.csv",
|
|
||||||
"data/ir_sequence_data.xml",
|
|
||||||
"data/master_order_stage_data.xml",
|
|
||||||
"data/order_unit_data.xml",
|
|
||||||
"data/master_order_checklist_data.xml",
|
|
||||||
# Wizards first (actions referenced by views)
|
|
||||||
"wizard/receive_products_wizard_views.xml",
|
|
||||||
"wizard/create_sales_order_wizard_views.xml",
|
|
||||||
"wizard/create_invoice_wizard_views.xml",
|
|
||||||
"wizard/add_custom_column_wizard_views.xml",
|
|
||||||
"wizard/export_wizard_views.xml",
|
|
||||||
"wizard/expense_wizard_views.xml",
|
|
||||||
"wizard/link_bill_wizard_views.xml",
|
|
||||||
"wizard/update_sent_wizard_views.xml",
|
|
||||||
"wizard/payment_validation_wizard_views.xml",
|
|
||||||
"wizard/set_vendor_wizard_views.xml",
|
|
||||||
"wizard/payment_customer_validation_wizard_views.xml",
|
|
||||||
"wizard/repair_customer_links_wizard_views.xml",
|
|
||||||
# PDF Reports (reusable templates)
|
|
||||||
"reports/report_base_templates.xml",
|
|
||||||
"reports/report_master_order_quotation.xml",
|
|
||||||
"reports/report_master_order_lines.xml",
|
|
||||||
"reports/report_master_order_summary.xml",
|
|
||||||
"reports/report_master_order_complete.xml",
|
|
||||||
"reports/report_master_order_customer.xml",
|
|
||||||
# Combined Invoice moved to vera_business module
|
|
||||||
"reports/report_customer_page.xml",
|
|
||||||
# Views (depend on wizard actions)
|
|
||||||
"views/master_order_views.xml",
|
|
||||||
"views/bill_tracking_views.xml",
|
|
||||||
"views/invoice_tracking_views.xml",
|
|
||||||
"views/inventory_tracking_views.xml",
|
|
||||||
"views/customer_page_views.xml",
|
|
||||||
"views/portal_templates.xml",
|
|
||||||
"views/report_invoice_document_inherit.xml",
|
|
||||||
"views/master_order_payment_views.xml",
|
|
||||||
"views/expense_box_views.xml",
|
|
||||||
"views/invoice_expense_views.xml",
|
|
||||||
"views/invoice_excel_views.xml",
|
|
||||||
"views/purchase_order_views.xml",
|
|
||||||
"views/payment_views.xml", # Transaction Photo for Odoo Standard Payments
|
|
||||||
"views/master_order_config_views.xml",
|
|
||||||
"views/res_config_settings_views.xml",
|
|
||||||
"views/user_preferences_views.xml",
|
|
||||||
"views/order_unit_views.xml",
|
|
||||||
"views/product_views.xml",
|
|
||||||
"views/order_line_field_config_views.xml",
|
|
||||||
# Removed: Client Cash Flow Views (module removed)
|
|
||||||
# "views/vera_client_cashflow_views.xml",
|
|
||||||
# "views/vera_client_transaction_views.xml",
|
|
||||||
# "views/vera_client_supplier_views.xml",
|
|
||||||
"views/shipment_views.xml",
|
|
||||||
"views/vera_advanced_supplier_views.xml",
|
|
||||||
"views/vera_supplier_client_views.xml",
|
|
||||||
"views/project_task_views.xml",
|
|
||||||
"views/master_order_excel_views.xml",
|
|
||||||
"views/account_move_commission_views.xml",
|
|
||||||
"views/mobile_oms_views.xml",
|
|
||||||
"views/oms_desktop_views.xml",
|
|
||||||
"views/menu.xml",
|
|
||||||
"views/theme_settings_views.xml",
|
|
||||||
],
|
|
||||||
"assets": {
|
|
||||||
"web.assets_backend": [
|
|
||||||
# Breadcrumb Navigation Enhancement (RTL support)
|
|
||||||
"at_master_order/static/src/scss/breadcrumb_navigation.scss",
|
|
||||||
# Theme files REMOVED — vera_theme handles all global styling
|
|
||||||
# Theme manager removed - using global vera_theme service
|
|
||||||
# "at_master_order/static/src/js/master_order_theme_manager.js", # Removed - conflicts with vera_theme
|
|
||||||
# Date Picker Override (numeric months)
|
|
||||||
"at_master_order/static/src/js/date_picker_override.js",
|
|
||||||
# Chart.js library (local)
|
|
||||||
"at_master_order/static/src/js/chart.umd.js",
|
|
||||||
"at_master_order/static/src/js/master_order_form.js",
|
|
||||||
# Delete Confirmation Dialog for Order Lines
|
|
||||||
"at_master_order/static/src/js/delete_confirmation.js",
|
|
||||||
"at_master_order/static/src/js/master_order_list_reorder.js",
|
|
||||||
# Order Line Column Configuration (per-record column visibility/order)
|
|
||||||
"at_master_order/static/src/js/order_line_column_config.js",
|
|
||||||
"at_master_order/static/src/scss/order_line_column_config.scss",
|
|
||||||
"at_master_order/static/src/css/master_order_form.css",
|
|
||||||
"at_master_order/static/src/scss/master_order_list_reorder.scss",
|
|
||||||
# Image Paste Widget (Excel-like paste for order lines)
|
|
||||||
"at_master_order/static/src/js/image_paste_widget.js",
|
|
||||||
"at_master_order/static/src/xml/image_paste_templates.xml",
|
|
||||||
"at_master_order/static/src/scss/image_paste.scss",
|
|
||||||
# Bulk Paste from Excel (Column Mapping)
|
|
||||||
"at_master_order/static/src/js/bulk_paste/excel_parser.js",
|
|
||||||
"at_master_order/static/src/js/bulk_paste/column_mapper.js",
|
|
||||||
"at_master_order/static/src/js/bulk_paste/bulk_paste_modal.js",
|
|
||||||
"at_master_order/static/src/js/bulk_paste/bulk_paste_action.js",
|
|
||||||
"at_master_order/static/src/xml/bulk_paste_templates.xml",
|
|
||||||
"at_master_order/static/src/scss/bulk_paste.scss",
|
|
||||||
# Inventory Tracking OWL Dashboard
|
|
||||||
"at_master_order/static/src/js/inventory_tracking_dashboard.js",
|
|
||||||
"at_master_order/static/src/xml/inventory_tracking_templates.xml",
|
|
||||||
"at_master_order/static/src/scss/inventory_tracking_dashboard.scss",
|
|
||||||
# Invoice Tracking OWL Dashboard
|
|
||||||
"at_master_order/static/src/js/invoice_tracking_dashboard.js",
|
|
||||||
"at_master_order/static/src/xml/invoice_tracking_templates.xml",
|
|
||||||
"at_master_order/static/src/scss/invoice_tracking_dashboard.scss",
|
|
||||||
# Bill Tracking OWL Dashboard
|
|
||||||
"at_master_order/static/src/js/bill_tracking_dashboard.js",
|
|
||||||
"at_master_order/static/src/xml/bill_tracking_templates.xml",
|
|
||||||
"at_master_order/static/src/scss/bill_tracking_dashboard.scss",
|
|
||||||
# Customer Page OWL Dashboard
|
|
||||||
"at_master_order/static/src/js/customer_page_dashboard.js",
|
|
||||||
"at_master_order/static/src/xml/customer_page_dashboard.xml",
|
|
||||||
"at_master_order/static/src/scss/customer_page_dashboard.scss",
|
|
||||||
"at_master_order/static/src/js/master_order_dashboard.js",
|
|
||||||
"at_master_order/static/src/xml/master_order_templates.xml",
|
|
||||||
"at_master_order/static/src/scss/master_order_dashboard.scss",
|
|
||||||
# Master Order Kanban - Vera Design
|
|
||||||
"at_master_order/static/src/scss/master_order_kanban.scss",
|
|
||||||
# Form Theme: Master Order form (section cards, order lines, summary)
|
|
||||||
"at_master_order/static/src/scss/master_order_theme_variables.scss",
|
|
||||||
"at_master_order/static/src/scss/master_order_form_theme.scss",
|
|
||||||
# Form Theme: Invoice/Bill form (vera-section-card, shipment tracking)
|
|
||||||
"at_master_order/static/src/scss/account_move_form_theme.scss",
|
|
||||||
# RTL overrides for Arabic (dir="rtl")
|
|
||||||
"at_master_order/static/src/scss/rtl_overrides.scss",
|
|
||||||
# Removed: Client Cash Flow Kanban (module removed)
|
|
||||||
# "at_master_order/static/src/scss/client_cashflow_kanban.scss",
|
|
||||||
# Combined Ledger extension moved to vera_oms_bridge addon
|
|
||||||
# OMS Mobile — mobile-first client action
|
|
||||||
"at_master_order/static/src/scss/mobile_oms_app.scss",
|
|
||||||
"at_master_order/static/src/js/mobile_oms_app.js",
|
|
||||||
"at_master_order/static/src/xml/mobile_oms_templates.xml",
|
|
||||||
# OMS shared utilities (imported by both mobile + desktop)
|
|
||||||
"at_master_order/static/src/js/oms_shared.js",
|
|
||||||
# OMS Desktop — desktop-first standalone client action
|
|
||||||
"at_master_order/static/src/scss/oms_desktop_app.scss",
|
|
||||||
"at_master_order/static/src/js/oms_desktop_app.js",
|
|
||||||
"at_master_order/static/src/xml/oms_desktop_templates.xml",
|
|
||||||
# Autocomplete dropdown fix for modals/wizards (load LAST)
|
|
||||||
"at_master_order/static/src/css/dropdown_fix.css",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"application": True,
|
|
||||||
"installable": True,
|
|
||||||
"auto_install": False,
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user