Merge origin/main (CI quality gates) into the 1.1.0 release
# Conflicts: # package-lock.json # src/views/NewInvoice.tsx
This commit is contained in:
@@ -102,7 +102,6 @@ export async function runE2eSteps(log: StepLog, reportPath: string): Promise<voi
|
||||
},
|
||||
);
|
||||
|
||||
let inv!: Invoice;
|
||||
const issued = await log.run("e2e: issue and archive", async () => {
|
||||
const outcome = await issueAndArchive(input, prefs, deps);
|
||||
invoice = outcome.invoice;
|
||||
@@ -112,7 +111,7 @@ export async function runE2eSteps(log: StepLog, reportPath: string): Promise<voi
|
||||
};
|
||||
});
|
||||
if (!issued.ok || !invoice) return;
|
||||
inv = invoice as Invoice;
|
||||
const inv = invoice as Invoice;
|
||||
|
||||
await log.run("e2e: archive status", async () => {
|
||||
const st = await api.archiveStatus(inv.id);
|
||||
|
||||
@@ -22,6 +22,7 @@ export function sanitize(value: string | null | undefined): string {
|
||||
.replace(/\r\n?/g, "\n")
|
||||
.replace(/[\t\n\v\f]/g, " ")
|
||||
.replace(/ /g, " ")
|
||||
// eslint-disable-next-line no-control-regex -- stripping control characters is the point
|
||||
.replace(/[\u0000-\u001F\u007F-\u009F]/g, "")
|
||||
.replace(/ {2,}/g, " ")
|
||||
.trim();
|
||||
|
||||
@@ -21,7 +21,7 @@ import { RenderTxt } from "./blocks/RenderTxt";
|
||||
import { TableGuard, guardPresence } from "./blocks/TableGuard";
|
||||
import { renderCore } from "./render/core";
|
||||
import { loadCanvas, inkPixels } from "./testing/rasterize";
|
||||
import { byId, doc, FAMILY, find, frameFor, PAD, PAGE_W, rawLayout, txt } from "./testing/docs";
|
||||
import { byId, doc, FAMILY, find, frameFor, PAD, rawLayout, txt } from "./testing/docs";
|
||||
import { FONTS_DIR, setupPdfTest } from "./testing/setup";
|
||||
import { stack } from "./fonts/register";
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ export const REVERSE_GAP = 4;
|
||||
export const HEADING_AFTER = 4;
|
||||
/** Space between the closing block's two columns. */
|
||||
export const CLOSING_COLUMN_GAP = 16;
|
||||
/** Slate: space between a mark and the business name in the dark band. */
|
||||
const NAME_AFTER_LOGO = 7.8;
|
||||
/** Space above and below the thin rule that separates the stack from TOTAL. */
|
||||
export const STACK_RULE = { before: 2, after: 8, thickness: 0.75 };
|
||||
const MIN_NAME_W = 40;
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { AuditIssue } from "../engine/audit";
|
||||
import { PAGE_SIZES } from "../engine/geometry";
|
||||
import { PAGE_LABEL_ID, TABLE_HEAD_ID, TABLE_ROW_PREFIX, CLOSING_ID, TOTALS_ID } from "../engine/ids";
|
||||
import { walk, type LayoutNode, type LayoutPage, type Rect } from "../engine/layoutTree";
|
||||
import { forbiddenProbe, SLOT_RULES, slotContent, type SlotName } from "../model/slots";
|
||||
import { frameForPrefs, renderInvoicePdf } from "../render/invoice";
|
||||
import { loadCanvas, pagePng } from "../testing/rasterize";
|
||||
import { SLOT_ID } from "../blocks/slots";
|
||||
import { META_ID, SUPPLIER_ID, TITLE_ID } from "./classic/ids";
|
||||
import { HEADER_GAP, headerCols } from "./classic/plan";
|
||||
import { FIXTURE_GSTIN, FIXTURES, buildFixture, ensureSetup, makeItems, PAGE_SIZE_IDS, prefsFor, renderFixture, type FixtureSpec, type Rendered } from "./harness";
|
||||
import { renderInvoicePdf } from "../render/invoice";
|
||||
import { FIXTURES, buildFixture, ensureSetup, PAGE_SIZE_IDS, prefsFor, renderFixture, type FixtureSpec } from "./harness";
|
||||
import { TEMPLATES } from "./registry";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Image, View } from "@react-pdf/renderer";
|
||||
import type { ReactNode } from "react";
|
||||
import { colsDetailed, pinned } from "../../engine/columns";
|
||||
import { pinned } from "../../engine/columns";
|
||||
import { CLOSING_ID, TABLE_HEAD_ID, TABLE_ROW_PREFIX, TOTALS_ID } from "../../engine/ids";
|
||||
import { AvailableWidthProvider } from "../../blocks/AvailableWidth";
|
||||
import { BottomSpacer } from "../../blocks/BottomSpacer";
|
||||
|
||||
@@ -202,7 +202,7 @@ function Parties({ ctx }: { ctx: Ctx }) {
|
||||
const blocks: ReactNode[] = [<ClientBlock key="a" ctx={ctx} width={text[0]} />];
|
||||
let k = 1;
|
||||
if (plan.info.hasBank) blocks.push(<PayableBlock key="b" ctx={ctx} width={text[k++]} />);
|
||||
if (plan.info.hasIds) blocks.push(<IdsBlock key="c" ctx={ctx} width={text[k++]} />);
|
||||
if (plan.info.hasIds) blocks.push(<IdsBlock key="c" ctx={ctx} width={text[k]} />);
|
||||
const specs: ColSpec[] = cols.map((w, i) => (i === cols.length - 1 ? { fill: true } : { fixed: w }));
|
||||
return (
|
||||
<View style={{ ...pinned(plan.W), paddingLeft: v.inset.left } as never}>
|
||||
|
||||
@@ -151,7 +151,7 @@ export function planMarble(model: RenderModel, frame: PageFrame, v: MarbleVarian
|
||||
let k = 0;
|
||||
const clientW = textW(k++);
|
||||
const bankW = hasBank ? textW(k++) : 0;
|
||||
const idsW = hasIds ? textW(k++) : 0;
|
||||
const idsW = hasIds ? textW(k) : 0;
|
||||
const head = leading(r.heading) + v.parties.headingGap;
|
||||
let clientH = head + textHeight(r.body, c.name, clientW);
|
||||
for (const l of c.addressLines) clientH += textHeight(r.body, l, clientW);
|
||||
|
||||
@@ -295,7 +295,7 @@ export function planNeutral(model: RenderModel, frame: PageFrame, v: NeutralVari
|
||||
for (const l of lines) payH += textHeight(r.address, l, payW);
|
||||
}
|
||||
if (v.id === "serenity") signH += leading(r.label) + 4;
|
||||
let closingH = 0;
|
||||
let closingH: number;
|
||||
if (v.payTo.where === "closing") closingH = Math.max(payH, signH);
|
||||
else closingH = signH;
|
||||
if (model.cancelled) closingH += 10 + textHeight(r.small, CANCELLED_SENTENCE, W);
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import type { StepResult } from "./lib/selfTestSteps";
|
||||
const WATCHDOG_MS = 120_000;
|
||||
|
||||
export async function maybeRunSelfTest(): Promise<void> {
|
||||
let reportPath: string | null = null;
|
||||
let reportPath: string | null;
|
||||
try {
|
||||
reportPath = await invoke<string | null>("selftest_config");
|
||||
} catch {
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
TabPanels,
|
||||
Tabs,
|
||||
Tile,
|
||||
Toggle,
|
||||
} from "@carbon/react";
|
||||
import { Add, Save, TrashCan } from "@carbon/icons-react";
|
||||
import { confirm } from "@tauri-apps/plugin-dialog";
|
||||
|
||||
@@ -48,8 +48,7 @@ import type {
|
||||
Settings,
|
||||
TaxType,
|
||||
} from "../lib/types";
|
||||
import { computeLineAmount, computeTotals } from "../lib/invoice";
|
||||
import { amountInWords } from "../lib/numberToWords";
|
||||
import { computeLineAmount } from "../lib/invoice";
|
||||
import { gstinFullError } from "../lib/validators";
|
||||
import { addDays, formatAmount, todayIso } from "../lib/format";
|
||||
import { modelFromAssets, prefsForInvoice } from "../lib/pdf";
|
||||
@@ -265,11 +264,6 @@ export default function NewInvoice({ settings, onSettingsChange, active, onActiv
|
||||
if (ok) badQuantities.current.delete(id);
|
||||
else badQuantities.current.add(id);
|
||||
};
|
||||
const totals = useMemo(
|
||||
() => computeTotals(items, discount, effectiveTaxType, taxRate),
|
||||
[items, discount, effectiveTaxType, taxRate],
|
||||
);
|
||||
const words = useMemo(() => amountInWords(totals.total), [totals.total]);
|
||||
const bank = useMemo(() => banks.find((b) => b.id === bankId) ?? null, [banks, bankId]);
|
||||
|
||||
const payload = useMemo(
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function SeriesSettings({
|
||||
embedded?: boolean;
|
||||
}) {
|
||||
const toast = useToast();
|
||||
const [active, setActive] = useState<InvoiceSeries | null>(null);
|
||||
const [, setActive] = useState<InvoiceSeries | null>(null);
|
||||
const [all, setAll] = useState<InvoiceSeries[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user