feat: Implement a comprehensive custom fields system with backend management and frontend rendering capabilities.
This commit is contained in:
@@ -20,6 +20,7 @@ import ReportEditorPage from "./pages/ReportEditorPage";
|
||||
import ModulesAdminPage from "./pages/ModulesAdminPage";
|
||||
import ModulePurchasePage from "./pages/ModulePurchasePage";
|
||||
import AutoCodesAdminPage from "./pages/AutoCodesAdminPage";
|
||||
import CustomFieldsAdminPage from "./pages/CustomFieldsAdminPage";
|
||||
import WarehouseRoutes from "./modules/warehouse/routes";
|
||||
import { ModuleGuard } from "./components/ModuleGuard";
|
||||
import { useRealTimeUpdates } from "./hooks/useRealTimeUpdates";
|
||||
@@ -101,6 +102,10 @@ function App() {
|
||||
path="admin/auto-codes"
|
||||
element={<AutoCodesAdminPage />}
|
||||
/>
|
||||
<Route
|
||||
path="admin/custom-fields"
|
||||
element={<CustomFieldsAdminPage />}
|
||||
/>
|
||||
{/* Warehouse Module */}
|
||||
<Route
|
||||
path="warehouse/*"
|
||||
|
||||
195
frontend/src/components/customFields/CustomFieldsRenderer.tsx
Normal file
195
frontend/src/components/customFields/CustomFieldsRenderer.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
TextField,
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
MenuItem,
|
||||
Typography,
|
||||
Box
|
||||
} from '@mui/material';
|
||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
||||
import { CustomFieldDefinition, CustomFieldType, CustomFieldValues } from '../../types/customFields';
|
||||
import { customFieldService } from '../../services/customFieldService';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
interface Props {
|
||||
entityName: string;
|
||||
values: CustomFieldValues;
|
||||
onChange: (fieldName: string, value: any) => void;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export const CustomFieldsRenderer: React.FC<Props> = ({ entityName, values, onChange, readOnly = false }) => {
|
||||
const [definitions, setDefinitions] = useState<CustomFieldDefinition[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const loadDefinitions = async () => {
|
||||
try {
|
||||
const defs = await customFieldService.getByEntity(entityName);
|
||||
setDefinitions(defs);
|
||||
} catch (error) {
|
||||
console.error("Failed to load custom fields", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
loadDefinitions();
|
||||
}, [entityName]);
|
||||
|
||||
if (loading) return null;
|
||||
if (definitions.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Box sx={{ mt: 2, mb: 2, p: 2, border: '1px dashed #ccc', borderRadius: 1 }}>
|
||||
<Typography variant="subtitle1" gutterBottom color="primary">
|
||||
Campi Personalizzati
|
||||
</Typography>
|
||||
<Box display="flex" flexWrap="wrap" gap={2}>
|
||||
{definitions.map(def => (
|
||||
<Box key={def.id} flexBasis={{ xs: '100%', sm: '48%', md: '32%' }} flexGrow={1}>
|
||||
<FieldRenderer
|
||||
definition={def}
|
||||
value={values?.[def.fieldName]}
|
||||
onChange={(val) => onChange(def.fieldName, val)}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const FieldRenderer: React.FC<{
|
||||
definition: CustomFieldDefinition;
|
||||
value: any;
|
||||
onChange: (value: any) => void;
|
||||
readOnly: boolean;
|
||||
}> = ({ definition, value, onChange, readOnly }) => {
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(e.target.value);
|
||||
};
|
||||
|
||||
switch (definition.type) {
|
||||
case CustomFieldType.Text:
|
||||
case CustomFieldType.Email:
|
||||
case CustomFieldType.Url:
|
||||
return (
|
||||
<TextField
|
||||
fullWidth
|
||||
label={definition.label}
|
||||
value={value || ''}
|
||||
onChange={handleChange}
|
||||
required={definition.isRequired}
|
||||
disabled={readOnly}
|
||||
placeholder={definition.placeholder}
|
||||
helperText={definition.description}
|
||||
size="small"
|
||||
/>
|
||||
);
|
||||
|
||||
case CustomFieldType.Number:
|
||||
return (
|
||||
<TextField
|
||||
fullWidth
|
||||
type="number"
|
||||
label={definition.label}
|
||||
value={value || ''}
|
||||
onChange={handleChange}
|
||||
required={definition.isRequired}
|
||||
disabled={readOnly}
|
||||
helperText={definition.description}
|
||||
size="small"
|
||||
/>
|
||||
);
|
||||
|
||||
case CustomFieldType.TextArea:
|
||||
return (
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={4}
|
||||
label={definition.label}
|
||||
value={value || ''}
|
||||
onChange={handleChange}
|
||||
required={definition.isRequired}
|
||||
disabled={readOnly}
|
||||
helperText={definition.description}
|
||||
size="small"
|
||||
/>
|
||||
);
|
||||
|
||||
case CustomFieldType.Boolean:
|
||||
return (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={!!value}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
}
|
||||
label={definition.label}
|
||||
/>
|
||||
);
|
||||
|
||||
case CustomFieldType.Date:
|
||||
return (
|
||||
<DatePicker
|
||||
label={definition.label}
|
||||
value={value ? dayjs(value) : null}
|
||||
onChange={(newValue) => onChange(newValue ? newValue.toISOString() : null)}
|
||||
disabled={readOnly}
|
||||
slotProps={{ textField: { fullWidth: true, required: definition.isRequired, helperText: definition.description, size: 'small' } }}
|
||||
/>
|
||||
);
|
||||
|
||||
case CustomFieldType.Select:
|
||||
let options: string[] = [];
|
||||
try {
|
||||
options = definition.optionsJson ? JSON.parse(definition.optionsJson) : [];
|
||||
} catch (e) {
|
||||
console.error("Invalid options JSON", e);
|
||||
}
|
||||
return (
|
||||
<TextField
|
||||
select
|
||||
fullWidth
|
||||
label={definition.label}
|
||||
value={value || ''}
|
||||
onChange={handleChange}
|
||||
required={definition.isRequired}
|
||||
disabled={readOnly}
|
||||
helperText={definition.description}
|
||||
size="small"
|
||||
>
|
||||
{options.map((opt) => (
|
||||
<MenuItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
);
|
||||
|
||||
case CustomFieldType.Color:
|
||||
return (
|
||||
<TextField
|
||||
fullWidth
|
||||
type="color"
|
||||
label={definition.label}
|
||||
value={value || '#000000'}
|
||||
onChange={handleChange}
|
||||
required={definition.isRequired}
|
||||
disabled={readOnly}
|
||||
helperText={definition.description}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
size="small"
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
TextField,
|
||||
Grid,
|
||||
|
||||
} from "@mui/material";
|
||||
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
||||
import {
|
||||
@@ -21,12 +21,15 @@ import {
|
||||
} from "@mui/icons-material";
|
||||
import { clientiService } from "../services/lookupService";
|
||||
import { Cliente } from "../types";
|
||||
import { CustomFieldsRenderer } from "../components/customFields/CustomFieldsRenderer";
|
||||
import { CustomFieldValues } from "../types/customFields";
|
||||
|
||||
export default function ClientiPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
const [formData, setFormData] = useState<Partial<Cliente>>({ attivo: true });
|
||||
const [customFields, setCustomFields] = useState<CustomFieldValues>({});
|
||||
|
||||
const { data: clienti = [], isLoading } = useQuery({
|
||||
queryKey: ["clienti"],
|
||||
@@ -59,22 +62,33 @@ export default function ClientiPage() {
|
||||
setOpenDialog(false);
|
||||
setEditingId(null);
|
||||
setFormData({ attivo: true });
|
||||
setCustomFields({});
|
||||
};
|
||||
|
||||
const handleEdit = (cliente: Cliente) => {
|
||||
setFormData(cliente);
|
||||
setEditingId(cliente.id);
|
||||
try {
|
||||
setCustomFields(cliente.customFieldsJson ? JSON.parse(cliente.customFieldsJson) : {});
|
||||
} catch (e) {
|
||||
setCustomFields({});
|
||||
}
|
||||
setOpenDialog(true);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const dataWithCustomFields = {
|
||||
...formData,
|
||||
customFieldsJson: JSON.stringify(customFields)
|
||||
};
|
||||
|
||||
if (editingId) {
|
||||
// In modifica, non inviamo il codice (non modificabile)
|
||||
const { codice: _codice, ...updateData } = formData;
|
||||
const { codice: _codice, ...updateData } = dataWithCustomFields;
|
||||
updateMutation.mutate({ id: editingId, data: updateData });
|
||||
} else {
|
||||
// In creazione, non inviamo il codice (generato automaticamente)
|
||||
const { codice: _codice, ...createData } = formData;
|
||||
const { codice: _codice, ...createData } = dataWithCustomFields;
|
||||
createMutation.mutate(createData);
|
||||
}
|
||||
};
|
||||
@@ -162,8 +176,8 @@ export default function ClientiPage() {
|
||||
{editingId ? "Modifica Cliente" : "Nuovo Cliente"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Grid container spacing={2} sx={{ mt: 1 }}>
|
||||
<Grid size={{ xs: 12, md: 3 }}>
|
||||
<Box display="flex" flexWrap="wrap" gap={2} mt={1}>
|
||||
<Box flexBasis={{ xs: '100%', md: '23%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Codice"
|
||||
fullWidth
|
||||
@@ -184,16 +198,16 @@ export default function ClientiPage() {
|
||||
sx={
|
||||
!editingId
|
||||
? {
|
||||
"& .MuiInputBase-input.Mui-disabled": {
|
||||
fontStyle: "italic",
|
||||
color: "text.secondary",
|
||||
},
|
||||
}
|
||||
"& .MuiInputBase-input.Mui-disabled": {
|
||||
fontStyle: "italic",
|
||||
color: "text.secondary",
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 3 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '23%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Codice Alternativo"
|
||||
fullWidth
|
||||
@@ -206,8 +220,8 @@ export default function ClientiPage() {
|
||||
}
|
||||
helperText="Opzionale"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Ragione Sociale"
|
||||
fullWidth
|
||||
@@ -217,8 +231,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, ragioneSociale: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 8 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '65%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Indirizzo"
|
||||
fullWidth
|
||||
@@ -227,8 +241,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, indirizzo: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 4 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '32%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="CAP"
|
||||
fullWidth
|
||||
@@ -237,8 +251,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, cap: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 8 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '65%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Città"
|
||||
fullWidth
|
||||
@@ -247,8 +261,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, citta: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 4 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '32%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Provincia"
|
||||
fullWidth
|
||||
@@ -257,8 +271,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, provincia: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Telefono"
|
||||
fullWidth
|
||||
@@ -267,8 +281,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, telefono: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Email"
|
||||
fullWidth
|
||||
@@ -278,8 +292,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, email: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="PEC"
|
||||
fullWidth
|
||||
@@ -288,8 +302,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, pec: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Codice Fiscale"
|
||||
fullWidth
|
||||
@@ -298,8 +312,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, codiceFiscale: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Partita IVA"
|
||||
fullWidth
|
||||
@@ -308,8 +322,8 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, partitaIva: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: '48%' }} flexGrow={1}>
|
||||
<TextField
|
||||
label="Codice Destinatario"
|
||||
fullWidth
|
||||
@@ -321,8 +335,8 @@ export default function ClientiPage() {
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
</Box>
|
||||
<Box flexBasis="100%">
|
||||
<TextField
|
||||
label="Note"
|
||||
fullWidth
|
||||
@@ -333,8 +347,15 @@ export default function ClientiPage() {
|
||||
setFormData({ ...formData, note: e.target.value })
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Box flexBasis="100%">
|
||||
<CustomFieldsRenderer
|
||||
entityName="Cliente"
|
||||
values={customFields}
|
||||
onChange={(field, value) => setCustomFields(prev => ({ ...prev, [field]: value }))}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDialog}>Annulla</Button>
|
||||
|
||||
302
frontend/src/pages/CustomFieldsAdminPage.tsx
Normal file
302
frontend/src/pages/CustomFieldsAdminPage.tsx
Normal file
@@ -0,0 +1,302 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControl,
|
||||
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
TextField,
|
||||
Typography,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Snackbar,
|
||||
Alert
|
||||
} from '@mui/material';
|
||||
import { DataGrid, GridColDef, GridActionsCellItem } from '@mui/x-data-grid';
|
||||
import { Add as AddIcon, Edit as EditIcon, Delete as DeleteIcon } from '@mui/icons-material';
|
||||
import { CustomFieldDefinition, CustomFieldType } from '../types/customFields';
|
||||
import { customFieldService } from '../services/customFieldService';
|
||||
|
||||
const ENTITIES = [
|
||||
{ value: 'Cliente', label: 'Clienti' },
|
||||
{ value: 'Articolo', label: 'Articoli (Catering)' },
|
||||
{ value: 'Evento', label: 'Eventi' },
|
||||
{ value: 'WarehouseArticle', label: 'Articoli Magazzino' },
|
||||
{ value: 'WarehouseLocation', label: 'Magazzini' },
|
||||
{ value: 'Risorsa', label: 'Risorse (Staff)' }
|
||||
];
|
||||
|
||||
const FIELD_TYPES = [
|
||||
{ value: CustomFieldType.Text, label: 'Testo' },
|
||||
{ value: CustomFieldType.Number, label: 'Numero' },
|
||||
{ value: CustomFieldType.Date, label: 'Data' },
|
||||
{ value: CustomFieldType.Boolean, label: 'Booleano (Sì/No)' },
|
||||
{ value: CustomFieldType.Select, label: 'Lista a discesa' },
|
||||
{ value: CustomFieldType.TextArea, label: 'Area di testo' },
|
||||
{ value: CustomFieldType.Color, label: 'Colore' },
|
||||
{ value: CustomFieldType.Url, label: 'URL' },
|
||||
{ value: CustomFieldType.Email, label: 'Email' }
|
||||
];
|
||||
|
||||
const CustomFieldsAdminPage: React.FC = () => {
|
||||
const [selectedEntity, setSelectedEntity] = useState<string>(ENTITIES[0].value);
|
||||
const [fields, setFields] = useState<CustomFieldDefinition[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [currentField, setCurrentField] = useState<Partial<CustomFieldDefinition>>({});
|
||||
const [snackbar, setSnackbar] = useState<{ open: boolean, message: string, severity: 'success' | 'error' }>({
|
||||
open: false,
|
||||
message: '',
|
||||
severity: 'success'
|
||||
});
|
||||
|
||||
const showSnackbar = (message: string, severity: 'success' | 'error') => {
|
||||
setSnackbar({ open: true, message, severity });
|
||||
};
|
||||
|
||||
const handleCloseSnackbar = () => setSnackbar({ ...snackbar, open: false });
|
||||
|
||||
const loadFields = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await customFieldService.getByEntity(selectedEntity);
|
||||
setFields(data);
|
||||
} catch (error) {
|
||||
showSnackbar('Errore nel caricamento dei campi', 'error');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadFields();
|
||||
}, [selectedEntity]);
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
if (currentField.id) {
|
||||
await customFieldService.update(currentField.id, currentField as CustomFieldDefinition);
|
||||
showSnackbar('Campo aggiornato con successo', 'success');
|
||||
} else {
|
||||
await customFieldService.create({
|
||||
...currentField,
|
||||
entityName: selectedEntity,
|
||||
isActive: true,
|
||||
sortOrder: fields.length + 1
|
||||
} as CustomFieldDefinition);
|
||||
showSnackbar('Campo creato con successo', 'success');
|
||||
}
|
||||
setDialogOpen(false);
|
||||
loadFields();
|
||||
} catch (error) {
|
||||
showSnackbar('Errore nel salvataggio', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
if (window.confirm('Sei sicuro di voler eliminare questo campo?')) {
|
||||
try {
|
||||
await customFieldService.delete(id);
|
||||
showSnackbar('Campo eliminato', 'success');
|
||||
loadFields();
|
||||
} catch (error) {
|
||||
showSnackbar('Errore durante l\'eliminazione', 'error');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{ field: 'label', headerName: 'Etichetta', flex: 1 },
|
||||
{ field: 'fieldName', headerName: 'Nome Interno', flex: 1 },
|
||||
{
|
||||
field: 'type',
|
||||
headerName: 'Tipo',
|
||||
width: 150,
|
||||
valueFormatter: (params) => FIELD_TYPES.find(t => t.value === params.value)?.label
|
||||
},
|
||||
{
|
||||
field: 'isRequired',
|
||||
headerName: 'Obbligatorio',
|
||||
width: 120,
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
field: 'sortOrder',
|
||||
headerName: 'Ordine',
|
||||
width: 100,
|
||||
type: 'number',
|
||||
editable: true
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
type: 'actions',
|
||||
headerName: 'Azioni',
|
||||
width: 100,
|
||||
getActions: (params) => [
|
||||
<GridActionsCellItem
|
||||
icon={<EditIcon />}
|
||||
label="Modifica"
|
||||
onClick={() => {
|
||||
setCurrentField(params.row);
|
||||
setDialogOpen(true);
|
||||
}}
|
||||
/>,
|
||||
<GridActionsCellItem
|
||||
icon={<DeleteIcon />}
|
||||
label="Elimina"
|
||||
onClick={() => handleDelete(params.row.id)}
|
||||
/>,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Box p={3}>
|
||||
<Typography variant="h4" gutterBottom>
|
||||
Gestione Campi Personalizzati
|
||||
</Typography>
|
||||
|
||||
<Card sx={{ mb: 3 }}>
|
||||
<CardContent>
|
||||
<Box display="flex" flexWrap="wrap" gap={2} alignItems="center">
|
||||
<Box flexBasis={{ xs: '100%', md: '33%' }} flexGrow={1}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Entità</InputLabel>
|
||||
<Select
|
||||
value={selectedEntity}
|
||||
label="Entità"
|
||||
onChange={(e) => setSelectedEntity(e.target.value)}
|
||||
>
|
||||
{ENTITIES.map(e => (
|
||||
<MenuItem key={e.value} value={e.value}>{e.label}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
<Box flexBasis={{ xs: '100%', md: 'auto' }} display="flex" justifyContent="flex-end">
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<AddIcon />}
|
||||
onClick={() => {
|
||||
setCurrentField({
|
||||
type: CustomFieldType.Text,
|
||||
isRequired: false,
|
||||
entityName: selectedEntity
|
||||
});
|
||||
setDialogOpen(true);
|
||||
}}
|
||||
>
|
||||
Nuovo Campo
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div style={{ height: 600, width: '100%' }}>
|
||||
<DataGrid
|
||||
rows={fields}
|
||||
columns={columns}
|
||||
loading={loading}
|
||||
disableRowSelectionOnClick
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>{currentField.id ? 'Modifica Campo' : 'Nuovo Campo'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box sx={{ pt: 2, display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<TextField
|
||||
label="Etichetta (Label)"
|
||||
fullWidth
|
||||
value={currentField.label || ''}
|
||||
onChange={(e) => setCurrentField({ ...currentField, label: e.target.value })}
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
label="Nome Interno (es. data_nascita)"
|
||||
fullWidth
|
||||
value={currentField.fieldName || ''}
|
||||
onChange={(e) => setCurrentField({ ...currentField, fieldName: e.target.value })}
|
||||
required
|
||||
helperText="Deve essere univoco per l'entità. Usa solo lettere minuscole e underscore."
|
||||
disabled={!!currentField.id}
|
||||
/>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Tipo</InputLabel>
|
||||
<Select
|
||||
value={currentField.type}
|
||||
label="Tipo"
|
||||
onChange={(e) => setCurrentField({ ...currentField, type: e.target.value as CustomFieldType })}
|
||||
>
|
||||
{FIELD_TYPES.map(t => (
|
||||
<MenuItem key={t.value} value={t.value}>{t.label}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{currentField.type === CustomFieldType.Select && (
|
||||
<TextField
|
||||
label="Opzioni (JSON Array)"
|
||||
fullWidth
|
||||
multiline
|
||||
rows={3}
|
||||
value={currentField.optionsJson || ''}
|
||||
onChange={(e) => setCurrentField({ ...currentField, optionsJson: e.target.value })}
|
||||
placeholder='["Opzione 1", "Opzione 2"]'
|
||||
helperText="Inserisci un array JSON valido di stringhe"
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextField
|
||||
label="Descrizione / Helper Text"
|
||||
fullWidth
|
||||
value={currentField.description || ''}
|
||||
onChange={(e) => setCurrentField({ ...currentField, description: e.target.value })}
|
||||
/>
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={currentField.isRequired || false}
|
||||
onChange={(e) => setCurrentField({ ...currentField, isRequired: e.target.checked })}
|
||||
/>
|
||||
}
|
||||
label="Obbligatorio"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Ordine"
|
||||
type="number"
|
||||
fullWidth
|
||||
value={currentField.sortOrder || 0}
|
||||
onChange={(e) => setCurrentField({ ...currentField, sortOrder: parseInt(e.target.value) })}
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setDialogOpen(false)}>Annulla</Button>
|
||||
<Button onClick={handleSave} variant="contained">Salva</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<Snackbar open={snackbar.open} autoHideDuration={6000} onClose={handleCloseSnackbar}>
|
||||
<Alert onClose={handleCloseSnackbar} severity={snackbar.severity} sx={{ width: '100%' }}>
|
||||
{snackbar.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomFieldsAdminPage;
|
||||
|
||||
24
frontend/src/services/customFieldService.ts
Normal file
24
frontend/src/services/customFieldService.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import api from './api';
|
||||
import { CustomFieldDefinition } from '../types/customFields';
|
||||
|
||||
export const customFieldService = {
|
||||
getAll: async () => {
|
||||
const response = await api.get<CustomFieldDefinition[]>('/custom-fields');
|
||||
return response.data;
|
||||
},
|
||||
getByEntity: async (entityName: string) => {
|
||||
const response = await api.get<CustomFieldDefinition[]>(`/custom-fields/entity/${entityName}`);
|
||||
return response.data;
|
||||
},
|
||||
create: async (definition: Omit<CustomFieldDefinition, 'id'>) => {
|
||||
const response = await api.post<CustomFieldDefinition>('/custom-fields', definition);
|
||||
return response.data;
|
||||
},
|
||||
update: async (id: number, definition: CustomFieldDefinition) => {
|
||||
const response = await api.put<CustomFieldDefinition>(`/custom-fields/${id}`, definition);
|
||||
return response.data;
|
||||
},
|
||||
delete: async (id: number) => {
|
||||
await api.delete(`/custom-fields/${id}`);
|
||||
}
|
||||
};
|
||||
32
frontend/src/types/customFields.ts
Normal file
32
frontend/src/types/customFields.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export enum CustomFieldType {
|
||||
Text = 0,
|
||||
Number = 1,
|
||||
Date = 2,
|
||||
Boolean = 3,
|
||||
Select = 4,
|
||||
MultiSelect = 5,
|
||||
TextArea = 6,
|
||||
Color = 7,
|
||||
Url = 8,
|
||||
Email = 9
|
||||
}
|
||||
|
||||
export interface CustomFieldDefinition {
|
||||
id: number;
|
||||
entityName: string;
|
||||
fieldName: string;
|
||||
label: string;
|
||||
type: CustomFieldType;
|
||||
isRequired: boolean;
|
||||
defaultValue?: string;
|
||||
optionsJson?: string;
|
||||
sortOrder: number;
|
||||
description?: string;
|
||||
isActive: boolean;
|
||||
validationRegex?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface CustomFieldValues {
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export interface BaseEntity {
|
||||
createdBy?: string;
|
||||
updatedAt?: string;
|
||||
updatedBy?: string;
|
||||
customFieldsJson?: string;
|
||||
}
|
||||
|
||||
export interface Cliente extends BaseEntity {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
using Apollinare.API.Services;
|
||||
using Apollinare.Domain.Entities;
|
||||
using Apollinare.Domain.Enums;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Apollinare.API.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/custom-fields")]
|
||||
public class CustomFieldDefinitionsController : ControllerBase
|
||||
{
|
||||
private readonly CustomFieldService _service;
|
||||
private readonly ILogger<CustomFieldDefinitionsController> _logger;
|
||||
|
||||
public CustomFieldDefinitionsController(CustomFieldService service, ILogger<CustomFieldDefinitionsController> logger)
|
||||
{
|
||||
_service = service;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<CustomFieldDefinitionDto>>> GetAll()
|
||||
{
|
||||
var defs = await _service.GetAllDefinitionsAsync();
|
||||
return Ok(defs.Select(ToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("entity/{entityName}")]
|
||||
public async Task<ActionResult<List<CustomFieldDefinitionDto>>> GetByEntity(string entityName)
|
||||
{
|
||||
var defs = await _service.GetDefinitionsByEntityAsync(entityName);
|
||||
return Ok(defs.Select(ToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult<CustomFieldDefinitionDto>> Get(int id)
|
||||
{
|
||||
var def = await _service.GetDefinitionAsync(id);
|
||||
if (def == null) return NotFound();
|
||||
return Ok(ToDto(def));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<CustomFieldDefinitionDto>> Create(CustomFieldDefinitionDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entity = new CustomFieldDefinition
|
||||
{
|
||||
EntityName = dto.EntityName,
|
||||
FieldName = dto.FieldName,
|
||||
Label = dto.Label,
|
||||
Type = dto.Type,
|
||||
IsRequired = dto.IsRequired,
|
||||
DefaultValue = dto.DefaultValue,
|
||||
OptionsJson = dto.OptionsJson,
|
||||
SortOrder = dto.SortOrder,
|
||||
Description = dto.Description,
|
||||
IsActive = dto.IsActive,
|
||||
ValidationRegex = dto.ValidationRegex,
|
||||
Placeholder = dto.Placeholder
|
||||
};
|
||||
|
||||
var created = await _service.CreateDefinitionAsync(entity);
|
||||
return CreatedAtAction(nameof(Get), new { id = created.Id }, ToDto(created));
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id:int}")]
|
||||
public async Task<ActionResult<CustomFieldDefinitionDto>> Update(int id, CustomFieldDefinitionDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entity = new CustomFieldDefinition
|
||||
{
|
||||
EntityName = dto.EntityName,
|
||||
FieldName = dto.FieldName,
|
||||
Label = dto.Label,
|
||||
Type = dto.Type,
|
||||
IsRequired = dto.IsRequired,
|
||||
DefaultValue = dto.DefaultValue,
|
||||
OptionsJson = dto.OptionsJson,
|
||||
SortOrder = dto.SortOrder,
|
||||
Description = dto.Description,
|
||||
IsActive = dto.IsActive,
|
||||
ValidationRegex = dto.ValidationRegex,
|
||||
Placeholder = dto.Placeholder
|
||||
};
|
||||
|
||||
var updated = await _service.UpdateDefinitionAsync(id, entity);
|
||||
return Ok(ToDto(updated));
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id:int}")]
|
||||
public async Task<ActionResult> Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _service.DeleteDefinitionAsync(id);
|
||||
return NoContent();
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
private static CustomFieldDefinitionDto ToDto(CustomFieldDefinition entity)
|
||||
{
|
||||
return new CustomFieldDefinitionDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
EntityName = entity.EntityName,
|
||||
FieldName = entity.FieldName,
|
||||
Label = entity.Label,
|
||||
Type = entity.Type,
|
||||
IsRequired = entity.IsRequired,
|
||||
DefaultValue = entity.DefaultValue,
|
||||
OptionsJson = entity.OptionsJson,
|
||||
SortOrder = entity.SortOrder,
|
||||
Description = entity.Description,
|
||||
IsActive = entity.IsActive,
|
||||
ValidationRegex = entity.ValidationRegex,
|
||||
Placeholder = entity.Placeholder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomFieldDefinitionDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public required string EntityName { get; set; }
|
||||
public required string FieldName { get; set; }
|
||||
public required string Label { get; set; }
|
||||
public CustomFieldType Type { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
public string? DefaultValue { get; set; }
|
||||
public string? OptionsJson { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public string? ValidationRegex { get; set; }
|
||||
public string? Placeholder { get; set; }
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using Apollinare.API.Hubs;
|
||||
using Apollinare.API.Services;
|
||||
// Trigger rebuild
|
||||
using Apollinare.API.Services.Reports;
|
||||
using Apollinare.API.Modules.Warehouse.Services;
|
||||
using Apollinare.Infrastructure.Data;
|
||||
@@ -20,6 +21,7 @@ builder.Services.AddScoped<DemoDataService>();
|
||||
builder.Services.AddScoped<ReportGeneratorService>();
|
||||
builder.Services.AddScoped<ModuleService>();
|
||||
builder.Services.AddScoped<AutoCodeService>();
|
||||
builder.Services.AddScoped<CustomFieldService>();
|
||||
builder.Services.AddSingleton<DataNotificationService>();
|
||||
|
||||
// Warehouse Module Services
|
||||
|
||||
91
src/Apollinare.API/Services/CustomFieldService.cs
Normal file
91
src/Apollinare.API/Services/CustomFieldService.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using Apollinare.Domain.Entities;
|
||||
using Apollinare.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Apollinare.API.Services;
|
||||
|
||||
public class CustomFieldService
|
||||
{
|
||||
private readonly AppollinareDbContext _context;
|
||||
private readonly ILogger<CustomFieldService> _logger;
|
||||
|
||||
public CustomFieldService(AppollinareDbContext context, ILogger<CustomFieldService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<List<CustomFieldDefinition>> GetAllDefinitionsAsync()
|
||||
{
|
||||
return await _context.CustomFieldDefinitions
|
||||
.OrderBy(x => x.EntityName)
|
||||
.ThenBy(x => x.SortOrder)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<CustomFieldDefinition>> GetDefinitionsByEntityAsync(string entityName)
|
||||
{
|
||||
return await _context.CustomFieldDefinitions
|
||||
.Where(x => x.EntityName == entityName && x.IsActive)
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<CustomFieldDefinition?> GetDefinitionAsync(int id)
|
||||
{
|
||||
return await _context.CustomFieldDefinitions.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<CustomFieldDefinition> CreateDefinitionAsync(CustomFieldDefinition definition)
|
||||
{
|
||||
// Check for duplicate field name in the same entity
|
||||
var exists = await _context.CustomFieldDefinitions
|
||||
.AnyAsync(x => x.EntityName == definition.EntityName && x.FieldName == definition.FieldName);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
throw new ArgumentException($"Field '{definition.FieldName}' already exists for entity '{definition.EntityName}'");
|
||||
}
|
||||
|
||||
definition.CreatedAt = DateTime.UtcNow;
|
||||
_context.CustomFieldDefinitions.Add(definition);
|
||||
await _context.SaveChangesAsync();
|
||||
return definition;
|
||||
}
|
||||
|
||||
public async Task<CustomFieldDefinition> UpdateDefinitionAsync(int id, CustomFieldDefinition updatedDef)
|
||||
{
|
||||
var existing = await _context.CustomFieldDefinitions.FindAsync(id);
|
||||
if (existing == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"CustomFieldDefinition with ID {id} not found");
|
||||
}
|
||||
|
||||
existing.Label = updatedDef.Label;
|
||||
existing.Type = updatedDef.Type;
|
||||
existing.IsRequired = updatedDef.IsRequired;
|
||||
existing.DefaultValue = updatedDef.DefaultValue;
|
||||
existing.OptionsJson = updatedDef.OptionsJson;
|
||||
existing.SortOrder = updatedDef.SortOrder;
|
||||
existing.Description = updatedDef.Description;
|
||||
existing.IsActive = updatedDef.IsActive;
|
||||
existing.ValidationRegex = updatedDef.ValidationRegex;
|
||||
existing.Placeholder = updatedDef.Placeholder;
|
||||
existing.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
return existing;
|
||||
}
|
||||
|
||||
public async Task DeleteDefinitionAsync(int id)
|
||||
{
|
||||
var existing = await _context.CustomFieldDefinitions.FindAsync(id);
|
||||
if (existing == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"CustomFieldDefinition with ID {id} not found");
|
||||
}
|
||||
|
||||
_context.CustomFieldDefinitions.Remove(existing);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -7,4 +7,9 @@ public abstract class BaseEntity
|
||||
public string? CreatedBy { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public string? UpdatedBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stores custom field values as JSON: {"birthday": "2023-01-01", "vip_status": "gold"}
|
||||
/// </summary>
|
||||
public string? CustomFieldsJson { get; set; }
|
||||
}
|
||||
|
||||
19
src/Apollinare.Domain/Entities/CustomFieldDefinition.cs
Normal file
19
src/Apollinare.Domain/Entities/CustomFieldDefinition.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Apollinare.Domain.Enums;
|
||||
|
||||
namespace Apollinare.Domain.Entities;
|
||||
|
||||
public class CustomFieldDefinition : BaseEntity
|
||||
{
|
||||
public required string EntityName { get; set; } // e.g. "Cliente", "WarehouseArticle"
|
||||
public required string FieldName { get; set; } // Internal name, e.g. "birthday"
|
||||
public required string Label { get; set; } // Display name, e.g. "Birthday"
|
||||
public CustomFieldType Type { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
public string? DefaultValue { get; set; }
|
||||
public string? OptionsJson { get; set; } // For Select/MultiSelect: ["Option A", "Option B"]
|
||||
public int SortOrder { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public string? ValidationRegex { get; set; }
|
||||
public string? Placeholder { get; set; }
|
||||
}
|
||||
15
src/Apollinare.Domain/Enums/CustomFieldType.cs
Normal file
15
src/Apollinare.Domain/Enums/CustomFieldType.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Apollinare.Domain.Enums;
|
||||
|
||||
public enum CustomFieldType
|
||||
{
|
||||
Text = 0,
|
||||
Number = 1,
|
||||
Date = 2,
|
||||
Boolean = 3,
|
||||
Select = 4,
|
||||
MultiSelect = 5,
|
||||
TextArea = 6,
|
||||
Color = 7,
|
||||
Url = 8,
|
||||
Email = 9
|
||||
}
|
||||
@@ -44,6 +44,9 @@ public class AppollinareDbContext : DbContext
|
||||
// Auto Code system
|
||||
public DbSet<AutoCode> AutoCodes => Set<AutoCode>();
|
||||
|
||||
// Custom Fields system
|
||||
public DbSet<CustomFieldDefinition> CustomFieldDefinitions => Set<CustomFieldDefinition>();
|
||||
|
||||
// Warehouse module entities
|
||||
public DbSet<WarehouseLocation> WarehouseLocations => Set<WarehouseLocation>();
|
||||
public DbSet<WarehouseArticle> WarehouseArticles => Set<WarehouseArticle>();
|
||||
@@ -284,6 +287,13 @@ public class AppollinareDbContext : DbContext
|
||||
entity.HasIndex(e => e.ModuleCode);
|
||||
});
|
||||
|
||||
// CustomFieldDefinition
|
||||
modelBuilder.Entity<CustomFieldDefinition>(entity =>
|
||||
{
|
||||
entity.HasIndex(e => new { e.EntityName, e.FieldName }).IsUnique();
|
||||
entity.HasIndex(e => e.EntityName);
|
||||
});
|
||||
|
||||
// ===============================================
|
||||
// WAREHOUSE MODULE ENTITIES
|
||||
// ===============================================
|
||||
|
||||
3303
src/Apollinare.Infrastructure/Migrations/20251129161359_AddCustomFieldsSystem.Designer.cs
generated
Normal file
3303
src/Apollinare.Infrastructure/Migrations/20251129161359_AddCustomFieldsSystem.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,472 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Apollinare.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddCustomFieldsSystem : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "WarehouseLocations",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "WarehouseArticles",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "WarehouseArticleCategories",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "VirtualDatasets",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Utenti",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiRisorsa",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiPasto",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiOspite",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiMateriale",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiEvento",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockValuations",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockValuationLayers",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockMovements",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockMovementLines",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockLevels",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Risorse",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ReportTemplates",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ReportImages",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ReportFonts",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "MovementReasons",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ModuleSubscriptions",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Location",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "InventoryCounts",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "InventoryCountLines",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDettaglioRisorse",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDettaglioPrelievo",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDettaglioOspiti",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDegustazioni",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiAltriCosti",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiAllegati",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiAcconti",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Eventi",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Configurazioni",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "CodiciCategoria",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Clienti",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "AutoCodes",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Articoli",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ArticleSerials",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ArticleBatches",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ArticleBarcodes",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "CustomFieldsJson",
|
||||
table: "AppModules",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "CustomFieldDefinitions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
EntityName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
FieldName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Label = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Type = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
IsRequired = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
DefaultValue = table.Column<string>(type: "TEXT", nullable: true),
|
||||
OptionsJson = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SortOrder = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Description = table.Column<string>(type: "TEXT", nullable: true),
|
||||
IsActive = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
ValidationRegex = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Placeholder = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
CreatedBy = table.Column<string>(type: "TEXT", nullable: true),
|
||||
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
UpdatedBy = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CustomFieldsJson = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_CustomFieldDefinitions", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CustomFieldDefinitions_EntityName",
|
||||
table: "CustomFieldDefinitions",
|
||||
column: "EntityName");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_CustomFieldDefinitions_EntityName_FieldName",
|
||||
table: "CustomFieldDefinitions",
|
||||
columns: new[] { "EntityName", "FieldName" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "CustomFieldDefinitions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "WarehouseLocations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "WarehouseArticles");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "WarehouseArticleCategories");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "VirtualDatasets");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Utenti");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiRisorsa");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiPasto");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiOspite");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiMateriale");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "TipiEvento");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockValuations");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockValuationLayers");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockMovements");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockMovementLines");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "StockLevels");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Risorse");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ReportTemplates");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ReportImages");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ReportFonts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "MovementReasons");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ModuleSubscriptions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Location");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "InventoryCounts");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "InventoryCountLines");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDettaglioRisorse");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDettaglioPrelievo");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDettaglioOspiti");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiDegustazioni");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiAltriCosti");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiAllegati");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "EventiAcconti");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Eventi");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Configurazioni");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "CodiciCategoria");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Clienti");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "AutoCodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "Articoli");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ArticleSerials");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ArticleBatches");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "ArticleBarcodes");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CustomFieldsJson",
|
||||
table: "AppModules");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Dependencies")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -107,6 +110,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -168,6 +174,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -263,6 +272,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -331,6 +343,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -362,6 +377,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -382,6 +400,76 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.ToTable("Configurazioni");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Apollinare.Domain.Entities.CustomFieldDefinition", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DefaultValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EntityName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FieldName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsRequired")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Label")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OptionsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Placeholder")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UpdatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ValidationRegex")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("EntityName");
|
||||
|
||||
b.HasIndex("EntityName", "FieldName")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("CustomFieldDefinitions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Apollinare.Domain.Entities.Evento", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -409,6 +497,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DataEvento")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -504,6 +595,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("DataPagamento")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -553,6 +647,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventoId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -600,6 +697,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -644,6 +744,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("DataDegustazione")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -699,6 +802,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventoId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -747,6 +853,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventoId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -792,6 +901,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventoId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -849,6 +961,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal?>("DistanzaKm")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -902,6 +1017,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("EndDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -956,6 +1074,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("FileSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -1011,6 +1132,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("FileSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -1064,6 +1188,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1122,6 +1249,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1170,6 +1300,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1209,6 +1342,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1243,6 +1379,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1277,6 +1416,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1311,6 +1453,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1344,6 +1489,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1397,6 +1545,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Descrizione")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1447,6 +1598,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1505,6 +1659,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ExpiryDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1583,6 +1740,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<int?>("CurrentWarehouseId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("CustomerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -1666,6 +1826,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1751,6 +1914,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("InventoryCountId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -1814,6 +1980,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
@@ -1881,6 +2050,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("LastInventoryDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1951,6 +2123,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("CustomerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -2038,6 +2213,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DestinationLocationCode")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -2116,6 +2294,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("InboundQuantity")
|
||||
.HasPrecision(18, 4)
|
||||
.HasColumnType("TEXT");
|
||||
@@ -2202,6 +2383,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsExhausted")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -2276,6 +2460,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal?>("Depth")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -2417,6 +2604,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("DefaultValuationMethod")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -2494,6 +2684,9 @@ namespace Apollinare.Infrastructure.Migrations
|
||||
b.Property<string>("CreatedBy")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomFieldsJson")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user