364 lines
9.3 KiB
Markdown
364 lines
9.3 KiB
Markdown
|
|
# Kassekladde (Supplier Invoices) - BMC Hub
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
BMC Hub's kassekladde module enables management of supplier invoices (incoming invoices that the company must pay) with full integration to e-conomic accounting system via the journals/vouchers API.
|
||
|
|
|
||
|
|
## Features
|
||
|
|
|
||
|
|
✅ **Complete CRUD Operations**
|
||
|
|
- Create, view, update, and delete supplier invoices
|
||
|
|
- Multi-line invoice support with VAT breakdown
|
||
|
|
- Vendor linking and automatic creation in e-conomic
|
||
|
|
|
||
|
|
✅ **Approval Workflow**
|
||
|
|
- Pending → Approved → Sent to e-conomic → Paid
|
||
|
|
- Approval tracking with user and timestamp
|
||
|
|
|
||
|
|
✅ **e-conomic Integration**
|
||
|
|
- Automatic supplier matching and creation
|
||
|
|
- Journal/voucher posting to kassekladde
|
||
|
|
- PDF attachment upload to vouchers
|
||
|
|
- Configurable journal number and default accounts
|
||
|
|
|
||
|
|
✅ **VAT Handling**
|
||
|
|
- Support for multiple VAT codes (I25, I0, IY25, etc.)
|
||
|
|
- Automatic VAT calculation per line
|
||
|
|
- VAT breakdown in e-conomic entries
|
||
|
|
|
||
|
|
✅ **Nordic Top UI**
|
||
|
|
- Modern, clean design
|
||
|
|
- Real-time statistics dashboard
|
||
|
|
- Filter and search functionality
|
||
|
|
- Responsive mobile support
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
### Database Schema
|
||
|
|
|
||
|
|
**Main Tables:**
|
||
|
|
- `supplier_invoices` - Invoice headers with e-conomic tracking
|
||
|
|
- `supplier_invoice_lines` - Line items with VAT and account details
|
||
|
|
- `supplier_invoice_settings` - System configuration
|
||
|
|
- `vendors` - Supplier information with e-conomic IDs
|
||
|
|
|
||
|
|
**Views:**
|
||
|
|
- `overdue_supplier_invoices` - All overdue unpaid invoices
|
||
|
|
- `pending_economic_sync` - Approved invoices ready for e-conomic
|
||
|
|
|
||
|
|
### Backend Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
app/
|
||
|
|
billing/
|
||
|
|
backend/
|
||
|
|
supplier_invoices.py # FastAPI router with endpoints
|
||
|
|
frontend/
|
||
|
|
supplier_invoices.html # Nordic Top UI
|
||
|
|
views.py # Frontend routes
|
||
|
|
services/
|
||
|
|
economic_service.py # e-conomic API integration
|
||
|
|
```
|
||
|
|
|
||
|
|
### API Endpoints
|
||
|
|
|
||
|
|
**CRUD Operations:**
|
||
|
|
- `GET /api/v1/supplier-invoices` - List invoices with filters
|
||
|
|
- `GET /api/v1/supplier-invoices/{id}` - Get invoice details
|
||
|
|
- `POST /api/v1/supplier-invoices` - Create new invoice
|
||
|
|
- `PUT /api/v1/supplier-invoices/{id}` - Update invoice
|
||
|
|
- `DELETE /api/v1/supplier-invoices/{id}` - Delete invoice
|
||
|
|
|
||
|
|
**Workflow Actions:**
|
||
|
|
- `POST /api/v1/supplier-invoices/{id}/approve` - Approve invoice
|
||
|
|
- `POST /api/v1/supplier-invoices/{id}/send-to-economic` - Send to e-conomic
|
||
|
|
|
||
|
|
**Statistics:**
|
||
|
|
- `GET /api/v1/supplier-invoices/stats/overview` - Payment overview
|
||
|
|
- `GET /api/v1/supplier-invoices/stats/by-vendor` - Stats by vendor
|
||
|
|
|
||
|
|
**e-conomic Integration:**
|
||
|
|
- `GET /api/v1/supplier-invoices/economic/journals` - Available kassekladder
|
||
|
|
|
||
|
|
## Installation & Setup
|
||
|
|
|
||
|
|
### 1. Database Migration
|
||
|
|
|
||
|
|
Run the migration to create tables:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Execute migration SQL
|
||
|
|
psql -U bmc_hub -d bmc_hub < migrations/008_supplier_invoices.sql
|
||
|
|
```
|
||
|
|
|
||
|
|
Or via Docker:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docker-compose exec postgres psql -U bmc_hub -d bmc_hub < /app/migrations/008_supplier_invoices.sql
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Configure e-conomic Credentials
|
||
|
|
|
||
|
|
Add to `.env` file:
|
||
|
|
|
||
|
|
```env
|
||
|
|
# e-conomic Integration
|
||
|
|
ECONOMIC_API_URL=https://restapi.e-conomic.com
|
||
|
|
ECONOMIC_APP_SECRET_TOKEN=your_app_secret_token
|
||
|
|
ECONOMIC_AGREEMENT_GRANT_TOKEN=your_agreement_grant_token
|
||
|
|
|
||
|
|
# Safety switches (ALWAYS start with these enabled)
|
||
|
|
ECONOMIC_READ_ONLY=true
|
||
|
|
ECONOMIC_DRY_RUN=true
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Configure Default Settings
|
||
|
|
|
||
|
|
The default journal number and accounts can be configured via database:
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- Update default kassekladde number
|
||
|
|
UPDATE supplier_invoice_settings
|
||
|
|
SET setting_value = '1'
|
||
|
|
WHERE setting_key = 'economic_default_journal';
|
||
|
|
|
||
|
|
-- Update default expense account
|
||
|
|
UPDATE supplier_invoice_settings
|
||
|
|
SET setting_value = '5810'
|
||
|
|
WHERE setting_key = 'economic_default_contra_account';
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Restart Application
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docker-compose restart api
|
||
|
|
```
|
||
|
|
|
||
|
|
## Usage Guide
|
||
|
|
|
||
|
|
### Creating a Supplier Invoice
|
||
|
|
|
||
|
|
1. Navigate to `/billing/supplier-invoices`
|
||
|
|
2. Click "Ny Faktura" button
|
||
|
|
3. Fill in required fields:
|
||
|
|
- Invoice number (from supplier)
|
||
|
|
- Vendor (select from dropdown)
|
||
|
|
- Invoice date
|
||
|
|
- Total amount (incl. VAT)
|
||
|
|
4. Add line items with:
|
||
|
|
- Description
|
||
|
|
- Quantity & price
|
||
|
|
- VAT code (25%, 0%, reverse charge, etc.)
|
||
|
|
5. Click "Gem" to save
|
||
|
|
|
||
|
|
### Approval Workflow
|
||
|
|
|
||
|
|
**Status Flow:**
|
||
|
|
```
|
||
|
|
pending → approved → sent_to_economic → paid
|
||
|
|
```
|
||
|
|
|
||
|
|
**Steps:**
|
||
|
|
1. Invoice created → Status: `pending`
|
||
|
|
2. Review and approve → Status: `approved`
|
||
|
|
3. Send to e-conomic → Status: `sent_to_economic` (voucher created)
|
||
|
|
4. Mark as paid → Status: `paid`
|
||
|
|
|
||
|
|
### Sending to e-conomic
|
||
|
|
|
||
|
|
**Prerequisites:**
|
||
|
|
- Invoice must be `approved`
|
||
|
|
- Vendor must exist (auto-created if needed)
|
||
|
|
- At least one line item
|
||
|
|
|
||
|
|
**Process:**
|
||
|
|
1. Click "Send til e-conomic" button
|
||
|
|
2. System will:
|
||
|
|
- Check/create vendor in e-conomic
|
||
|
|
- Build VAT breakdown from lines
|
||
|
|
- Create journal voucher entry
|
||
|
|
- Upload PDF attachment (if available)
|
||
|
|
- Update invoice with voucher number
|
||
|
|
|
||
|
|
**Result:**
|
||
|
|
- Voucher created in e-conomic kassekladde
|
||
|
|
- Invoice status → `sent_to_economic`
|
||
|
|
- Voucher number stored for reference
|
||
|
|
|
||
|
|
## e-conomic Integration Details
|
||
|
|
|
||
|
|
### Safety Modes
|
||
|
|
|
||
|
|
**READ_ONLY Mode** (default: `true`)
|
||
|
|
- Blocks ALL write operations to e-conomic
|
||
|
|
- Only GET requests allowed
|
||
|
|
- Use for testing API connection
|
||
|
|
|
||
|
|
**DRY_RUN Mode** (default: `true`)
|
||
|
|
- Logs all operations but doesn't send to e-conomic
|
||
|
|
- Full payload preview in logs
|
||
|
|
- Safe for development/testing
|
||
|
|
|
||
|
|
**Production Mode** (both `false`)
|
||
|
|
- Actually sends data to e-conomic
|
||
|
|
- ⚠️ **Use with caution!**
|
||
|
|
- Always test with dry-run first
|
||
|
|
|
||
|
|
### Journal Voucher Structure
|
||
|
|
|
||
|
|
e-conomic vouchers use this format:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"accountingYear": {"year": "2025"},
|
||
|
|
"journal": {"journalNumber": 1},
|
||
|
|
"entries": {
|
||
|
|
"supplierInvoices": [
|
||
|
|
{
|
||
|
|
"supplier": {"supplierNumber": 123},
|
||
|
|
"amount": 1250.00,
|
||
|
|
"contraAccount": {"accountNumber": 5810},
|
||
|
|
"currency": {"code": "DKK"},
|
||
|
|
"date": "2025-12-06",
|
||
|
|
"dueDate": "2026-01-05",
|
||
|
|
"supplierInvoiceNumber": "INV-12345",
|
||
|
|
"text": "Invoice description",
|
||
|
|
"contraVatAccount": {"vatCode": "I25"},
|
||
|
|
"contraVatAmount": 250.00
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### VAT Code Mapping
|
||
|
|
|
||
|
|
| VAT Code | Description | Rate | Use Case |
|
||
|
|
|----------|-------------|------|----------|
|
||
|
|
| `I25` | Indenlandsk købsmoms 25% | 25% | Standard Danish purchases |
|
||
|
|
| `I0` | Momsfri køb | 0% | VAT exempt |
|
||
|
|
| `IY25` | Omvendt betalingspligt 25% | 25% | Reverse charge |
|
||
|
|
| `IYEU` | Omvendt EU | 0% | EU reverse charge |
|
||
|
|
| `IVEU` | Erhvervelse EU | 25% | EU acquisition |
|
||
|
|
|
||
|
|
### Account Number Mapping
|
||
|
|
|
||
|
|
Default expense accounts (can be customized per line):
|
||
|
|
|
||
|
|
- `5810` - Drift og materialer (default)
|
||
|
|
- `5820` - IT og software
|
||
|
|
- `5830` - Telefoni og internet
|
||
|
|
- `5840` - Kontorartikler
|
||
|
|
- `6000` - Løn og honorarer
|
||
|
|
|
||
|
|
## Development Guide
|
||
|
|
|
||
|
|
### Adding New Features
|
||
|
|
|
||
|
|
**1. Backend Endpoint:**
|
||
|
|
|
||
|
|
```python
|
||
|
|
# app/billing/backend/supplier_invoices.py
|
||
|
|
@router.post("/supplier-invoices/{invoice_id}/custom-action")
|
||
|
|
async def custom_action(invoice_id: int):
|
||
|
|
# Your logic here
|
||
|
|
return {"success": True}
|
||
|
|
```
|
||
|
|
|
||
|
|
**2. Frontend Integration:**
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// supplier_invoices.html
|
||
|
|
async function customAction(invoiceId) {
|
||
|
|
const response = await fetch(`/api/v1/supplier-invoices/${invoiceId}/custom-action`, {
|
||
|
|
method: 'POST'
|
||
|
|
});
|
||
|
|
// Handle response
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Testing e-conomic Integration
|
||
|
|
|
||
|
|
**1. Test Connection:**
|
||
|
|
|
||
|
|
```python
|
||
|
|
from app.services.economic_service import get_economic_service
|
||
|
|
|
||
|
|
economic = get_economic_service()
|
||
|
|
result = await economic.test_connection()
|
||
|
|
# Should return True if credentials are valid
|
||
|
|
```
|
||
|
|
|
||
|
|
**2. Test Dry-Run Mode:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# In .env
|
||
|
|
ECONOMIC_READ_ONLY=false
|
||
|
|
ECONOMIC_DRY_RUN=true
|
||
|
|
```
|
||
|
|
|
||
|
|
Then create and approve an invoice, send to e-conomic. Check logs for full payload without actually posting.
|
||
|
|
|
||
|
|
**3. Production Test:**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# WARNING: This will create real data in e-conomic!
|
||
|
|
ECONOMIC_READ_ONLY=false
|
||
|
|
ECONOMIC_DRY_RUN=false
|
||
|
|
```
|
||
|
|
|
||
|
|
Start with a small test invoice to verify everything works.
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Issue: "No journals found"
|
||
|
|
|
||
|
|
**Solution:** Check e-conomic credentials and ensure user has access to journals/kassekladder.
|
||
|
|
|
||
|
|
### Issue: "Supplier not found in e-conomic"
|
||
|
|
|
||
|
|
**Solution:** System will auto-create supplier if `ECONOMIC_DRY_RUN=false`. Verify vendor name is correct.
|
||
|
|
|
||
|
|
### Issue: "VAT validation failed"
|
||
|
|
|
||
|
|
**Solution:** Ensure VAT codes match e-conomic settings. Check `vat_code` in line items (I25, I0, etc.).
|
||
|
|
|
||
|
|
### Issue: "Voucher creation failed"
|
||
|
|
|
||
|
|
**Solution:**
|
||
|
|
1. Check e-conomic API logs in application logs
|
||
|
|
2. Verify journal number exists in e-conomic
|
||
|
|
3. Ensure all required fields are present (supplier, amount, date)
|
||
|
|
4. Check contra account number is valid
|
||
|
|
|
||
|
|
## Integration with OmniSync
|
||
|
|
|
||
|
|
This module is based on OmniSync's proven kassekladde implementation with the following enhancements:
|
||
|
|
|
||
|
|
- ✅ PostgreSQL instead of SQLite
|
||
|
|
- ✅ Nordic Top design instead of custom CSS
|
||
|
|
- ✅ Integrated with BMC Hub's vendor system
|
||
|
|
- ✅ Simplified approval workflow
|
||
|
|
- ✅ Better error handling and logging
|
||
|
|
|
||
|
|
## References
|
||
|
|
|
||
|
|
- **e-conomic API Docs:** https://restdocs.e-conomic.com/
|
||
|
|
- **Journals API:** https://restdocs.e-conomic.com/#journals
|
||
|
|
- **Vouchers API:** https://restdocs.e-conomic.com/#vouchers
|
||
|
|
- **Suppliers API:** https://restdocs.e-conomic.com/#suppliers
|
||
|
|
|
||
|
|
## Support
|
||
|
|
|
||
|
|
For issues or questions:
|
||
|
|
1. Check application logs: `docker-compose logs -f api`
|
||
|
|
2. Review e-conomic API response in logs
|
||
|
|
3. Test with DRY_RUN mode first
|
||
|
|
4. Contact system administrator
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Last Updated:** December 6, 2025
|
||
|
|
**Version:** 1.0.0
|
||
|
|
**Maintained by:** BMC Networks Development Team
|