Ctrl K
ComunidadGuías…

src/lib/money.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * An offer amount, in whole units — the one reading of it in the app.
 *
 * No imports, so the offer card can run the same parse in the browser that the
 * data layer runs on the server. The card used to have its own, which turned
 * "€215.000" into 215 and "$" into 0 after this one had been taught to refuse
 * both: two parsers, and the screen where people compare offers had the wrong
 * one.
 *
 * Assistants send "215k", "$215,000" and 215000 for the same figure, and a
 * person typing into the form sends "". All three of the first shapes mean the
 * same thing and are accepted; "" and null mean "not sent" and come back
 * undefined; anything else that is not a number is refused rather than stored
 * as zero, because zero on a comparison table reads as "they offered nothing".
 */

/** Postgres INTEGER, and the reason amounts are whole units rather than cents. */
export const MAX_AMOUNT = 2_147_483_647;

export function parseAmount(value: unknown, field: string): number | undefined {
  if (value === undefined || value === null || value === "") return undefined;
  let n: number;
  if (typeof value === "number") n = value;
  else if (typeof value === "string") {
    const raw = value.trim().toLowerCase();
    // A string with no digit in it is not a figure. Without this, " ", "$" and
    // "k" all reach Number("") and land as a real zero.
    if (!/\d/.test(raw)) throw new Error(`${field} is not a number: "${value}"`);
    // "215.000" is two hundred and fifteen thousand in half of Europe and two
    // hundred and fifteen everywhere else, and guessing wrong is a 1000× error
    // sitting next to correctly-read columns. Refused rather than guessed.
    if (/\d\.\d{3}(\D|$)/.test(raw)) {
      throw new Error(
        `${field} is ambiguous: "${value}" could be thousands or a decimal. Send it as digits, like 215000.`,
      );
    }
    const clean = raw.replace(/[$£€,\s]/g, "");
    const k = clean.endsWith("k");
    const parsed = Number(k ? clean.slice(0, -1) : clean);
    if (!Number.isFinite(parsed)) throw new Error(`${field} is not a number: "${value}"`);
    n = k ? parsed * 1000 : parsed;
  } else throw new Error(`${field} is not a number`);
  if (!Number.isFinite(n) || n < 0) throw new Error(`${field} must be zero or more`);
  if (n > MAX_AMOUNT) throw new Error(`${field} is larger than this column can hold`);
  return Math.round(n);
}