cd78f72f33
Complete rename of all technical identifiers across the codebase: Package names (11 packages): - @planarchy/* → @capakraken/* in all package.json, tsconfig, imports Import statements: 277 files, 548 occurrences replaced Database & Docker: - PostgreSQL user/db: planarchy → capakraken - Docker volumes: planarchy_pgdata → capakraken_pgdata - Connection strings updated in docker-compose, .env, CI CI/CD: - GitHub Actions workflow: all filter commands updated - Test database credentials updated Infrastructure: - Redis channel: planarchy:sse → capakraken:sse - Logger service name: planarchy-api → capakraken-api - Anonymization seed updated - Start/stop/restart scripts updated Test data: - Seed emails: @planarchy.dev → @capakraken.dev - E2E test credentials: all 11 spec files updated - Email defaults: @planarchy.app → @capakraken.app - localStorage keys: planarchy_* → capakraken_* Documentation: 30+ .md files updated Verification: - pnpm install: workspace resolution works - TypeScript: only pre-existing TS2589 (no new errors) - Engine: 310/310 tests pass - Staffing: 37/37 tests pass Co-Authored-By: claude-flow <ruv@ruv.net>
174 lines
4.6 KiB
TypeScript
174 lines
4.6 KiB
TypeScript
import * as XLSX from "xlsx";
|
|
import {
|
|
EstimateExportFormat,
|
|
EstimateStatus,
|
|
EstimateVersionStatus,
|
|
} from "@capakraken/shared";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
serializeEstimateExport,
|
|
type EstimateExportSource,
|
|
} from "../estimate/export-serializer.js";
|
|
|
|
const createdAt = new Date("2026-03-13T08:00:00.000Z");
|
|
|
|
function buildSource(): EstimateExportSource {
|
|
return {
|
|
estimate: {
|
|
id: "est_1",
|
|
projectId: "project_1",
|
|
name: "CGI Estimate",
|
|
opportunityId: "OP-42",
|
|
baseCurrency: "EUR",
|
|
status: EstimateStatus.APPROVED,
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
},
|
|
project: {
|
|
id: "project_1",
|
|
shortCode: "CGI-001",
|
|
name: "CGI Project",
|
|
status: "ACTIVE",
|
|
startDate: "2026-03-01T00:00:00.000Z",
|
|
endDate: "2026-04-01T00:00:00.000Z",
|
|
},
|
|
version: {
|
|
id: "ver_1",
|
|
versionNumber: 1,
|
|
label: "Approved",
|
|
status: EstimateVersionStatus.APPROVED,
|
|
notes: "Ready for export",
|
|
lockedAt: createdAt,
|
|
projectSnapshot: {},
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
assumptions: [
|
|
{
|
|
id: "assumption_1",
|
|
category: "commercial",
|
|
key: "pricingStructure",
|
|
label: "Pricing Structure",
|
|
valueType: "string",
|
|
value: "fixed-bid",
|
|
sortOrder: 0,
|
|
notes: null,
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
},
|
|
],
|
|
scopeItems: [
|
|
{
|
|
id: "scope_1",
|
|
sequenceNo: 10,
|
|
scopeType: "SHOT",
|
|
packageCode: "PKG-1",
|
|
name: "Shot 010",
|
|
description: "Main hero shot",
|
|
scene: "Scene 01",
|
|
page: null,
|
|
location: "Berlin",
|
|
assumptionCategory: null,
|
|
technicalSpec: { resolution: "4K" },
|
|
frameCount: 120,
|
|
itemCount: 1,
|
|
unitMode: "shot",
|
|
internalComments: null,
|
|
externalComments: null,
|
|
metadata: {},
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
},
|
|
],
|
|
demandLines: [
|
|
{
|
|
id: "line_1",
|
|
scopeItemId: "scope_1",
|
|
roleId: "role_1",
|
|
resourceId: "resource_1",
|
|
lineType: "LABOR",
|
|
name: "Comp Artist",
|
|
chapter: "Compositing",
|
|
hours: 40,
|
|
days: 5,
|
|
fte: 1,
|
|
rateSource: "resource",
|
|
costRateCents: 5000,
|
|
billRateCents: 8000,
|
|
currency: "EUR",
|
|
costTotalCents: 200000,
|
|
priceTotalCents: 320000,
|
|
monthlySpread: { "2026-03": 40 },
|
|
staffingAttributes: { location: "Berlin" },
|
|
metadata: {},
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
},
|
|
],
|
|
resourceSnapshots: [
|
|
{
|
|
id: "snapshot_1",
|
|
resourceId: "resource_1",
|
|
sourceEid: "E100",
|
|
displayName: "Alex Artist",
|
|
chapter: "Compositing",
|
|
roleId: "role_1",
|
|
currency: "EUR",
|
|
lcrCents: 5000,
|
|
ucrCents: 8000,
|
|
fte: 1,
|
|
location: "Berlin",
|
|
country: "DE",
|
|
level: "Senior",
|
|
workType: "Remote",
|
|
attributes: {},
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
},
|
|
],
|
|
metrics: [
|
|
{
|
|
id: "metric_1",
|
|
key: "total_hours",
|
|
label: "Total Hours",
|
|
metricGroup: "summary",
|
|
valueDecimal: 40,
|
|
valueCents: null,
|
|
currency: null,
|
|
metadata: {},
|
|
createdAt,
|
|
updatedAt: createdAt,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("estimate export serializer", () => {
|
|
it("creates a structured JSON export payload", () => {
|
|
const payload = serializeEstimateExport(buildSource(), EstimateExportFormat.JSON);
|
|
|
|
expect(payload.encoding).toBe("utf8");
|
|
expect(payload.mimeType).toBe("application/json; charset=utf-8");
|
|
expect(payload.summary.totalHours).toBe(40);
|
|
expect(payload.content).toContain('"estimateId": "est_1"');
|
|
expect(payload.previewText).toContain('"schemaVersion": 1');
|
|
});
|
|
|
|
it("creates a multi-sheet xlsx export payload", () => {
|
|
const payload = serializeEstimateExport(buildSource(), EstimateExportFormat.XLSX);
|
|
const workbook = XLSX.read(payload.content, { type: "base64" });
|
|
|
|
expect(payload.encoding).toBe("base64");
|
|
expect(payload.sheetNames).toEqual([
|
|
"Overview",
|
|
"Assumptions",
|
|
"Scope",
|
|
"DemandLines",
|
|
"Resources",
|
|
"Metrics",
|
|
]);
|
|
expect(workbook.SheetNames).toContain("DemandLines");
|
|
expect(payload.byteLength).toBeGreaterThan(100);
|
|
});
|
|
});
|