Merge remote-tracking branch 'origin/dev' into feature/inam-platform-core-setup
This commit is contained in:
@@ -14,8 +14,8 @@ export class AttributeGroupRepository {
|
||||
through: { attributes: ['display_order'] }
|
||||
}
|
||||
],
|
||||
order: [
|
||||
['name', 'ASC']
|
||||
order: options.order || [
|
||||
['created_at', 'DESC']
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ export class AttributeSetRepository {
|
||||
]
|
||||
}
|
||||
],
|
||||
order: [
|
||||
['name', 'ASC']
|
||||
order: options.order || [
|
||||
['created_at', 'DESC']
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { applyTenantScope, applyTenantWriteScope } from '../../../utils/helpers/
|
||||
export class AttributeRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ export class AttributeService {
|
||||
}
|
||||
|
||||
// Sorting
|
||||
let order = [['display_order', 'ASC']];
|
||||
let order = [['created_at', 'DESC']];
|
||||
if (query.sortBy) {
|
||||
const direction = query.sortDir?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
||||
if (query.sortBy === 'name') order = [['name', direction]];
|
||||
|
||||
@@ -15,6 +15,7 @@ export class RoleRepository {
|
||||
};
|
||||
|
||||
return await models.Role.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
include: [
|
||||
{
|
||||
model: models.PermissionNode,
|
||||
|
||||
@@ -8,6 +8,7 @@ export class UserRepository {
|
||||
? { tenant_id: context.tenantId }
|
||||
: {};
|
||||
return await models.User.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
attributes: { exclude: ['password_hash'] },
|
||||
include: [
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ import { applyTenantScope, applyTenantWriteScope } from '../../../utils/helpers/
|
||||
export class BrandRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { applyTenantScope, applyTenantWriteScope } from '../../../utils/helpers/
|
||||
export class UnitRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyTenantScope(options.where || {}, context)
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ 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: [
|
||||
['name', 'ASC']
|
||||
order: options.order || [
|
||||
['created_at', 'DESC']
|
||||
],
|
||||
...queryOptions
|
||||
});
|
||||
|
||||
@@ -249,11 +249,15 @@ 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') continue;
|
||||
if (key === 'allowedBrands' || key === 'allowedUnits' || key === 'productType') continue;
|
||||
const weight = Number(val);
|
||||
if (isNaN(weight)) {
|
||||
throw new Error(`Completeness rule weight for "${key}" must be a number`);
|
||||
@@ -522,11 +526,17 @@ 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') continue;
|
||||
if (key === 'allowedBrands' || key === 'allowedUnits' || key === 'productType') continue;
|
||||
const weight = Number(val);
|
||||
if (isNaN(weight)) {
|
||||
throw new Error(`Completeness rule weight for "${key}" must be a number`);
|
||||
@@ -756,6 +766,7 @@ 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: [
|
||||
@@ -778,8 +789,13 @@ export class CatalogService {
|
||||
}
|
||||
]
|
||||
});
|
||||
if (setRecord && setRecord.groups) {
|
||||
groups = setRecord.groups;
|
||||
if (setRecord) {
|
||||
const setObj = setRecord.toJSON
|
||||
? setRecord.toJSON()
|
||||
: JSON.parse(JSON.stringify(setRecord));
|
||||
|
||||
attributeSetObj = setObj;
|
||||
groups = setObj.groups || [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,7 +850,9 @@ export class CatalogService {
|
||||
name: family.name,
|
||||
description: family.description,
|
||||
category: family.category,
|
||||
attributeSet: family.attributeSet,
|
||||
attributeSet: attributeSetObj || family.attributeSet || null,
|
||||
attribute_set_id: family.attribute_set_id || null,
|
||||
attributeSetId: family.attribute_set_id || null,
|
||||
groups,
|
||||
attributes: family.attributes || [],
|
||||
variantAxes: family.variantAxes || [],
|
||||
@@ -845,7 +863,8 @@ export class CatalogService {
|
||||
workflow: workflow,
|
||||
allowedBrands: completenessRules.allowedBrands || [],
|
||||
allowedUnits: completenessRules.allowedUnits || [],
|
||||
completenessRules: completenessRules
|
||||
completenessRules: completenessRules,
|
||||
productType: completenessRules.productType || null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,17 @@ export const createValidation = [
|
||||
.isString(),
|
||||
body('categoryId')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.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'])
|
||||
];
|
||||
|
||||
export const updateValidation = [
|
||||
@@ -103,7 +113,17 @@ export const updateValidation = [
|
||||
.isString(),
|
||||
body('categoryId')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.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'])
|
||||
];
|
||||
|
||||
export const deleteValidation = [
|
||||
|
||||
@@ -4,7 +4,11 @@ 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({ ...options, where });
|
||||
return await models.ChannelType.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -4,7 +4,11 @@ import { applyTenantScope, applyTenantWriteScope } from '../../../utils/helpers/
|
||||
export class ChannelRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
return await models.Channel.findAll({ ...options, where });
|
||||
return await models.Channel.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -79,6 +79,21 @@ 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(processOutboxEvents, intervalMs);
|
||||
setInterval(guardedProcessOutboxEvents, intervalMs);
|
||||
};
|
||||
|
||||
@@ -14,7 +14,11 @@ export class AssetFamilyRepository {
|
||||
|
||||
async findAll(options = {}, context = {}) {
|
||||
const where = applyTenantScope(options.where || {}, context);
|
||||
const queryOptions = { ...options, where };
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
};
|
||||
try {
|
||||
return await models.AssetFamily.findAll(queryOptions);
|
||||
} catch (err) {
|
||||
|
||||
@@ -4,7 +4,11 @@ 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({ ...options, where });
|
||||
return await models.AssetType.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -10,6 +10,7 @@ const applyProductTenantScope = (where = {}, context = {}) => {
|
||||
export class ProductRepository {
|
||||
async findAll(options = {}, context = {}) {
|
||||
const queryOptions = {
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where: applyProductTenantScope(options.where || {}, context)
|
||||
};
|
||||
@@ -68,79 +69,16 @@ 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', '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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
attributes: ['id', 'name']
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -46,7 +46,7 @@ export class VariantRepository {
|
||||
// there is no global baseline variant that tenant workspaces should inherit.
|
||||
const where = applyTenantWriteScope(options.where || {}, context);
|
||||
return await models.Variant.findAll({
|
||||
order: [['sku', 'ASC']],
|
||||
order: options.order || [['created_at', 'DESC']],
|
||||
include: options.include || getDefaultVariantIncludes(),
|
||||
...options,
|
||||
where
|
||||
|
||||
@@ -4,7 +4,11 @@ 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({ ...options, where });
|
||||
return await models.WorkflowRegistry.findAll({
|
||||
order: [['created_at', 'DESC']],
|
||||
...options,
|
||||
where
|
||||
});
|
||||
}
|
||||
|
||||
async findById(id, options = {}, context = {}) {
|
||||
|
||||
@@ -20,6 +20,12 @@ 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: {
|
||||
@@ -30,6 +36,12 @@ 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