Some checks failed
farmcontrol/farmcontrol-api/pipeline/head There was a failure building this commit
436 lines
12 KiB
JavaScript
436 lines
12 KiB
JavaScript
import config from '../../config.js';
|
|
import { listingVarientModel } from '../../database/schemas/sales/listingvarient.schema.js';
|
|
import log4js from 'log4js';
|
|
import mongoose from 'mongoose';
|
|
import {
|
|
deleteObject,
|
|
listObjects,
|
|
getObject,
|
|
editObject,
|
|
newObject,
|
|
listObjectsByProperties,
|
|
getModelStats,
|
|
getModelHistory,
|
|
checkStates,
|
|
} from '../../database/database.js';
|
|
import { listingModel } from '../../database/schemas/sales/listing.schema.js';
|
|
import {
|
|
hasIntegration,
|
|
publishMarketplaceOfferForSku,
|
|
withdrawMarketplaceOfferForSku,
|
|
} from '../../integrations/marketplaceworker.js';
|
|
|
|
const logger = log4js.getLogger('ListingVarients');
|
|
logger.level = config.server.logLevel;
|
|
|
|
const POPULATE_FIELDS = ['listing', 'product', 'productSku', 'priceTaxRate'];
|
|
|
|
export const listListingVarientsRouteHandler = async (
|
|
req,
|
|
res,
|
|
page = 1,
|
|
limit = 25,
|
|
property = '',
|
|
filter = {},
|
|
search = '',
|
|
sort = '',
|
|
order = 'ascend'
|
|
) => {
|
|
const result = await listObjects({
|
|
model: listingVarientModel,
|
|
page,
|
|
limit,
|
|
property,
|
|
filter,
|
|
search,
|
|
sort,
|
|
order,
|
|
populate: POPULATE_FIELDS,
|
|
});
|
|
|
|
if (result?.error) {
|
|
logger.error('Error listing listing varients.');
|
|
res.status(result.code).send(result);
|
|
return;
|
|
}
|
|
|
|
logger.debug(`List of listing varients (Page ${page}, Limit ${limit}). Count: ${result.length}.`);
|
|
res.send(result);
|
|
};
|
|
|
|
export const listListingVarientsByPropertiesRouteHandler = async (
|
|
req,
|
|
res,
|
|
properties = '',
|
|
filter = {}
|
|
) => {
|
|
const result = await listObjectsByProperties({
|
|
model: listingVarientModel,
|
|
properties,
|
|
filter,
|
|
populate: POPULATE_FIELDS,
|
|
});
|
|
|
|
if (result?.error) {
|
|
logger.error('Error listing listing varients.');
|
|
res.status(result.code).send(result);
|
|
return;
|
|
}
|
|
|
|
logger.debug(`List of listing varients. Count: ${result.length}`);
|
|
res.send(result);
|
|
};
|
|
|
|
export const getListingVarientRouteHandler = async (req, res) => {
|
|
const id = req.params.id;
|
|
const result = await getObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
populate: POPULATE_FIELDS,
|
|
});
|
|
if (result?.error) {
|
|
logger.warn(`Listing varient not found with supplied id.`);
|
|
return res.status(result.code).send(result);
|
|
}
|
|
logger.debug(`Retrieved listing varient with ID: ${id}`);
|
|
res.send(result);
|
|
};
|
|
|
|
export const editListingVarientRouteHandler = async (req, res) => {
|
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
|
|
|
logger.trace(`Listing varient with ID: ${id}`);
|
|
|
|
const updateData = {
|
|
updatedAt: new Date(),
|
|
listing: req.body.listing,
|
|
product: req.body.product,
|
|
productSku: req.body.productSku,
|
|
price: req.body.price,
|
|
currency: req.body.currency,
|
|
priceTaxRate: req.body.priceTaxRate,
|
|
priceWithTax: req.body.priceWithTax,
|
|
};
|
|
const result = await editObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
updateData,
|
|
user: req.user,
|
|
populate: POPULATE_FIELDS,
|
|
});
|
|
|
|
if (result.error) {
|
|
logger.error('Error editing listing varient:', result.error);
|
|
res.status(result.code).send(result);
|
|
return;
|
|
}
|
|
|
|
logger.debug(`Edited listing varient with ID: ${id}`);
|
|
res.send(result);
|
|
};
|
|
|
|
export const newListingVarientRouteHandler = async (req, res) => {
|
|
const newData = {
|
|
updatedAt: new Date(),
|
|
listing: req.body.listing,
|
|
product: req.body.product,
|
|
productSku: req.body.productSku,
|
|
state: req.body.state || { type: 'draft' },
|
|
price: req.body.price,
|
|
currency: req.body.currency,
|
|
priceTaxRate: req.body.priceTaxRate,
|
|
priceWithTax: req.body.priceWithTax,
|
|
};
|
|
const result = await newObject({
|
|
model: listingVarientModel,
|
|
newData,
|
|
user: req.user,
|
|
});
|
|
if (result.error) {
|
|
logger.error('No listing varient created:', result.error);
|
|
return res.status(result.code).send(result);
|
|
}
|
|
|
|
logger.debug(`New listing varient with ID: ${result._id}`);
|
|
res.send(result);
|
|
};
|
|
|
|
export const deleteListingVarientRouteHandler = async (req, res) => {
|
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
|
|
|
logger.trace(`Listing varient with ID: ${id}`);
|
|
|
|
const result = await deleteObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
user: req.user,
|
|
});
|
|
if (result.error) {
|
|
logger.error('No listing varient deleted:', result.error);
|
|
return res.status(result.code).send(result);
|
|
}
|
|
|
|
logger.debug(`Deleted listing varient with ID: ${result._id}`);
|
|
res.send(result);
|
|
};
|
|
|
|
export const getListingVarientStatsRouteHandler = async (req, res) => {
|
|
const result = await getModelStats({ model: listingVarientModel });
|
|
if (result?.error) {
|
|
logger.error('Error fetching listing varient stats:', result.error);
|
|
return res.status(result.code).send(result);
|
|
}
|
|
logger.trace('Listing varient stats:', result);
|
|
res.send(result);
|
|
};
|
|
|
|
export const getListingVarientHistoryRouteHandler = async (req, res) => {
|
|
const from = req.query.from;
|
|
const to = req.query.to;
|
|
const result = await getModelHistory({ model: listingVarientModel, from, to });
|
|
if (result?.error) {
|
|
logger.error('Error fetching listing varient history:', result.error);
|
|
return res.status(result.code).send(result);
|
|
}
|
|
logger.trace('Listing varient history:', result);
|
|
res.send(result);
|
|
};
|
|
|
|
const VARIENT_POPULATE_MARKETPLACE = [
|
|
{ path: 'listing', populate: { path: 'marketplace' } },
|
|
'product',
|
|
'productSku',
|
|
'priceTaxRate',
|
|
];
|
|
|
|
export const publishListingVarientRouteHandler = async (req, res) => {
|
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
|
|
|
const stateOk = await checkStates({
|
|
model: listingVarientModel,
|
|
id,
|
|
states: ['draft', 'inactive'],
|
|
});
|
|
if (stateOk?.error) {
|
|
logger.error('Error checking listing varient state:', stateOk.error);
|
|
return res.status(stateOk.code).send(stateOk);
|
|
}
|
|
if (stateOk === false) {
|
|
return res.status(400).send({
|
|
error: 'Listing varient must be in draft or inactive state to publish.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const syncingCheck = await checkStates({
|
|
model: listingVarientModel,
|
|
id,
|
|
states: ['syncing'],
|
|
});
|
|
if (syncingCheck === true) {
|
|
return res.status(400).send({
|
|
error: 'Listing varient is syncing; wait for sync to finish before publishing.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const doc = await listingVarientModel
|
|
.findById(id)
|
|
.populate({
|
|
path: 'listing',
|
|
populate: [{ path: 'marketplace' }, { path: 'vendor' }, { path: 'stockLocation' }],
|
|
})
|
|
.lean();
|
|
if (!doc) {
|
|
return res.status(404).send({ error: 'Listing varient not found.', code: 404 });
|
|
}
|
|
if (!doc._reference) {
|
|
return res.status(400).send({
|
|
error: 'Listing varient must have a reference (SKU) before publishing.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const marketplace = doc.listing?.marketplace;
|
|
const marketplaceId = marketplace?._id || marketplace;
|
|
if (!marketplaceId) {
|
|
return res.status(400).send({
|
|
error: 'Listing has no marketplace; cannot publish offer.',
|
|
code: 400,
|
|
});
|
|
}
|
|
if (!marketplace?.active) {
|
|
return res.status(400).send({ error: 'Marketplace is not active.', code: 400 });
|
|
}
|
|
if (!hasIntegration(marketplace.provider)) {
|
|
return res.status(400).send({
|
|
error: 'No integration is configured for this marketplace.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
try {
|
|
if (!doc.listing?.stockLocation) {
|
|
return res.status(400).send({
|
|
error: 'Listing must have a stock location before publishing.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const apiResult = await publishMarketplaceOfferForSku(
|
|
marketplace,
|
|
req.user,
|
|
doc._reference,
|
|
doc.listing
|
|
);
|
|
|
|
await editObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
updateData: {
|
|
updatedAt: new Date(),
|
|
state: { type: 'active' },
|
|
lastSyncedAt: new Date(),
|
|
},
|
|
user: req.user,
|
|
});
|
|
|
|
const listingId = doc.listing?._id || doc.listing;
|
|
if (listingId) {
|
|
const listingUpdate = {
|
|
updatedAt: new Date(),
|
|
state: { type: 'active' },
|
|
lastSyncedAt: new Date(),
|
|
};
|
|
if (apiResult?.listingId) {
|
|
listingUpdate.url = `https://www.ebay.com/itm/${apiResult.listingId}`;
|
|
}
|
|
await editObject({
|
|
model: listingModel,
|
|
id: listingId,
|
|
updateData: listingUpdate,
|
|
user: req.user,
|
|
});
|
|
}
|
|
|
|
const updated = await getObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
populate: VARIENT_POPULATE_MARKETPLACE,
|
|
});
|
|
res.send(updated);
|
|
} catch (err) {
|
|
logger.error(`Publish listing varient failed: ${err.message}`);
|
|
res.status(500).send({ error: err.message, code: 500 });
|
|
}
|
|
};
|
|
|
|
export const unpublishListingVarientRouteHandler = async (req, res) => {
|
|
const id = new mongoose.Types.ObjectId(req.params.id);
|
|
|
|
const stateOk = await checkStates({
|
|
model: listingVarientModel,
|
|
id,
|
|
states: ['active'],
|
|
});
|
|
if (stateOk?.error) {
|
|
logger.error('Error checking listing varient state:', stateOk.error);
|
|
return res.status(stateOk.code).send(stateOk);
|
|
}
|
|
if (stateOk === false) {
|
|
return res.status(400).send({
|
|
error: 'Listing varient must be in active state to unpublish.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const syncingCheck = await checkStates({
|
|
model: listingVarientModel,
|
|
id,
|
|
states: ['syncing'],
|
|
});
|
|
if (syncingCheck === true) {
|
|
return res.status(400).send({
|
|
error: 'Listing varient is syncing; wait for sync to finish before unpublishing.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const doc = await listingVarientModel
|
|
.findById(id)
|
|
.populate({ path: 'listing', populate: { path: 'marketplace' } })
|
|
.lean();
|
|
if (!doc) {
|
|
return res.status(404).send({ error: 'Listing varient not found.', code: 404 });
|
|
}
|
|
if (!doc._reference) {
|
|
return res.status(400).send({
|
|
error: 'Listing varient must have a reference (SKU) before unpublishing.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
const marketplace = doc.listing?.marketplace;
|
|
const marketplaceId = marketplace?._id || marketplace;
|
|
if (!marketplaceId) {
|
|
return res.status(400).send({
|
|
error: 'Listing has no marketplace; cannot withdraw offer.',
|
|
code: 400,
|
|
});
|
|
}
|
|
if (!marketplace?.active) {
|
|
return res.status(400).send({ error: 'Marketplace is not active.', code: 400 });
|
|
}
|
|
if (!hasIntegration(marketplace.provider)) {
|
|
return res.status(400).send({
|
|
error: 'No integration is configured for this marketplace.',
|
|
code: 400,
|
|
});
|
|
}
|
|
|
|
try {
|
|
await withdrawMarketplaceOfferForSku(marketplace, req.user, doc._reference);
|
|
|
|
await editObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
updateData: {
|
|
updatedAt: new Date(),
|
|
state: { type: 'draft' },
|
|
lastSyncedAt: new Date(),
|
|
},
|
|
user: req.user,
|
|
});
|
|
|
|
const parentListingId = doc.listing?._id || doc.listing;
|
|
if (parentListingId) {
|
|
const siblings = await listingVarientModel.find({ listing: parentListingId }).lean();
|
|
const anyActive = siblings.some(
|
|
(v) => String(v._id) !== String(id) && v.state?.type === 'active'
|
|
);
|
|
if (!anyActive) {
|
|
await editObject({
|
|
model: listingModel,
|
|
id: parentListingId,
|
|
updateData: {
|
|
updatedAt: new Date(),
|
|
state: { type: 'draft' },
|
|
lastSyncedAt: new Date(),
|
|
},
|
|
user: req.user,
|
|
});
|
|
}
|
|
}
|
|
|
|
const updated = await getObject({
|
|
model: listingVarientModel,
|
|
id,
|
|
populate: VARIENT_POPULATE_MARKETPLACE,
|
|
});
|
|
res.send(updated);
|
|
} catch (err) {
|
|
logger.error(`Unpublish listing varient failed: ${err.message}`);
|
|
res.status(500).send({ error: err.message, code: 500 });
|
|
}
|
|
};
|