Files
starr-philproperties/controllers/admin/plan_prices.controller.js
T
kennethobsequio 1372f4e975 testing 101
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
2026-06-30 19:31:45 +08:00

140 lines
6.5 KiB
JavaScript

/***********************************************************************************************************************************************************************
* File Name: plan_prices.controller.js (admin)
* Type of Program: Controller
* Description: Admin CRUD for localized price overrides per tier plan.
* Routes: GET/POST/PUT/DELETE /admin/tiers/:id/prices[/:currency]
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jun. 29, 2026
***********************************************************************************************************************************************************************/
'use strict';
const mdl_TierPlans = require('../../models/tiers/tier_plans.mdl');
const mdl_PlanPrices = require('../../models/tiers/plan_prices.mdl');
const R = require('../../utils/response.util');
const logActivity = require('../../utils/logActivity.util');
const { isSupported, SUPPORTED_CURRENCIES, validateLocalizedPrice } = require('../../utils/currency.util');
// ─── GET /admin/tiers/:id/prices ─────────────────────────────────────────────
exports.getPrices = async (req, res) => {
try {
const plan = await mdl_TierPlans.findByPk(req.params.id);
if (!plan) return R.error(res, 'Plan not found.', 404);
const prices = await mdl_PlanPrices.findAll({
where: { plan_id: plan.plan_id },
order: [['currency', 'ASC']],
});
return R.success(res, 'Localized prices retrieved.', prices);
} catch (err) {
console.error('[ADMIN][GET PLAN PRICES]', err);
return R.error(res, 'Could not retrieve localized prices.', 500);
}
};
// ─── POST /admin/tiers/:id/prices ────────────────────────────────────────────
exports.addPrice = async (req, res) => {
try {
const plan = await mdl_TierPlans.findByPk(req.params.id);
if (!plan) return R.error(res, 'Plan not found.', 404);
const { currency, price } = req.body;
if (!currency || price === undefined) return R.error(res, 'currency and price are required.', 400);
if (!isSupported(currency)) return R.error(res, `Unsupported currency: ${currency}.`, 400);
if (currency === plan.currency) return R.error(res, `${currency} is already the plan's base currency.`, 400);
if (Number(price) < 0) return R.error(res, 'Price must be 0 or greater.', 400);
const exists = await mdl_PlanPrices.findOne({ where: { plan_id: plan.plan_id, currency } });
if (exists) return R.error(res, `A localized price for ${currency} already exists. Use PUT to update it.`, 409);
// ── Rate validation ────────────────────────────────────────────────────────
const validation = await validateLocalizedPrice(plan.price, plan.currency, price, currency);
if (validation.zone === 'block') return R.error(res, validation.message, 422);
const entry = await mdl_PlanPrices.create({
plan_id: plan.plan_id,
currency: currency.toUpperCase(),
price: Number(price),
});
logActivity(req.user?.user_id, 'add_plan_price', {
entityType: 'plan_price',
details: { plan_id: plan.plan_id, currency, price },
});
if (validation.zone === 'warn')
return res.status(201).json({ success: true, warning: true, message: validation.message, data: entry });
return R.success(res, 'Localized price added.', entry, 201);
} catch (err) {
console.error('[ADMIN][ADD PLAN PRICE]', err);
return R.error(res, 'Could not add localized price.', 500);
}
};
// ─── PUT /admin/tiers/:id/prices/:currency ───────────────────────────────────
exports.updatePrice = async (req, res) => {
try {
const { id, currency } = req.params;
const { price } = req.body;
if (price === undefined) return R.error(res, 'price is required.', 400);
if (Number(price) < 0) return R.error(res, 'Price must be 0 or greater.', 400);
const entry = await mdl_PlanPrices.findOne({ where: { plan_id: id, currency: currency.toUpperCase() } });
if (!entry) return R.error(res, `No localized price found for ${currency} on this plan.`, 404);
// ── Rate validation ────────────────────────────────────────────────────────
const plan = await mdl_TierPlans.findByPk(id);
const validation = await validateLocalizedPrice(plan.price, plan.currency, price, currency);
if (validation.zone === 'block') return R.error(res, validation.message, 422);
await entry.update({ price: Number(price) });
logActivity(req.user?.user_id, 'update_plan_price', {
entityType: 'plan_price',
details: { plan_id: id, currency, price },
});
if (validation.zone === 'warn')
return res.status(200).json({ success: true, warning: true, message: validation.message, data: entry });
return R.success(res, 'Localized price updated.', entry);
} catch (err) {
console.error('[ADMIN][UPDATE PLAN PRICE]', err);
return R.error(res, 'Could not update localized price.', 500);
}
};
// ─── DELETE /admin/tiers/:id/prices/:currency ────────────────────────────────
exports.removePrice = async (req, res) => {
try {
const { id, currency } = req.params;
const entry = await mdl_PlanPrices.findOne({ where: { plan_id: id, currency: currency.toUpperCase() } });
if (!entry) return R.error(res, `No localized price found for ${currency} on this plan.`, 404);
await entry.destroy();
logActivity(req.user?.user_id, 'remove_plan_price', {
entityType: 'plan_price',
details: { plan_id: id, currency },
});
return R.success(res, 'Localized price removed.');
} catch (err) {
console.error('[ADMIN][REMOVE PLAN PRICE]', err);
return R.error(res, 'Could not remove localized price.', 500);
}
};
// ─── GET /admin/currencies ────────────────────────────────────────────────────
exports.getCurrencies = async (_req, res) => {
return R.success(res, 'Supported currencies retrieved.', SUPPORTED_CURRENCIES);
};