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 => { 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 = { 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 => { 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 => { 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 => { 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), }; };