Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ee30fdb8e | ||
|
|
d141e5b034 |
@@ -14,8 +14,8 @@ export class AttributeGroupRepository {
|
||||
through: { attributes: ['display_order'] }
|
||||
}
|
||||
],
|
||||
order: options.order || [
|
||||
['created_at', 'DESC']
|
||||
order: [
|
||||
['name', 'ASC']
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ export class AttributeSetRepository {
|
||||
]
|
||||
}
|
||||
],
|
||||
order: options.order || [
|
||||
['created_at', 'DESC']
|
||||
order: [
|
||||
['name', 'ASC']
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class AttributeRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
|
||||
@@ -54,7 +54,7 @@ export class AttributeService {
|
||||
}
|
||||
|
||||
// Sorting
|
||||
let order = [['created_at', 'DESC']];
|
||||
let order = [['display_order', 'ASC']];
|
||||
if (query.sortBy) {
|
||||
const direction = query.sortDir?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
||||
if (query.sortBy === 'name') order = [['name', direction]];
|
||||
|
||||
@@ -15,7 +15,6 @@ export class RoleRepository {
|
||||
};
|
||||
|
||||
return await models.Role.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
include: [
|
||||
{
|
||||
model: models.PermissionNode,
|
||||
|
||||
@@ -8,7 +8,6 @@ export class UserRepository {
|
||||
? { tenant_id: context.tenantId }
|
||||
: {};
|
||||
return await models.User.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
attributes: { exclude: ['password_hash'] },
|
||||
include: [
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class BrandRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class UnitRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ export class CatalogController {
|
||||
: (raw.category_id || ''),
|
||||
attributeSetId: raw.attribute_set_id || (raw.attributeSet ? raw.attributeSet.id : null) || '',
|
||||
workflowCode: raw.workflow_code || 'standard',
|
||||
productType: raw.completeness_rules?.productType || raw.completenessRules?.productType || raw.productType || raw.product_type || null,
|
||||
completenessRules: raw.completeness_rules || {},
|
||||
allowedBrands: raw.allowedBrands || [],
|
||||
allowedUnits: raw.allowedUnits || [],
|
||||
|
||||
@@ -57,8 +57,8 @@ export class CatalogRepository {
|
||||
]
|
||||
}
|
||||
],
|
||||
order: options.order || [
|
||||
['created_at', 'DESC']
|
||||
order: [
|
||||
['name', 'ASC']
|
||||
],
|
||||
...queryOptions
|
||||
});
|
||||
|
||||
@@ -243,15 +243,11 @@ export class CatalogService {
|
||||
const completenessRules = data.completenessRules || data.completeness_rules || {};
|
||||
completenessRules.allowedBrands = data.allowedBrands || data.allowed_brands || [];
|
||||
completenessRules.allowedUnits = data.allowedUnits || data.allowed_units || [];
|
||||
const incomingProductType = data.productType || data.product_type || data.type;
|
||||
if (incomingProductType) {
|
||||
completenessRules.productType = incomingProductType;
|
||||
}
|
||||
|
||||
let totalWeight = 0;
|
||||
let hasRules = false;
|
||||
for (const [key, val] of Object.entries(completenessRules)) {
|
||||
if (key === 'allowedBrands' || key === 'allowedUnits' || key === 'productType') continue;
|
||||
if (key === 'allowedBrands' || key === 'allowedUnits') continue;
|
||||
const weight = Number(val);
|
||||
if (isNaN(weight)) {
|
||||
throw new Error(`Completeness rule weight for "${key}" must be a number`);
|
||||
@@ -519,17 +515,11 @@ export class CatalogService {
|
||||
if (data.hasOwnProperty('allowedUnits') || data.hasOwnProperty('allowed_units')) {
|
||||
completenessRules.allowedUnits = data.allowedUnits || data.allowed_units || [];
|
||||
}
|
||||
if (data.hasOwnProperty('productType') || data.hasOwnProperty('product_type') || data.hasOwnProperty('type')) {
|
||||
const pType = data.productType || data.product_type || data.type;
|
||||
if (pType) {
|
||||
completenessRules.productType = pType;
|
||||
}
|
||||
}
|
||||
|
||||
let totalWeight = 0;
|
||||
let hasRules = false;
|
||||
for (const [key, val] of Object.entries(completenessRules)) {
|
||||
if (key === 'allowedBrands' || key === 'allowedUnits' || key === 'productType') continue;
|
||||
if (key === 'allowedBrands' || key === 'allowedUnits') continue;
|
||||
const weight = Number(val);
|
||||
if (isNaN(weight)) {
|
||||
throw new Error(`Completeness rule weight for "${key}" must be a number`);
|
||||
@@ -758,7 +748,6 @@ export class CatalogService {
|
||||
if (!family) throw new Error('Product Family not found');
|
||||
|
||||
let groups = [];
|
||||
let attributeSetObj = null;
|
||||
if (family.attribute_set_id) {
|
||||
const setRecord = await models.AttributeSet.findByPk(family.attribute_set_id, {
|
||||
include: [
|
||||
@@ -781,13 +770,8 @@ export class CatalogService {
|
||||
}
|
||||
]
|
||||
});
|
||||
if (setRecord) {
|
||||
const setObj = setRecord.toJSON
|
||||
? setRecord.toJSON()
|
||||
: JSON.parse(JSON.stringify(setRecord));
|
||||
|
||||
attributeSetObj = setObj;
|
||||
groups = setObj.groups || [];
|
||||
if (setRecord && setRecord.groups) {
|
||||
groups = setRecord.groups;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,9 +826,7 @@ export class CatalogService {
|
||||
name: family.name,
|
||||
description: family.description,
|
||||
category: family.category,
|
||||
attributeSet: attributeSetObj || family.attributeSet || null,
|
||||
attribute_set_id: family.attribute_set_id || null,
|
||||
attributeSetId: family.attribute_set_id || null,
|
||||
attributeSet: family.attributeSet,
|
||||
groups,
|
||||
attributes: family.attributes || [],
|
||||
variantAxes: family.variantAxes || [],
|
||||
@@ -855,8 +837,7 @@ export class CatalogService {
|
||||
workflow: workflow,
|
||||
allowedBrands: completenessRules.allowedBrands || [],
|
||||
allowedUnits: completenessRules.allowedUnits || [],
|
||||
completenessRules: completenessRules,
|
||||
productType: completenessRules.productType || null
|
||||
completenessRules: completenessRules
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,17 +48,7 @@ export const createValidation = [
|
||||
.isString(),
|
||||
body('categoryId')
|
||||
.optional({ nullable: true })
|
||||
.isString(),
|
||||
body('productType')
|
||||
.optional({ nullable: true })
|
||||
.isIn(['simple', 'variant'])
|
||||
.withMessage('Product type must be either simple or variant'),
|
||||
body('product_type')
|
||||
.optional({ nullable: true })
|
||||
.isIn(['simple', 'variant']),
|
||||
body('type')
|
||||
.optional({ nullable: true })
|
||||
.isIn(['simple', 'variant'])
|
||||
.isString()
|
||||
];
|
||||
|
||||
export const updateValidation = [
|
||||
@@ -113,17 +103,7 @@ export const updateValidation = [
|
||||
.isString(),
|
||||
body('categoryId')
|
||||
.optional({ nullable: true })
|
||||
.isString(),
|
||||
body('productType')
|
||||
.optional({ nullable: true })
|
||||
.isIn(['simple', 'variant'])
|
||||
.withMessage('Product type must be either simple or variant'),
|
||||
body('product_type')
|
||||
.optional({ nullable: true })
|
||||
.isIn(['simple', 'variant']),
|
||||
body('type')
|
||||
.optional({ nullable: true })
|
||||
.isIn(['simple', 'variant'])
|
||||
.isString()
|
||||
];
|
||||
|
||||
export const deleteValidation = [
|
||||
|
||||
@@ -4,11 +4,7 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class ChannelTypeRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
return await models.ChannelType.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
return await models.ChannelType.findAll({ ...options, where });
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -4,11 +4,7 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class ChannelRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
return await models.Channel.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
return await models.Channel.findAll({ ...options, where });
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -16,7 +16,7 @@ export const shopifyMapper = {
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/(^-|-$)/g, '');
|
||||
const uniqueSuffix = String(identity.id).replace(/[^a-z0-9]/gi, '').slice(0, 10);
|
||||
const uniqueSuffix = String(identity.id).replace(/[^a-z0-9]/gi, '').slice(0, 8) + '-' + Date.now().toString(36);
|
||||
input.handle = `${baseSlug}-${uniqueSuffix}`;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,16 @@ export const shopifyMapper = {
|
||||
});
|
||||
|
||||
if (optionKeys.size > 0) {
|
||||
input.productOptions = Array.from(optionKeys).slice(0, 3).map(optName => ({
|
||||
const priorityOrder = ['color', 'size', 'storage', 'ram', 'rom', 'material', 'style'];
|
||||
const top3OptionKeys = Array.from(optionKeys).sort((a, b) => {
|
||||
const idxA = priorityOrder.indexOf(a.toLowerCase());
|
||||
const idxB = priorityOrder.indexOf(b.toLowerCase());
|
||||
const pA = idxA === -1 ? 99 : idxA;
|
||||
const pB = idxB === -1 ? 99 : idxB;
|
||||
return pA - pB;
|
||||
}).slice(0, 3);
|
||||
|
||||
input.productOptions = top3OptionKeys.map(optName => ({
|
||||
name: optName,
|
||||
values: Array.from(new Set(
|
||||
variants
|
||||
|
||||
@@ -9,6 +9,16 @@ const PRODUCT_CREATE_MUTATION = `
|
||||
title
|
||||
handle
|
||||
onlineStoreUrl
|
||||
variants(first: 50) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
selectedOptions {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
userErrors {
|
||||
field
|
||||
@@ -26,6 +36,16 @@ const PRODUCT_UPDATE_MUTATION = `
|
||||
title
|
||||
handle
|
||||
onlineStoreUrl
|
||||
variants(first: 50) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
selectedOptions {
|
||||
name
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
userErrors {
|
||||
field
|
||||
@@ -62,6 +82,38 @@ const PRODUCT_CREATE_MEDIA_MUTATION = `
|
||||
}
|
||||
`;
|
||||
|
||||
const BULK_VARIANTS_CREATE_MUTATION = `
|
||||
mutation productVariantsBulkCreate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
|
||||
productVariantsBulkCreate(productId: $productId, variants: $variants) {
|
||||
productVariants {
|
||||
id
|
||||
title
|
||||
price
|
||||
}
|
||||
userErrors {
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const BULK_VARIANTS_UPDATE_MUTATION = `
|
||||
mutation productVariantsBulkUpdate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
|
||||
productVariantsBulkUpdate(productId: $productId, variants: $variants) {
|
||||
productVariants {
|
||||
id
|
||||
title
|
||||
price
|
||||
}
|
||||
userErrors {
|
||||
field
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export class ShopifyPublisher {
|
||||
constructor(shopifyClient) {
|
||||
this.client = shopifyClient;
|
||||
@@ -107,14 +159,119 @@ export class ShopifyPublisher {
|
||||
throw new Error('Shopify GraphQL returned empty product response');
|
||||
}
|
||||
|
||||
// Attach media/image URLs to Shopify product
|
||||
const validMedia = (canonicalProduct.media || [])
|
||||
.filter(m => m.url && (m.url.startsWith('http://') || m.url.startsWith('https://')))
|
||||
.map(m => ({
|
||||
mediaContentType: 'IMAGE',
|
||||
originalSource: m.url,
|
||||
alt: canonicalProduct.content.name
|
||||
}));
|
||||
// ── Multi-Variant Bulk Creation & Synchronization ──
|
||||
const pimVariants = canonicalProduct.variants || [];
|
||||
if (Array.isArray(pimVariants) && pimVariants.length > 0) {
|
||||
const existingNodes = shopifyProduct.variants?.nodes || [];
|
||||
|
||||
// Allowed option names in Shopify product
|
||||
const allowedOptionNames = (input.productOptions || []).map(o => o.name);
|
||||
|
||||
const newVariantsInput = [];
|
||||
const updateVariantsInput = [];
|
||||
|
||||
for (const v of pimVariants) {
|
||||
let optionValues = Object.entries(v.attributes || {}).map(([optName, optVal]) => ({
|
||||
optionName: optName,
|
||||
name: String(optVal)
|
||||
}));
|
||||
|
||||
if (allowedOptionNames.length > 0) {
|
||||
optionValues = optionValues.filter(ov =>
|
||||
allowedOptionNames.some(aName => aName.toLowerCase() === ov.optionName.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (optionValues.length === 0) continue;
|
||||
|
||||
// Try matching with existing Shopify variant node
|
||||
const matchedNode = existingNodes.find(node => {
|
||||
const nodeOptions = node.selectedOptions || [];
|
||||
return optionValues.every(ov =>
|
||||
nodeOptions.some(no => no.name.toLowerCase() === ov.optionName.toLowerCase() && no.value.toLowerCase() === ov.name.toLowerCase())
|
||||
);
|
||||
});
|
||||
|
||||
if (matchedNode) {
|
||||
updateVariantsInput.push({
|
||||
id: matchedNode.id,
|
||||
price: String(v.price || 0),
|
||||
compareAtPrice: v.costPrice ? String(v.costPrice) : null,
|
||||
inventoryItem: {
|
||||
sku: v.sku || '',
|
||||
tracked: true
|
||||
}
|
||||
});
|
||||
} else {
|
||||
newVariantsInput.push({
|
||||
optionValues,
|
||||
price: String(v.price || 0),
|
||||
compareAtPrice: v.costPrice ? String(v.costPrice) : null,
|
||||
inventoryItem: {
|
||||
sku: v.sku || '',
|
||||
tracked: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create new variants
|
||||
if (newVariantsInput.length > 0) {
|
||||
try {
|
||||
const createRes = await this.client.graphql(BULK_VARIANTS_CREATE_MUTATION, {
|
||||
productId: shopifyProduct.id,
|
||||
variants: newVariantsInput
|
||||
});
|
||||
const createErrors = createRes.data?.productVariantsBulkCreate?.userErrors;
|
||||
if (createErrors?.length > 0) {
|
||||
console.warn('[ShopifyPublisher] Variant create warnings:', createErrors.map(e => e.message).join(', '));
|
||||
}
|
||||
} catch (vErr) {
|
||||
console.warn('[ShopifyPublisher] Variant bulk create error:', vErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update existing variants
|
||||
if (updateVariantsInput.length > 0) {
|
||||
try {
|
||||
const updateRes = await this.client.graphql(BULK_VARIANTS_UPDATE_MUTATION, {
|
||||
productId: shopifyProduct.id,
|
||||
variants: updateVariantsInput
|
||||
});
|
||||
const updateErrors = updateRes.data?.productVariantsBulkUpdate?.userErrors;
|
||||
if (updateErrors?.length > 0) {
|
||||
console.warn('[ShopifyPublisher] Variant update warnings:', updateErrors.map(e => e.message).join(', '));
|
||||
}
|
||||
} catch (vErr) {
|
||||
console.warn('[ShopifyPublisher] Variant bulk update error:', vErr.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Attach Media/Images to Shopify Product ──
|
||||
const allMediaUrls = new Set();
|
||||
|
||||
// Product level media
|
||||
(canonicalProduct.media || []).forEach(m => {
|
||||
if (m.url && (m.url.startsWith('http://') || m.url.startsWith('https://'))) {
|
||||
allMediaUrls.add(m.url);
|
||||
}
|
||||
});
|
||||
|
||||
// Variant level media
|
||||
(pimVariants).forEach(v => {
|
||||
(v.media || []).forEach(m => {
|
||||
if (m.url && (m.url.startsWith('http://') || m.url.startsWith('https://'))) {
|
||||
allMediaUrls.add(m.url);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const validMedia = Array.from(allMediaUrls).map(url => ({
|
||||
mediaContentType: 'IMAGE',
|
||||
originalSource: url,
|
||||
alt: canonicalProduct.content.name
|
||||
}));
|
||||
|
||||
if (validMedia.length > 0 && shopifyProduct?.id) {
|
||||
try {
|
||||
@@ -158,7 +315,8 @@ export class ShopifyPublisher {
|
||||
}
|
||||
|
||||
async deleteProduct(context) {
|
||||
const { tenantId, integrationId, productId } = context;
|
||||
const { tenantId, integrationId, canonicalProduct } = context;
|
||||
const productId = canonicalProduct.identity.id;
|
||||
|
||||
const existingResource = await models.ExternalResource.findOne({
|
||||
where: {
|
||||
@@ -170,19 +328,24 @@ export class ShopifyPublisher {
|
||||
});
|
||||
|
||||
if (!existingResource) {
|
||||
return { success: true, message: 'Resource was not published to Shopify' };
|
||||
return { success: true, message: 'No external mapping found for deletion' };
|
||||
}
|
||||
|
||||
const response = await this.client.graphql(PRODUCT_DELETE_MUTATION, {
|
||||
input: { id: existingResource.external_id }
|
||||
});
|
||||
|
||||
const result = response.data?.productDelete;
|
||||
if (result?.userErrors?.length > 0) {
|
||||
throw new Error(`Shopify productDelete user errors: ${result.userErrors.map(e => e.message).join(', ')}`);
|
||||
const userErrors = response.data?.productDelete?.userErrors;
|
||||
if (userErrors?.length > 0) {
|
||||
throw new Error(`Shopify productDelete user errors: ${userErrors.map(e => e.message).join(', ')}`);
|
||||
}
|
||||
|
||||
await existingResource.destroy();
|
||||
return { success: true, deletedProductId: result.deletedProductId };
|
||||
|
||||
return {
|
||||
success: true,
|
||||
externalId: existingResource.external_id,
|
||||
operation: 'DELETE'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,8 +80,31 @@ export const canonicalProductBuilder = {
|
||||
const vAttrs = {};
|
||||
if (Array.isArray(v.values)) {
|
||||
v.values.forEach(val => {
|
||||
const key = val.axis?.code || val.attribute_id;
|
||||
vAttrs[key] = val.value_text || val.value_number;
|
||||
const rawKey = val.axis?.name || val.axis?.code || '';
|
||||
let key = rawKey
|
||||
.replace(/_variant$/i, '')
|
||||
.replace(/_/g, ' ')
|
||||
.trim()
|
||||
.replace(/\b\w/g, l => l.toUpperCase());
|
||||
|
||||
// Normalize common option names
|
||||
if (/^colors?$/i.test(key)) key = 'Color';
|
||||
if (/^sizes?$/i.test(key)) key = 'Size';
|
||||
if (/^ram$/i.test(key)) key = 'RAM';
|
||||
|
||||
// Skip brand / vendor from variant options
|
||||
if (!key || /^brand$/i.test(key) || /^vendor$/i.test(key) || key.toLowerCase().includes('brand_id')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let valStr = String(val.value_text || val.value_number || '').trim();
|
||||
// Skip if value looks like a UUID
|
||||
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(valStr)) {
|
||||
return;
|
||||
}
|
||||
if (valStr.length > 0) {
|
||||
vAttrs[key] = valStr.length <= 3 ? valStr.toUpperCase() : valStr.charAt(0).toUpperCase() + valStr.slice(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -79,21 +79,6 @@ export const processOutboxEvents = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
let isProcessing = false;
|
||||
|
||||
export const guardedProcessOutboxEvents = async () => {
|
||||
if (isProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
isProcessing = true;
|
||||
try {
|
||||
await processOutboxEvents();
|
||||
} finally {
|
||||
isProcessing = false;
|
||||
}
|
||||
};
|
||||
|
||||
export const startOutboxWorker = (intervalMs = 10000) => {
|
||||
setInterval(guardedProcessOutboxEvents, intervalMs);
|
||||
setInterval(processOutboxEvents, intervalMs);
|
||||
};
|
||||
|
||||
@@ -14,11 +14,7 @@ export class AssetFamilyRepository {
|
||||
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
};
|
||||
const queryOptions = { ...options, where };
|
||||
try {
|
||||
return await models.AssetFamily.findAll(queryOptions);
|
||||
} catch (err) {
|
||||
|
||||
@@ -4,11 +4,7 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class AssetTypeRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
return await models.AssetType.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
return await models.AssetType.findAll({ ...options, where });
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import { applyTenantScope } from '../../../utils/helpers/common.helper.js';
|
||||
export class ProductRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
@@ -63,16 +62,79 @@ export class ProductRepository {
|
||||
as: 'family',
|
||||
required: false,
|
||||
include: [
|
||||
{
|
||||
model: models.Attribute,
|
||||
as: 'attributes',
|
||||
through: { attributes: [] },
|
||||
required: false,
|
||||
include: [
|
||||
{
|
||||
model: models.AttributeOption,
|
||||
as: 'optionsList',
|
||||
attributes: ['id', 'code', 'label', 'sort_order', 'status'],
|
||||
required: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: models.Attribute,
|
||||
as: 'variantAxes',
|
||||
through: { attributes: [] },
|
||||
required: false,
|
||||
include: [
|
||||
{
|
||||
model: models.AttributeOption,
|
||||
as: 'optionsList',
|
||||
attributes: ['id', 'code', 'label', 'sort_order', 'status'],
|
||||
required: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: models.AssetFamily,
|
||||
as: 'assetRequirements',
|
||||
through: { attributes: [] },
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: models.FamilyChannel,
|
||||
as: 'channels',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: models.Categorie,
|
||||
as: 'category',
|
||||
attributes: ['id', 'name']
|
||||
attributes: ['id', 'name', 'code'],
|
||||
required: false
|
||||
},
|
||||
{
|
||||
model: models.AttributeSet,
|
||||
as: 'attributeSet',
|
||||
required: false,
|
||||
include: [
|
||||
{
|
||||
model: models.AttributeGroup,
|
||||
as: 'groups',
|
||||
through: { attributes: [] },
|
||||
required: false,
|
||||
include: [
|
||||
{
|
||||
model: models.Attribute,
|
||||
as: 'attributes',
|
||||
through: { attributes: [] },
|
||||
required: false,
|
||||
include: [
|
||||
{
|
||||
model: models.AttributeOption,
|
||||
as: 'optionsList',
|
||||
attributes: ['id', 'code', 'label', 'sort_order', 'status'],
|
||||
required: false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -44,7 +44,7 @@ export class VariantRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
return await models.Variant.findAll({
|
||||
order: options.order || [['created_at', 'DESC']],
|
||||
order: [['sku', 'ASC']],
|
||||
include: options.include || getDefaultVariantIncludes(),
|
||||
...options,
|
||||
where
|
||||
|
||||
@@ -4,11 +4,7 @@ import { applyTenantScope } from '../../utils/helpers/common.helper.js';
|
||||
export class WorkflowRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
return await models.WorkflowRegistry.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
return await models.WorkflowRegistry.findAll({ ...options, where });
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -20,12 +20,6 @@ module.exports = {
|
||||
port: process.env.DB_PORT || 5432,
|
||||
dialect: process.env.DB_DIALECT || "postgres",
|
||||
logging: false,
|
||||
pool: {
|
||||
max: 15,
|
||||
min: 2,
|
||||
acquire: 30000,
|
||||
idle: 10000,
|
||||
},
|
||||
},
|
||||
|
||||
development: {
|
||||
@@ -36,12 +30,6 @@ module.exports = {
|
||||
port: process.env.DB_PORT || 5432,
|
||||
dialect: process.env.DB_DIALECT || "postgres",
|
||||
logging: false,
|
||||
pool: {
|
||||
max: 15,
|
||||
min: 2,
|
||||
acquire: 30000,
|
||||
idle: 10000,
|
||||
},
|
||||
},
|
||||
|
||||
test: {
|
||||
|
||||
Reference in New Issue
Block a user