import { Cart, CartRepository, Order } from '@inmotion/swift-libs'; import { UserSettingsRepository } from '@inmotion/swift-libs/data/UserSettingsRepository'; import { CartModificationService } from '@inmotion/swift-libs/services/cart-modification.service'; import { AppSettingsService, DiscountService, DiscountValidationResponse, OrderUtils, Product, ProductRepository, RequestService, UrlBuilder, Validator } from '@whittaker/swift-libs'; import { BaseCartState, CartBuilderInput, LineInput } from './models.cart'; import { CartCreateUtils } from './cart-creation.utils'; // TODO second Type InputType extends CartBuilderInput... then add fflNumber to whittaker's implementation export abstract class BaseCartCreationService { constructor(protected _productRepo: ProductRepository, protected _cartModService: CartModificationService, protected _cartRepo: CartRepository, protected _urlBuilder: UrlBuilder, protected _requestService: RequestService, protected _userSettingsRepo: UserSettingsRepository, protected _appSettingsService: AppSettingsService, protected _discountService: DiscountService, protected _cartUtils: CartCreateUtils) { } // *** GET OR CREATE *** public async getOrCreateCartAndBuildState(cartGuid: string): Promise { let cart: Cart, state: StateType; cart = await this.getOrCreate(cartGuid); state = await this.buildState(cart); return state; } public abstract defaultCart(): Cart; // *** UPDATE OR CREATE *** public async updateOrCreateCartAndBuildState(input: CartBuilderInput): Promise { let state: StateType, cart: Cart; cart = await this.updateOrCreateFrom(input); state = await this.buildState(cart); state.cart = await this.saveCart(state.cart); return state; } // *** CHARGE *** public async charge(cartGuid: string) { throw new Error('not implemented'); } // *** COMMON *** protected abstract async buildState(cart: Cart): Promise; protected async getOrCreate(cartGuid: string): Promise { if (this._cartUtils.isNew(cartGuid)) { console.log('CREATING CART'); return this.defaultCart(); } else { console.log('FETCHING CART'); return this.fetchAndSetCartId(cartGuid); } } protected async updateOrCreateFrom(input: CartBuilderInput): Promise { let cart: Cart = await this.getOrCreate(input.guid); return this.setCartDataFromInput(input, cart); } protected async setCartDataFromInput(input: CartBuilderInput, cart: Cart): Promise { cart = await this.setCommonCartProperties(input, cart); cart = await this.setClientSpecificCartProperties(input, cart); return cart; } protected async setCommonCartProperties(input: CartBuilderInput, cart: Cart): Promise { Validator.throwIfNil(input, `Missing input`); cart.Billing = input.billing; cart.Shipping = input.shipping; cart.Payment.Method = input.paymentMethod; cart.Payment.Voucher = input.paymentVoucher; cart.Lines = []; for (let lineInput of input.lines || []) { cart = await this.addLine(lineInput, cart); } // ----- discount ------ if (input.discountSku && OrderUtils.getDiscountSku(cart) != input.discountSku) { // fetch deal instance and apply to cart if valid let discountResponse: DiscountValidationResponse; discountResponse = await this._discountService.checkValidity(input.discountSku, input.userEmail); if (discountResponse.success) { cart = this._discountService.setTrimmedDiscountInstance(discountResponse.data.Deal, cart); } } // ----- discount ------ return cart; } protected abstract async setClientSpecificCartProperties(input: CartBuilderInput, cart: Cart): Promise; protected async addLine(lineInput: LineInput, cart: Cart): Promise { let quantity: number, product: Product; this._cartUtils.validateLineInput(lineInput); quantity = lineInput.quantity; product = await this._productRepo.getAsync(lineInput.product.Id); Validator.throwIfNil(product, `Failed to fetch product.`); cart = this._cartModService.addProductToCart(product, quantity, cart); return cart; } public async saveCart(cart: Cart): Promise { let order: Order, url: string; Validator.throwIfNil(cart, `Missing cart`); cart.Lines = cart.Lines || []; if (this._cartUtils.isNew(OrderUtils.getGuid(cart))) { // save new cart url = this._urlBuilder.orderUpdateEndpoint(); order = await this._requestService.post(url, cart); return this.fetchAndSetCartId(order.CartId); } else { // save existing cart cart = await this._cartRepo.saveAsync(cart); cart.__i = new Cart().__i; cart.__i.guid = cart.CartId; return cart; } } protected abstract transformClientSpecificCartBuilderInputProperties(input: CartBuilderInput, cart: Cart): CartBuilderInput; protected async fetchAndSetCartId(cartGuid: string): Promise { let cart: Cart = await this._cartRepo.getAsync(cartGuid); Validator.throwIfNil(cart, `Failed to fetch cart ${cartGuid}`); cart.CartId = OrderUtils.getGuid(cart); return cart; } }