Files
Pier_v1/tests/rebar-quantity.test.ts
T
ypshimandClaude Haiku 5.5 e999b11a21 feat(rebar): piece lengths for C9–C13 and P1·P2 so the 재료표 has no missing bars
A row can now carry pieces (length, count). C9, C10, C11 and C9·C11 사이 have U and 삿갓 pieces per position (3 loops each);
C12 and C13 have 일자 and ㄷ pieces per bar; P1 and P2 have their splice pieces, each counting all the bars. The
재료표 totals from the pieces, so no bar is left out. 개수 stays the 철근표 count; the piece count is kept beside it. The
삿갓 length h − 100 is an assumption (Q-K5) and is noted on the ④ screen.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-23 10:27:17 +09:00

51 lines
2.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { buildPier } from '../src/core/geometry';
import { DEFAULT_MATERIAL } from '../src/core/rebar/common';
import { buildQuantity, quantityRows, unitWeight } from '../src/core/rebar/quantity';
import { rebarSummary } from '../src/core/rebar/summary';
import { loadP1 } from './helpers';
const model = buildPier(loadP1());
const rows = rebarSummary(model).rows;
describe('철근재료표 (승인 2026-10-23)', () => {
it('unit weight is 0.00617 · d² on the nominal diameter (H19 19.1 → 2.25 kg/m)', () => {
expect(unitWeight(19.1)).toBeCloseTo(2.25, 2);
});
it('groups by 강종 then diameter; the group totals add up to the total', () => {
const q = buildQuantity(rows, DEFAULT_MATERIAL);
expect(q.groups.map((g) => g.grade)).toEqual([300, 400, 500].filter((g) => q.groups.some((x) => x.grade === g)));
expect(q.groups.reduce((s, g) => s + g.kg, 0)).toBeCloseTo(q.kg, 6);
for (const g of q.groups) {
expect(g.diameters.reduce((s, d) => s + d.kg, 0)).toBeCloseTo(g.kg, 6);
}
});
it('a row with several lengths is totalled from its pieces, not left out', () => {
const q = buildQuantity(rows, DEFAULT_MATERIAL);
expect(q.missing).toEqual([]);
expect(q.groups.flatMap((g) => g.diameters.flatMap((d) => d.lines.map((l) => l.name)))).toContain('C9');
});
it('couplers are counted, not weighed', () => {
const q = buildQuantity(rows, DEFAULT_MATERIAL);
expect(q.couplers.find((c) => c.name === '주철근 커플러')!.count).toBe(368);
expect(q.couplers.find((c) => c.name === '띠철근 커플러')!.count).toBe(35);
});
it('할증 is 0% by default and multiplies the total when set', () => {
const q0 = buildQuantity(rows, DEFAULT_MATERIAL);
expect(q0.kgWithWaste).toBeCloseTo(q0.kg, 6);
const q10 = buildQuantity(rows, { ...DEFAULT_MATERIAL, waste: 10 });
expect(q10.kgWithWaste).toBeCloseTo(q0.kg * 1.1, 6);
expect(q10.ton).toBeCloseTo(q0.kg * 1.1 / 1000, 9);
});
it('the Excel rows start with the header and end with the couplers', () => {
const aoa = quantityRows(buildQuantity(rows, DEFAULT_MATERIAL));
expect(aoa[0][0]).toBe('강종');
expect(aoa[aoa.length - 1][0]).toBe('커플러 (개수만)');
});
});