modifiche a required dataRitiro e dataConsegna
This commit is contained in:
parent
797f425a24
commit
c289bd5003
8 changed files with 109 additions and 52 deletions
|
|
@ -50,14 +50,16 @@ CREATE TABLE IF NOT EXISTS cliente (
|
||||||
|
|
||||||
-- ----------------------------------------------------------------
|
-- ----------------------------------------------------------------
|
||||||
-- Tabella consegna (API: GET/POST/PUT/DELETE /consegna)
|
-- Tabella consegna (API: GET/POST/PUT/DELETE /consegna)
|
||||||
-- DataRitiro e DataConsegna sono eventi indipendenti: nessun vincolo
|
-- DataRitiro = giorno in cui il corriere ritira dal cliente.
|
||||||
-- di ordine tra le due date, solo retroattività rispetto a oggi.
|
-- DataConsegna = giorno in cui il corriere consegna al cliente.
|
||||||
|
-- Sono eventi indipendenti (nessun vincolo di ordine); almeno una delle due
|
||||||
|
-- date deve essere valorizzata. Se presente, la data non può essere retroattiva.
|
||||||
-- ----------------------------------------------------------------
|
-- ----------------------------------------------------------------
|
||||||
CREATE TABLE IF NOT EXISTS consegna (
|
CREATE TABLE IF NOT EXISTS consegna (
|
||||||
ConsegnaID INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
ConsegnaID INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
ClienteID INT UNSIGNED NOT NULL,
|
ClienteID INT UNSIGNED NOT NULL,
|
||||||
DataRitiro DATE NOT NULL,
|
DataRitiro DATE NULL,
|
||||||
DataConsegna DATE NOT NULL,
|
DataConsegna DATE NULL,
|
||||||
Stato ENUM('da ritirare', 'in deposito', 'in consegna', 'in giacenza') NOT NULL DEFAULT 'in deposito',
|
Stato ENUM('da ritirare', 'in deposito', 'in consegna', 'in giacenza') NOT NULL DEFAULT 'in deposito',
|
||||||
ChiaveConsegna VARCHAR(8) NOT NULL,
|
ChiaveConsegna VARCHAR(8) NOT NULL,
|
||||||
|
|
||||||
|
|
@ -69,7 +71,10 @@ CREATE TABLE IF NOT EXISTS consegna (
|
||||||
FOREIGN KEY (ClienteID)
|
FOREIGN KEY (ClienteID)
|
||||||
REFERENCES cliente (ClienteID)
|
REFERENCES cliente (ClienteID)
|
||||||
ON UPDATE CASCADE
|
ON UPDATE CASCADE
|
||||||
ON DELETE RESTRICT
|
ON DELETE RESTRICT,
|
||||||
|
|
||||||
|
CONSTRAINT chk_consegna_almeno_una_data
|
||||||
|
CHECK (DataRitiro IS NOT NULL OR DataConsegna IS NOT NULL)
|
||||||
|
|
||||||
) ENGINE=InnoDB
|
) ENGINE=InnoDB
|
||||||
DEFAULT CHARSET=utf8mb4
|
DEFAULT CHARSET=utf8mb4
|
||||||
|
|
@ -89,12 +94,12 @@ CREATE TRIGGER trg_consegna_no_retroattivita_insert
|
||||||
BEFORE INSERT ON consegna
|
BEFORE INSERT ON consegna
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
IF NEW.DataRitiro < CURDATE() THEN
|
IF NEW.DataRitiro IS NOT NULL AND NEW.DataRitiro < CURDATE() THEN
|
||||||
SIGNAL SQLSTATE '45000'
|
SIGNAL SQLSTATE '45000'
|
||||||
SET MESSAGE_TEXT = 'DataRitiro non può essere anteriore alla data odierna';
|
SET MESSAGE_TEXT = 'DataRitiro non può essere anteriore alla data odierna';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF NEW.DataConsegna < CURDATE() THEN
|
IF NEW.DataConsegna IS NOT NULL AND NEW.DataConsegna < CURDATE() THEN
|
||||||
SIGNAL SQLSTATE '45000'
|
SIGNAL SQLSTATE '45000'
|
||||||
SET MESSAGE_TEXT = 'DataConsegna non può essere anteriore alla data odierna';
|
SET MESSAGE_TEXT = 'DataConsegna non può essere anteriore alla data odierna';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
@ -104,12 +109,16 @@ CREATE TRIGGER trg_consegna_no_retroattivita_update
|
||||||
BEFORE UPDATE ON consegna
|
BEFORE UPDATE ON consegna
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
IF NEW.DataRitiro <> OLD.DataRitiro AND NEW.DataRitiro < CURDATE() THEN
|
IF NEW.DataRitiro IS NOT NULL
|
||||||
|
AND (OLD.DataRitiro IS NULL OR NEW.DataRitiro <> OLD.DataRitiro)
|
||||||
|
AND NEW.DataRitiro < CURDATE() THEN
|
||||||
SIGNAL SQLSTATE '45000'
|
SIGNAL SQLSTATE '45000'
|
||||||
SET MESSAGE_TEXT = 'DataRitiro non può essere anteriore alla data odierna';
|
SET MESSAGE_TEXT = 'DataRitiro non può essere anteriore alla data odierna';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF NEW.DataConsegna <> OLD.DataConsegna AND NEW.DataConsegna < CURDATE() THEN
|
IF NEW.DataConsegna IS NOT NULL
|
||||||
|
AND (OLD.DataConsegna IS NULL OR NEW.DataConsegna <> OLD.DataConsegna)
|
||||||
|
AND NEW.DataConsegna < CURDATE() THEN
|
||||||
SIGNAL SQLSTATE '45000'
|
SIGNAL SQLSTATE '45000'
|
||||||
SET MESSAGE_TEXT = 'DataConsegna non può essere anteriore alla data odierna';
|
SET MESSAGE_TEXT = 'DataConsegna non può essere anteriore alla data odierna';
|
||||||
END IF;
|
END IF;
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,13 @@ INSERT INTO cliente (Nome, Cognome, Via, Comune, Provincia, Telefono, Email, Not
|
||||||
-- ClienteID: 1=Mario, 2=Laura, 3=Marco, 4=Giulia, 5=Paolo
|
-- ClienteID: 1=Mario, 2=Laura, 3=Marco, 4=Giulia, 5=Paolo
|
||||||
-- ------------------------------------------------------------
|
-- ------------------------------------------------------------
|
||||||
-- Date dinamiche: compatibili con i trigger di retroattività (>= CURDATE())
|
-- Date dinamiche: compatibili con i trigger di retroattività (>= CURDATE())
|
||||||
-- DataRitiro e DataConsegna sono indipendenti (es. riga 4: consegna prima del ritiro)
|
-- DataRitiro e DataConsegna sono indipendenti; almeno una delle due è obbligatoria.
|
||||||
|
-- Esempi: solo ritiro (CNSG2406), solo consegna (CNSG5522), entrambe (CNSG9001).
|
||||||
INSERT INTO consegna (ClienteID, DataRitiro, DataConsegna, Stato, ChiaveConsegna) VALUES
|
INSERT INTO consegna (ClienteID, DataRitiro, DataConsegna, Stato, ChiaveConsegna) VALUES
|
||||||
(1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 2 DAY), 'in deposito', 'CNSG9001'),
|
(1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 2 DAY), 'in deposito', 'CNSG9001'),
|
||||||
(2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 DAY), 'in consegna', 'CNSG3344'),
|
(2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 DAY), 'in consegna', 'CNSG3344'),
|
||||||
(3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 DAY), 'da ritirare', 'CNSG2406'),
|
(3, CURDATE(), NULL, 'da ritirare', 'CNSG2406'),
|
||||||
(4, DATE_ADD(CURDATE(), INTERVAL 5 DAY), DATE_ADD(CURDATE(), INTERVAL 2 DAY), 'in giacenza', 'CNSG7788'),
|
(4, DATE_ADD(CURDATE(), INTERVAL 5 DAY), DATE_ADD(CURDATE(), INTERVAL 2 DAY), 'in giacenza', 'CNSG7788'),
|
||||||
(5, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 1 DAY), 'in deposito', 'CNSG5522'),
|
(5, NULL, DATE_ADD(CURDATE(), INTERVAL 1 DAY), 'in deposito', 'CNSG5522'),
|
||||||
(1, DATE_ADD(CURDATE(), INTERVAL 1 DAY), DATE_ADD(CURDATE(), INTERVAL 3 DAY), 'da ritirare', 'CNSG9876'),
|
(1, DATE_ADD(CURDATE(), INTERVAL 1 DAY), DATE_ADD(CURDATE(), INTERVAL 3 DAY), 'da ritirare', 'CNSG9876'),
|
||||||
(2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 2 DAY), 'in consegna', 'CNSG1122');
|
(2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 2 DAY), 'in consegna', 'CNSG1122');
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,16 @@ export function mapDbError(error: unknown): DbErrorResponse | null {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err.code === "ER_CHECK_CONSTRAINT_VIOLATED") {
|
||||||
|
if (message.includes("chk_consegna_almeno_una_data")) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
error: "Inserire almeno una data di ritiro o di consegna",
|
||||||
|
fields: { _form: "Inserire almeno una data di ritiro o di consegna" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (err.code === "ER_ROW_IS_REFERENCED_2") {
|
if (err.code === "ER_ROW_IS_REFERENCED_2") {
|
||||||
return {
|
return {
|
||||||
status: 409,
|
status: 409,
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ const router = Router();
|
||||||
interface ConsegnaRow extends RowDataPacket {
|
interface ConsegnaRow extends RowDataPacket {
|
||||||
ConsegnaID: number;
|
ConsegnaID: number;
|
||||||
ClienteID: number;
|
ClienteID: number;
|
||||||
DataRitiro: Date | string;
|
DataRitiro: Date | string | null;
|
||||||
DataConsegna: Date | string;
|
DataConsegna: Date | string | null;
|
||||||
Stato: string;
|
Stato: string;
|
||||||
ChiaveConsegna: string;
|
ChiaveConsegna: string;
|
||||||
ClienteNome: string;
|
ClienteNome: string;
|
||||||
|
|
@ -37,7 +37,8 @@ const SELECT_BASE = `
|
||||||
INNER JOIN cliente cl ON cl.ClienteID = c.ClienteID
|
INNER JOIN cliente cl ON cl.ClienteID = c.ClienteID
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function formatDate(value: Date | string): string {
|
function formatDate(value: Date | string | null): string | null {
|
||||||
|
if (value == null) return null;
|
||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
return value.toISOString().slice(0, 10);
|
return value.toISOString().slice(0, 10);
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +143,7 @@ router.put("/:id", validateBody(validateConsegnaUpdate), async (req, res) => {
|
||||||
|
|
||||||
const updates = res.locals.validatedBody as Partial<ConsegnaInput>;
|
const updates = res.locals.validatedBody as Partial<ConsegnaInput>;
|
||||||
const setClauses: string[] = [];
|
const setClauses: string[] = [];
|
||||||
const values: (string | number)[] = [];
|
const values: (string | number | null)[] = [];
|
||||||
|
|
||||||
if (updates.dataRitiro !== undefined) {
|
if (updates.dataRitiro !== undefined) {
|
||||||
setClauses.push("DataRitiro = ?");
|
setClauses.push("DataRitiro = ?");
|
||||||
|
|
@ -165,6 +166,23 @@ router.put("/:id", validateBody(validateConsegnaUpdate), async (req, res) => {
|
||||||
values.push(updates.clienteId);
|
values.push(updates.clienteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mergedRitiro =
|
||||||
|
updates.dataRitiro !== undefined
|
||||||
|
? updates.dataRitiro
|
||||||
|
: formatDate(existing.DataRitiro);
|
||||||
|
const mergedConsegna =
|
||||||
|
updates.dataConsegna !== undefined
|
||||||
|
? updates.dataConsegna
|
||||||
|
: formatDate(existing.DataConsegna);
|
||||||
|
|
||||||
|
if (!mergedRitiro && !mergedConsegna) {
|
||||||
|
res.status(400).json({
|
||||||
|
error: "Inserire almeno una data di ritiro o di consegna",
|
||||||
|
fields: { _form: "Inserire almeno una data di ritiro o di consegna" },
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await pool.execute<ResultSetHeader>(
|
await pool.execute<ResultSetHeader>(
|
||||||
`UPDATE consegna SET ${setClauses.join(", ")} WHERE ConsegnaID = ?`,
|
`UPDATE consegna SET ${setClauses.join(", ")} WHERE ConsegnaID = ?`,
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,16 @@ import {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
interface VerificaRow extends RowDataPacket {
|
interface VerificaRow extends RowDataPacket {
|
||||||
DataRitiro: Date | string;
|
DataRitiro: Date | string | null;
|
||||||
DataConsegna: Date | string;
|
DataConsegna: Date | string | null;
|
||||||
Stato: string;
|
Stato: string;
|
||||||
ClienteNome: string;
|
ClienteNome: string;
|
||||||
ClienteCognome: string;
|
ClienteCognome: string;
|
||||||
Comune: string;
|
Comune: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDate(value: Date | string): string {
|
function formatDate(value: Date | string | null): string | null {
|
||||||
|
if (value == null) return null;
|
||||||
if (value instanceof Date) {
|
if (value instanceof Date) {
|
||||||
return value.toISOString().slice(0, 10);
|
return value.toISOString().slice(0, 10);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ export interface Cliente {
|
||||||
export interface Consegna {
|
export interface Consegna {
|
||||||
ConsegnaID: number;
|
ConsegnaID: number;
|
||||||
ClienteID: number;
|
ClienteID: number;
|
||||||
DataRitiro: Date;
|
DataRitiro: Date | null;
|
||||||
DataConsegna: Date;
|
DataConsegna: Date | null;
|
||||||
Stato: StatoConsegna;
|
Stato: StatoConsegna;
|
||||||
ChiaveConsegna: string;
|
ChiaveConsegna: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,18 @@ import {
|
||||||
} from "./primitives.js";
|
} from "./primitives.js";
|
||||||
|
|
||||||
export interface ConsegnaInput {
|
export interface ConsegnaInput {
|
||||||
dataRitiro: string;
|
dataRitiro: string | null;
|
||||||
dataConsegna: string;
|
dataConsegna: string | null;
|
||||||
stato: StatoConsegna;
|
stato: StatoConsegna;
|
||||||
chiaveConsegna: string;
|
chiaveConsegna: string;
|
||||||
clienteId: number;
|
clienteId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeOptionalDate(value: unknown): string | null {
|
||||||
|
const str = normalizeString(value);
|
||||||
|
return str || null;
|
||||||
|
}
|
||||||
|
|
||||||
function todayAtMidnight(): Date {
|
function todayAtMidnight(): Date {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
@ -48,10 +53,31 @@ function validateDateNotRetroactive(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateOptionalDateField(
|
||||||
|
errors: FieldErrors,
|
||||||
|
field: string,
|
||||||
|
value: string | null,
|
||||||
|
label: string
|
||||||
|
): void {
|
||||||
|
if (!value) return;
|
||||||
|
validateDate(errors, field, value, label);
|
||||||
|
validateDateNotRetroactive(errors, field, value, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateAtLeastOneDate(
|
||||||
|
errors: FieldErrors,
|
||||||
|
dataRitiro: string | null,
|
||||||
|
dataConsegna: string | null
|
||||||
|
): void {
|
||||||
|
if (!dataRitiro && !dataConsegna) {
|
||||||
|
errors._form = "Inserire almeno una data di ritiro o di consegna";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeConsegnaInput(raw: Record<string, unknown>): ConsegnaInput {
|
export function normalizeConsegnaInput(raw: Record<string, unknown>): ConsegnaInput {
|
||||||
return {
|
return {
|
||||||
dataRitiro: normalizeString(raw.dataRitiro),
|
dataRitiro: normalizeOptionalDate(raw.dataRitiro),
|
||||||
dataConsegna: normalizeString(raw.dataConsegna),
|
dataConsegna: normalizeOptionalDate(raw.dataConsegna),
|
||||||
stato: normalizeString(raw.stato || "in deposito") as StatoConsegna,
|
stato: normalizeString(raw.stato || "in deposito") as StatoConsegna,
|
||||||
chiaveConsegna: normalizeString(raw.chiaveConsegna).toUpperCase(),
|
chiaveConsegna: normalizeString(raw.chiaveConsegna).toUpperCase(),
|
||||||
clienteId: normalizePositiveInt(raw.clienteId),
|
clienteId: normalizePositiveInt(raw.clienteId),
|
||||||
|
|
@ -65,20 +91,17 @@ function validateConsegnaFields(
|
||||||
options: { requireAll: boolean }
|
options: { requireAll: boolean }
|
||||||
): void {
|
): void {
|
||||||
if (options.requireAll || "dataRitiro" in raw) {
|
if (options.requireAll || "dataRitiro" in raw) {
|
||||||
validateRequired(errors, "dataRitiro", data.dataRitiro, "Data ritiro obbligatoria");
|
validateOptionalDateField(errors, "dataRitiro", data.dataRitiro, "Data ritiro");
|
||||||
validateDate(errors, "dataRitiro", data.dataRitiro, "Data ritiro");
|
|
||||||
validateDateNotRetroactive(errors, "dataRitiro", data.dataRitiro, "Data ritiro");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.requireAll || "dataConsegna" in raw) {
|
if (options.requireAll || "dataConsegna" in raw) {
|
||||||
validateRequired(
|
validateOptionalDateField(errors, "dataConsegna", data.dataConsegna, "Data consegna");
|
||||||
errors,
|
}
|
||||||
"dataConsegna",
|
|
||||||
data.dataConsegna,
|
if (options.requireAll) {
|
||||||
"Data consegna obbligatoria"
|
validateAtLeastOneDate(errors, data.dataRitiro, data.dataConsegna);
|
||||||
);
|
} else if ("dataRitiro" in raw && "dataConsegna" in raw) {
|
||||||
validateDate(errors, "dataConsegna", data.dataConsegna, "Data consegna");
|
validateAtLeastOneDate(errors, data.dataRitiro, data.dataConsegna);
|
||||||
validateDateNotRetroactive(errors, "dataConsegna", data.dataConsegna, "Data consegna");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.requireAll || "stato" in raw) {
|
if (options.requireAll || "stato" in raw) {
|
||||||
|
|
@ -129,22 +152,17 @@ export function validateConsegnaUpdate(
|
||||||
const data: Partial<ConsegnaInput> = {};
|
const data: Partial<ConsegnaInput> = {};
|
||||||
|
|
||||||
if ("dataRitiro" in raw) {
|
if ("dataRitiro" in raw) {
|
||||||
data.dataRitiro = normalizeString(raw.dataRitiro);
|
data.dataRitiro = normalizeOptionalDate(raw.dataRitiro);
|
||||||
validateRequired(errors, "dataRitiro", data.dataRitiro, "Data ritiro obbligatoria");
|
validateOptionalDateField(errors, "dataRitiro", data.dataRitiro, "Data ritiro");
|
||||||
validateDate(errors, "dataRitiro", data.dataRitiro, "Data ritiro");
|
|
||||||
validateDateNotRetroactive(errors, "dataRitiro", data.dataRitiro, "Data ritiro");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("dataConsegna" in raw) {
|
if ("dataConsegna" in raw) {
|
||||||
data.dataConsegna = normalizeString(raw.dataConsegna);
|
data.dataConsegna = normalizeOptionalDate(raw.dataConsegna);
|
||||||
validateRequired(
|
validateOptionalDateField(errors, "dataConsegna", data.dataConsegna, "Data consegna");
|
||||||
errors,
|
}
|
||||||
"dataConsegna",
|
|
||||||
data.dataConsegna,
|
if ("dataRitiro" in raw && "dataConsegna" in raw) {
|
||||||
"Data consegna obbligatoria"
|
validateAtLeastOneDate(errors, data.dataRitiro ?? null, data.dataConsegna ?? null);
|
||||||
);
|
|
||||||
validateDate(errors, "dataConsegna", data.dataConsegna, "Data consegna");
|
|
||||||
validateDateNotRetroactive(errors, "dataConsegna", data.dataConsegna, "Data consegna");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("stato" in raw) {
|
if ("stato" in raw) {
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ export const UTENTE_CONSTRAINTS = {
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
||||||
export const CONSEGNA_CONSTRAINTS = {
|
export const CONSEGNA_CONSTRAINTS = {
|
||||||
dataconsegna: { required: true },
|
dataRitiro: { required: false },
|
||||||
dataritiro: {required: true},
|
dataConsegna: { required: false },
|
||||||
stato: {
|
stato: {
|
||||||
allowedValues: ["da ritirare", "in consegna", "in giacenza", "in deposito"],
|
allowedValues: ["da ritirare", "in consegna", "in giacenza", "in deposito"],
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue