131 lines
4.4 KiB
JavaScript
131 lines
4.4 KiB
JavaScript
const { getDb } = require('../config/db');
|
|
const { AppError } = require('../middleware/error-handler');
|
|
const { formatMoney, parseMoney } = require('../utils/money');
|
|
const { fetchProviderServices } = require('./provider.service');
|
|
|
|
function parseMarkupPercent(value = process.env.DEFAULT_MARKUP_PERCENT || '0') {
|
|
const raw = String(value).trim();
|
|
|
|
if (!/^\d+(?:\.\d{1,4})?$/.test(raw)) {
|
|
throw new AppError('Invalid markup percent', 400, 'INVALID_MARKUP_PERCENT');
|
|
}
|
|
|
|
return raw;
|
|
}
|
|
|
|
function calculateSellPrice(providerPrice, markupPercent = process.env.DEFAULT_MARKUP_PERCENT || '0') {
|
|
const price = parseMoney(String(providerPrice));
|
|
const markup = BigInt(Math.round(Number(markupPercent) * 10000));
|
|
const sellPrice = (price * (1000000n + markup) + 500000n) / 1000000n;
|
|
|
|
return formatMoney(sellPrice);
|
|
}
|
|
|
|
function normalizeProviderService(service, markupPercent) {
|
|
const providerServiceId = String(service.service ?? service.provider_service_id ?? '').trim();
|
|
const name = String(service.name ?? '').trim();
|
|
const category = String(service.category ?? 'Uncategorized').trim() || 'Uncategorized';
|
|
const providerPrice = String(service.rate ?? service.price ?? service.provider_price ?? '').trim();
|
|
const min = Number(service.min);
|
|
const max = Number(service.max);
|
|
|
|
if (!providerServiceId || !name || !providerPrice || !Number.isInteger(min) || !Number.isInteger(max)) {
|
|
throw new AppError('Provider service item is invalid', 502, 'INVALID_PROVIDER_SERVICE');
|
|
}
|
|
|
|
return {
|
|
provider_service_id: providerServiceId,
|
|
name,
|
|
category,
|
|
provider_price: formatMoney(parseMoney(providerPrice)),
|
|
markup_percent: markupPercent,
|
|
sell_price: calculateSellPrice(providerPrice, markupPercent),
|
|
min,
|
|
max,
|
|
is_active: true
|
|
};
|
|
}
|
|
|
|
async function syncServicesFromProvider(markupPercentInput) {
|
|
const markupPercent = parseMarkupPercent(markupPercentInput);
|
|
const providerServices = await fetchProviderServices();
|
|
const services = providerServices.map((service) => normalizeProviderService(service, markupPercent));
|
|
const db = getDb();
|
|
|
|
await db.transaction(async (trx) => {
|
|
for (const service of services) {
|
|
const existing = await trx('services')
|
|
.select('id')
|
|
.where({ provider_service_id: service.provider_service_id })
|
|
.first();
|
|
|
|
if (existing) {
|
|
await trx('services').where({ id: existing.id }).update(service);
|
|
} else {
|
|
await trx('services').insert(service);
|
|
}
|
|
}
|
|
});
|
|
|
|
return {
|
|
synced: services.length
|
|
};
|
|
}
|
|
|
|
async function listActiveServices() {
|
|
return getDb()('services')
|
|
.select('id', 'provider_service_id', 'name', 'category', 'sell_price', 'min', 'max', 'is_active', 'updated_at')
|
|
.where({ is_active: true })
|
|
.orderBy('category', 'asc')
|
|
.orderBy('name', 'asc');
|
|
}
|
|
|
|
async function listAdminServices() {
|
|
return getDb()('services')
|
|
.select('id', 'provider_service_id', 'name', 'category', 'provider_price', 'markup_percent', 'sell_price', 'min', 'max', 'is_active', 'updated_at')
|
|
.orderBy('category', 'asc')
|
|
.orderBy('name', 'asc');
|
|
}
|
|
|
|
async function updateService(serviceId, input) {
|
|
const db = getDb();
|
|
const service = await db('services').where({ id: serviceId }).first();
|
|
|
|
if (!service) {
|
|
throw new AppError('Service not found', 404, 'SERVICE_NOT_FOUND');
|
|
}
|
|
|
|
const update = {};
|
|
|
|
if (input.name !== undefined) update.name = input.name;
|
|
if (input.category !== undefined) update.category = input.category;
|
|
if (input.min !== undefined) update.min = input.min;
|
|
if (input.max !== undefined) update.max = input.max;
|
|
if (input.is_active !== undefined) update.is_active = input.is_active;
|
|
if (input.sell_price !== undefined) update.sell_price = formatMoney(parseMoney(input.sell_price));
|
|
|
|
if (input.markup_percent !== undefined) {
|
|
update.markup_percent = parseMarkupPercent(input.markup_percent);
|
|
update.sell_price = calculateSellPrice(service.provider_price, update.markup_percent);
|
|
}
|
|
|
|
if (Object.keys(update).length === 0) {
|
|
throw new AppError('No service changes provided', 400, 'NO_SERVICE_CHANGES');
|
|
}
|
|
|
|
await db('services').where({ id: serviceId }).update(update);
|
|
|
|
return db('services')
|
|
.select('id', 'provider_service_id', 'name', 'category', 'provider_price', 'markup_percent', 'sell_price', 'min', 'max', 'is_active', 'updated_at')
|
|
.where({ id: serviceId })
|
|
.first();
|
|
}
|
|
|
|
module.exports = {
|
|
calculateSellPrice,
|
|
listActiveServices,
|
|
listAdminServices,
|
|
syncServicesFromProvider,
|
|
updateService
|
|
};
|