826 lines
25 KiB
TypeScript
826 lines
25 KiB
TypeScript
import { BaseSQLDataSource } from './BaseSQLDataSource';
|
|
import { camelcase } from 'stringcase';
|
|
import slug from 'slug';
|
|
import * as sql from './sql';
|
|
import {
|
|
Product,
|
|
ProductGroup,
|
|
PrintProduct,
|
|
ProductType,
|
|
ProductBlacklist,
|
|
ProductWallpaperType,
|
|
ProductFields,
|
|
Orientation,
|
|
ProductsFilterInput,
|
|
ProductInfoInput,
|
|
ProductBlacklistMarketInput,
|
|
ProductMaterials,
|
|
ProductSizeTypes,
|
|
ProductProportionsInput,
|
|
Batch,
|
|
} from '../types/product-types';
|
|
import { Maybe } from '../types/types';
|
|
import {
|
|
booleanStringField,
|
|
isInt,
|
|
nullOrNumber,
|
|
roundOrDefaultTo,
|
|
} from './utils';
|
|
import { UserInputError } from 'apollo-server';
|
|
import { sendPathChangedCmd } from '../bernard/client';
|
|
|
|
const MINUTE = 60;
|
|
|
|
export class ProductAPI extends BaseSQLDataSource {
|
|
constructor(config) {
|
|
super(config);
|
|
}
|
|
|
|
// async getMaterial(printId: number): Promise<any> {
|
|
// TODO: discuss if this is still needed. Anneli wants to remove the choice of standard, premium and self-adhesive
|
|
// SELECT materialid, material, (price / 100::float) as price FROM "product-materials"
|
|
// }
|
|
|
|
getWallpaperTypes(row: any): Array<ProductWallpaperType> {
|
|
if (!row.wallpapertypes) {
|
|
return [];
|
|
}
|
|
return row.wallpapertypes.map((t: number) => ProductWallpaperType[t]);
|
|
}
|
|
|
|
getProductTypes(row: any): Array<ProductType> {
|
|
if (!row.types) {
|
|
return [];
|
|
}
|
|
return row.types.map((type: any) => {
|
|
if (type === 'typeillustration') {
|
|
return ProductType[ProductType.ILLUSTRATION];
|
|
}
|
|
if (type === 'typephotography') {
|
|
return ProductType[ProductType.PHOTO];
|
|
}
|
|
return ProductType[ProductType.UNKNOWN];
|
|
});
|
|
}
|
|
|
|
getBlacklisting(row: any): Array<ProductBlacklist> {
|
|
if (!row.blacklisting) {
|
|
return [];
|
|
}
|
|
return row.blacklisting.map((bl: any) => {
|
|
bl.group = ProductGroup[bl.groupId];
|
|
return bl;
|
|
});
|
|
}
|
|
|
|
getPrintProducts(row: any): Array<PrintProduct> {
|
|
if (!row.printproducts) {
|
|
return [];
|
|
}
|
|
return row.printproducts.map((pp: any) => {
|
|
pp.group = ProductGroup[pp.groupId];
|
|
pp.id = pp.printId;
|
|
return pp;
|
|
});
|
|
}
|
|
|
|
getProductFields(row: any): ProductFields {
|
|
if (!row.fields) {
|
|
return null; // 53460 for example
|
|
}
|
|
// Convert to camelCase first
|
|
const fields = Object.keys(row.fields).reduce((mem, key) => {
|
|
mem[camelcase(key)] = row.fields[key];
|
|
return mem;
|
|
}, {});
|
|
|
|
fields['artNo'] = fields['artNo'] ?? ''; // some actually exists without artNo
|
|
fields['name'] = fields['name'] ?? ''; // some actually exists without name
|
|
fields['copyright'] = fields['copyright'] ?? '';
|
|
fields['comments'] = fields['comments'] ?? '';
|
|
fields['batch'] = fields['batch'] ?? '';
|
|
fields['height'] = nullOrNumber(fields['height'] ?? null);
|
|
fields['width'] = nullOrNumber(fields['width'] ?? null);
|
|
fields['printFileWidth'] = nullOrNumber(fields['printFileWidth'] ?? null);
|
|
fields['printFileHeight'] = nullOrNumber(fields['printFileHeight'] ?? null);
|
|
fields['printFileDpi'] = nullOrNumber(fields['printFileDpi'] ?? null);
|
|
fields['marginWidthMax'] = nullOrNumber(fields['marginWidthMax'] ?? null);
|
|
fields['marginWidthMin'] = nullOrNumber(fields['marginWidthMin'] ?? null);
|
|
fields['marginHeightMax'] = nullOrNumber(fields['marginHeightMax'] ?? null);
|
|
fields['marginHeightMin'] = nullOrNumber(fields['marginHeightMin'] ?? null);
|
|
fields['focusXpoint'] = nullOrNumber(fields['focusXpoint'] ?? null);
|
|
fields['focusYpoint'] = nullOrNumber(fields['focusYpoint'] ?? null);
|
|
|
|
// Focus points values are in %.
|
|
// Should be rounded to two decimals. Is sometimes missing in DB.
|
|
// If missing should be treated as 50%, 50%
|
|
fields['focusXpoint2'] = roundOrDefaultTo(fields['focusXpoint2'], 2, 50);
|
|
fields['focusYpoint2'] = roundOrDefaultTo(fields['focusYpoint2'], 2, 50);
|
|
|
|
fields['photowallResolution'] = nullOrNumber(
|
|
fields['photowallResolution'] ?? null,
|
|
);
|
|
fields['canvasResolution'] = nullOrNumber(
|
|
fields['canvasResolution'] ?? null,
|
|
);
|
|
fields['wallpaperResolution'] = nullOrNumber(
|
|
fields['wallpaperResolution'] ?? null,
|
|
);
|
|
fields['proportionsWarning'] = booleanStringField(
|
|
fields['proportionsWarning'] ?? null,
|
|
);
|
|
fields['imageResolution'] = nullOrNumber(fields['imageResolution'] ?? null);
|
|
return <ProductFields>fields;
|
|
}
|
|
|
|
getOrientation(fields: ProductFields): string {
|
|
if (!fields || !fields.width || !fields.height) {
|
|
return Orientation[Orientation.UNKNOWN];
|
|
}
|
|
if (fields.width > fields.height) {
|
|
return Orientation[Orientation.LANDSCAPE];
|
|
}
|
|
if (fields.width < fields.height) {
|
|
return Orientation[Orientation.STANDING];
|
|
}
|
|
return Orientation[Orientation.SQUARE];
|
|
}
|
|
|
|
createProductFromRow(row: any) {
|
|
row.blacklisting = this.getBlacklisting(row);
|
|
row.printProducts = this.getPrintProducts(row);
|
|
row.type = this.getProductTypes(row);
|
|
row.wallpaperTypes = this.getWallpaperTypes(row);
|
|
row.fields = this.getProductFields(row);
|
|
row.designerId = row.designerid;
|
|
row.orientation = this.getOrientation(row.fields);
|
|
return row;
|
|
}
|
|
|
|
async getProductsTotal(input: ProductsFilterInput): Promise<number> {
|
|
const query = sql.productsTotal(input);
|
|
const res = await this.cachedRaw(query)
|
|
.cache(MINUTE * 60)
|
|
.then((data) => data.rows);
|
|
return res[0]['count'] as number; // Optimize later to return Promise
|
|
}
|
|
|
|
async getProducts(input: ProductsFilterInput): Promise<Array<Product>> {
|
|
// TODO: handle offset
|
|
const query = sql.products(input);
|
|
|
|
return this.knex
|
|
.raw(query)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async getProduct(id: number): Promise<Maybe<Product>> {
|
|
const query = sql.product(id);
|
|
|
|
const res = await this.knex.raw(query).then((data) =>
|
|
data.rows.map((row) => {
|
|
const prod = this.createProductFromRow(row);
|
|
return prod;
|
|
}),
|
|
);
|
|
return res.find(Boolean);
|
|
}
|
|
|
|
async getDesignerProducts(designerId: number): Promise<Array<Product>> {
|
|
const query = sql.designerProducts();
|
|
|
|
return this.knex
|
|
.raw(query, designerId)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async getCategoryProducts(categoryId: number): Promise<Array<Product>> {
|
|
const query = sql.categoryProducts(categoryId);
|
|
|
|
return this.knex
|
|
.raw(query)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async getCategoryWithSubCategoriesProducts(
|
|
categoryId: number,
|
|
): Promise<Array<Product>> {
|
|
const query = sql.categoryWithAllSubCategoriesProducts(categoryId);
|
|
|
|
return this.knex
|
|
.raw(query)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async getKeywordProducts(keywordId: number): Promise<Array<Product>> {
|
|
const query = sql.keywordProducts(keywordId);
|
|
|
|
return this.knex
|
|
.raw(query)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async searchByName(name: string): Promise<Array<Product>> {
|
|
const query = sql.searchByFieldValue(2); // name
|
|
|
|
return this.knex
|
|
.raw(query, name)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async searchByArtNo(name: string): Promise<Array<Product>> {
|
|
const query = sql.searchByFieldValue(1); // artNo
|
|
|
|
return this.knex
|
|
.raw(query, name)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
}
|
|
|
|
async searchByBatch(name: string): Promise<Array<Batch>> {
|
|
const query = sql.searchByFieldValue(36); // batch
|
|
|
|
const batchRes = await this.knex
|
|
.raw(query, name)
|
|
.then((data) => data.rows.map((row) => this.createProductFromRow(row)));
|
|
|
|
const batches = {};
|
|
batchRes.forEach((p) => {
|
|
if (!batches[p.fields.batch]) {
|
|
batches[p.fields.batch] = [];
|
|
}
|
|
batches[p.fields.batch].push(p);
|
|
});
|
|
|
|
return Object.keys(batches).map((key) => {
|
|
return <Batch>{
|
|
name: key,
|
|
products: batches[key],
|
|
};
|
|
});
|
|
}
|
|
|
|
async getRelatedProducts(id: number): Promise<Array<Product>> {
|
|
const ids = await this.knex
|
|
.raw(
|
|
`SELECT productid2 FROM "product-products_products" WHERE productid1 = ${id}`,
|
|
)
|
|
.then((data) => data.rows.map((r) => r.productid2));
|
|
|
|
return ids.map(async (id) => this.getProduct(id));
|
|
}
|
|
|
|
// mutations below
|
|
async updateProductField(
|
|
productId: number,
|
|
field: string,
|
|
value: string,
|
|
): Promise<void> {
|
|
return this.knex.raw(
|
|
/* sql */ `
|
|
INSERT INTO "product-products_fields" (productid, fieldid, value, inserted, updated)
|
|
VALUES (?, (SELECT fieldid FROM "product-fields" WHERE field = ?), ?, now(), now())
|
|
ON CONFLICT (productid, fieldid)
|
|
DO UPDATE SET "value" = ?`,
|
|
[productId, field, value, value],
|
|
);
|
|
}
|
|
|
|
async getUniqueProductPath(
|
|
path: string,
|
|
productId?: number,
|
|
): Promise<string> {
|
|
// If productId is set then check if path is already
|
|
// set on that product.
|
|
if (productId) {
|
|
const productPath = await this.knex.raw(
|
|
`SELECT path FROM "product-products" WHERE productid = ?`,
|
|
[productId],
|
|
);
|
|
if (productPath.rows[0].path === path) {
|
|
return path;
|
|
}
|
|
}
|
|
|
|
// Check for unique-path
|
|
// Get paths from product-products
|
|
const basePath = slug(path, { remove: /(\d+)(?!.*\d)$/g }); // Remove last number adf-1-asdf-123 = adf-1-asdf-
|
|
const pathResponse = await this.knex.raw(
|
|
`SELECT path FROM "product-products" WHERE path LIKE ?`,
|
|
[basePath + '%'],
|
|
);
|
|
const paths = pathResponse.rows.map((item) => item.path);
|
|
// Evaluate new pathname
|
|
let newPath = '';
|
|
// If the exact match of the slug is not listed
|
|
// in the paths queried from db. Then use the
|
|
// tested slug.
|
|
if (!paths.includes(path)) {
|
|
newPath = path;
|
|
} else {
|
|
// Try to find any numbers in the pathname
|
|
const numbers = paths
|
|
.map((p) => {
|
|
const numStr = p.replace(basePath + '-', '');
|
|
return isInt(numStr) ? parseInt(numStr) : null;
|
|
})
|
|
.filter(Boolean); // Remove falsy
|
|
|
|
if (numbers.length === 0) {
|
|
// No numbers found, add 1.
|
|
newPath = path + '-1';
|
|
} else {
|
|
// Get highest number + 1
|
|
const next = Math.max(...numbers) + 1;
|
|
newPath = basePath + '-' + next;
|
|
}
|
|
}
|
|
return newPath;
|
|
}
|
|
|
|
async addProduct(name: string, batch: string): Promise<number> {
|
|
const path = batch ? batch + ' ' + name : name;
|
|
const productPath = slug(path);
|
|
const safePath = await this.getUniqueProductPath(productPath, null);
|
|
|
|
const idres = await this.knex
|
|
.table('product-products')
|
|
.insert({
|
|
path: slug(safePath),
|
|
browsable: 0,
|
|
visible: 0,
|
|
})
|
|
.returning('productid');
|
|
const id = idres[0];
|
|
this.updateProductField(id, 'artNo', `e${id}`);
|
|
await this.updateProductField(id, 'name', name);
|
|
await this.updateProductField(id, 'batch', batch);
|
|
return id;
|
|
}
|
|
|
|
async updateProductInfo(
|
|
productId: number,
|
|
info: ProductInfoInput,
|
|
): Promise<any[]> {
|
|
// Check path and insert new unique path if taken
|
|
const uniquePath = await this.getUniqueProductPath(info.path, productId);
|
|
const oldProd = await this.getProduct(productId);
|
|
if (oldProd.path !== uniquePath) {
|
|
sendPathChangedCmd(oldProd.path, uniquePath);
|
|
}
|
|
|
|
const promises = [];
|
|
promises.push(
|
|
this.knex
|
|
.table('product-products')
|
|
.update({
|
|
path: uniquePath,
|
|
browsable: info.browsable ? 1 : 0,
|
|
visible: info.visible ? 1 : 0,
|
|
designerid: info.designerId ?? null,
|
|
ref1: info.ref1,
|
|
ref2: info.ref2,
|
|
ref3: info.ref3,
|
|
})
|
|
.where({ productid: productId }),
|
|
this.updateProductField(productId, 'artNo', info.artNo),
|
|
this.updateProductField(productId, 'name', info.name),
|
|
this.updateProductField(productId, 'batch', info.batch),
|
|
this.updateProductField(productId, 'copyright', info.copyright),
|
|
this.updateProductField(
|
|
productId,
|
|
'print_file_width',
|
|
info.printFileWidth.toString(),
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'print_file_height',
|
|
info.printFileHeight.toString(),
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'print_file_dpi',
|
|
info.printFileDpi.toString(),
|
|
),
|
|
);
|
|
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
async updateFocusPoint(
|
|
productId: number,
|
|
focusXpoint2: number,
|
|
focusYpoint2: number,
|
|
): Promise<Promise<void>[]> {
|
|
const promises = [];
|
|
promises.push(
|
|
this.updateProductField(
|
|
productId,
|
|
'focusXpoint2',
|
|
focusXpoint2.toString(),
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'focusYpoint2',
|
|
focusYpoint2.toString(),
|
|
),
|
|
);
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
async updateComments(productId: number, comments: string): Promise<void> {
|
|
return this.updateProductField(productId, 'comments', comments);
|
|
}
|
|
|
|
async updateBlacklisting(
|
|
productId: number,
|
|
markets: Array<ProductBlacklistMarketInput>,
|
|
) {
|
|
const sql = /* sql */ `
|
|
INSERT INTO product_blacklist (product_id, group_id, market_id, created_at, updated_at)
|
|
VALUES (?, ?, ?, now(), now())
|
|
ON CONFLICT (product_id, group_id, market_id)
|
|
DO NOTHING`;
|
|
|
|
await this.knex.raw(
|
|
/* sql */ `
|
|
DELETE FROM product_blacklist WHERE product_id = ?
|
|
`,
|
|
[productId],
|
|
);
|
|
|
|
const insertsPromises = [];
|
|
markets.forEach((market) => {
|
|
market.groupIds.forEach((groupId) => {
|
|
const query = this.knex.raw(sql, [productId, groupId, market.marketId]);
|
|
insertsPromises.push(query);
|
|
});
|
|
});
|
|
return Promise.all(insertsPromises);
|
|
}
|
|
|
|
async setWallpaperTypes(
|
|
productId: number,
|
|
wallpaperTypes: Array<ProductWallpaperType>,
|
|
): Promise<any[]> {
|
|
await this.knex('product_wallpapertypes')
|
|
.where('product_id', productId)
|
|
.del();
|
|
|
|
const promises = wallpaperTypes.map((wpt) => {
|
|
const typeId = ProductWallpaperType[wpt];
|
|
return this.knex('product_wallpapertypes').insert({
|
|
product_id: productId,
|
|
wallpapertype_id: typeId,
|
|
});
|
|
});
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
async updateGroups(productId: number, groupIds: number[]): Promise<any[]> {
|
|
// Fetch the current state
|
|
const allPrintProducts = await this.knex.raw(
|
|
`SELECT printid, groupid FROM "product-printproducts" WHERE productid = ?`,
|
|
[productId],
|
|
);
|
|
|
|
const currentGroups = allPrintProducts.rows.map((item) => item.groupid);
|
|
// What we should add
|
|
const shouldAdd = groupIds.filter((group) => {
|
|
return !currentGroups.includes(group);
|
|
});
|
|
// What we should delete
|
|
const shouldDelete = allPrintProducts.rows.filter((item) => {
|
|
return !groupIds.includes(item.groupid);
|
|
});
|
|
|
|
// DELETE
|
|
if (shouldDelete.length > 0) {
|
|
// Delete from product-printproducts_materials
|
|
const printProductIds = shouldDelete.map((item) => item.printid);
|
|
|
|
// Delete from materials first, must be in sequence since we have constraint in materials to printproducts
|
|
await this.knex
|
|
.from('product-printproducts_materials')
|
|
.whereIn('printid', printProductIds)
|
|
.del();
|
|
|
|
// Delete from product-printproducts
|
|
await this.knex
|
|
.from('product-printproducts')
|
|
.whereIn('printid', printProductIds)
|
|
.del();
|
|
}
|
|
|
|
// INSERT
|
|
if (shouldAdd.length > 0) {
|
|
const insertionProducts = [];
|
|
// Insert in product-printproducts
|
|
// TODO: Depracate typeid, its not used to check repeating
|
|
const insertQuery = `INSERT INTO "product-printproducts" (productid, groupid, typeid) VALUES (?, ?, ?)`;
|
|
shouldAdd.forEach((groupId) => {
|
|
insertionProducts.push(
|
|
this.knex.raw(insertQuery, [
|
|
productId,
|
|
groupId,
|
|
groupId === ProductGroup.WALLPAPER
|
|
? ProductSizeTypes.TILING
|
|
: ProductSizeTypes.SCALING,
|
|
]),
|
|
);
|
|
});
|
|
|
|
// Run parallell
|
|
await Promise.all(insertionProducts);
|
|
|
|
// Fetch the new printids to be able to insert new materials
|
|
const newPrintIds = await this.knex
|
|
.from('product-printproducts')
|
|
.select('printid', 'groupid')
|
|
.where('productid', productId)
|
|
.whereIn('groupid', shouldAdd);
|
|
|
|
// Insert printIds into product-printproducts_materials
|
|
const insertMaterials = [];
|
|
const insertMaterialQuery = `INSERT INTO "product-printproducts_materials" (printid, materialid) VALUES (?, ?)`;
|
|
newPrintIds.forEach(({ printid, groupid }) => {
|
|
switch (groupid) {
|
|
case ProductGroup.PHOTO_WALLPAPER:
|
|
case ProductGroup.WALLPAPER:
|
|
insertMaterials.push(
|
|
this.knex.raw(insertMaterialQuery, [
|
|
printid,
|
|
ProductMaterials.STANDARD_WALLPAPER,
|
|
]),
|
|
);
|
|
insertMaterials.push(
|
|
this.knex.raw(insertMaterialQuery, [
|
|
printid,
|
|
ProductMaterials.SELF_ADHESIVE_WALLPAPER,
|
|
]),
|
|
);
|
|
insertMaterials.push(
|
|
this.knex.raw(insertMaterialQuery, [
|
|
printid,
|
|
ProductMaterials.PREMIUM_WALLPAPER,
|
|
]),
|
|
);
|
|
break;
|
|
case ProductGroup.CANVAS:
|
|
insertMaterials.push(
|
|
this.knex.raw(insertMaterialQuery, [
|
|
printid,
|
|
ProductMaterials.STANDARD_CANVAS,
|
|
]),
|
|
);
|
|
break;
|
|
case ProductGroup.POSTER:
|
|
insertMaterials.push(
|
|
this.knex.raw(insertMaterialQuery, [
|
|
printid,
|
|
ProductMaterials.PREMIUM_POSTER,
|
|
]),
|
|
);
|
|
break;
|
|
case ProductGroup.FRAMED_PRINT:
|
|
insertMaterials.push(
|
|
this.knex.raw(insertMaterialQuery, [
|
|
printid,
|
|
ProductMaterials.PREMIUM_FRAMED_PRINT,
|
|
]),
|
|
);
|
|
break;
|
|
}
|
|
});
|
|
return Promise.all(insertMaterials);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @func productGroupInteriors
|
|
*
|
|
* @param productId The product id for the targeted product
|
|
* @param groupId The targeted group id, if product does not have group it will be created
|
|
* @param uris The uris for the interiors, common for rooms and own uploades
|
|
*/
|
|
async addInteriorsToProductGroup(
|
|
productId: number,
|
|
groupId: number,
|
|
uris: string[],
|
|
): Promise<Promise<void>[]> {
|
|
// Create object to hold data as we go along all the db calls
|
|
// This object needs to support new own uploaded images, these should have
|
|
// some other folder prefix, like /interiors-buffer/. These will
|
|
// be inserted into DB first and then the image on s3 will be
|
|
// moved to /interior-images/ with the correct id.
|
|
const insertRegistry = uris.map((uri, index) => {
|
|
let id = null;
|
|
let roomName = null;
|
|
if (uri.indexOf('/') !== 0) {
|
|
uri = '/' + uri;
|
|
}
|
|
if (uri.indexOf('/interiors/') > -1) {
|
|
const spl = uri.split('/');
|
|
roomName = `${spl[5].split('.')[0]}_${spl[4]}_${spl[3]}`;
|
|
}
|
|
if (uri.indexOf('/interior-images/') > -1) {
|
|
id = parseInt(uri.split('/')[2].split('.')[0]);
|
|
}
|
|
return {
|
|
id: id,
|
|
uri: uri,
|
|
roomName: roomName || null,
|
|
roomId: null,
|
|
position: index,
|
|
};
|
|
});
|
|
|
|
// Get all the printproducts from the productId defined.
|
|
// This is needed for the insert and for the check if a product
|
|
// has the defined group or not.
|
|
const allPrintProducts = await this.knex.raw(
|
|
`SELECT printid, groupid FROM "product-printproducts" WHERE productid = ?`,
|
|
[productId],
|
|
);
|
|
|
|
// Predefine the print id needed for insert.
|
|
let insertPrintId;
|
|
|
|
// Map out the group ids.
|
|
const currentGroupIds = allPrintProducts.rows.map(({ groupid }) => groupid);
|
|
|
|
// This product does not have group with groupId
|
|
if (!currentGroupIds.includes(groupId)) {
|
|
await this.updateGroups(productId, currentGroupIds.concat([groupId]));
|
|
|
|
// Get the new printId
|
|
const printIdsResponse = await this.knex.raw(
|
|
`SELECT printid FROM "product-printproducts" WHERE productid = ? AND groupid = ?`,
|
|
[productId, groupId],
|
|
);
|
|
insertPrintId = printIdsResponse.rows.map(({ printid }) => printid)[0];
|
|
} else {
|
|
// This product had the targeted groupid, then we use
|
|
// the previous query for printId to save some DB calls.
|
|
insertPrintId = allPrintProducts.rows
|
|
.map(({ printid, groupid }) => {
|
|
if (groupid === groupId) {
|
|
return printid;
|
|
}
|
|
})
|
|
.filter(Boolean)[0]; // Remove falsy
|
|
}
|
|
|
|
// Delete all prior interiors
|
|
await this.knex.raw(`DELETE FROM interiors WHERE print_id = ?`, [
|
|
insertPrintId,
|
|
]);
|
|
|
|
// Insert room-ids, printids and position into "interiors" table.
|
|
const insertPromises = [];
|
|
insertRegistry.forEach((item) => {
|
|
if (item.roomName) {
|
|
insertPromises.push(
|
|
this.knex.raw(
|
|
`INSERT INTO interiors (print_id, room_id, position) VALUES (?, (SELECT id FROM rooms WHERE name = ?), ?)`,
|
|
[insertPrintId, item.roomName, item.position],
|
|
),
|
|
);
|
|
} else {
|
|
insertPromises.push(
|
|
this.knex.raw(
|
|
`INSERT INTO interiors (id, print_id, position) VALUES (?, ?, ?)`,
|
|
[item.id, insertPrintId, item.position],
|
|
),
|
|
);
|
|
}
|
|
});
|
|
|
|
return Promise.all(insertPromises);
|
|
}
|
|
|
|
async setProportionsWarning(
|
|
productId: number,
|
|
proportions: ProductProportionsInput,
|
|
): Promise<Promise<void>[]> {
|
|
const promises = [];
|
|
promises.push(
|
|
this.updateProductField(
|
|
productId,
|
|
'proportions_warning',
|
|
proportions.proportionsWarning ? '1' : '0',
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'margin_width_max',
|
|
proportions.marginWidthMax.toString(),
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'margin_width_min',
|
|
proportions.marginWidthMin.toString(),
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'margin_height_max',
|
|
proportions.marginHeightMax.toString(),
|
|
),
|
|
this.updateProductField(
|
|
productId,
|
|
'margin_height_min',
|
|
proportions.marginHeightMin.toString(),
|
|
),
|
|
);
|
|
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
async addRelatedProducts(
|
|
productId: number,
|
|
articleNumbers: Array<string>,
|
|
): Promise<Promise<void>[]> {
|
|
const whereClause = articleNumbers.map((a) => `'${a}'`).join(',');
|
|
const query = /* sql */ `
|
|
SELECT pp.productid, ppf.value FROM "product-products" pp
|
|
LEFT JOIN "product-products_fields" ppf
|
|
ON ppf.productid = pp.productid AND ppf.fieldid = 1
|
|
WHERE ppf.value IN (${whereClause});
|
|
`;
|
|
|
|
const res = await this.cachedRaw(query)
|
|
.cache(MINUTE)
|
|
.then((data) => data.rows);
|
|
|
|
if (!res.length) {
|
|
throw new UserInputError('No articles found');
|
|
}
|
|
const fieldsToInsert = res.map((item) => ({
|
|
productid1: productId,
|
|
productid2: item.productid,
|
|
}));
|
|
|
|
// Add this productId as related to its friends
|
|
res.forEach((item) => {
|
|
fieldsToInsert.push({
|
|
productid1: item.productid,
|
|
productid2: productId,
|
|
});
|
|
});
|
|
|
|
return this.knex.raw(`? ON CONFLICT (productid1, productid2) DO NOTHING;`, [
|
|
this.knex('product-products_products').insert(fieldsToInsert),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Removes variant products from productid as well as the sent in
|
|
* productid from its variants
|
|
*/
|
|
async removeRelatedProducts(
|
|
productId,
|
|
relatedProductIds,
|
|
): Promise<Promise<void>[]> {
|
|
// remove connection between the friends id and this produdId
|
|
const promises = relatedProductIds.map((pId) => {
|
|
return this.knex('product-products_products')
|
|
.where('productid1', pId)
|
|
.where('productid2', productId)
|
|
.del();
|
|
});
|
|
// remove connection to friends for this productsid
|
|
const query = this.knex('product-products_products')
|
|
.where('productid1', productId)
|
|
.whereIn('productid2', relatedProductIds)
|
|
.del();
|
|
promises.push(query);
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
async addCategoriesToProduct(
|
|
productId,
|
|
categoryIds,
|
|
): Promise<Promise<void>[]> {
|
|
const promises = categoryIds.map((categoryId) =>
|
|
this.knex('product_category').insert({
|
|
product_id: productId,
|
|
category_id: categoryId,
|
|
}),
|
|
);
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
async removeCategoriesFromProduct(
|
|
productId,
|
|
categoryIds,
|
|
): Promise<Promise<void>[]> {
|
|
const promises = categoryIds.map((categoryId) =>
|
|
this.knex('product_category')
|
|
.where({
|
|
product_id: productId,
|
|
category_id: categoryId,
|
|
})
|
|
.del(),
|
|
);
|
|
return Promise.all(promises);
|
|
}
|
|
}
|