Files
roa2web-service-auto/shared/frontend/stores/accountingPeriod.js
Marius Mutu 1a6e9b17d2 feat: Add shared components, refactor stores, improve data-entry workflow
Shared Components:
- Add CompanySelector.vue and PeriodSelector.vue components
- Add AppHeader.vue and SlideMenu.vue layout components
- Add shared stores factories (companies.js, accountingPeriod.js)
- Add shared routes factories (companies.py, calendar.py)
- Add shared models (company.py, calendar.py)
- Add shared layout styles (header.css, navigation.css)

Data Entry App:
- Update CLAUDE.md with prod/test server documentation
- Improve nomenclature sync service with better error handling
- Update receipts router and CRUD operations
- Add company/period stores using shared factories
- Update App.vue layout with shared components
- Fix OCRUploadZone file handling

Reports App:
- Refactor stores to use shared factories
- Update App.vue to use shared layout components

Infrastructure:
- Replace start-data-entry.sh with separate dev/test scripts
- Add .claude/rules for authentication, backend patterns, etc.
- Add implementation plan for OCR receipt improvements
- Clean up old documentation files

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 15:00:45 +02:00

159 lines
4.5 KiB
JavaScript

/**
* Shared Accounting Period Store Factory
*
* Creates a Pinia store for accounting period selection that can be used by any ROA2WEB application.
* Each app passes its own apiService and store references.
*
* Usage:
* import { createAccountingPeriodStore } from '@shared/frontend/stores/accountingPeriod';
* import { apiService } from '../services/api';
* import { useAuthStore } from './auth';
* import { useCompanyStore } from './companies';
* export const useAccountingPeriodStore = createAccountingPeriodStore(apiService, useAuthStore, useCompanyStore);
*/
import { defineStore } from "pinia";
import { ref, computed } from "vue";
/**
* Factory function to create an accounting period store
* @param {Object} apiService - Axios instance configured for the app's API
* @param {Function} useAuthStore - Reference to the auth store function
* @param {Function} useCompanyStore - Reference to the company store function
* @returns {Function} Pinia store definition
*/
export function createAccountingPeriodStore(apiService, useAuthStore, useCompanyStore) {
return defineStore("accountingPeriod", () => {
// State
const periods = ref([]);
const selectedPeriod = ref(null);
const isLoading = ref(false);
const error = ref(null);
// Getters
const hasPeriods = computed(() => periods.value.length > 0);
const currentPeriod = computed(() => selectedPeriod.value);
// Computed date range for current period (first/last day of month)
const dateRange = computed(() => {
if (!selectedPeriod.value) return { dateFrom: null, dateTo: null };
const { an, luna } = selectedPeriod.value;
const firstDay = new Date(an, luna - 1, 1);
const lastDay = new Date(an, luna, 0);
return {
dateFrom: firstDay,
dateTo: lastDay,
};
});
// localStorage helpers
const getStorageKey = () => {
const authStore = useAuthStore();
const companyStore = useCompanyStore();
const username = authStore.user?.username;
const companyId = companyStore.selectedCompany?.id_firma;
if (!username || !companyId) return null;
return `selected_period_${username}_${companyId}`;
};
const initializeSelectedPeriod = () => {
const key = getStorageKey();
if (!key) return null;
const saved = localStorage.getItem(key);
if (saved) {
try {
return JSON.parse(saved);
} catch (e) {
localStorage.removeItem(key);
}
}
return null;
};
const persistSelectedPeriod = (period) => {
const key = getStorageKey();
if (key && period) {
localStorage.setItem(key, JSON.stringify(period));
}
};
// Actions
const loadPeriods = async (companyId) => {
if (!companyId) return { success: false };
isLoading.value = true;
error.value = null;
try {
const response = await apiService.get("/calendar/periods", {
params: { company: companyId },
});
periods.value = response.data.periods || [];
// Try to restore saved period or use most recent
const saved = initializeSelectedPeriod();
if (saved) {
const exists = periods.value.find(
(p) => p.an === saved.an && p.luna === saved.luna
);
if (exists) {
selectedPeriod.value = exists;
} else if (response.data.current_period) {
setSelectedPeriod(response.data.current_period);
}
} else if (response.data.current_period) {
setSelectedPeriod(response.data.current_period);
}
return { success: true };
} catch (err) {
error.value = err.response?.data?.detail || "Failed to load periods";
return { success: false, error: error.value };
} finally {
isLoading.value = false;
}
};
const setSelectedPeriod = (period) => {
selectedPeriod.value = period;
persistSelectedPeriod(period);
};
const resetToLatest = () => {
if (periods.value.length > 0) {
setSelectedPeriod(periods.value[0]);
}
};
const reset = () => {
periods.value = [];
selectedPeriod.value = null;
isLoading.value = false;
error.value = null;
};
return {
// State
periods,
selectedPeriod,
isLoading,
error,
// Getters
hasPeriods,
currentPeriod,
dateRange,
// Actions
loadPeriods,
setSelectedPeriod,
resetToLatest,
reset,
};
});
}