infoalloggi-monorepo/apps/infoalloggi/src/server/controllers/pagamenti.controller.ts

450 lines
13 KiB
TypeScript
Raw Normal View History

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 { createOrdine } from "../services/ordini.service";
import { getPrezziarioByIdHandler } from "../services/prezziario.service";
import { getServizioById } from "../services/servizio.service";
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<{success: true, data: PreparedPaymentData} | {success:false, message: string}> => {
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) {
return {
success: false,
message: `Acconto non trovato per il servizio: ${acconto.message}`,
};
}
if (!acconto.pack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per acconto: ${acconto.pack.nome_it}`,
};
}
// servizio sta aspettando attivazione acconto
const preExistingAcconto = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Acconto,
);
if (preExistingAcconto) {
// acconto già creato, procedere con questo
return {success: true, data: {
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 {success: true, data: {
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,
}};
}
return{
success: false,
message: "Il servizio ha già un acconto attivo, non è possibile preparare un nuovo pagamento"
}
}
case OrderTypeEnum.Saldo: {
if (!servizio.isOkAcconto) {
return {
success: false,
message: "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) {
return {
success: false,
message: `Saldo non trovato per il servizio: ${saldo.message}`,
};
}
if (!saldo.pack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per saldo: ${saldo.pack.nome_it}`,
};
}
const preExistingSaldo = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Saldo,
);
if (preExistingSaldo) {
// saldo già creato, procedere con questo
return{success: true, data: {
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 {success: true, data: {
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,
}};
}
return{
success: false,
message: "Il servizio ha già un saldo attivo, non è possibile preparare un nuovo pagamento"
}
}
case OrderTypeEnum.Consulenza: {
if (!servizio.isOkAcconto || !servizio.isOkSaldo) {
return {
success: false,
message: "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) {
return {
success: false,
message: `Pack della consulenza non trovato: ${existingConsulenza.packid}`,
}
}
if (!consulenzaPack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per consulenza: ${consulenzaPack.nome_it}`,
};
}
return {
success: true,
data: {
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,
},
};
}
return {
success: false,
message: "Il servizio non ha un pacchetto di consulenza, è necessario inserirlo prima di procedere",
};
}
return {
success: false,
message: "Il servizio ha già una consulenza attiva, non è possibile preparare un nuovo pagamento",
};
}
case OrderTypeEnum.Rinnovo : {
const existingRinnovo = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Rinnovo,
);
if (existingRinnovo) {
// rinnovo già creato, procedere con questo
const rinnovoPack = await getPrezziarioByIdHandler(
existingRinnovo.packid,
);
if (!rinnovoPack) {
return {
success: false,
message: `Pack del rinnovo non trovato: ${existingRinnovo.packid}`,
}
}
if (!rinnovoPack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per rinnovo: ${rinnovoPack.nome_it}`,
};
}
return {
success: true,
data: {
ordine_id: existingRinnovo.ordine_id,
packid: existingRinnovo.packid,
amount_cent: existingRinnovo.amount_cent,
sconto: existingRinnovo.sconto,
packName: rinnovoPack.nome_it,
packDesc: rinnovoPack.desc_it,
condizioni: rinnovoPack.testo_condizioni,
},
};
}
return {
success: false,
message: "Il servizio non ha un pacchetto di rinnovo, è necessario inserirlo prima di procedere",
};
}
case OrderTypeEnum.Altro : {
const existingAltro = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Altro,
);
if (existingAltro) {
// altro già creato, procedere con questo
const altroPack = await getPrezziarioByIdHandler(
existingAltro.packid,
);
if (!altroPack) {
return {
success: false,
message: `Pack dell'altro non trovato: ${existingAltro.packid}`,
}
}
if (!altroPack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per altro: ${altroPack.nome_it}`,
};
}
return {
success: true,
data: {
ordine_id: existingAltro.ordine_id,
packid: existingAltro.packid,
amount_cent: existingAltro.amount_cent,
sconto: existingAltro.sconto,
packName: altroPack.nome_it,
packDesc: altroPack.desc_it,
condizioni: altroPack.testo_condizioni,
},
};
}
return {
success: false,
message: "La preparazione del pagamento per questa tipologia non è supportata",
};
}
default:
throw new Error(`Tipo di ordine non valido: ${type satisfies never}`);
}
} 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),
};
};