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 { 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 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.tipologia); 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 = { // 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; }; export const AccontoSolver = async ( tipologia: TipologiaPosizioneEnum, ): Promise => { switch (tipologia) { case TipologiaPosizioneEnum.Transitorio: { const pack = await db .selectFrom("prezziario") .selectAll() .where("isActive", "=", true) .where("order_type", "=", OrderTypeEnum.Acconto) .where("service_type", "=", TipologiaPosizioneEnum.Transitorio) .limit(1) .executeTakeFirst(); if (!pack) { return { success: false, message: "Pack non trovato" }; } return { success: true, pack, }; } case TipologiaPosizioneEnum.Stabile: { const pack = await db .selectFrom("prezziario") .selectAll() .where("isActive", "=", true) .where("order_type", "=", OrderTypeEnum.Acconto) .where("service_type", "=", TipologiaPosizioneEnum.Stabile) .limit(1) .executeTakeFirst(); if (!pack) { return { success: false, message: "Pack non trovato" }; } return { success: true, pack, }; } default: return { success: false, message: "Seleziona tipologia" }; } }; export const SaldoSolver = async ({ tipologia, permanenza, budget, }: { tipologia: TipologiaPosizioneEnum; permanenza: number | null; budget: number; }): Promise => { switch (tipologia) { case TipologiaPosizioneEnum.Transitorio: { if (permanenza === null) { return { success: false, message: "Permanenza non selezionata", }; } const pack = await db .selectFrom("prezziario") .selectAll() .where("isActive", "=", true) .where("order_type", "=", OrderTypeEnum.Saldo) .where("service_type", "=", TipologiaPosizioneEnum.Transitorio) .where("service_variant", "=", permanenza) .limit(1) .executeTakeFirst(); if (!pack) { return { success: false, message: "Pack non trovato" }; } return { success: true, pack }; } case TipologiaPosizioneEnum.Stabile: { const budget_step = budget > 700 ? 2 : budget > 500 ? 1 : 0; const pack = await db .selectFrom("prezziario") .selectAll() .where("isActive", "=", true) .where("order_type", "=", OrderTypeEnum.Saldo) .where("service_type", "=", TipologiaPosizioneEnum.Stabile) .where("service_variant", "=", budget_step) .limit(1) .executeTakeFirst(); 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.tipologia), saldo: await SaldoSolver({ tipologia: servizio.tipologia, permanenza: servizio.permanenza, budget: servizio.budget, }), }; };