// Where a member's personal link places the people who use it. // // Two settings, chosen by the member: // // 'team' — the moving link. A join is routed to the next position IN THEIR OWN LEG that // still needs directs. This is rotation inside their downline, never the company // rotation: a member's personal link must never hand their referral to someone // they have never met. (Marty, 2026-09-18 — exactly what went wrong for Terry // #840, whose link was offering company position #148.) // 'direct' — every join lands directly under them, as depth in their own leg. // // A new member STARTS on whatever their sponsor was using at the moment they joined — so a // team duplicates its leader without anyone being locked in. That is a one-time copy, not a // live link: if the sponsor changes their mind later, nobody already in the team moves. An // explicit choice is never overwritten from above. // // `explicit` records whether the member chose it or simply inherited it. It is what lets the // dashboard say where the setting came from, and it is why a third "follow my sponsor" state // was not needed. 'use strict'; const fs = require('fs'); const path = require('path'); let DATA_DIR = null; let FILE = null; let db = null; // { ids: () => configured always-direct ids } function init(opts) { DATA_DIR = opts.dataDir; FILE = path.join(DATA_DIR, 'placement.json'); db = opts; } function load() { try { return JSON.parse(fs.readFileSync(FILE, 'utf8')) || {}; } catch (e) { return {}; } } function save(o) { try { const tmp = FILE + '.tmp'; fs.writeFileSync(tmp, JSON.stringify(o)); fs.renameSync(tmp, FILE); } catch (e) { console.error('placement save', e.message); } } const MODES = ['team', 'direct']; const clean = m => (MODES.includes(String(m)) ? String(m) : null); // The admin list stays as a support override and as the migration path for the positions that // were on it before this existed. An entry there reads as an explicit 'direct'. function adminDirectIds(cfg) { return String((cfg && cfg.directDefaultIds) || '') .split(',').map(s => s.trim()).filter(Boolean); } // What is this position set to, and where did that come from? function get(id, cfg) { const key = String(id); const rec = load()[key]; if (rec && clean(rec.mode)) { return { mode: rec.mode, explicit: !!rec.explicit, from: rec.from || null, ts: rec.ts || null }; } if (adminDirectIds(cfg).includes(key)) { return { mode: 'direct', explicit: true, from: 'admin', ts: null }; } return { mode: 'team', explicit: false, from: null, ts: null }; } // The member chose it themselves. function set(id, mode) { const m = clean(mode); if (!m) return { error: 'Pick either team or direct.' }; const o = load(); o[String(id)] = { mode: m, explicit: true, from: null, ts: Date.now() }; save(o); return { ok: true, ...get(id) }; } // A new position inherits its sponsor's CURRENT setting, once, at join. Never overwrites a // member who has already chosen, and never touches anyone else in the leg. function inherit(newId, sponsorId, cfg) { const key = String(newId); const o = load(); if (o[key] && o[key].explicit) return { skipped: 'already chosen' }; const parent = get(sponsorId, cfg); if (parent.mode === 'team') { // the default; no row needed if (!o[key]) return { skipped: 'default' }; } o[key] = { mode: parent.mode, explicit: false, from: String(sponsorId), ts: Date.now() }; save(o); return { ok: true, mode: parent.mode, from: String(sponsorId) }; } module.exports = { init, get, set, inherit, MODES };