onboard never writes duplicate subscription levels: the App Store groupLevel when it is set and unique in the group, else a stable order (App Store level, higher price per month = higher level, longer period, id) with a note per derived level; assignLevels in cli/lib/onboard.ts, tests with a live-shape App Store group (groupLevel 2 and 1) and Play-only / tied cases
This commit is contained in:
parent
346bf61166
commit
5ad3c0002d
2 changed files with 80 additions and 1 deletions
|
|
@ -55,6 +55,8 @@ export function importCatalog(config: Pick<ShipConfig, 'languages' | 'products'
|
|||
const oldSubs = new Map((config.subscriptions ?? []).flatMap(g => g.subscriptions.map(x => [x.id, { group: g, sub: x }] as const)));
|
||||
const ids = [...new Set([...A.keys(), ...P.keys()])];
|
||||
const groups = new Map<string, SubscriptionGroup>();
|
||||
/** App Store groupLevel per subscription id (the only store that proves a level) */
|
||||
const appleLevel = new Map<string, number>();
|
||||
|
||||
for (const id of ids) {
|
||||
const a = A.get(id), p = P.get(id);
|
||||
|
|
@ -100,6 +102,7 @@ export function importCatalog(config: Pick<ShipConfig, 'languages' | 'products'
|
|||
if (!period || !PERIODS.includes(period)) { out.skipped.push({ id, why: `billing period ${period ?? 'unknown'} is not one the config supports (${PERIODS.join(', ')})` }); continue; }
|
||||
if (a?.period && p?.period && a.period !== p.period) note(id, `bills every ${a.period} on the App Store, ${p.period} on Google Play: period follows the App Store`, 'warn');
|
||||
const prev = oldSubs.get(id);
|
||||
if (typeof a?.level === 'number') appleLevel.set(id, a.level);
|
||||
const sub: Subscription = { ...(prev?.sub ?? {}), id, period, level: a?.level ?? prev?.sub.level ?? 1, usd: cents(usd), listings, ...platforms,
|
||||
...(overrides.length || prev?.sub.overrides ? { overrides: mergeOverrides(prev?.sub.overrides, overrides) } : {}) } as Subscription;
|
||||
if (!both || (a && p)) delete sub.platforms;
|
||||
|
|
@ -135,9 +138,39 @@ export function importCatalog(config: Pick<ShipConfig, 'languages' | 'products'
|
|||
note(x.id, 'in the config, on no store yet: kept');
|
||||
}
|
||||
out.subscriptions = [...groups.values()];
|
||||
for (const g of out.subscriptions) for (const n of assignLevels(g, appleLevel)) note(n.id, n.msg, n.level);
|
||||
return out;
|
||||
}
|
||||
|
||||
const PERIOD_MONTHS: Record<SubscriptionPeriod, number> = { P1W: 12 / 52, P1M: 1, P2M: 2, P3M: 3, P6M: 6, P1Y: 12 };
|
||||
|
||||
/**
|
||||
* Levels inside one group (1 = highest level of service), never duplicated (config validation refuses duplicates).
|
||||
* The App Store's groupLevel when it proves every level in the group without ties; otherwise a stable order: App
|
||||
* Store level first, then the higher price per month, then the longer period, then the id. Mutates the group.
|
||||
*/
|
||||
export function assignLevels(g: SubscriptionGroup, appleLevel: Map<string, number>): ImportNote[] {
|
||||
const subs = g.subscriptions;
|
||||
const levels = subs.map(x => appleLevel.get(x.id));
|
||||
if (levels.every(l => l !== undefined) && new Set(levels).size === subs.length) {
|
||||
for (const x of subs) x.level = appleLevel.get(x.id)!;
|
||||
return [];
|
||||
}
|
||||
const monthly = (x: Subscription) => x.usd / PERIOD_MONTHS[x.period];
|
||||
const order = [...subs].sort((x, y) => (appleLevel.get(x.id) ?? Infinity) - (appleLevel.get(y.id) ?? Infinity)
|
||||
|| monthly(y) - monthly(x) || PERIOD_MONTHS[y.period] - PERIOD_MONTHS[x.period] || x.id.localeCompare(y.id));
|
||||
const notes: ImportNote[] = [];
|
||||
order.forEach((x, i) => {
|
||||
const al = appleLevel.get(x.id);
|
||||
const how = al === undefined
|
||||
? 'no App Store groupLevel (Google Play has no levels): ordered by price per month (higher price = higher level, level 1 the highest), then the longer period'
|
||||
: `App Store groupLevel ${al} is shared with another subscription in the group: ties ordered by price per month (higher first), then the longer period`;
|
||||
if (al !== i + 1) notes.push({ id: x.id, level: 'warn', msg: `group "${g.name}": level ${i + 1}, ${how}; check it in App Store Connect` });
|
||||
x.level = i + 1;
|
||||
});
|
||||
return notes;
|
||||
}
|
||||
|
||||
function mergeOverrides(prev: { region: string; currency: string; price: number; territory?: string }[] | undefined, add: { region: string; currency: string; price: number }[]) {
|
||||
const m = new Map((prev ?? []).map(o => [o.region, o] as const));
|
||||
for (const o of add) m.set(o.region, { ...m.get(o.region), ...o });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue