UNPKG

@apihawk/billia-sdk

Version:

The ApiHawk Billia SDK

502 lines (501 loc) 19.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const moment = require("moment"); const qs = require("qs"); const billia_sdk_service_base_1 = require("../lib/billia-sdk-service-base"); const billia_sdk_catalog_1 = require("./billia-sdk-catalog"); const query_builder_1 = require("./common/query-builder"); const to_rest_resource_1 = require("./common/to-rest-resource"); const OPTIONS_VISIBILITY = { 'product-details': 'customer-portal__product-details', 'product-configuration': 'customer-portal__product-configuration' }; class BilliaSDKCustomerProduct extends billia_sdk_service_base_1.BilliaSDKServiceBase { constructor(api) { super(api); this.billiaSDKCatalog = new billia_sdk_catalog_1.BilliaSDKCatalog(api); } /** * Get all customer products * @param {IApihawkSession} session * @param {IRestPaginationQuery} pagination * @returns {Promise<IRestPaginatedResource<ICustomerProduct>>} */ getAll(session, pagination) { return __awaiter(this, void 0, void 0, function* () { const query = query_builder_1.default.parse(pagination); const customerProducts = yield this.api.call({ url: `/customer/product`, method: 'GET', session, query, headers: { 'Accept-Response': 'Advanced' } }); return to_rest_resource_1.toRestResource(customerProducts, 'customer_product'); }); } /** * Get products by cateogry_id * @param {number} categoryId * @param {IApihawkSession} session * @param {IRestPaginationQuery} pagination * @returns {Promise<IRestPaginatedResource<ICustomerProduct>>} */ getProductsByCategoryId(categoryId, session, pagination) { if (!pagination.filter) { pagination.filter = {}; } if (!pagination.equalTo) { pagination.equalTo = { 'catalog_product.category_id': `${categoryId}` }; } else { pagination.equalTo['catalog_product.category_id'] = `${categoryId}`; } // if the client provided filter by category_id (e.g. where in (ids..) ) // remove the equalTo query and use the explicitly set one if (Array.isArray(pagination.filter['catalog_product.category_id']) && pagination.filter['catalog_product.category_id'].length > 0) { delete pagination.equalTo; } return this.getAll(session, pagination); } /** * Get expiring customer products * @param {IApihawkSession} session user session * @param {IExpiringProductsPeriod} period filter only products expiring this week/month/quarter */ getExpiringProducts(session, period) { return __awaiter(this, void 0, void 0, function* () { let days = 31; switch (period) { case 'week': days = 7; break; case 'month': days = 31; break; case 'quarter': days = 31 * 3; break; } const queryFilter = { where: [ { field: 'expire_date', where: 'and', type: 'lessThanOrEqualTo', value: moment().add(days, 'days').format('YYYY-MM-DD HH:mm:ss') }, { field: 'expire_date', where: 'and', type: 'greaterThanOrEqualTo', value: moment().format('YYYY-MM-DD HH:mm:ss') }, { field: 'billing_type', where: 'and', type: 'notEqualTo', value: 'onetime' }, { field: 'status', where: 'and', type: 'in', values: ['active', 'expired'] } ], order: [ { sort: 'ASC', field: 'expire_date' } ] }; const filter = qs.stringify(queryFilter); return this.api .call({ url: `/customer/product?page_size=-1${filter ? '&' + filter : ''}`, method: 'GET', session, headers: { 'Accept-Response': 'Advanced' } }) .then((response) => { const categories = Array.from(new Set(response._embedded.customer_product .map((p) => (p.product ? p.product.category_id : 0)) .filter((p) => p > 0))); if (categories.length) { const filterData = qs.stringify({ where: [ { field: 'id', where: 'and', type: 'in', values: categories } ] }); return this.api .call({ url: `/billing/category?${filterData}`, method: 'GET', headers: { Accept: 'application/vnd.image+json' } }) .then((res) => { const categoriesDetails = res._embedded.catalog_category || []; const categoriesById = categoriesDetails.reduce((acc, c) => { acc[c.id] = c; return acc; }, {}); response._embedded.customer_product.forEach((p) => { if (p.product) { p.product.category = categoriesById[p.product.category_id]; } }); return response._embedded.customer_product; }); } return []; }); }); } /** * Get customer product by ID * @param {IApihawkSession} session * @param {number} customerProductId * @param {{}} query * @returns {Promise<ICustomerProduct>} */ getCustomerProductById(session, customerProductId, query = {}) { return __awaiter(this, void 0, void 0, function* () { const customerProduct = yield this.api.call({ url: `/customer/product/${customerProductId}`, method: 'GET', session, headers: { 'Accept-Response': 'Advanced' } }); const catalogProduct = yield this.getCatalogProduct(session, customerProduct.product_id); customerProduct.options = (catalogProduct.options || []) .map((catalogOption) => { const matchingOption = (customerProduct.options || []).find((po) => po.catalog_option_id === catalogOption.option_id); if (matchingOption) { return this.catalogOptionToProductOption(customerProduct, // @ts-ignore customerProduct.options.find((po) => po.catalog_option_id === catalogOption.option_id), catalogOption); } return this.catalogOptionToProductOption(customerProduct, // @ts-ignore { customer_product_option_id: 0, value: '', user_id: customerProduct.user_id }, catalogOption); }) .filter((o) => !!o) .map((option, index, arr) => { // calculates the option's value option.value = this.getCurrentOptionValue(option); // finds group option's children and moves them inside the parent group option if (Array.isArray(option.option.group)) { return this.attachGroupChildren(option, arr); } return option; }) .filter(this.filterVisibleOptions(query)) .filter((o) => o.option.provider === 'user' || o.option.provider === 'system'); return customerProduct; }); } /** * Gets customer product notes * @param {IApihawkSession} session * @param {number} customerProductId */ getCustomerProductNotes(session, customerProductId) { return __awaiter(this, void 0, void 0, function* () { const notes = yield this.api.call({ url: `/customer/product/${customerProductId}/notes`, method: 'GET', session }); return to_rest_resource_1.toRestResource(notes, 'notes'); }); } /** * Edits a customer product. * * There are three types of editing: * * 1. Renew * { 'renew': { 'quantity': 12 } } * * 2. Edit * { 'edit': { 'auto_renew': 'disabled', options: [{ 'option_id': 42, value: 'new value' }] } } * * 3. Upgrade * { 'upgrade': { product_id: 1 } } * * @param {Object} session - session * @param {number} productId - customer product ID * @param {Object} data - request body * @param {boolean} dryRun - calculate prices without altering the product */ edit(session, productId, data = {}, dryRun = false) { return __awaiter(this, void 0, void 0, function* () { let queryString = ''; if (dryRun) { queryString = 'dryrun=true'; } return this.api.call({ url: `/customer/product-redact/${productId}?${queryString}`, method: 'POST', body: data, session }); }); } /** * Get products count per category. * * @param {IApihawkSession} session * @returns hash-table (category ID -> count) */ getProductsCountPerCategory(session) { return __awaiter(this, void 0, void 0, function* () { const response = yield this.api.call({ url: '/customer/product-category-counter', method: 'GET', session }); const hashTable = response.reduce((result, category) => { result[category.category_id] = parseInt(category.count, 10); return result; }, {}); return hashTable; }); } /** * Toggle auto-renew * @param {IApihawkSession} session * @param {number} productId * @param {boolean} autoRenew * @returns {Promise<ICustomerProduct>} */ toggleAutoRenew(session, productId, autoRenew) { return __awaiter(this, void 0, void 0, function* () { return this.api.call({ url: `/customer/product/${productId}`, method: 'PATCH', body: { auto_renew: autoRenew }, session }); }); } /** * Check if product can be temporary unsuspended * @param {IApihawkSession} session * @param {number} customerProductId * @returns {Promise<boolean>} */ checkIsCustomerProductTemporaryUnsuspendActive(session, customerProductId) { return __awaiter(this, void 0, void 0, function* () { return this.api .call({ url: `/customer/product-temporary-unsuspend?customer_product_id=${customerProductId}`, method: 'GET', session }) .then((result) => result.valid); }); } /** * Temporary unsuspend customer product * @param {IApihawkSession} session * @param {number} customerProductId * @returns {Promise<boolean>} */ customerProductTemporaryUnsuspend(session, customerProductId) { return __awaiter(this, void 0, void 0, function* () { return this.api .call({ url: `/customer/product-temporary-unsuspend?customer_product_id=${customerProductId}`, method: 'POST', session }) .then(() => true); }); } /** * Delete customer product option * @param {IApihawkSession} session * @param {number} customerProductOptionId * @returns {Promise<boolean>} */ deleteCustomerProductOption(session, customerProductOptionId) { return __awaiter(this, void 0, void 0, function* () { return this.api .call({ url: `/customer/product_option/${customerProductOptionId}`, method: 'DELETE', session }) .then(() => true); }); } /** * Get catalog product * @param {} session * @param {number} productId * @returns {Promise<ICatalogProduct>} */ getCatalogProduct(session, productId) { return __awaiter(this, void 0, void 0, function* () { return this.api.call({ url: `/billing/product/${productId}`, method: 'GET', session, headers: { 'Accept-Response': 'Advanced' } }); }); } /** * Hash customer product options * @param {ICustomerProductOption[]} options * @param {ICatalogOption[]} catalogProductOptions * @returns {ICustomerProductOption[]} */ hashOptions(options, catalogProductOptions) { const hashedOptions = options.reduce((hash, option) => { // @ts-ignore hash[option.option.option_key] = this.getCurrentOptionValue(option); return hash; }, {}); catalogProductOptions.forEach((o) => { if (!hashedOptions[o.option_key]) { hashedOptions[o.option_key] = null; } }); return hashedOptions; } /** * Calculates the current option's value in order to be * data-bound to the front-end form controls. * * @param {Object} option - product option * * @returns {String | number | boolean} - primitive value */ getCurrentOptionValue(option) { if (!option) { return ''; } if (typeof option.choice !== 'undefined') { if (option.choice.name === 'on' || option.choice.name === 'off') { // in case it's of type 'on/off' - use 'on' or 'off' as value return option.choice.name; } else { // in case it's another type - use the choice_id as value return option.choice.choice_id; } } else { // if there is no choice associated with this option - use the text value return option.value; } } /** * Maps a catalog option (not attached to a product) * to a product option (attached to a product). * * @param {ICustomerProduct} product * @param {ICustomerProductOption} productOption * @param {ICatalogOption} catalogOption * * @returns {ICustomerProductOption} - product option */ catalogOptionToProductOption(product, productOption, catalogOption) { if (!product || !productOption || !catalogOption) { throw new Error('Missing required paramethers.'); } const result = { customer_product_option_id: productOption.customer_product_option_id, customer_product_id: product.customer_product_id, catalog_option_id: catalogOption.option_id, catalog_choice_id: '', value: productOption.value, trash: catalogOption.trash || 0, user_id: productOption.user_id, state: productOption.state, previous: productOption.previous }; result.option = catalogOption; // if there is a chosen option, find it from the catalog product options // and assign it in order to send more detailed information to the client if (productOption.choice) { result.choice = (catalogOption.choices || []).find((c) => productOption.choice.choice_id === c.choice_id); } else { // otherwise set the default choice result.choice = (catalogOption.choices || []).find((c) => c.default === 1); } return result; } /** * Moves option group children options inside the parent option group. * * @param {ICustomerProductOption} optionGroup - product option of type "group" * @param {ICustomerProductOption[]} allOptions - list of all product options * * @returns {ICustomerProductOption} - option group with attached children options */ attachGroupChildren(optionGroup, allOptions) { if (optionGroup.option && Array.isArray(optionGroup.option.group)) { optionGroup.option.group = allOptions.filter((option) => { if (!optionGroup.option || !Array.isArray(optionGroup.option.group)) { return false; } return optionGroup.option.group.find((groupChild) => groupChild.option_id === option.catalog_option_id); }); } return optionGroup; } /** * Filters visible options according to a given criteria. * * @param {Object} query - HTTP query hash * * @returns {Function} - predicate function */ filterVisibleOptions(query = {}) { return (option) => { if (!option.option) { return false; } return query.options_visibility ? (option.option.visibility || []).includes(OPTIONS_VISIBILITY[query.options_visibility]) : true; }; } } exports.BilliaSDKCustomerProduct = BilliaSDKCustomerProduct;