From 6488a829454317f8ae9898d0d61e8f54256351db Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 20 Aug 2025 14:13:53 -0500 Subject: [PATCH 1/8] feat: add variants attributes and values tables, remove price from products --- .../migration.sql | 43 +++++++++++++++++++ prisma/schema.prisma | 35 +++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 prisma/migrations/20250820183901_add_tables_variants_products/migration.sql diff --git a/prisma/migrations/20250820183901_add_tables_variants_products/migration.sql b/prisma/migrations/20250820183901_add_tables_variants_products/migration.sql new file mode 100644 index 0000000..f64a02c --- /dev/null +++ b/prisma/migrations/20250820183901_add_tables_variants_products/migration.sql @@ -0,0 +1,43 @@ +/* + Warnings: + + - You are about to drop the column `price` on the `products` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "products" DROP COLUMN "price"; + +-- CreateTable +CREATE TABLE "variants_attributes" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "created_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "variants_attributes_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "variants_attributes_values" ( + "id" SERIAL NOT NULL, + "attribute_id" INTEGER NOT NULL, + "product_id" INTEGER NOT NULL, + "value" TEXT NOT NULL, + "price" DECIMAL(10,2) NOT NULL, + "created_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "variants_attributes_values_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "variants_attributes_name_key" ON "variants_attributes"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "variants_attributes_values_attribute_id_product_id_value_key" ON "variants_attributes_values"("attribute_id", "product_id", "value"); + +-- AddForeignKey +ALTER TABLE "variants_attributes_values" ADD CONSTRAINT "variants_attributes_values_attribute_id_fkey" FOREIGN KEY ("attribute_id") REFERENCES "variants_attributes"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "variants_attributes_values" ADD CONSTRAINT "variants_attributes_values_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "products"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e0f992b..7c30db5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -55,7 +55,6 @@ model Product { title String imgSrc String @map("img_src") alt String? - price Decimal @db.Decimal(10, 2) description String? categoryId Int? @map("category_id") isOnSale Boolean @default(false) @map("is_on_sale") @@ -63,13 +62,41 @@ model Product { createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) - category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) - cartItems CartItem[] - orderItems OrderItem[] + category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) + cartItems CartItem[] + orderItems OrderItem[] + VariantsAttributeValue VariantsAttributeValue[] @@map("products") } +model VariantsAttribute { + id Int @id @default(autoincrement()) + name String @unique + createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) + updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) + + variantsAttributeValue VariantsAttributeValue[] + + @@map("variants_attributes") +} + +model VariantsAttributeValue { + id Int @id @default(autoincrement()) + attributeId Int @map("attribute_id") + productId Int @map("product_id") + value String + price Decimal @db.Decimal(10, 2) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) + updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) + + variantAttribute VariantsAttribute @relation(fields: [attributeId], references: [id]) + product Product @relation(fields: [productId], references: [id]) + + @@unique([attributeId, productId, value], name: "unique_attribute_product_value") + @@map("variants_attributes_values") +} + model Cart { id Int @id @default(autoincrement()) sessionCartId String @unique @default(dbgenerated("gen_random_uuid()")) @map("session_cart_id") @db.Uuid From 719ffaa1cfa365c1c7769386737fc1435e2e96f3 Mon Sep 17 00:00:00 2001 From: Mike Vera <63758388+mykeVera@users.noreply.github.com> Date: Wed, 20 Aug 2025 19:46:58 -0500 Subject: [PATCH 2/8] Update prisma/schema.prisma Co-authored-by: Janet Milagros Huacahuasi Garcia --- prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7c30db5..86bee8a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -65,7 +65,7 @@ model Product { category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) cartItems CartItem[] orderItems OrderItem[] - VariantsAttributeValue VariantsAttributeValue[] + variantAttributeValues VariantAttributeValue[] @@map("products") } From 806c5113c8707e837c4c925d33c9599cc2bf83d1 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 20 Aug 2025 19:50:17 -0500 Subject: [PATCH 3/8] refactor: rename VariantsAttribute and VariantsAttributeValue models for consistency --- prisma/schema.prisma | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 86bee8a..d251f4a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -70,18 +70,18 @@ model Product { @@map("products") } -model VariantsAttribute { +model VariantAttribute { id Int @id @default(autoincrement()) name String @unique createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) - variantsAttributeValue VariantsAttributeValue[] + variantsAttributeValue VariantAttributeValue[] @@map("variants_attributes") } -model VariantsAttributeValue { +model VariantAttributeValue { id Int @id @default(autoincrement()) attributeId Int @map("attribute_id") productId Int @map("product_id") @@ -90,7 +90,7 @@ model VariantsAttributeValue { createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) - variantAttribute VariantsAttribute @relation(fields: [attributeId], references: [id]) + variantAttribute VariantAttribute @relation(fields: [attributeId], references: [id]) product Product @relation(fields: [productId], references: [id]) @@unique([attributeId, productId, value], name: "unique_attribute_product_value") From a8c0fcc1217275073bd08a9d23bbe3030d16157c Mon Sep 17 00:00:00 2001 From: Janet Huacahuasi Date: Wed, 20 Aug 2025 19:30:55 -0500 Subject: [PATCH 4/8] feat: update initial data --- prisma/initial_data.ts | 86 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/prisma/initial_data.ts b/prisma/initial_data.ts index 0520e9e..317815a 100644 --- a/prisma/initial_data.ts +++ b/prisma/initial_data.ts @@ -29,6 +29,12 @@ export const categories = [ }, ]; +export const variantAttributes = [ + { name: "talla" }, + { name: "dimensiones" }, + { name: "no aplica" }, +] + export const products = [ { title: "Polo React", @@ -371,3 +377,83 @@ export const products = [ ], }, ]; + +export const variantAttributeValues = [ + // --- POLOS (talla: S, M, L) --- + { attributeId: 1, productId: 1, value: "S", price: 20.0 }, + { attributeId: 1, productId: 1, value: "M", price: 20.0 }, + { attributeId: 1, productId: 1, value: "L", price: 20.0 }, + + { attributeId: 1, productId: 2, value: "S", price: 20.0 }, + { attributeId: 1, productId: 2, value: "M", price: 20.0 }, + { attributeId: 1, productId: 2, value: "L", price: 20.0 }, + + { attributeId: 1, productId: 3, value: "S", price: 20.0 }, + { attributeId: 1, productId: 3, value: "M", price: 20.0 }, + { attributeId: 1, productId: 3, value: "L", price: 20.0 }, + + { attributeId: 1, productId: 4, value: "S", price: 20.0 }, + { attributeId: 1, productId: 4, value: "M", price: 20.0 }, + { attributeId: 1, productId: 4, value: "L", price: 20.0 }, + + { attributeId: 1, productId: 5, value: "S", price: 25.0 }, + { attributeId: 1, productId: 5, value: "M", price: 25.0 }, + { attributeId: 1, productId: 5, value: "L", price: 25.0 }, + + { attributeId: 1, productId: 6, value: "S", price: 25.0 }, + { attributeId: 1, productId: 6, value: "M", price: 25.0 }, + { attributeId: 1, productId: 6, value: "L", price: 25.0 }, + + { attributeId: 1, productId: 7, value: "S", price: 25.0 }, + { attributeId: 1, productId: 7, value: "M", price: 25.0 }, + { attributeId: 1, productId: 7, value: "L", price: 25.0 }, + + { attributeId: 1, productId: 8, value: "S", price: 15.0 }, + { attributeId: 1, productId: 8, value: "M", price: 15.0 }, + { attributeId: 1, productId: 8, value: "L", price: 15.0 }, + + { attributeId: 1, productId: 9, value: "S", price: 15.0 }, + { attributeId: 1, productId: 9, value: "M", price: 15.0 }, + { attributeId: 1, productId: 9, value: "L", price: 15.0 }, + + // --- STICKERS (dimensiones: 3x3, 6x6, 9x9) --- + { attributeId: 2, productId: 10, value: "3x3", price: 2.99 }, + { attributeId: 2, productId: 10, value: "6x6", price: 3.99 }, + { attributeId: 2, productId: 10, value: "9x9", price: 4.99 }, + + { attributeId: 2, productId: 11, value: "3x3", price: 2.49 }, + { attributeId: 2, productId: 11, value: "6x6", price: 3.49 }, + { attributeId: 2, productId: 11, value: "9x9", price: 4.49 }, + + { attributeId: 2, productId: 12, value: "3x3", price: 3.99 }, + { attributeId: 2, productId: 12, value: "6x6", price: 4.99 }, + { attributeId: 2, productId: 12, value: "9x9", price: 5.99 }, + + { attributeId: 2, productId: 13, value: "3x3", price: 2.99 }, + { attributeId: 2, productId: 13, value: "6x6", price: 3.99 }, + { attributeId: 2, productId: 13, value: "9x9", price: 4.99 }, + + { attributeId: 2, productId: 14, value: "3x3", price: 2.49 }, + { attributeId: 2, productId: 14, value: "6x6", price: 3.49 }, + { attributeId: 2, productId: 14, value: "9x9", price: 4.49 }, + + { attributeId: 2, productId: 15, value: "3x3", price: 2.49 }, + { attributeId: 2, productId: 15, value: "6x6", price: 3.49 }, + { attributeId: 2, productId: 15, value: "9x9", price: 4.49 }, + + { attributeId: 2, productId: 16, value: "3x3", price: 2.99 }, + { attributeId: 2, productId: 16, value: "6x6", price: 3.99 }, + { attributeId: 2, productId: 16, value: "9x9", price: 4.99 }, + + { attributeId: 2, productId: 17, value: "3x3", price: 2.99 }, + { attributeId: 2, productId: 17, value: "6x6", price: 3.99 }, + { attributeId: 2, productId: 17, value: "9x9", price: .99 }, + + // --- TAZAS (no aplica: Único) --- + { attributeId: 3, productId: 18, value: "Único", price: 14.99 }, + { attributeId: 3, productId: 19, value: "Único", price: 13.99 }, + { attributeId: 3, productId: 20, value: "Único", price: 12.99 }, + { attributeId: 3, productId: 21, value: "Único", price: 15.99 }, + { attributeId: 3, productId: 22, value: "Único", price: 13.99 }, + { attributeId: 3, productId: 23, value: "Único", price: 14.99 }, +]; \ No newline at end of file From fe0cfff64563939961f2128c46fa8b5b746e1430 Mon Sep 17 00:00:00 2001 From: Janet Huacahuasi Date: Wed, 20 Aug 2025 20:34:42 -0500 Subject: [PATCH 5/8] feat: update prisma seed for product variants --- prisma/initial_data.ts | 28 +--------------------------- prisma/seed.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/prisma/initial_data.ts b/prisma/initial_data.ts index 317815a..75661f8 100644 --- a/prisma/initial_data.ts +++ b/prisma/initial_data.ts @@ -6,7 +6,6 @@ export const categories = [ { title: "Polos", slug: "polos" as CategorySlug, - imgSrc: `${imagesBaseUrl}/polos.jpg`, alt: "Hombre luciendo polo azul", description: "Polos exclusivos con diseños que todo desarrollador querrá lucir. Ideales para llevar el código a donde vayas.", @@ -14,7 +13,6 @@ export const categories = [ { title: "Tazas", slug: "tazas" as CategorySlug, - imgSrc: `${imagesBaseUrl}/tazas.jpg`, alt: "Tazas con diseño de código", description: "Tazas que combinan perfectamente con tu café matutino y tu pasión por la programación. ¡Empieza el día con estilo!", @@ -22,7 +20,6 @@ export const categories = [ { title: "Stickers", slug: "stickers" as CategorySlug, - imgSrc: `${imagesBaseUrl}/stickers.jpg`, alt: "Stickers de desarrollo web", description: "Personaliza tu espacio de trabajo con nuestros stickers únicos y muestra tu amor por el desarrollo web.", @@ -30,16 +27,15 @@ export const categories = [ ]; export const variantAttributes = [ + { name: "no aplica" }, { name: "talla" }, { name: "dimensiones" }, - { name: "no aplica" }, ] export const products = [ { title: "Polo React", imgSrc: `${imagesBaseUrl}/polos/polo-react.png`, - price: 20.0, description: "Viste tu pasión por React con estilo y comodidad en cada línea de código.", categoryId: 1, @@ -54,7 +50,6 @@ export const products = [ { title: "Polo JavaScript", imgSrc: `${imagesBaseUrl}/polos/polo-js.png`, - price: 20.0, description: "Deja que tu amor por JavaScript hable a través de cada hilo de este polo.", categoryId: 1, @@ -69,7 +64,6 @@ export const products = [ { title: "Polo Node.js", imgSrc: `${imagesBaseUrl}/polos/polo-node.png`, - price: 20.0, description: "Conéctate al estilo con este polo de Node.js, tan robusto como tu código.", categoryId: 1, @@ -84,7 +78,6 @@ export const products = [ { title: "Polo TypeScript", imgSrc: `${imagesBaseUrl}/polos/polo-ts.png`, - price: 20.0, description: "Tipa tu estilo con precisión: lleva tu pasión por TypeScript en cada hilo.", categoryId: 1, @@ -99,7 +92,6 @@ export const products = [ { title: "Polo Backend Developer", imgSrc: `${imagesBaseUrl}/polos/polo-backend.png`, - price: 25.0, description: "Domina el servidor con estilo: viste con orgullo tu título de Backend Developer.", categoryId: 1, @@ -114,7 +106,6 @@ export const products = [ { title: "Polo Frontend Developer", imgSrc: `${imagesBaseUrl}/polos/polo-frontend.png`, - price: 25.0, description: "Construye experiencias con estilo: luce con orgullo tu polo de Frontend Developer.", categoryId: 1, @@ -129,7 +120,6 @@ export const products = [ { title: "Polo Full-Stack Developer", imgSrc: `${imagesBaseUrl}/polos/polo-fullstack.png`, - price: 25.0, description: "Domina ambos mundos con estilo: lleva tu título de FullStack Developer en cada línea de tu look.", categoryId: 1, @@ -144,7 +134,6 @@ export const products = [ { title: "Polo It's A Feature", imgSrc: `${imagesBaseUrl}/polos/polo-feature.png`, - price: 15.0, description: "Cuando el bug se convierte en arte: lleva con orgullo tu polo 'It's a feature'.", categoryId: 1, @@ -159,7 +148,6 @@ export const products = [ { title: "Polo It Works On My Machine", imgSrc: `${imagesBaseUrl}/polos/polo-works.png`, - price: 15.0, description: "El clásico del desarrollador: presume tu confianza con 'It works on my machine'.", categoryId: 1, @@ -174,7 +162,6 @@ export const products = [ { title: "Sticker JavaScript", imgSrc: `${imagesBaseUrl}/stickers/sticker-js.png`, - price: 2.99, description: "Muestra tu amor por JavaScript con este elegante sticker clásico.", categoryId: 3, @@ -189,7 +176,6 @@ export const products = [ { title: "Sticker React", imgSrc: `${imagesBaseUrl}/stickers/sticker-react.png`, - price: 2.49, description: "Decora tus dispositivos con el icónico átomo giratorio de React.", categoryId: 3, @@ -204,7 +190,6 @@ export const products = [ { title: "Sticker Git", imgSrc: `${imagesBaseUrl}/stickers/sticker-git.png`, - price: 3.99, description: "Visualiza el poder del control de versiones con este sticker de Git.", categoryId: 3, @@ -219,7 +204,6 @@ export const products = [ { title: "Sticker Docker", imgSrc: `${imagesBaseUrl}/stickers/sticker-docker.png`, - price: 2.99, description: "La adorable ballena de Docker llevando contenedores en un sticker único.", categoryId: 3, @@ -234,7 +218,6 @@ export const products = [ { title: "Sticker Linux", imgSrc: `${imagesBaseUrl}/stickers/sticker-linux.png`, - price: 2.49, description: "El querido pingüino Tux, mascota oficial de Linux, en formato sticker.", categoryId: 3, @@ -249,7 +232,6 @@ export const products = [ { title: "Sticker VS Code", imgSrc: `${imagesBaseUrl}/stickers/sticker-vscode.png`, - price: 2.49, description: "El elegante logo del editor favorito de los desarrolladores.", categoryId: 3, isOnSale: false, @@ -263,7 +245,6 @@ export const products = [ { title: "Sticker GitHub", imgSrc: `${imagesBaseUrl}/stickers/sticker-github.png`, - price: 2.99, description: "El alojamiento de repositorios más popular en un sticker de alta calidad.", categoryId: 3, @@ -278,7 +259,6 @@ export const products = [ { title: "Sticker HTML", imgSrc: `${imagesBaseUrl}/stickers/sticker-html.png`, - price: 2.99, description: "El escudo naranja de HTML5, el lenguaje que estructura la web.", categoryId: 3, @@ -293,7 +273,6 @@ export const products = [ { title: "Taza JavaScript", imgSrc: `${imagesBaseUrl}/tazas/taza-js.png`, - price: 14.99, description: "Disfruta tu café mientras programas con el logo de JavaScript.", categoryId: 2, @@ -308,7 +287,6 @@ export const products = [ { title: "Taza React", imgSrc: `${imagesBaseUrl}/tazas/taza-react.png`, - price: 13.99, description: "Una taza que hace render de tu bebida favorita con estilo React.", categoryId: 2, @@ -323,7 +301,6 @@ export const products = [ { title: "Taza Git", imgSrc: `${imagesBaseUrl}/tazas/taza-git.png`, - price: 12.99, description: "Commit a tu rutina diaria de café con esta taza de Git.", categoryId: 2, isOnSale: false, @@ -337,7 +314,6 @@ export const products = [ { title: "Taza SQL", imgSrc: `${imagesBaseUrl}/tazas/taza-sql.png`, - price: 15.99, description: "Tu amor por los lenguajes estructurados en una taza de SQL.", categoryId: 2, isOnSale: false, @@ -351,7 +327,6 @@ export const products = [ { title: "Taza Linux", imgSrc: `${imagesBaseUrl}/tazas/taza-linux.png`, - price: 13.99, description: "Toma tu café con la libertad que solo Linux puede ofrecer.", categoryId: 2, isOnSale: false, @@ -365,7 +340,6 @@ export const products = [ { title: "Taza GitHub", imgSrc: `${imagesBaseUrl}/tazas/taza-github.png`, - price: 14.99, description: "Colabora con tu café en esta taza con el logo de GitHub.", categoryId: 2, isOnSale: false, diff --git a/prisma/seed.ts b/prisma/seed.ts index 106da46..4a2b3e5 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,4 +1,4 @@ -import { categories, products } from "./initial_data"; +import { categories, products, variantAttributes, variantAttributeValues } from "./initial_data"; import { PrismaClient } from "../generated/prisma/client"; const prisma = new PrismaClient(); @@ -9,10 +9,23 @@ async function seedDb() { }); console.log("1. Categories successfully inserted"); + await prisma.variantAttribute.createMany({ + data: variantAttributes, + }) + console.log("2. Variant Attributes successfully inserted"); + await prisma.product.createMany({ data: products, + }); - console.log("2. Products successfully inserted"); + console.log("3. Products successfully inserted"); + + await prisma.variantAttributeValue.createMany({ + data: variantAttributeValues, + }) + + console.log("4. Variant Attribute Values successfully inserted"); + } seedDb() From 44efc5a4f55fdcce2ceb61ad8e3282411846bab0 Mon Sep 17 00:00:00 2001 From: Janet Huacahuasi Date: Wed, 20 Aug 2025 20:49:39 -0500 Subject: [PATCH 6/8] fix: add category images --- prisma/initial_data.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prisma/initial_data.ts b/prisma/initial_data.ts index 75661f8..e8aee38 100644 --- a/prisma/initial_data.ts +++ b/prisma/initial_data.ts @@ -6,6 +6,7 @@ export const categories = [ { title: "Polos", slug: "polos" as CategorySlug, + imgSrc: `${imagesBaseUrl}/polos.jpg`, alt: "Hombre luciendo polo azul", description: "Polos exclusivos con diseños que todo desarrollador querrá lucir. Ideales para llevar el código a donde vayas.", @@ -13,6 +14,7 @@ export const categories = [ { title: "Tazas", slug: "tazas" as CategorySlug, + imgSrc: `${imagesBaseUrl}/tazas.jpg`, alt: "Tazas con diseño de código", description: "Tazas que combinan perfectamente con tu café matutino y tu pasión por la programación. ¡Empieza el día con estilo!", @@ -20,6 +22,7 @@ export const categories = [ { title: "Stickers", slug: "stickers" as CategorySlug, + imgSrc: `${imagesBaseUrl}/stickers.jpg`, alt: "Stickers de desarrollo web", description: "Personaliza tu espacio de trabajo con nuestros stickers únicos y muestra tu amor por el desarrollo web.", From 1bf4cd90ca782d8cc55d38b615ad678266d942de Mon Sep 17 00:00:00 2001 From: mike Date: Fri, 22 Aug 2025 21:41:42 -0500 Subject: [PATCH 7/8] feat: update cart schema and service to use attribute_value_id instead of product_id --- .../migration.sql | 23 ++++ prisma/schema.prisma | 10 +- src/models/cart.model.ts | 1 + src/services/cart.service.ts | 117 +++++++++++------- 4 files changed, 99 insertions(+), 52 deletions(-) create mode 100644 prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql diff --git a/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql b/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql new file mode 100644 index 0000000..aab9f1b --- /dev/null +++ b/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - You are about to drop the column `product_id` on the `cart_items` table. All the data in the column will be lost. + - A unique constraint covering the columns `[cart_id,attribute_value_id]` on the table `cart_items` will be added. If there are existing duplicate values, this will fail. + - Added the required column `attribute_value_id` to the `cart_items` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "cart_items" DROP CONSTRAINT "cart_items_product_id_fkey"; + +-- DropIndex +DROP INDEX "cart_items_cart_id_product_id_key"; + +-- AlterTable +ALTER TABLE "cart_items" DROP COLUMN "product_id", +ADD COLUMN "attribute_value_id" INTEGER NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "cart_items_cart_id_attribute_value_id_key" ON "cart_items"("cart_id", "attribute_value_id"); + +-- AddForeignKey +ALTER TABLE "cart_items" ADD CONSTRAINT "cart_items_attribute_value_id_fkey" FOREIGN KEY ("attribute_value_id") REFERENCES "variants_attributes_values"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d251f4a..b8df94a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -63,7 +63,7 @@ model Product { updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) - cartItems CartItem[] + orderItems OrderItem[] variantAttributeValues VariantAttributeValue[] @@ -92,6 +92,8 @@ model VariantAttributeValue { variantAttribute VariantAttribute @relation(fields: [attributeId], references: [id]) product Product @relation(fields: [productId], references: [id]) + + CartItem CartItem[] @@unique([attributeId, productId, value], name: "unique_attribute_product_value") @@map("variants_attributes_values") @@ -113,15 +115,15 @@ model Cart { model CartItem { id Int @id @default(autoincrement()) cartId Int @map("cart_id") - productId Int @map("product_id") + attributeValueId Int @map("attribute_value_id") quantity Int createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) cart Cart @relation(fields: [cartId], references: [id], onDelete: Cascade) - product Product @relation(fields: [productId], references: [id], onDelete: Cascade) + variantAttributeValue VariantAttributeValue @relation(fields: [attributeValueId], references: [id], onDelete: Cascade) - @@unique([cartId, productId], name: "unique_cart_item") + @@unique([cartId, attributeValueId], name: "unique_cart_item") @@map("cart_items") } diff --git a/src/models/cart.model.ts b/src/models/cart.model.ts index ad4206a..8550190 100644 --- a/src/models/cart.model.ts +++ b/src/models/cart.model.ts @@ -33,6 +33,7 @@ export type CartProductInfo = Pick< export type CartItemWithProduct = { product: CartProductInfo; quantity: number; + attributeId: number; }; // Tipo para el carrito con items y productos incluidos diff --git a/src/services/cart.service.ts b/src/services/cart.service.ts index f742706..d3a96e6 100644 --- a/src/services/cart.service.ts +++ b/src/services/cart.service.ts @@ -20,18 +20,22 @@ async function getCart( if (!whereCondition) return null; const data = await prisma.cart.findFirst({ + // se modifico esta funcion where: whereCondition, include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, @@ -49,9 +53,10 @@ async function getCart( items: data.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } @@ -82,14 +87,17 @@ export async function getOrCreateCart( include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, @@ -104,9 +112,10 @@ export async function getOrCreateCart( items: newCart.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } @@ -130,7 +139,7 @@ export async function createRemoteItems( await prisma.cartItem.createMany({ data: items.map((item) => ({ cartId: cart.id, - productId: item.product.id, + attributeValueId: item.attributeId, // modificar quantity: item.quantity, })), }); @@ -146,12 +155,14 @@ export async function createRemoteItems( export async function alterQuantityCartItem( userId: User["id"] | undefined, sessionCartId: string | undefined, - productId: number, + attributeId: number, quantity: number = 1 ): Promise { const cart = await getOrCreateCart(userId, sessionCartId); - const existingItem = cart.items.find((item) => item.product.id === productId); + const existingItem = cart.items.find( + (item) => item.attributeValueId === attributeId + ); if (existingItem) { const newQuantity = existingItem.quantity + quantity; @@ -170,7 +181,7 @@ export async function alterQuantityCartItem( await prisma.cartItem.create({ data: { cartId: cart.id, - productId, + attributeValueId: attributeId, quantity, }, }); @@ -236,14 +247,17 @@ export async function linkCartToUser( include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, @@ -258,9 +272,10 @@ export async function linkCartToUser( items: updatedCart.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } @@ -285,41 +300,46 @@ export async function mergeGuestCartWithUserCart( include: { items: { include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - price: true, - isOnSale: true, + variantAttributeValue: { + include: { + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + isOnSale: true, + }, + }, }, }, }, }, }, }); + return { ...updatedCart, items: updatedCart.items.map((item) => ({ ...item, product: { - ...item.product, - price: item.product.price.toNumber(), + ...item.variantAttributeValue.product, + price: item.variantAttributeValue.price.toNumber(), }, + variantAttributeValue: item.variantAttributeValue, })), }; } // Obtener productos duplicados para eliminarlos del carrito del usuario - const guestProductIds = guestCart.items.map((item) => item.productId); + const guestAttributeValueIds = guestCart.items.map((item) => item.attributeValueId); // Eliminar productos del carrito usuario que también existan en el carrito invitado await prisma.cartItem.deleteMany({ where: { cartId: userCart.id, - productId: { - in: guestProductIds, + attributeValueId: { + in: guestAttributeValueIds, }, }, }); @@ -328,7 +348,7 @@ export async function mergeGuestCartWithUserCart( await prisma.cartItem.createMany({ data: guestCart.items.map((item) => ({ cartId: userCart.id, - productId: item.productId, + attributeValueId: item.attributeValueId, quantity: item.quantity, })), }); @@ -341,3 +361,4 @@ export async function mergeGuestCartWithUserCart( // Devolver el carrito actualizado del usuario return await getCart(userId); } + From 6856380a098b355aab2d954f7c6cae49497ca4a1 Mon Sep 17 00:00:00 2001 From: Mike Vera <63758388+mykeVera@users.noreply.github.com> Date: Fri, 22 Aug 2025 21:48:09 -0500 Subject: [PATCH 8/8] Revert "feat: update cart schema and service to use attribute_value_id instead of product_id" --- prisma/initial_data.ts | 109 ++++------------ .../migration.sql | 43 ------- .../migration.sql | 23 ---- prisma/schema.prisma | 43 ++----- prisma/seed.ts | 17 +-- src/models/cart.model.ts | 1 - src/services/cart.service.ts | 117 +++++++----------- 7 files changed, 80 insertions(+), 273 deletions(-) delete mode 100644 prisma/migrations/20250820183901_add_tables_variants_products/migration.sql delete mode 100644 prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql diff --git a/prisma/initial_data.ts b/prisma/initial_data.ts index e8aee38..0520e9e 100644 --- a/prisma/initial_data.ts +++ b/prisma/initial_data.ts @@ -29,16 +29,11 @@ export const categories = [ }, ]; -export const variantAttributes = [ - { name: "no aplica" }, - { name: "talla" }, - { name: "dimensiones" }, -] - export const products = [ { title: "Polo React", imgSrc: `${imagesBaseUrl}/polos/polo-react.png`, + price: 20.0, description: "Viste tu pasión por React con estilo y comodidad en cada línea de código.", categoryId: 1, @@ -53,6 +48,7 @@ export const products = [ { title: "Polo JavaScript", imgSrc: `${imagesBaseUrl}/polos/polo-js.png`, + price: 20.0, description: "Deja que tu amor por JavaScript hable a través de cada hilo de este polo.", categoryId: 1, @@ -67,6 +63,7 @@ export const products = [ { title: "Polo Node.js", imgSrc: `${imagesBaseUrl}/polos/polo-node.png`, + price: 20.0, description: "Conéctate al estilo con este polo de Node.js, tan robusto como tu código.", categoryId: 1, @@ -81,6 +78,7 @@ export const products = [ { title: "Polo TypeScript", imgSrc: `${imagesBaseUrl}/polos/polo-ts.png`, + price: 20.0, description: "Tipa tu estilo con precisión: lleva tu pasión por TypeScript en cada hilo.", categoryId: 1, @@ -95,6 +93,7 @@ export const products = [ { title: "Polo Backend Developer", imgSrc: `${imagesBaseUrl}/polos/polo-backend.png`, + price: 25.0, description: "Domina el servidor con estilo: viste con orgullo tu título de Backend Developer.", categoryId: 1, @@ -109,6 +108,7 @@ export const products = [ { title: "Polo Frontend Developer", imgSrc: `${imagesBaseUrl}/polos/polo-frontend.png`, + price: 25.0, description: "Construye experiencias con estilo: luce con orgullo tu polo de Frontend Developer.", categoryId: 1, @@ -123,6 +123,7 @@ export const products = [ { title: "Polo Full-Stack Developer", imgSrc: `${imagesBaseUrl}/polos/polo-fullstack.png`, + price: 25.0, description: "Domina ambos mundos con estilo: lleva tu título de FullStack Developer en cada línea de tu look.", categoryId: 1, @@ -137,6 +138,7 @@ export const products = [ { title: "Polo It's A Feature", imgSrc: `${imagesBaseUrl}/polos/polo-feature.png`, + price: 15.0, description: "Cuando el bug se convierte en arte: lleva con orgullo tu polo 'It's a feature'.", categoryId: 1, @@ -151,6 +153,7 @@ export const products = [ { title: "Polo It Works On My Machine", imgSrc: `${imagesBaseUrl}/polos/polo-works.png`, + price: 15.0, description: "El clásico del desarrollador: presume tu confianza con 'It works on my machine'.", categoryId: 1, @@ -165,6 +168,7 @@ export const products = [ { title: "Sticker JavaScript", imgSrc: `${imagesBaseUrl}/stickers/sticker-js.png`, + price: 2.99, description: "Muestra tu amor por JavaScript con este elegante sticker clásico.", categoryId: 3, @@ -179,6 +183,7 @@ export const products = [ { title: "Sticker React", imgSrc: `${imagesBaseUrl}/stickers/sticker-react.png`, + price: 2.49, description: "Decora tus dispositivos con el icónico átomo giratorio de React.", categoryId: 3, @@ -193,6 +198,7 @@ export const products = [ { title: "Sticker Git", imgSrc: `${imagesBaseUrl}/stickers/sticker-git.png`, + price: 3.99, description: "Visualiza el poder del control de versiones con este sticker de Git.", categoryId: 3, @@ -207,6 +213,7 @@ export const products = [ { title: "Sticker Docker", imgSrc: `${imagesBaseUrl}/stickers/sticker-docker.png`, + price: 2.99, description: "La adorable ballena de Docker llevando contenedores en un sticker único.", categoryId: 3, @@ -221,6 +228,7 @@ export const products = [ { title: "Sticker Linux", imgSrc: `${imagesBaseUrl}/stickers/sticker-linux.png`, + price: 2.49, description: "El querido pingüino Tux, mascota oficial de Linux, en formato sticker.", categoryId: 3, @@ -235,6 +243,7 @@ export const products = [ { title: "Sticker VS Code", imgSrc: `${imagesBaseUrl}/stickers/sticker-vscode.png`, + price: 2.49, description: "El elegante logo del editor favorito de los desarrolladores.", categoryId: 3, isOnSale: false, @@ -248,6 +257,7 @@ export const products = [ { title: "Sticker GitHub", imgSrc: `${imagesBaseUrl}/stickers/sticker-github.png`, + price: 2.99, description: "El alojamiento de repositorios más popular en un sticker de alta calidad.", categoryId: 3, @@ -262,6 +272,7 @@ export const products = [ { title: "Sticker HTML", imgSrc: `${imagesBaseUrl}/stickers/sticker-html.png`, + price: 2.99, description: "El escudo naranja de HTML5, el lenguaje que estructura la web.", categoryId: 3, @@ -276,6 +287,7 @@ export const products = [ { title: "Taza JavaScript", imgSrc: `${imagesBaseUrl}/tazas/taza-js.png`, + price: 14.99, description: "Disfruta tu café mientras programas con el logo de JavaScript.", categoryId: 2, @@ -290,6 +302,7 @@ export const products = [ { title: "Taza React", imgSrc: `${imagesBaseUrl}/tazas/taza-react.png`, + price: 13.99, description: "Una taza que hace render de tu bebida favorita con estilo React.", categoryId: 2, @@ -304,6 +317,7 @@ export const products = [ { title: "Taza Git", imgSrc: `${imagesBaseUrl}/tazas/taza-git.png`, + price: 12.99, description: "Commit a tu rutina diaria de café con esta taza de Git.", categoryId: 2, isOnSale: false, @@ -317,6 +331,7 @@ export const products = [ { title: "Taza SQL", imgSrc: `${imagesBaseUrl}/tazas/taza-sql.png`, + price: 15.99, description: "Tu amor por los lenguajes estructurados en una taza de SQL.", categoryId: 2, isOnSale: false, @@ -330,6 +345,7 @@ export const products = [ { title: "Taza Linux", imgSrc: `${imagesBaseUrl}/tazas/taza-linux.png`, + price: 13.99, description: "Toma tu café con la libertad que solo Linux puede ofrecer.", categoryId: 2, isOnSale: false, @@ -343,6 +359,7 @@ export const products = [ { title: "Taza GitHub", imgSrc: `${imagesBaseUrl}/tazas/taza-github.png`, + price: 14.99, description: "Colabora con tu café en esta taza con el logo de GitHub.", categoryId: 2, isOnSale: false, @@ -354,83 +371,3 @@ export const products = [ ], }, ]; - -export const variantAttributeValues = [ - // --- POLOS (talla: S, M, L) --- - { attributeId: 1, productId: 1, value: "S", price: 20.0 }, - { attributeId: 1, productId: 1, value: "M", price: 20.0 }, - { attributeId: 1, productId: 1, value: "L", price: 20.0 }, - - { attributeId: 1, productId: 2, value: "S", price: 20.0 }, - { attributeId: 1, productId: 2, value: "M", price: 20.0 }, - { attributeId: 1, productId: 2, value: "L", price: 20.0 }, - - { attributeId: 1, productId: 3, value: "S", price: 20.0 }, - { attributeId: 1, productId: 3, value: "M", price: 20.0 }, - { attributeId: 1, productId: 3, value: "L", price: 20.0 }, - - { attributeId: 1, productId: 4, value: "S", price: 20.0 }, - { attributeId: 1, productId: 4, value: "M", price: 20.0 }, - { attributeId: 1, productId: 4, value: "L", price: 20.0 }, - - { attributeId: 1, productId: 5, value: "S", price: 25.0 }, - { attributeId: 1, productId: 5, value: "M", price: 25.0 }, - { attributeId: 1, productId: 5, value: "L", price: 25.0 }, - - { attributeId: 1, productId: 6, value: "S", price: 25.0 }, - { attributeId: 1, productId: 6, value: "M", price: 25.0 }, - { attributeId: 1, productId: 6, value: "L", price: 25.0 }, - - { attributeId: 1, productId: 7, value: "S", price: 25.0 }, - { attributeId: 1, productId: 7, value: "M", price: 25.0 }, - { attributeId: 1, productId: 7, value: "L", price: 25.0 }, - - { attributeId: 1, productId: 8, value: "S", price: 15.0 }, - { attributeId: 1, productId: 8, value: "M", price: 15.0 }, - { attributeId: 1, productId: 8, value: "L", price: 15.0 }, - - { attributeId: 1, productId: 9, value: "S", price: 15.0 }, - { attributeId: 1, productId: 9, value: "M", price: 15.0 }, - { attributeId: 1, productId: 9, value: "L", price: 15.0 }, - - // --- STICKERS (dimensiones: 3x3, 6x6, 9x9) --- - { attributeId: 2, productId: 10, value: "3x3", price: 2.99 }, - { attributeId: 2, productId: 10, value: "6x6", price: 3.99 }, - { attributeId: 2, productId: 10, value: "9x9", price: 4.99 }, - - { attributeId: 2, productId: 11, value: "3x3", price: 2.49 }, - { attributeId: 2, productId: 11, value: "6x6", price: 3.49 }, - { attributeId: 2, productId: 11, value: "9x9", price: 4.49 }, - - { attributeId: 2, productId: 12, value: "3x3", price: 3.99 }, - { attributeId: 2, productId: 12, value: "6x6", price: 4.99 }, - { attributeId: 2, productId: 12, value: "9x9", price: 5.99 }, - - { attributeId: 2, productId: 13, value: "3x3", price: 2.99 }, - { attributeId: 2, productId: 13, value: "6x6", price: 3.99 }, - { attributeId: 2, productId: 13, value: "9x9", price: 4.99 }, - - { attributeId: 2, productId: 14, value: "3x3", price: 2.49 }, - { attributeId: 2, productId: 14, value: "6x6", price: 3.49 }, - { attributeId: 2, productId: 14, value: "9x9", price: 4.49 }, - - { attributeId: 2, productId: 15, value: "3x3", price: 2.49 }, - { attributeId: 2, productId: 15, value: "6x6", price: 3.49 }, - { attributeId: 2, productId: 15, value: "9x9", price: 4.49 }, - - { attributeId: 2, productId: 16, value: "3x3", price: 2.99 }, - { attributeId: 2, productId: 16, value: "6x6", price: 3.99 }, - { attributeId: 2, productId: 16, value: "9x9", price: 4.99 }, - - { attributeId: 2, productId: 17, value: "3x3", price: 2.99 }, - { attributeId: 2, productId: 17, value: "6x6", price: 3.99 }, - { attributeId: 2, productId: 17, value: "9x9", price: .99 }, - - // --- TAZAS (no aplica: Único) --- - { attributeId: 3, productId: 18, value: "Único", price: 14.99 }, - { attributeId: 3, productId: 19, value: "Único", price: 13.99 }, - { attributeId: 3, productId: 20, value: "Único", price: 12.99 }, - { attributeId: 3, productId: 21, value: "Único", price: 15.99 }, - { attributeId: 3, productId: 22, value: "Único", price: 13.99 }, - { attributeId: 3, productId: 23, value: "Único", price: 14.99 }, -]; \ No newline at end of file diff --git a/prisma/migrations/20250820183901_add_tables_variants_products/migration.sql b/prisma/migrations/20250820183901_add_tables_variants_products/migration.sql deleted file mode 100644 index f64a02c..0000000 --- a/prisma/migrations/20250820183901_add_tables_variants_products/migration.sql +++ /dev/null @@ -1,43 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `price` on the `products` table. All the data in the column will be lost. - -*/ --- AlterTable -ALTER TABLE "products" DROP COLUMN "price"; - --- CreateTable -CREATE TABLE "variants_attributes" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "created_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "variants_attributes_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "variants_attributes_values" ( - "id" SERIAL NOT NULL, - "attribute_id" INTEGER NOT NULL, - "product_id" INTEGER NOT NULL, - "value" TEXT NOT NULL, - "price" DECIMAL(10,2) NOT NULL, - "created_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "variants_attributes_values_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "variants_attributes_name_key" ON "variants_attributes"("name"); - --- CreateIndex -CREATE UNIQUE INDEX "variants_attributes_values_attribute_id_product_id_value_key" ON "variants_attributes_values"("attribute_id", "product_id", "value"); - --- AddForeignKey -ALTER TABLE "variants_attributes_values" ADD CONSTRAINT "variants_attributes_values_attribute_id_fkey" FOREIGN KEY ("attribute_id") REFERENCES "variants_attributes"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "variants_attributes_values" ADD CONSTRAINT "variants_attributes_values_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "products"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql b/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql deleted file mode 100644 index aab9f1b..0000000 --- a/prisma/migrations/20250822015032_update_table_cart_with_attribute_id/migration.sql +++ /dev/null @@ -1,23 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `product_id` on the `cart_items` table. All the data in the column will be lost. - - A unique constraint covering the columns `[cart_id,attribute_value_id]` on the table `cart_items` will be added. If there are existing duplicate values, this will fail. - - Added the required column `attribute_value_id` to the `cart_items` table without a default value. This is not possible if the table is not empty. - -*/ --- DropForeignKey -ALTER TABLE "cart_items" DROP CONSTRAINT "cart_items_product_id_fkey"; - --- DropIndex -DROP INDEX "cart_items_cart_id_product_id_key"; - --- AlterTable -ALTER TABLE "cart_items" DROP COLUMN "product_id", -ADD COLUMN "attribute_value_id" INTEGER NOT NULL; - --- CreateIndex -CREATE UNIQUE INDEX "cart_items_cart_id_attribute_value_id_key" ON "cart_items"("cart_id", "attribute_value_id"); - --- AddForeignKey -ALTER TABLE "cart_items" ADD CONSTRAINT "cart_items_attribute_value_id_fkey" FOREIGN KEY ("attribute_value_id") REFERENCES "variants_attributes_values"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b8df94a..e0f992b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -55,6 +55,7 @@ model Product { title String imgSrc String @map("img_src") alt String? + price Decimal @db.Decimal(10, 2) description String? categoryId Int? @map("category_id") isOnSale Boolean @default(false) @map("is_on_sale") @@ -62,43 +63,13 @@ model Product { createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) - category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) - - orderItems OrderItem[] - variantAttributeValues VariantAttributeValue[] + category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull) + cartItems CartItem[] + orderItems OrderItem[] @@map("products") } -model VariantAttribute { - id Int @id @default(autoincrement()) - name String @unique - createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) - updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) - - variantsAttributeValue VariantAttributeValue[] - - @@map("variants_attributes") -} - -model VariantAttributeValue { - id Int @id @default(autoincrement()) - attributeId Int @map("attribute_id") - productId Int @map("product_id") - value String - price Decimal @db.Decimal(10, 2) - createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) - updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) - - variantAttribute VariantAttribute @relation(fields: [attributeId], references: [id]) - product Product @relation(fields: [productId], references: [id]) - - CartItem CartItem[] - - @@unique([attributeId, productId, value], name: "unique_attribute_product_value") - @@map("variants_attributes_values") -} - model Cart { id Int @id @default(autoincrement()) sessionCartId String @unique @default(dbgenerated("gen_random_uuid()")) @map("session_cart_id") @db.Uuid @@ -115,15 +86,15 @@ model Cart { model CartItem { id Int @id @default(autoincrement()) cartId Int @map("cart_id") - attributeValueId Int @map("attribute_value_id") + productId Int @map("product_id") quantity Int createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(0) updatedAt DateTime @default(now()) @map("updated_at") @db.Timestamp(0) cart Cart @relation(fields: [cartId], references: [id], onDelete: Cascade) - variantAttributeValue VariantAttributeValue @relation(fields: [attributeValueId], references: [id], onDelete: Cascade) + product Product @relation(fields: [productId], references: [id], onDelete: Cascade) - @@unique([cartId, attributeValueId], name: "unique_cart_item") + @@unique([cartId, productId], name: "unique_cart_item") @@map("cart_items") } diff --git a/prisma/seed.ts b/prisma/seed.ts index 4a2b3e5..106da46 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,4 +1,4 @@ -import { categories, products, variantAttributes, variantAttributeValues } from "./initial_data"; +import { categories, products } from "./initial_data"; import { PrismaClient } from "../generated/prisma/client"; const prisma = new PrismaClient(); @@ -9,23 +9,10 @@ async function seedDb() { }); console.log("1. Categories successfully inserted"); - await prisma.variantAttribute.createMany({ - data: variantAttributes, - }) - console.log("2. Variant Attributes successfully inserted"); - await prisma.product.createMany({ data: products, - }); - console.log("3. Products successfully inserted"); - - await prisma.variantAttributeValue.createMany({ - data: variantAttributeValues, - }) - - console.log("4. Variant Attribute Values successfully inserted"); - + console.log("2. Products successfully inserted"); } seedDb() diff --git a/src/models/cart.model.ts b/src/models/cart.model.ts index 8550190..ad4206a 100644 --- a/src/models/cart.model.ts +++ b/src/models/cart.model.ts @@ -33,7 +33,6 @@ export type CartProductInfo = Pick< export type CartItemWithProduct = { product: CartProductInfo; quantity: number; - attributeId: number; }; // Tipo para el carrito con items y productos incluidos diff --git a/src/services/cart.service.ts b/src/services/cart.service.ts index d3a96e6..f742706 100644 --- a/src/services/cart.service.ts +++ b/src/services/cart.service.ts @@ -20,22 +20,18 @@ async function getCart( if (!whereCondition) return null; const data = await prisma.cart.findFirst({ - // se modifico esta funcion where: whereCondition, include: { items: { include: { - variantAttributeValue: { - include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - isOnSale: true, - }, - }, + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + price: true, + isOnSale: true, }, }, }, @@ -53,10 +49,9 @@ async function getCart( items: data.items.map((item) => ({ ...item, product: { - ...item.variantAttributeValue.product, - price: item.variantAttributeValue.price.toNumber(), + ...item.product, + price: item.product.price.toNumber(), }, - variantAttributeValue: item.variantAttributeValue, })), }; } @@ -87,17 +82,14 @@ export async function getOrCreateCart( include: { items: { include: { - variantAttributeValue: { - include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - isOnSale: true, - }, - }, + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + price: true, + isOnSale: true, }, }, }, @@ -112,10 +104,9 @@ export async function getOrCreateCart( items: newCart.items.map((item) => ({ ...item, product: { - ...item.variantAttributeValue.product, - price: item.variantAttributeValue.price.toNumber(), + ...item.product, + price: item.product.price.toNumber(), }, - variantAttributeValue: item.variantAttributeValue, })), }; } @@ -139,7 +130,7 @@ export async function createRemoteItems( await prisma.cartItem.createMany({ data: items.map((item) => ({ cartId: cart.id, - attributeValueId: item.attributeId, // modificar + productId: item.product.id, quantity: item.quantity, })), }); @@ -155,14 +146,12 @@ export async function createRemoteItems( export async function alterQuantityCartItem( userId: User["id"] | undefined, sessionCartId: string | undefined, - attributeId: number, + productId: number, quantity: number = 1 ): Promise { const cart = await getOrCreateCart(userId, sessionCartId); - const existingItem = cart.items.find( - (item) => item.attributeValueId === attributeId - ); + const existingItem = cart.items.find((item) => item.product.id === productId); if (existingItem) { const newQuantity = existingItem.quantity + quantity; @@ -181,7 +170,7 @@ export async function alterQuantityCartItem( await prisma.cartItem.create({ data: { cartId: cart.id, - attributeValueId: attributeId, + productId, quantity, }, }); @@ -247,17 +236,14 @@ export async function linkCartToUser( include: { items: { include: { - variantAttributeValue: { - include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - isOnSale: true, - }, - }, + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + price: true, + isOnSale: true, }, }, }, @@ -272,10 +258,9 @@ export async function linkCartToUser( items: updatedCart.items.map((item) => ({ ...item, product: { - ...item.variantAttributeValue.product, - price: item.variantAttributeValue.price.toNumber(), + ...item.product, + price: item.product.price.toNumber(), }, - variantAttributeValue: item.variantAttributeValue, })), }; } @@ -300,46 +285,41 @@ export async function mergeGuestCartWithUserCart( include: { items: { include: { - variantAttributeValue: { - include: { - product: { - select: { - id: true, - title: true, - imgSrc: true, - alt: true, - isOnSale: true, - }, - }, + product: { + select: { + id: true, + title: true, + imgSrc: true, + alt: true, + price: true, + isOnSale: true, }, }, }, }, }, }); - return { ...updatedCart, items: updatedCart.items.map((item) => ({ ...item, product: { - ...item.variantAttributeValue.product, - price: item.variantAttributeValue.price.toNumber(), + ...item.product, + price: item.product.price.toNumber(), }, - variantAttributeValue: item.variantAttributeValue, })), }; } // Obtener productos duplicados para eliminarlos del carrito del usuario - const guestAttributeValueIds = guestCart.items.map((item) => item.attributeValueId); + const guestProductIds = guestCart.items.map((item) => item.productId); // Eliminar productos del carrito usuario que también existan en el carrito invitado await prisma.cartItem.deleteMany({ where: { cartId: userCart.id, - attributeValueId: { - in: guestAttributeValueIds, + productId: { + in: guestProductIds, }, }, }); @@ -348,7 +328,7 @@ export async function mergeGuestCartWithUserCart( await prisma.cartItem.createMany({ data: guestCart.items.map((item) => ({ cartId: userCart.id, - attributeValueId: item.attributeValueId, + productId: item.productId, quantity: item.quantity, })), }); @@ -361,4 +341,3 @@ export async function mergeGuestCartWithUserCart( // Devolver el carrito actualizado del usuario return await getCart(userId); } -