diff --git a/src/dto/food-detail.dto.ts b/src/dto/food-detail.dto.ts index e5acc99..20370c5 100644 --- a/src/dto/food-detail.dto.ts +++ b/src/dto/food-detail.dto.ts @@ -32,9 +32,11 @@ class BasicCustomization { description: TextByLang[]; } interface PackagingInfo { + packaging_id: number; image_url: string; name: TextByLang[]; description: TextByLang[]; price: number; currency: string; + is_default: boolean; } diff --git a/src/entity/cart-item.entity.ts b/src/entity/cart-item.entity.ts index c68fabf..dbcebdb 100644 --- a/src/entity/cart-item.entity.ts +++ b/src/entity/cart-item.entity.ts @@ -2,8 +2,11 @@ import { Column, CreateDateColumn, Entity, + JoinColumn, + ManyToOne, PrimaryGeneratedColumn, } from 'typeorm'; +import { Packaging } from './packaging.entity'; @Entity('Cart_Item') export class CartItem { @@ -40,6 +43,9 @@ export class CartItem { @Column({ type: 'int', unique: false, nullable: true }) public restaurant_id: number; + @Column({ type: 'int', unique: false, nullable: true }) + public packaging_id: number; + @CreateDateColumn({ type: 'datetime', nullable: false, @@ -47,4 +53,9 @@ export class CartItem { default: () => 'CURRENT_TIMESTAMP', }) public created_at: Date; + + //RELATIONSHIP + @ManyToOne(() => Packaging) + @JoinColumn({ name: 'packaging_id', referencedColumnName: 'packaging_id' }) + public packaging_obj: Packaging; } diff --git a/src/entity/menuitem-packaging.entity.ts b/src/entity/menuitem-packaging.entity.ts index cafba2c..5925da2 100644 --- a/src/entity/menuitem-packaging.entity.ts +++ b/src/entity/menuitem-packaging.entity.ts @@ -22,6 +22,9 @@ export class MenuItemPackaging { @Column({ type: 'int', nullable: false }) public image: number; + @Column({ type: 'tinyint', nullable: false, unique: false }) + public is_default: number; + @CreateDateColumn({ type: 'datetime', nullable: false, diff --git a/src/entity/packaging.entity.ts b/src/entity/packaging.entity.ts index defb65e..72d537e 100644 --- a/src/entity/packaging.entity.ts +++ b/src/entity/packaging.entity.ts @@ -3,11 +3,8 @@ import { CreateDateColumn, PrimaryGeneratedColumn, Column, - ManyToOne, - JoinColumn, OneToMany, } from 'typeorm'; -import { MenuItem } from './menu-item.entity'; import { PackagingExt } from './packaging-ext.entity'; import { Media } from './media.entity'; @@ -38,13 +35,6 @@ export class Packaging { //RELATIONSHIPS - // @ManyToOne(() => MenuItem, (menuItem) => menuItem.packaging_obj) - // @JoinColumn({ - // name: 'menu_item_id', - // referencedColumnName: 'menu_item_id', - // }) - // public menu_item_obj: MenuItem; - @OneToMany(() => PackagingExt, (packagingExt) => packagingExt.packaging_obj) public packaging_ext_obj: PackagingExt[]; diff --git a/src/feature/cart/cart.controller.ts b/src/feature/cart/cart.controller.ts index 9bce96d..9a1622f 100644 --- a/src/feature/cart/cart.controller.ts +++ b/src/feature/cart/cart.controller.ts @@ -36,6 +36,8 @@ export class CartController { advanced_taste_customization_obj, basic_taste_customization_obj, notes, + lang, + packaging_id, } = data; const res = new AddToCartResponse(200, ''); try { @@ -46,6 +48,7 @@ export class CartController { advanced_taste_customization_obj, basic_taste_customization_obj, notes, + packaging_id, ); let restaurant: RestaurantBasicInfo = { @@ -138,6 +141,7 @@ export class CartController { basic_taste_customization_obj, notes, lang = 'vie', + packaging_id = null, } = data; const res = new UpdateCartAdvancedResponse(200, ''); try { @@ -150,6 +154,7 @@ export class CartController { advanced_taste_customization_obj, basic_taste_customization_obj, notes, + packaging_id, lang, ); let restaurant: RestaurantBasicInfo = { @@ -365,6 +370,14 @@ export class CartController { return res; } + //Get standard package for menu_item_id + let packaging_id = null; + const packaging = + await this.commonService.getStandardPackagingByMenuItem(menu_item_id); + if (packaging) { + packaging_id = packaging.packaging_id; + } + const qtyOrdered = 1; try { @@ -372,6 +385,10 @@ export class CartController { customer_id, sku.sku_id, qtyOrdered, + [], + [], + '', + packaging_id, ); let restaurant: RestaurantBasicInfo = { diff --git a/src/feature/cart/cart.service.ts b/src/feature/cart/cart.service.ts index 8562ab9..0e6f2d9 100644 --- a/src/feature/cart/cart.service.ts +++ b/src/feature/cart/cart.service.ts @@ -7,6 +7,7 @@ import { CommonService } from '../common/common.service'; import { SKU } from 'src/entity/sku.entity'; import { BasicTasteSelection, + CartPackagingInfo, DayShift, FullCartItem, OptionSelection, @@ -18,6 +19,7 @@ import { import { DAY_ID, DAY_NAME } from 'src/constant'; import { Shift } from 'src/enum'; import { AhamoveService } from 'src/dependency/ahamove/ahamove.service'; +import { MenuItemPackaging } from 'src/entity/menuitem-packaging.entity'; @Injectable() export class CartService { @@ -34,6 +36,7 @@ export class CartService { advanced_taste_customization_obj: OptionSelection[] = [], basic_taste_customization_obj: BasicTasteSelection[] = [], notes: string = '', + packaging_id: number = null, lang: string = 'vie', ): Promise { const advanced_taste_customization_obj_txt = @@ -55,6 +58,16 @@ export class CartService { throw new HttpException('SKU does not exist', 404); } + //Check the package info does belongs to the SKU + if (packaging_id) { + if (!(await this.checkIfThePackageBelongsToSKU(packaging_id, sku_id))) { + throw new HttpException( + 'The package info does belongs to the SKU', + 400, + ); + } + } + // Check if the advanced_taste_customization_obj is all available to this SKU const advancedTasteCustomizationValidation = advanced_taste_customization_obj.length > 0 @@ -120,6 +133,7 @@ export class CartService { basic_taste_customization_obj_txt, notes, sku.menu_item.restaurant_id, + packaging_id, ); return await this.getCart(customer_id); } @@ -141,7 +155,8 @@ export class CartService { i.advanced_taste_customization_obj == advanced_taste_customization_obj_txt && i.basic_taste_customization_obj == basic_taste_customization_obj_txt && - i.notes == notes, + i.notes == notes && + i.packaging_info.packaging_id == packaging_id, ); if (existingItem) { //The item does exist => increase the quantity @@ -157,6 +172,7 @@ export class CartService { existingItem.basic_taste_customization_obj, existingItem.notes, existingItem.restaurant_id, + existingItem.packaging_info.packaging_id, ); return await this.getCart(customer_id); } @@ -172,6 +188,7 @@ export class CartService { basic_taste_customization_obj_txt, notes, sku.menu_item.restaurant_id, + packaging_id, ); return await this.getCart(customer_id); } // end of addCartItem @@ -180,6 +197,8 @@ export class CartService { const fullCart: FullCartItem[] = []; const cartItems = await this.entityManager .createQueryBuilder(CartItem, 'cart') + .leftJoinAndSelect('cart.packaging_obj', 'packaging') + .leftJoinAndSelect('packaging.packaging_ext_obj', 'packagingExt') .where('cart.customer_id = :customer_id', { customer_id }) .getMany(); @@ -192,6 +211,17 @@ export class CartService { const additionalInfoForSku = additionalInfoForSkus.find( (i) => i.sku_id == item.sku_id, ); + const packagingInfo: CartPackagingInfo = { + packaging_id: item.packaging_id, + name: [], + price: item.packaging_obj?.price, + }; + item.packaging_obj?.packaging_ext_obj.forEach((ext) => { + packagingInfo.name.push({ + ISO_language_code: ext.ISO_language_code, + text: ext.name, + }); + }); const fullItem: FullCartItem = { item_id: item.item_id, item_name: additionalInfoForSku.sku_name, @@ -209,6 +239,7 @@ export class CartService { basic_taste_customization_obj: item.basic_taste_customization_obj, notes: item.notes, restaurant_id: item.restaurant_id, + packaging_info: packagingInfo, }; fullCart.push(fullItem); } @@ -226,6 +257,7 @@ export class CartService { basic_taste_customization_obj: string, notes: string, restaurant_id: number, + packaging_id: number, ): Promise { await this.entityManager .createQueryBuilder() @@ -242,6 +274,7 @@ export class CartService { basic_taste_customization_obj: basic_taste_customization_obj, notes: notes, restaurant_id: restaurant_id, + packaging_id: packaging_id, }) .execute(); } // end of insertCart @@ -258,6 +291,7 @@ export class CartService { basic_taste_customization_obj: string, notes: string, restaurant_id: number, + packaging_id: number, ): Promise { await this.entityManager .createQueryBuilder() @@ -273,6 +307,7 @@ export class CartService { basic_taste_customization_obj: basic_taste_customization_obj, notes: notes, restaurant_id: restaurant_id, + packaging_id: packaging_id, }) .where('item_id = :item_id', { item_id }) .execute(); @@ -286,6 +321,7 @@ export class CartService { advanced_taste_customization_obj: OptionSelection[], basic_taste_customization_obj: BasicTasteSelection[], notes: string, + packaging_id: number, lang: string, ): Promise { // https://n-festa.atlassian.net/browse/FES-28 @@ -301,6 +337,13 @@ export class CartService { ); } + // Check if the package does belong to SKU + if (packaging_id) { + if (!(await this.checkIfThePackageBelongsToSKU(packaging_id, sku_id))) { + throw new HttpException('The package does not belong to SKU', 400); + } + } + const advanced_taste_customization_obj_txt = JSON.stringify( advanced_taste_customization_obj, ); @@ -316,7 +359,8 @@ export class CartService { mentionedCartItem.advanced_taste_customization_obj && basic_taste_customization_obj_txt == mentionedCartItem.basic_taste_customization_obj && - notes == mentionedCartItem.notes + notes == mentionedCartItem.notes && + packaging_id == mentionedCartItem.packaging_id ) { throw new HttpException('No new data for updating', 400); } @@ -331,7 +375,7 @@ export class CartService { throw new HttpException('SKU does not exist', 404); } - // If there is any cart item which is IDENTICAL to the updated item + // If there is any other cart item which is IDENTICAL to the updated item const identicalCartItem = await this.entityManager .createQueryBuilder(CartItem, 'cart') .where('cart.customer_id = :customer_id', { customer_id }) @@ -349,6 +393,7 @@ export class CartService { { basic_taste_customization_obj: basic_taste_customization_obj_txt }, ) .andWhere('cart.notes = :notes', { notes }) + .andWhere('cart.packaging_id = :packaging_id', { packaging_id }) .getOne(); if (identicalCartItem) { @@ -484,6 +529,7 @@ export class CartService { basic_taste_customization_obj_txt, notes, mentionedCartItem.restaurant_id, + packaging_id, ); return await this.getCart(customer_id); @@ -913,4 +959,23 @@ export class CartService { } return timeSlots; } // end of getAvailableDeliveryTimeFromEndPoint + + async checkIfThePackageBelongsToSKU( + packaging_id: number, + sku_id: number, + ): Promise { + let result = false; + + const record = await this.entityManager + .createQueryBuilder(MenuItemPackaging, 'packaging') + .leftJoinAndSelect('packaging.menu_item_obj', 'menuItem') + .leftJoinAndSelect('menuItem.skus', 'sku') + .where('packaging.packaging_id = :packaging_id', { packaging_id }) + .andWhere('sku.sku_id = :sku_id', { sku_id }) + .getOne(); + + result = record ? true : false; + + return result; + } } diff --git a/src/feature/cart/dto/add-to-cart-request.dto.ts b/src/feature/cart/dto/add-to-cart-request.dto.ts index 2f88a5a..23e9907 100644 --- a/src/feature/cart/dto/add-to-cart-request.dto.ts +++ b/src/feature/cart/dto/add-to-cart-request.dto.ts @@ -6,6 +6,7 @@ export class AddToCartRequest { basic_taste_customization_obj: BasicTasteSelection[]; notes: string; lang?: string; + packaging_id?: number; } interface OptionSelection { diff --git a/src/feature/cart/dto/add-to-cart-response.dto.ts b/src/feature/cart/dto/add-to-cart-response.dto.ts index 9c5ceea..1134a37 100644 --- a/src/feature/cart/dto/add-to-cart-response.dto.ts +++ b/src/feature/cart/dto/add-to-cart-response.dto.ts @@ -30,4 +30,11 @@ interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } diff --git a/src/feature/cart/dto/delete-cart-item-response.dto.ts b/src/feature/cart/dto/delete-cart-item-response.dto.ts index 873dcb1..1d66e8e 100644 --- a/src/feature/cart/dto/delete-cart-item-response.dto.ts +++ b/src/feature/cart/dto/delete-cart-item-response.dto.ts @@ -29,4 +29,11 @@ interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } diff --git a/src/feature/cart/dto/get-cart-detail-response.dto.ts b/src/feature/cart/dto/get-cart-detail-response.dto.ts index c6ee1ea..3080dd0 100644 --- a/src/feature/cart/dto/get-cart-detail-response.dto.ts +++ b/src/feature/cart/dto/get-cart-detail-response.dto.ts @@ -30,4 +30,11 @@ interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } diff --git a/src/feature/cart/dto/quick-add-to-cart-response.dto.ts b/src/feature/cart/dto/quick-add-to-cart-response.dto.ts index 4b16ce5..475fa71 100644 --- a/src/feature/cart/dto/quick-add-to-cart-response.dto.ts +++ b/src/feature/cart/dto/quick-add-to-cart-response.dto.ts @@ -30,4 +30,11 @@ interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } diff --git a/src/feature/cart/dto/update-cart-advanced-request.dto.ts b/src/feature/cart/dto/update-cart-advanced-request.dto.ts index 33a24aa..579d9b3 100644 --- a/src/feature/cart/dto/update-cart-advanced-request.dto.ts +++ b/src/feature/cart/dto/update-cart-advanced-request.dto.ts @@ -7,6 +7,7 @@ export class UpdateCartAdvancedRequest { basic_taste_customization_obj: BasicTasteSelection[]; notes: string; lang?: string; + packaging_id?: number; } interface OptionSelection { diff --git a/src/feature/cart/dto/update-cart-advanced-response.dto.ts b/src/feature/cart/dto/update-cart-advanced-response.dto.ts index 5fe2ad1..272ca5d 100644 --- a/src/feature/cart/dto/update-cart-advanced-response.dto.ts +++ b/src/feature/cart/dto/update-cart-advanced-response.dto.ts @@ -30,4 +30,11 @@ interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } diff --git a/src/feature/cart/dto/update-cart-basic-response.dto.ts b/src/feature/cart/dto/update-cart-basic-response.dto.ts index 3b1a70a..88d7f35 100644 --- a/src/feature/cart/dto/update-cart-basic-response.dto.ts +++ b/src/feature/cart/dto/update-cart-basic-response.dto.ts @@ -30,4 +30,11 @@ interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } diff --git a/src/feature/common/common.service.ts b/src/feature/common/common.service.ts index 82ed536..8abc657 100644 --- a/src/feature/common/common.service.ts +++ b/src/feature/common/common.service.ts @@ -38,6 +38,8 @@ import { RestaurantDayOff } from 'src/entity/restaurant-day-off.entity'; import { ManualOpenRestaurant } from 'src/entity/manual-open-restaurant.entity'; import { AhamoveService } from 'src/dependency/ahamove/ahamove.service'; import { ConfigService } from '@nestjs/config'; +import { Packaging } from 'src/entity/packaging.entity'; +import { MenuItemPackaging } from 'src/entity/menuitem-packaging.entity'; @Injectable() export class CommonService { @@ -831,4 +833,23 @@ export class CommonService { //return data after adjusting with time offset return planningDateTmestamp - timeZoneOffset; } // end of getPlanningDate + + async getStandardPackagingByMenuItem( + menu_item_id: number, + ): Promise { + const menuItemPackaging = await this.entityManager + .createQueryBuilder(MenuItemPackaging, 'menuItemPackaging') + .where('menuItemPackaging.menu_item_id = :menu_item_id', { menu_item_id }) + .andWhere('menuItemPackaging.is_default = 1') + .getOne(); + if (!menuItemPackaging) { + return null; + } + return await this.entityManager + .createQueryBuilder(Packaging, 'packaging') + .where('packaging.packaging_id = :packaging_id', { + packaging_id: menuItemPackaging.packaging_id, + }) + .getOne(); + } //end of getStandardPackagingByMenuItem } diff --git a/src/feature/food/dto/get-food-detail-response.dto.ts b/src/feature/food/dto/get-food-detail-response.dto.ts index c3f8d54..ded6f95 100644 --- a/src/feature/food/dto/get-food-detail-response.dto.ts +++ b/src/feature/food/dto/get-food-detail-response.dto.ts @@ -66,9 +66,11 @@ interface TextByLang { } interface PackagingInfo { + packaging_id: number; image_url: string; name: TextByLang[]; description: TextByLang[]; price: number; currency: string; + is_default: boolean; } diff --git a/src/feature/food/food.service.ts b/src/feature/food/food.service.ts index de5fe66..9b05ef4 100644 --- a/src/feature/food/food.service.ts +++ b/src/feature/food/food.service.ts @@ -381,12 +381,19 @@ export class FoodService { description.push(descriptionExt); }); + console.log( + 'menuItemPackaging.is_default', + menuItemPackaging.is_default, + typeof menuItemPackaging.is_default, + ); const packagingInfo: PackagingInfo = { + packaging_id: menuItemPackaging.packaging_id, image_url: image_url, name: name, description: description, price: menuItemPackaging.packaging_obj.price, currency: currency.symbol, + is_default: Boolean(menuItemPackaging.is_default), }; packagingInfos.push(packagingInfo); } diff --git a/src/type/index.ts b/src/type/index.ts index 6eaaec0..93ac28c 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -159,6 +159,13 @@ export interface FullCartItem { basic_taste_customization_obj: string; notes: string; restaurant_id: number; + packaging_info: CartPackagingInfo; +} + +export interface CartPackagingInfo { + packaging_id: number; + name: TextByLang[]; + price: number; } export interface AdditionalInfoForSKU { @@ -175,9 +182,11 @@ export interface PriceUnitByMenuItem { } export interface PackagingInfo { + packaging_id: number; image_url: string; name: TextByLang[]; description: TextByLang[]; price: number; currency: string; + is_default: boolean; }