feat: Add data-entry-app for fiscal receipts with approval workflow

New application for entering fiscal receipts (bonuri fiscale) with:

Backend (FastAPI + SQLModel + Alembic):
- Receipt, ReceiptAttachment, AccountingEntry models
- CRUD operations with async SQLite database
- Workflow: DRAFT → PENDING_REVIEW → APPROVED/REJECTED
- Auto-generation of accounting entries with VAT calculation
- File upload support (images, PDFs)
- Predefined expense types (Fuel, Materials, Office, etc.)
- Nomenclature service for partners, accounts, cash registers

Frontend (Vue.js 3 + PrimeVue + Pinia):
- ReceiptsListView with filters and stats
- ReceiptCreateView with image upload
- ReceiptDetailView with accounting entries
- ReceiptApprovalView for accountant approval

Documentation:
- REQUIREMENTS.md with functional specifications
- ARCHITECTURE.md with technical decisions
- CLAUDE.md for AI assistant guidance

Phase 1 MVP uses SQLite, prepared for Oracle integration in Phase 2.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-11 17:30:51 +02:00
parent 5823cedb94
commit 21c12ddb0f
45 changed files with 7524 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
// Constants for the application
export const EXPENSE_TYPES = {
FUEL: 'Combustibil',
MATERIALS: 'Materiale consumabile',
OFFICE: 'Rechizite birou',
PHONE: 'Telefonie / Internet',
PARKING: 'Parcare',
FOOD: 'Alimentatie',
TRANSPORT: 'Transport',
OTHER: 'Altele',
}
export const RECEIPT_TYPES = {
bon_fiscal: 'Bon Fiscal',
chitanta: 'Chitanta',
}
export const RECEIPT_DIRECTIONS = {
cheltuiala: 'Cheltuiala',
incasare: 'Incasare',
}
export const RECEIPT_STATUSES = {
draft: { label: 'Ciorna', class: 'status-draft', severity: 'info' },
pending_review: { label: 'In asteptare', class: 'status-pending', severity: 'warning' },
approved: { label: 'Aprobat', class: 'status-approved', severity: 'success' },
rejected: { label: 'Respins', class: 'status-rejected', severity: 'danger' },
synced: { label: 'Sincronizat', class: 'status-synced', severity: 'success' },
}
export const formatDate = (dateStr) => {
if (!dateStr) return '-'
return new Date(dateStr).toLocaleDateString('ro-RO')
}
export const formatDateTime = (dateStr) => {
if (!dateStr) return '-'
return new Date(dateStr).toLocaleString('ro-RO')
}
export const formatAmount = (amount, currency = 'RON') => {
return new Intl.NumberFormat('ro-RO', {
style: 'currency',
currency,
}).format(amount)
}