diff --git a/ENV_READS.md b/ENV_READS.md new file mode 100644 index 0000000..2bdcc01 --- /dev/null +++ b/ENV_READS.md @@ -0,0 +1,15 @@ +| file | variable | default | +| --- | --- | --- | +| src/server.js | PORT | 8080 | +| src/lib/log.js | LOG_LEVEL | info | +| src/modules/webhooks/service.js | WEBHOOK_TIMEOUT_MS | 5000 | +| src/modules/files/repo.js | FILES_BUCKET | local-bucket | +| src/modules/scheduler/service.js | SCHED_TZ | UTC | +| src/modules/notifications/service.js | SMTP_HOST | localhost | +| src/modules/notifications/service.js | SMTP_PORT | 2525 | +| src/modules/inventory/service.js | LOW_STOCK_THRESHOLD | 5 | +| src/modules/search/service.js | SEARCH_MAX_RESULTS | 50 | +| src/modules/billing/constants.js | TAX_REGION | US | +| src/modules/auth/service.js | AUTH_SECRET | dev-secret | +| src/modules/auth/service.js | TOKEN_TTL_S | 3600 | +| src/modules/reports/formatters.js | REPORT_LOCALE | en-US | diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..155e6dd --- /dev/null +++ b/src/config.js @@ -0,0 +1,19 @@ +// Single place where environment variables are read. Getters keep lookups lazy, +// so values that were read at call time before still are. +const env = process.env; + +export const config = { + get port() { return Number(env.PORT ?? 8080); }, + get logLevel() { return env.LOG_LEVEL || 'info'; }, + get webhookTimeoutMs() { return Number(env.WEBHOOK_TIMEOUT_MS ?? '5000'); }, + get filesBucket() { return env.FILES_BUCKET || 'local-bucket'; }, + get schedTz() { return env.SCHED_TZ || 'UTC'; }, + get smtpHost() { return env.SMTP_HOST || 'localhost'; }, + get smtpPort() { return Number(env.SMTP_PORT || 2525); }, + get lowStockThreshold() { return Number(env.LOW_STOCK_THRESHOLD ?? 5); }, + get searchMaxResults() { return parseInt(env.SEARCH_MAX_RESULTS || '50', 10); }, + get taxRegion() { return env.TAX_REGION || 'US'; }, + get authSecret() { return env.AUTH_SECRET || 'dev-secret'; }, + get tokenTtlS() { return Number(env.TOKEN_TTL_S ?? '3600'); }, + get reportLocale() { return env.REPORT_LOCALE ?? 'en-US'; }, +}; diff --git a/src/modules/returns/policy.js b/src/modules/returns/policy.js index 4203b3f..8ac4c0b 100644 --- a/src/modules/returns/policy.js +++ b/src/modules/returns/policy.js @@ -1,14 +1,3 @@ -import { taxFor } from '../billing/tax.js'; - -// Adjusts a refund for tax. Discounted orders are taxed on the discounted amount, -// so the refund must carry that tax too. -export function withTaxAdjustment(refund, priced, order) { - if (order.discountCode) { - return refund + taxFor(priced.subtotal - priced.discount, order.region); - } - return refund; -} - export function withinWindow(orderDate, today, days) { return (new Date(today) - new Date(orderDate)) / 86400000 <= days; } diff --git a/src/modules/returns/service.js b/src/modules/returns/service.js index f49d691..62c9263 100644 --- a/src/modules/returns/service.js +++ b/src/modules/returns/service.js @@ -1,6 +1,6 @@ import { ValidationError } from '../../lib/errors.js'; import { priceLines } from '../billing/service.js'; -import { withTaxAdjustment, withinWindow } from './policy.js'; +import { withinWindow } from './policy.js'; import { RETURN_WINDOW_DAYS, REASONS } from './constants.js'; // Refund for the returned lines of an order, in integer cents. @@ -8,7 +8,8 @@ export function computeRefund(order, returnedLineIds) { const lines = order.lines.filter((l) => returnedLineIds.includes(l.id)); if (lines.length === 0) throw new ValidationError('no returned lines'); const priced = priceLines(lines, { discountCode: order.discountCode, region: order.region }); - return withTaxAdjustment(priced.total, priced, order); + // priced.total already includes tax on the discounted amount. + return priced.total; } export function canReturn(order, today, reason) { diff --git a/tests/returns.test.js b/tests/returns.test.js index 21088b2..a464956 100644 --- a/tests/returns.test.js +++ b/tests/returns.test.js @@ -11,6 +11,12 @@ test('returns: refund for one line, no discount', () => { assert.equal(computeRefund(order, ['a']), 1070); }); +test('returns: discounted refund does not double-count tax', () => { + // 1000 - 10% = 900, tax 7% = 63 -> 963; never more than the customer paid. + const discounted = { ...order, discountCode: 'SAVE10' }; + assert.equal(computeRefund(discounted, ['a']), 963); +}); + test('returns: return window and reasons', () => { assert.equal(canReturn(order, '2026-10-20', 'damaged'), true); assert.equal(canReturn(order, '2026-11-20', 'damaged'), false);