346 lines
10 KiB
TypeScript
346 lines
10 KiB
TypeScript
|
|
import { TRPCError } from "@trpc/server";
|
||
|
|
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
|
||
|
|
import type { OrdiniOrdineId } from "~/schemas/public/Ordini";
|
||
|
|
import type {
|
||
|
|
Prezziario,
|
||
|
|
PrezziarioIdprezziario,
|
||
|
|
} from "~/schemas/public/Prezziario";
|
||
|
|
import type { Servizio, ServizioServizioId } from "~/schemas/public/Servizio";
|
||
|
|
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
||
|
|
import { db } from "../db";
|
||
|
|
import { getPrezziarioByIdHandler } from "../services/prezziario.service";
|
||
|
|
import { createOrdine } from "./ordini.controller";
|
||
|
|
import { getServizioById } from "./servizio.controller";
|
||
|
|
|
||
|
|
export const ACCONTO_TRANSITORIO = "ACCONTO_BD" as PrezziarioIdprezziario;
|
||
|
|
export const ACCONTO_STABILE = "ACCONTO_CA" as PrezziarioIdprezziario;
|
||
|
|
|
||
|
|
export type PreparedPaymentData = {
|
||
|
|
ordine_id: OrdiniOrdineId;
|
||
|
|
packid: PrezziarioIdprezziario;
|
||
|
|
amount_cent: number;
|
||
|
|
sconto: number;
|
||
|
|
packName: string;
|
||
|
|
packDesc: string;
|
||
|
|
condizioni: string;
|
||
|
|
};
|
||
|
|
export const preparePayment = async (
|
||
|
|
servizioId: ServizioServizioId,
|
||
|
|
type: OrderTypeEnum,
|
||
|
|
): Promise<PreparedPaymentData> => {
|
||
|
|
try {
|
||
|
|
const servizio = await getServizioById(servizioId);
|
||
|
|
const inactiveOrders = await db
|
||
|
|
.selectFrom("ordini")
|
||
|
|
.selectAll()
|
||
|
|
.where("servizio_id", "=", servizioId)
|
||
|
|
.where("isActive", "=", false)
|
||
|
|
.where("paymentstatus", "is", null)
|
||
|
|
.execute();
|
||
|
|
|
||
|
|
switch (type) {
|
||
|
|
case OrderTypeEnum.Acconto: {
|
||
|
|
if (!servizio.isOkAcconto) {
|
||
|
|
const acconto = await AccontoSolver(servizio);
|
||
|
|
if (!acconto.success) {
|
||
|
|
throw new Error(
|
||
|
|
`Acconto non trovato per il servizio: ${acconto.message}`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (!acconto.pack.testo_condizioni) {
|
||
|
|
throw new Error("Condizioni non trovate per acconto");
|
||
|
|
}
|
||
|
|
|
||
|
|
// servizio sta aspettando attivazione acconto
|
||
|
|
|
||
|
|
const preExistingAcconto = inactiveOrders.find(
|
||
|
|
(ordine) => ordine.type === OrderTypeEnum.Acconto,
|
||
|
|
);
|
||
|
|
|
||
|
|
if (preExistingAcconto) {
|
||
|
|
// acconto già creato, procedere con questo
|
||
|
|
return {
|
||
|
|
ordine_id: preExistingAcconto.ordine_id,
|
||
|
|
packid: preExistingAcconto.packid,
|
||
|
|
amount_cent: preExistingAcconto.amount_cent,
|
||
|
|
sconto: preExistingAcconto.sconto,
|
||
|
|
packName: acconto.pack.nome_it,
|
||
|
|
packDesc: acconto.pack.desc_it,
|
||
|
|
condizioni: acconto.pack.testo_condizioni,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
// nessun acconto preesistente, crearne uno nuovo
|
||
|
|
|
||
|
|
const ordine = await createOrdine({
|
||
|
|
servizio_id: servizioId,
|
||
|
|
amount_cent: acconto.pack.prezzo_cent,
|
||
|
|
packid: acconto.pack.idprezziario,
|
||
|
|
type: OrderTypeEnum.Acconto,
|
||
|
|
userid: servizio.user_id,
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
ordine_id: ordine.ordine_id,
|
||
|
|
packid: acconto.pack.idprezziario,
|
||
|
|
amount_cent: acconto.pack.prezzo_cent,
|
||
|
|
sconto: 0,
|
||
|
|
packName: acconto.pack.nome_it,
|
||
|
|
packDesc: acconto.pack.desc_it,
|
||
|
|
condizioni: acconto.pack.testo_condizioni,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
throw new Error(
|
||
|
|
"Il servizio ha già un acconto attivo, non è possibile preparare un nuovo pagamento",
|
||
|
|
);
|
||
|
|
}
|
||
|
|
case OrderTypeEnum.Saldo: {
|
||
|
|
if (!servizio.isOkAcconto) {
|
||
|
|
throw new Error(
|
||
|
|
"Non è possibile procedere con il pagamento del saldo prima di aver attivato l'acconto",
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (!servizio.isOkSaldo) {
|
||
|
|
//find saldo pack based on servizio tipologia and permanenza/budget, if tipologia posizione
|
||
|
|
const saldo = await SaldoSolver(servizio);
|
||
|
|
if (!saldo.success) {
|
||
|
|
throw new Error(
|
||
|
|
`Saldo non trovato per il servizio: ${saldo.message}`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (!saldo.pack.testo_condizioni) {
|
||
|
|
throw new Error("Condizioni non trovate per saldo");
|
||
|
|
}
|
||
|
|
const preExistingSaldo = inactiveOrders.find(
|
||
|
|
(ordine) => ordine.type === OrderTypeEnum.Saldo,
|
||
|
|
);
|
||
|
|
if (preExistingSaldo) {
|
||
|
|
// saldo già creato, procedere con questo
|
||
|
|
return {
|
||
|
|
ordine_id: preExistingSaldo.ordine_id,
|
||
|
|
packid: preExistingSaldo.packid,
|
||
|
|
amount_cent: preExistingSaldo.amount_cent,
|
||
|
|
sconto: preExistingSaldo.sconto,
|
||
|
|
packName: saldo.pack.nome_it,
|
||
|
|
packDesc: saldo.pack.desc_it,
|
||
|
|
condizioni: saldo.pack.testo_condizioni,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
// nessun saldo preesistente, crearne uno nuovo
|
||
|
|
const ordine = await createOrdine({
|
||
|
|
servizio_id: servizioId,
|
||
|
|
amount_cent: saldo.pack.prezzo_cent,
|
||
|
|
packid: saldo.pack.idprezziario,
|
||
|
|
type: OrderTypeEnum.Saldo,
|
||
|
|
userid: servizio.user_id,
|
||
|
|
});
|
||
|
|
return {
|
||
|
|
ordine_id: ordine.ordine_id,
|
||
|
|
packid: saldo.pack.idprezziario,
|
||
|
|
amount_cent: saldo.pack.prezzo_cent,
|
||
|
|
sconto: 0,
|
||
|
|
packName: saldo.pack.nome_it,
|
||
|
|
packDesc: saldo.pack.desc_it,
|
||
|
|
condizioni: saldo.pack.testo_condizioni,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
throw new Error(
|
||
|
|
"Il servizio ha già un saldo attivo, non è possibile preparare un nuovo pagamento",
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
case OrderTypeEnum.Consulenza: {
|
||
|
|
if (!servizio.isOkAcconto || !servizio.isOkSaldo) {
|
||
|
|
throw new Error(
|
||
|
|
"Non è possibile procedere con il pagamento della consulenza prima di aver attivato acconto e saldo",
|
||
|
|
);
|
||
|
|
}
|
||
|
|
if (!servizio.isOkConsulenza) {
|
||
|
|
const existingConsulenza = inactiveOrders.find(
|
||
|
|
(ordine) => ordine.type === OrderTypeEnum.Consulenza,
|
||
|
|
);
|
||
|
|
if (existingConsulenza) {
|
||
|
|
// consulenza già creato, procedere con questo
|
||
|
|
const consulenzaPack = await getPrezziarioByIdHandler(
|
||
|
|
existingConsulenza.packid,
|
||
|
|
);
|
||
|
|
if (!consulenzaPack) {
|
||
|
|
throw new Error("Pack consulenza non trovato");
|
||
|
|
}
|
||
|
|
if (!consulenzaPack.testo_condizioni) {
|
||
|
|
throw new Error("Condizioni non trovate per consulenza");
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
ordine_id: existingConsulenza.ordine_id,
|
||
|
|
packid: existingConsulenza.packid,
|
||
|
|
amount_cent: existingConsulenza.amount_cent,
|
||
|
|
sconto: existingConsulenza.sconto,
|
||
|
|
packName: consulenzaPack.nome_it,
|
||
|
|
packDesc: consulenzaPack.desc_it,
|
||
|
|
condizioni: consulenzaPack.testo_condizioni,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
//todo vedere se oltre a emettere un errore si vuole anche passare un messaggio alla pagina di errore
|
||
|
|
throw new Error(
|
||
|
|
"Il servizio non ha un pacchetto di consulenza, è necessario inserirlo prima di procedere",
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
throw new Error(
|
||
|
|
"Il servizio ha già una consulenza attiva, non è possibile preparare un nuovo pagamento",
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
default:
|
||
|
|
throw new Error("Tipo di ordine non valido");
|
||
|
|
}
|
||
|
|
} catch (e) {
|
||
|
|
throw new TRPCError({
|
||
|
|
code: "INTERNAL_SERVER_ERROR",
|
||
|
|
message: `Errore durante la preparazione del pagamento: ${(e as Error).message}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const getPagamentoDataForCheckout = async (ordineId: OrdiniOrdineId) => {
|
||
|
|
try {
|
||
|
|
return await db
|
||
|
|
.selectFrom("ordini")
|
||
|
|
.select([
|
||
|
|
"ordini.ordine_id",
|
||
|
|
"ordini.packid",
|
||
|
|
"ordini.amount_cent",
|
||
|
|
"ordini.servizio_id",
|
||
|
|
"ordini.userid",
|
||
|
|
"ordini.sconto",
|
||
|
|
])
|
||
|
|
.innerJoin("users", "users.id", "ordini.userid")
|
||
|
|
.select(["users.username", "users.email"])
|
||
|
|
.innerJoin("prezziario", "prezziario.idprezziario", "ordini.packid")
|
||
|
|
.select(["prezziario.nome_it", "prezziario.desc_it"])
|
||
|
|
.where("ordini.ordine_id", "=", ordineId)
|
||
|
|
.executeTakeFirstOrThrow(
|
||
|
|
() => new Error(`Payment not found - ordineId: ${ordineId}`),
|
||
|
|
);
|
||
|
|
} catch (e) {
|
||
|
|
throw new TRPCError({
|
||
|
|
code: "NOT_FOUND",
|
||
|
|
message: `Payment not found: ${(e as Error).message}`,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const SaldoTransitorioMapping: Record<number, PrezziarioIdprezziario> = {
|
||
|
|
0: "SALDOBD_1MESE" as PrezziarioIdprezziario,
|
||
|
|
1: "SALDOBD_3MESI" as PrezziarioIdprezziario,
|
||
|
|
2: "SALDOBD_6MESI" as PrezziarioIdprezziario,
|
||
|
|
3: "SALDOBD_9MESI" as PrezziarioIdprezziario,
|
||
|
|
4: "SALDOBD_12MESI" as PrezziarioIdprezziario,
|
||
|
|
};
|
||
|
|
|
||
|
|
// const ConsulenzaPriceMapping = {
|
||
|
|
// "4 + 4": "CONS_4+4" as PrezziarioIdprezziario,
|
||
|
|
// "30 Giorni": "CONS_30GG" as PrezziarioIdprezziario,
|
||
|
|
// Agevolato: "CONS_3+2" as PrezziarioIdprezziario,
|
||
|
|
// Commerciale: "CONS_COMM" as PrezziarioIdprezziario,
|
||
|
|
// Foresteria: "CONS_COMM" as PrezziarioIdprezziario,
|
||
|
|
// "Transi.Age": "CONS_3+2" as PrezziarioIdprezziario,
|
||
|
|
// Transitorio: "CONS_TRAN" as PrezziarioIdprezziario,
|
||
|
|
// };
|
||
|
|
|
||
|
|
type GetPackError = { success: false; message: string };
|
||
|
|
type GetPackResult = { success: true; pack: Prezziario };
|
||
|
|
|
||
|
|
type ServizioPacks = {
|
||
|
|
acconto: GetPackResult | GetPackError;
|
||
|
|
saldo: GetPackResult | GetPackError;
|
||
|
|
};
|
||
|
|
|
||
|
|
const AccontoSolver = async (
|
||
|
|
servizio: Servizio,
|
||
|
|
): Promise<GetPackResult | GetPackError> => {
|
||
|
|
switch (servizio.tipologia) {
|
||
|
|
case TipologiaPosizioneEnum.Transitorio: {
|
||
|
|
const pack = await getPrezziarioByIdHandler(ACCONTO_TRANSITORIO);
|
||
|
|
|
||
|
|
if (!pack) {
|
||
|
|
return { success: false, message: "Pack non trovato" };
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
pack,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
case TipologiaPosizioneEnum.Stabile: {
|
||
|
|
const pack = await getPrezziarioByIdHandler(ACCONTO_STABILE);
|
||
|
|
|
||
|
|
if (!pack) {
|
||
|
|
return { success: false, message: "Pack non trovato" };
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
pack,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
return { success: false, message: "Seleziona tipologia" };
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const SaldoSolver = async (
|
||
|
|
servizio: Servizio,
|
||
|
|
): Promise<GetPackResult | GetPackError> => {
|
||
|
|
switch (servizio.tipologia) {
|
||
|
|
case TipologiaPosizioneEnum.Transitorio: {
|
||
|
|
const mpp =
|
||
|
|
servizio.permanenza && SaldoTransitorioMapping[servizio.permanenza];
|
||
|
|
if (!servizio.permanenza || !mpp) {
|
||
|
|
return {
|
||
|
|
success: false,
|
||
|
|
message: "Permanenza non selezionata",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
const pack = await getPrezziarioByIdHandler(mpp);
|
||
|
|
if (!pack) {
|
||
|
|
return { success: false, message: "Pack non trovato" };
|
||
|
|
}
|
||
|
|
|
||
|
|
return { success: true, pack };
|
||
|
|
}
|
||
|
|
case TipologiaPosizioneEnum.Stabile: {
|
||
|
|
const pack = await getPrezziarioByIdHandler(
|
||
|
|
(servizio.budget > 700
|
||
|
|
? "SALDOCA700+"
|
||
|
|
: servizio.budget > 500
|
||
|
|
? "SALDOCA500+"
|
||
|
|
: "SALDOCA500") as PrezziarioIdprezziario,
|
||
|
|
);
|
||
|
|
if (!pack) {
|
||
|
|
return { success: false, message: "Pack non trovato" };
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
success: true,
|
||
|
|
pack,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
return { success: false, message: "Seleziona tipologia" };
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
export const getPacksPerServizio = async (
|
||
|
|
servizioId: ServizioServizioId,
|
||
|
|
): Promise<ServizioPacks> => {
|
||
|
|
const servizio = await getServizioById(servizioId);
|
||
|
|
if (!servizio) {
|
||
|
|
throw new TRPCError({
|
||
|
|
code: "NOT_FOUND",
|
||
|
|
message: "Servizio non trovato",
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
acconto: await AccontoSolver(servizio),
|
||
|
|
saldo: await SaldoSolver(servizio),
|
||
|
|
};
|
||
|
|
};
|