diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 3399c2f..29beede 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -52,6 +52,30 @@ XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve
**Lavoro completato nell'ultima sessione:**
+- **NUOVA FEATURE: Gestione Inventario (Frontend)** - COMPLETATO
+ - **Obiettivo:** Interfaccia utente per la gestione completa degli inventari fisici
+ - **Frontend implementato:**
+ - `InventoryListPage.tsx` - Lista inventari con stato, filtri e indicatori di progresso
+ - `InventoryFormPage.tsx` - Form per creazione e modifica testata inventario (con gestione stati)
+ - `InventoryCountPage.tsx` - Pagina di conteggio con griglia editabile, calcolo differenze live
+ - Aggiornati `routes.tsx` e `pages/index.ts` per includere le nuove rotte
+ - **Funzionalità:**
+ - Creazione inventari (Completo, Parziale per categoria/magazzino)
+ - Workflow stati: Bozza -> In Corso -> Completato -> Confermato
+ - Avvio inventario: generazione automatica righe basata su giacenza teorica
+ - Conteggio: inserimento quantità rilevate, evidenziazione differenze
+ - Conferma: generazione automatica movimenti di rettifica (positivo/negativo)
+ - **Integrazione:**
+ - Utilizza `inventoryService` per comunicare con `InventoryController`
+ - Gestione date con `dayjs`
+ - UI coerente con Material-UI e DataGrid
+
+- **FIX: Tasto Inventario in Dashboard Magazzino** - RISOLTO
+ - **Problema:** Il tasto "Inventario" nelle azioni rapide portava a una pagina 404 (`/warehouse/inventories/new`)
+ - **Causa:** Errore nel hook `useWarehouseNavigation` che usava il plurale `inventories` invece del singolare `inventory` definito nelle rotte
+ - **Soluzione:** Corretti i percorsi in `useWarehouseNavigation.ts` per corrispondere a `routes.tsx`
+ - **File modificati:** `frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts`
+
- **FIX: Campo Codice Readonly e Codice Alternativo** - COMPLETATO
- **Obiettivo:** Il campo "Codice" deve essere sempre auto-generato (non modificabile), aggiungere campo "Codice Alternativo" opzionale
- **Backend modificato:**
@@ -444,7 +468,7 @@ XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve
1. [x] **Implementare modulo Magazzino (warehouse)** - COMPLETATO (backend)
- Backend: Entities, Service, Controllers, API completi
- Manca: Frontend (pagine React per gestione articoli, movimenti, giacenze)
-2. [ ] **Frontend modulo Magazzino** - Pagine React per warehouse
+2. [x] **Frontend modulo Magazzino** - Pagine React per warehouse (Articoli, Movimenti, Giacenze, Inventario)
3. [ ] **Implementare modulo Acquisti (purchases)** - Dipende da Magazzino
4. [ ] **Implementare modulo Vendite (sales)** - Dipende da Magazzino
5. [ ] **Implementare modulo Produzione (production)** - Dipende da Magazzino
diff --git a/frontend/src/modules/warehouse/components/WarehouseLayout.tsx b/frontend/src/modules/warehouse/components/WarehouseLayout.tsx
new file mode 100644
index 0000000..edea8a2
--- /dev/null
+++ b/frontend/src/modules/warehouse/components/WarehouseLayout.tsx
@@ -0,0 +1,101 @@
+import { useState, useEffect } from "react";
+import { Outlet, useLocation, useNavigate } from "react-router-dom";
+import { Box, Tabs, Tab, Paper, Typography, Breadcrumbs, Link } from "@mui/material";
+import {
+ Dashboard as DashboardIcon,
+ Inventory as ArticleIcon,
+ Place as LocationIcon,
+ SwapHoriz as MovementIcon,
+ Assessment as StockIcon,
+ FactCheck as InventoryIcon,
+} from "@mui/icons-material";
+
+const navItems = [
+ { label: "Dashboard", path: "/warehouse", icon: },
+ { label: "Articoli", path: "/warehouse/articles", icon: },
+ { label: "Magazzini", path: "/warehouse/locations", icon: },
+ { label: "Movimenti", path: "/warehouse/movements", icon: },
+ { label: "Giacenze", path: "/warehouse/stock", icon: },
+ { label: "Inventario", path: "/warehouse/inventory", icon: },
+];
+
+export default function WarehouseLayout() {
+ const location = useLocation();
+ const navigate = useNavigate();
+ const [value, setValue] = useState(0);
+
+ useEffect(() => {
+ // Find the matching tab based on current path
+ const index = navItems.findIndex((item) => {
+ if (item.path === "/warehouse") {
+ return location.pathname === "/warehouse";
+ }
+ return location.pathname.startsWith(item.path);
+ });
+
+ if (index !== -1) {
+ setValue(index);
+ } else {
+ // If no match (e.g. sub-pages), keep the closest parent or default
+ // Logic could be improved here but keeping it simple for now
+ if (location.pathname.includes("articles")) setValue(1);
+ else if (location.pathname.includes("locations")) setValue(2);
+ else if (location.pathname.includes("movements")) setValue(3);
+ else if (location.pathname.includes("stock")) setValue(4);
+ else if (location.pathname.includes("inventory")) setValue(5);
+ else setValue(0);
+ }
+ }, [location.pathname]);
+
+ const handleChange = (_event: React.SyntheticEvent, newValue: number) => {
+ setValue(newValue);
+ navigate(navItems[newValue].path);
+ };
+
+ return (
+
+ {/* Header & Navigation */}
+
+
+
+ Gestione Magazzino
+
+
+
+ Home
+
+ Magazzino
+ {navItems[value]?.label !== "Dashboard" && (
+ {navItems[value]?.label}
+ )}
+
+
+
+
+ {navItems.map((item, index) => (
+
+ ))}
+
+
+
+ {/* Content Area */}
+
+
+
+
+ );
+}
diff --git a/frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts b/frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts
index 86f5692..28314a2 100644
--- a/frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts
+++ b/frontend/src/modules/warehouse/hooks/useWarehouseNavigation.ts
@@ -104,15 +104,15 @@ export function useWarehouseNavigation() {
// Inventory
const goToInventories = useCallback(() => {
- navigate('/warehouse/inventories');
+ navigate('/warehouse/inventory');
}, [navigate]);
const goToInventory = useCallback((id: number) => {
- navigate(`/warehouse/inventories/${id}`);
+ navigate(`/warehouse/inventory/${id}`);
}, [navigate]);
const goToNewInventory = useCallback(() => {
- navigate('/warehouse/inventories/new');
+ navigate('/warehouse/inventory/new');
}, [navigate]);
// Dashboard
diff --git a/frontend/src/modules/warehouse/pages/InventoryCountPage.tsx b/frontend/src/modules/warehouse/pages/InventoryCountPage.tsx
new file mode 100644
index 0000000..adf4ea5
--- /dev/null
+++ b/frontend/src/modules/warehouse/pages/InventoryCountPage.tsx
@@ -0,0 +1,333 @@
+import { useState } from "react";
+import { useParams, useNavigate } from "react-router-dom";
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import {
+ Box,
+ Typography,
+ Button,
+ Paper,
+ Grid,
+ Chip,
+ Card,
+ CardContent,
+ Alert,
+ CircularProgress,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogContentText,
+ DialogActions,
+} from "@mui/material";
+import {
+ DataGrid,
+ GridColDef,
+ GridRenderCellParams,
+ GridToolbar,
+ GridRowModel,
+} from "@mui/x-data-grid";
+import {
+ PlayArrow as StartIcon,
+ CheckCircle as CompleteIcon,
+ DoneAll as ConfirmIcon,
+ ArrowBack as ArrowBackIcon,
+} from "@mui/icons-material";
+import { inventoryService } from "../services/warehouseService";
+import { InventoryStatus, InventoryCountLineDto } from "../types";
+import dayjs from "dayjs";
+
+export default function InventoryCountPage() {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const queryClient = useQueryClient();
+ const inventoryId = Number(id);
+
+ const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
+
+ const { data: inventory, isLoading } = useQuery({
+ queryKey: ["inventory", inventoryId],
+ queryFn: () => inventoryService.getById(inventoryId),
+ });
+
+ const startMutation = useMutation({
+ mutationFn: () => inventoryService.start(inventoryId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["inventory", inventoryId] });
+ },
+ });
+
+ const completeMutation = useMutation({
+ mutationFn: () => inventoryService.complete(inventoryId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["inventory", inventoryId] });
+ },
+ });
+
+ const confirmMutation = useMutation({
+ mutationFn: () => inventoryService.confirm(inventoryId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["inventory", inventoryId] });
+ setConfirmDialogOpen(false);
+ navigate("/warehouse/inventory");
+ },
+ });
+
+ const updateLineMutation = useMutation({
+ mutationFn: ({
+ lineId,
+ quantity,
+ }: {
+ lineId: number;
+ quantity: number;
+ }) => inventoryService.updateLine(lineId, quantity),
+ onSuccess: () => {
+ // Optimistic update or invalidate
+ queryClient.invalidateQueries({ queryKey: ["inventory", inventoryId] });
+ },
+ });
+
+ const handleProcessRowUpdate = async (newRow: GridRowModel) => {
+ const line = newRow as InventoryCountLineDto;
+ if (line.countedQuantity !== undefined && line.countedQuantity !== null) {
+ await updateLineMutation.mutateAsync({
+ lineId: line.id,
+ quantity: Number(line.countedQuantity),
+ });
+ }
+ return newRow;
+ };
+
+ if (isLoading || !inventory) {
+ return (
+
+
+
+ );
+ }
+
+ const isEditable = inventory.status === InventoryStatus.InProgress;
+
+ const columns: GridColDef[] = [
+ { field: "articleCode", headerName: "Codice Articolo", width: 150 },
+ {
+ field: "articleDescription",
+ headerName: "Descrizione",
+ flex: 1,
+ minWidth: 200,
+ },
+ { field: "batchNumber", headerName: "Lotto", width: 120 },
+ { field: "locationCode", headerName: "Ubicazione", width: 120 },
+ {
+ field: "theoreticalQuantity",
+ headerName: "Qta Teorica",
+ width: 120,
+ type: "number",
+ valueFormatter: (value) => (value ? Number(value).toFixed(2) : "0"),
+ },
+ {
+ field: "countedQuantity",
+ headerName: "Qta Contata",
+ width: 150,
+ type: "number",
+ editable: isEditable,
+ cellClassName: isEditable ? "editable-cell" : "",
+ valueFormatter: (value) =>
+ value !== undefined && value !== null
+ ? Number(value).toFixed(2)
+ : "-",
+ },
+ {
+ field: "difference",
+ headerName: "Differenza",
+ width: 120,
+ type: "number",
+ valueGetter: (_value, row) => {
+ if (
+ row.countedQuantity === undefined ||
+ row.countedQuantity === null
+ )
+ return null;
+ return row.countedQuantity - row.theoreticalQuantity;
+ },
+ renderCell: (params: GridRenderCellParams) => {
+ if (params.value === null || params.value === undefined) return "-";
+ const val = Number(params.value);
+ return (
+
+ {val > 0 ? "+" : ""}
+ {val.toFixed(2)}
+
+ );
+ },
+ },
+ ];
+
+ return (
+
+
+
+ }
+ onClick={() => navigate("/warehouse/inventory")}
+ >
+ Indietro
+
+
+ Inventario: {inventory.description}
+
+
+
+
+ {inventory.status === InventoryStatus.Draft && (
+ }
+ onClick={() => startMutation.mutate()}
+ >
+ Avvia Inventario
+
+ )}
+ {inventory.status === InventoryStatus.InProgress && (
+ }
+ onClick={() => completeMutation.mutate()}
+ >
+ Completa Conteggio
+
+ )}
+ {inventory.status === InventoryStatus.Completed && (
+ }
+ onClick={() => setConfirmDialogOpen(true)}
+ >
+ Conferma e Rettifica
+
+ )}
+
+
+
+
+
+
+
+
+ Data Inventario
+
+
+ {dayjs(inventory.inventoryDate).format("DD/MM/YYYY")}
+
+
+
+
+
+
+
+
+ Magazzino
+
+ {inventory.warehouseName}
+
+
+
+
+
+
+
+ Righe Totali
+
+ {inventory.lineCount}
+
+
+
+
+
+
+
+ Righe Contate
+
+
+ {inventory.lines.filter((l) => l.countedQuantity !== null).length}
+
+
+
+
+
+
+ {inventory.status === InventoryStatus.Completed && (
+
+ L'inventario è completato. Verifica le differenze prima di confermare.
+ La conferma genererà automaticamente i movimenti di rettifica.
+
+ )}
+
+
+ console.error(error)}
+ slots={{ toolbar: GridToolbar }}
+ slotProps={{
+ toolbar: {
+ showQuickFilter: true,
+ },
+ }}
+ disableRowSelectionOnClick
+ sx={{
+ "& .editable-cell": {
+ backgroundColor: "#f0f8ff",
+ },
+ }}
+ />
+
+
+
+
+ );
+}
diff --git a/frontend/src/modules/warehouse/pages/InventoryFormPage.tsx b/frontend/src/modules/warehouse/pages/InventoryFormPage.tsx
new file mode 100644
index 0000000..47fff61
--- /dev/null
+++ b/frontend/src/modules/warehouse/pages/InventoryFormPage.tsx
@@ -0,0 +1,258 @@
+import { useState, useEffect } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import {
+ Box,
+ Typography,
+ Button,
+ Paper,
+ Grid,
+ TextField,
+ MenuItem,
+ FormControl,
+ InputLabel,
+ Select,
+ Breadcrumbs,
+ Link,
+ CircularProgress,
+} from "@mui/material";
+import { Save as SaveIcon, ArrowBack as ArrowBackIcon } from "@mui/icons-material";
+import {
+ inventoryService,
+ warehouseLocationService,
+ categoryService,
+} from "../services/warehouseService";
+import {
+ CreateInventoryCountDto,
+ InventoryType,
+} from "../types";
+import dayjs from "dayjs";
+
+export default function InventoryFormPage() {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const queryClient = useQueryClient();
+ const isEditing = !!id;
+
+ const [formData, setFormData] = useState({
+ description: "",
+ inventoryDate: dayjs().format("YYYY-MM-DD"),
+ type: InventoryType.Full,
+ notes: "",
+ });
+
+ const { data: warehouses = [] } = useQuery({
+ queryKey: ["warehouse-locations"],
+ queryFn: () => warehouseLocationService.getAll(),
+ });
+
+ const { data: categories = [] } = useQuery({
+ queryKey: ["categories"],
+ queryFn: () => categoryService.getAll(),
+ });
+
+ const { data: inventory, isLoading: isLoadingInventory } = useQuery({
+ queryKey: ["inventory", id],
+ queryFn: () => inventoryService.getById(Number(id)),
+ enabled: isEditing,
+ });
+
+ useEffect(() => {
+ if (inventory) {
+ setFormData({
+ description: inventory.description,
+ inventoryDate: dayjs(inventory.inventoryDate).format("YYYY-MM-DD"),
+ warehouseId: inventory.warehouseId,
+ categoryId: inventory.categoryId,
+ type: inventory.type,
+ notes: inventory.notes,
+ });
+ }
+ }, [inventory]);
+
+ const createMutation = useMutation({
+ mutationFn: (data: CreateInventoryCountDto) => inventoryService.create(data),
+ onSuccess: (data) => {
+ queryClient.invalidateQueries({ queryKey: ["inventory-counts"] });
+ navigate(`/warehouse/inventory/${data.id}/count`);
+ },
+ });
+
+ // TODO: Add update mutation if needed, currently only creation is critical
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (isEditing) {
+ // Implement update logic if needed
+ } else {
+ createMutation.mutate(formData);
+ }
+ };
+
+ if (isEditing && isLoadingInventory) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ {
+ e.preventDefault();
+ navigate("/warehouse/inventory");
+ }}
+ >
+ Inventari
+
+
+ {isEditing ? "Modifica Inventario" : "Nuovo Inventario"}
+
+
+
+
+
+ {isEditing ? `Inventario ${inventory?.code}` : "Nuovo Inventario"}
+
+ }
+ onClick={() => navigate("/warehouse/inventory")}
+ >
+ Indietro
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/modules/warehouse/pages/InventoryListPage.tsx b/frontend/src/modules/warehouse/pages/InventoryListPage.tsx
new file mode 100644
index 0000000..2b8ab57
--- /dev/null
+++ b/frontend/src/modules/warehouse/pages/InventoryListPage.tsx
@@ -0,0 +1,204 @@
+import { useState } from "react";
+import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
+import { useNavigate } from "react-router-dom";
+import {
+ Box,
+ Typography,
+ Button,
+ Paper,
+ Chip,
+ IconButton,
+ Tooltip,
+} from "@mui/material";
+import {
+ DataGrid,
+ GridColDef,
+ GridRenderCellParams,
+ GridToolbar,
+} from "@mui/x-data-grid";
+import {
+ Add as AddIcon,
+ Visibility as ViewIcon,
+ PlayArrow as StartIcon,
+ Cancel as CancelIcon,
+} from "@mui/icons-material";
+import { inventoryService } from "../services/warehouseService";
+import { InventoryCountDto, InventoryStatus } from "../types";
+import dayjs from "dayjs";
+
+export default function InventoryListPage() {
+ const navigate = useNavigate();
+ const queryClient = useQueryClient();
+ const [statusFilter] = useState(
+ undefined
+ );
+
+ const { data: inventories = [], isLoading } = useQuery({
+ queryKey: ["inventory-counts", statusFilter],
+ queryFn: () => inventoryService.getAll(statusFilter),
+ });
+
+ const cancelMutation = useMutation({
+ mutationFn: (id: number) => inventoryService.cancel(id),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["inventory-counts"] });
+ },
+ });
+
+ const handleCreate = () => {
+ navigate("/warehouse/inventory/new");
+ };
+
+ const handleView = (id: number) => {
+ navigate(`/warehouse/inventory/${id}`);
+ };
+
+ const handleStart = (id: number) => {
+ navigate(`/warehouse/inventory/${id}/count`);
+ };
+
+ const getStatusChip = (status: InventoryStatus) => {
+ switch (status) {
+ case InventoryStatus.Draft:
+ return ;
+ case InventoryStatus.InProgress:
+ return ;
+ case InventoryStatus.Completed:
+ return ;
+ case InventoryStatus.Confirmed:
+ return ;
+ case InventoryStatus.Cancelled:
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ const columns: GridColDef[] = [
+ { field: "code", headerName: "Codice", width: 120 },
+ { field: "description", headerName: "Descrizione", flex: 1, minWidth: 200 },
+ {
+ field: "inventoryDate",
+ headerName: "Data Inventario",
+ width: 150,
+ valueFormatter: (value) =>
+ value ? dayjs(value).format("DD/MM/YYYY") : "",
+ },
+ { field: "warehouseName", headerName: "Magazzino", width: 180 },
+ { field: "categoryName", headerName: "Categoria", width: 150 },
+ {
+ field: "status",
+ headerName: "Stato",
+ width: 120,
+ renderCell: (params: GridRenderCellParams) =>
+ getStatusChip(params.row.status),
+ },
+ {
+ field: "progress",
+ headerName: "Progresso",
+ width: 150,
+ valueGetter: (_value, row) => {
+ if (!row.lineCount) return "0%";
+ const percentage = Math.round(
+ (row.countedLineCount / row.lineCount) * 100
+ );
+ return `${row.countedLineCount}/${row.lineCount} (${percentage}%)`;
+ },
+ },
+ {
+ field: "actions",
+ headerName: "Azioni",
+ width: 180,
+ sortable: false,
+ renderCell: (params: GridRenderCellParams) => (
+
+
+ handleView(params.row.id)}>
+
+
+
+ {params.row.status === InventoryStatus.Draft && (
+
+ handleStart(params.row.id)}
+ >
+
+
+
+ )}
+ {params.row.status === InventoryStatus.InProgress && (
+
+ handleStart(params.row.id)}
+ >
+
+
+
+ )}
+ {params.row.status === InventoryStatus.Draft && (
+
+ {
+ if (confirm("Sei sicuro di voler annullare questo inventario?")) {
+ cancelMutation.mutate(params.row.id);
+ }
+ }}
+ >
+
+
+
+ )}
+
+ ),
+ },
+ ];
+
+ return (
+
+
+ Inventari Fisici
+ }
+ onClick={handleCreate}
+ >
+ Nuovo Inventario
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/modules/warehouse/pages/WarehouseDashboard.tsx b/frontend/src/modules/warehouse/pages/WarehouseDashboard.tsx
index 65947a1..f7ef359 100644
--- a/frontend/src/modules/warehouse/pages/WarehouseDashboard.tsx
+++ b/frontend/src/modules/warehouse/pages/WarehouseDashboard.tsx
@@ -174,38 +174,30 @@ export default function WarehouseDashboard() {
return (
{/* Header */}
+ {/* Actions */}
-
-
- Magazzino
-
-
- Dashboard e panoramica giacenze
-
-
-
- }
- onClick={nav.goToNewInbound}
- >
- Nuovo Carico
-
- }
- onClick={nav.goToStockLevels}
- >
- Giacenze
-
-
+ }
+ onClick={nav.goToNewInbound}
+ >
+ Nuovo Carico
+
+ }
+ onClick={nav.goToStockLevels}
+ >
+ Giacenze
+
{/* Stats Cards */}
@@ -313,11 +305,11 @@ export default function WarehouseDashboard() {
size="small"
color={
getMovementTypeColor(movement.type) as
- | "success"
- | "error"
- | "info"
- | "warning"
- | "default"
+ | "success"
+ | "error"
+ | "info"
+ | "warning"
+ | "default"
}
/>
diff --git a/frontend/src/modules/warehouse/pages/index.ts b/frontend/src/modules/warehouse/pages/index.ts
index 203c1aa..32aeca4 100644
--- a/frontend/src/modules/warehouse/pages/index.ts
+++ b/frontend/src/modules/warehouse/pages/index.ts
@@ -16,3 +16,8 @@ export { default as TransferMovementPage } from './TransferMovementPage';
// Stock
export { default as StockLevelsPage } from './StockLevelsPage';
+
+// Inventory
+export { default as InventoryListPage } from './InventoryListPage';
+export { default as InventoryFormPage } from './InventoryFormPage';
+export { default as InventoryCountPage } from './InventoryCountPage';
diff --git a/frontend/src/modules/warehouse/routes.tsx b/frontend/src/modules/warehouse/routes.tsx
index 25dcb01..458b761 100644
--- a/frontend/src/modules/warehouse/routes.tsx
+++ b/frontend/src/modules/warehouse/routes.tsx
@@ -10,42 +10,55 @@ import {
OutboundMovementPage,
TransferMovementPage,
StockLevelsPage,
+ InventoryListPage,
+ InventoryFormPage,
+ InventoryCountPage,
} from "./pages";
+import WarehouseLayout from "./components/WarehouseLayout";
+
export default function WarehouseRoutes() {
return (
- {/* Dashboard */}
- } />
+ }>
+ {/* Dashboard */}
+ } />
- {/* Articles */}
- } />
- } />
- } />
- } />
+ {/* Articles */}
+ } />
+ } />
+ } />
+ } />
- {/* Warehouse Locations */}
- } />
+ {/* Warehouse Locations */}
+ } />
- {/* Movements */}
- } />
- } />
- } />
- }
- />
- }
- />
+ {/* Movements */}
+ } />
+ } />
+ } />
+ }
+ />
+ }
+ />
- {/* Stock */}
- } />
+ {/* Stock */}
+ } />
- {/* Fallback */}
- } />
+ {/* Inventory */}
+ } />
+ } />
+ } />
+ } />
+
+ {/* Fallback */}
+ } />
+
);
diff --git a/src/Apollinare.API/apollinare.db-shm b/src/Apollinare.API/apollinare.db-shm
new file mode 100644
index 0000000..ad2011c
Binary files /dev/null and b/src/Apollinare.API/apollinare.db-shm differ
diff --git a/src/Apollinare.API/apollinare.db-wal b/src/Apollinare.API/apollinare.db-wal
new file mode 100644
index 0000000..f270da5
Binary files /dev/null and b/src/Apollinare.API/apollinare.db-wal differ