115 lines
3.6 KiB
JavaScript
115 lines
3.6 KiB
JavaScript
const CoffeeRepository = require('../../repositories/subscriptions/CoffeeRepository');
|
|
const UnitOfWork = require('../../database/UnitOfWork');
|
|
const { logger } = require('../../middleware/logger');
|
|
|
|
function validate(data) {
|
|
const errors = [];
|
|
if (!data.title || String(data.title).trim() === '') errors.push('title');
|
|
if (!data.description || String(data.description).trim() === '') errors.push('description');
|
|
const q = Number(data.quantity);
|
|
if (!Number.isFinite(q) || q < 0) errors.push('quantity');
|
|
const price = Number(data.price);
|
|
if (!Number.isFinite(price) || price < 0) errors.push('price');
|
|
// state is boolean (available=true/unavailable=false)
|
|
if (typeof data.state !== 'boolean') errors.push('state');
|
|
|
|
// currency optional; default EUR if missing
|
|
if (data.currency && String(data.currency).length > 3) errors.push('currency');
|
|
|
|
// tax_rate optional must be >= 0 if provided
|
|
if (data.tax_rate !== undefined && data.tax_rate !== null) {
|
|
const tr = Number(data.tax_rate);
|
|
if (!Number.isFinite(tr) || tr < 0) errors.push('tax_rate');
|
|
}
|
|
|
|
// is_featured boolean
|
|
if (typeof data.is_featured !== 'boolean') errors.push('is_featured');
|
|
|
|
// billing_interval/interval_count validation
|
|
if (data.billing_interval !== undefined || data.interval_count !== undefined) {
|
|
const allowed = ['day','week','month','year'];
|
|
if (data.billing_interval && !allowed.includes(String(data.billing_interval))) errors.push('billing_interval');
|
|
if (data.interval_count !== undefined) {
|
|
const ic = Number(data.interval_count);
|
|
if (!Number.isFinite(ic) || ic <= 0) errors.push('interval_count');
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
class CoffeeService {
|
|
async list() {
|
|
return CoffeeRepository.listAll();
|
|
}
|
|
|
|
async get(id) {
|
|
return CoffeeRepository.getById(id);
|
|
}
|
|
|
|
async create(data) {
|
|
const errors = validate(data);
|
|
if (errors.length) {
|
|
logger.warn('[CoffeeService.create] validation_failed', { errors });
|
|
throw Object.assign(new Error('Validation failed'), { code: 'VALIDATION_ERROR', errors });
|
|
}
|
|
const uow = new UnitOfWork();
|
|
try {
|
|
await uow.start();
|
|
const result = await CoffeeRepository.create(data, uow.connection);
|
|
await uow.commit();
|
|
return result;
|
|
} catch (e) {
|
|
try { await uow.rollback(e); } catch(_) {}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
async update(id, data) {
|
|
const errors = validate(data);
|
|
if (errors.length) {
|
|
logger.warn('[CoffeeService.update] validation_failed', { id, errors });
|
|
throw Object.assign(new Error('Validation failed'), { code: 'VALIDATION_ERROR', errors });
|
|
}
|
|
const uow = new UnitOfWork();
|
|
try {
|
|
await uow.start();
|
|
await CoffeeRepository.update(id, data, uow.connection);
|
|
const updated = await CoffeeRepository.getById(id, uow.connection);
|
|
await uow.commit();
|
|
return updated;
|
|
} catch (e) {
|
|
try { await uow.rollback(e); } catch(_) {}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
async setState(id, state) {
|
|
const uow = new UnitOfWork();
|
|
try {
|
|
await uow.start();
|
|
await CoffeeRepository.setState(id, !!state, uow.connection);
|
|
const updated = await CoffeeRepository.getById(id, uow.connection);
|
|
await uow.commit();
|
|
return updated;
|
|
} catch (e) {
|
|
try { await uow.rollback(e); } catch(_) {}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
async delete(id) {
|
|
const uow = new UnitOfWork();
|
|
try {
|
|
await uow.start();
|
|
const ok = await CoffeeRepository.delete(id, uow.connection);
|
|
await uow.commit();
|
|
return ok;
|
|
} catch (e) {
|
|
try { await uow.rollback(e); } catch(_) {}
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = new CoffeeService();
|