@cosmicdrift/kumiko-samples 0.363.0 → 0.365.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/package.json +1 -1
- package/packages/bundled-features/package.json +7 -1
- package/packages/bundled-features/src/audit/changes.json +18 -0
- package/packages/bundled-features/src/audit/feature.ts +1 -1
- package/packages/bundled-features/src/audit/handlers/list.query.ts +10 -5
- package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +63 -11
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +30 -11
- package/packages/bundled-features/src/auth-email-password/changes.json +55 -0
- package/packages/bundled-features/src/auth-email-password/email-rate-limits.ts +16 -0
- package/packages/bundled-features/src/auth-email-password/feature.ts +4 -7
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +6 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +2 -3
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +6 -4
- package/packages/bundled-features/src/auth-email-password/handlers/logout.write.ts +7 -2
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +7 -3
- package/packages/bundled-features/src/auth-email-password/mail-i18n.ts +10 -0
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +24 -5
- package/packages/bundled-features/src/billing-foundation/changes.json +48 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +57 -5
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +22 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +4 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +11 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +9 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +41 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +18 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +35 -19
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +35 -0
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +11 -3
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +10 -4
- package/packages/bundled-features/src/billing-foundation/i18n.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +27 -7
- package/packages/bundled-features/src/billing-foundation/types.ts +7 -2
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +20 -8
- package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +2 -0
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +7 -2
- package/packages/bundled-features/src/cap-counter/changes.json +19 -0
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +75 -5
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +2 -0
- package/packages/bundled-features/src/cap-counter/index.ts +1 -0
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -0
- package/packages/bundled-features/src/cap-overview/changes.json +6 -0
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/handlers/tenant-options.query.ts +2 -1
- package/packages/bundled-features/src/channel-discord/discord-channel.ts +4 -2
- package/packages/bundled-features/src/channel-email/changes.json +18 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +47 -24
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +7 -1
- package/packages/bundled-features/src/channel-slack/slack-channel.ts +4 -2
- package/packages/bundled-features/src/channel-teams/teams-channel.ts +4 -2
- package/packages/bundled-features/src/config/changes.json +6 -0
- package/packages/bundled-features/src/config/handlers/set.write.ts +2 -2
- package/packages/bundled-features/src/config/resolver.ts +15 -12
- package/packages/bundled-features/src/config/write-helpers.ts +23 -0
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +54 -27
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +0 -1
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +4 -5
- package/packages/bundled-features/src/delivery/attachment-ref.ts +39 -1
- package/packages/bundled-features/src/delivery/changes.json +30 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +31 -8
- package/packages/bundled-features/src/delivery/delivery-service.ts +93 -92
- package/packages/bundled-features/src/delivery/index.ts +2 -0
- package/packages/bundled-features/src/delivery/jobs.ts +51 -14
- package/packages/bundled-features/src/delivery/types.ts +7 -1
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/feature-toggles/changes.json +7 -0
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +26 -12
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-foundation/changes.json +6 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +6 -3
- package/packages/bundled-features/src/files-tenant-data/changes.json +6 -0
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +4 -0
- package/packages/bundled-features/src/files-tenant-data/index.ts +3 -1
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +12 -3
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +13 -8
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +3 -1
- package/packages/bundled-features/src/jobs/changes.json +31 -0
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +12 -4
- package/packages/bundled-features/src/jobs/feature.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +3 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +5 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +56 -18
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +7 -2
- package/packages/bundled-features/src/ledger/changes.json +7 -0
- package/packages/bundled-features/src/ledger/entity.ts +3 -1
- package/packages/bundled-features/src/metrics/changes.json +6 -0
- package/packages/bundled-features/src/metrics/filters.ts +28 -12
- package/packages/bundled-features/src/personal-access-tokens/changes.json +6 -0
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +7 -8
- package/packages/bundled-features/src/renderer-foundation/types.ts +2 -0
- package/packages/bundled-features/src/renderer-pdf/changes.json +12 -0
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +162 -82
- package/packages/bundled-features/src/secrets/changes.json +12 -0
- package/packages/bundled-features/src/secrets/feature.ts +4 -3
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +3 -1
- package/packages/bundled-features/src/secrets/secrets-context.ts +23 -3
- package/packages/bundled-features/src/secrets/write-gate.ts +8 -2
- package/packages/bundled-features/src/shared/changes.json +6 -0
- package/packages/bundled-features/src/shared/chunked-entity-migration.ts +4 -1
- package/packages/bundled-features/src/shared/index.ts +8 -0
- package/packages/bundled-features/src/shared/mail-template-body.ts +17 -0
- package/packages/bundled-features/src/shared/resource-image-formats.ts +31 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +7 -8
- package/packages/bundled-features/src/step-dispatcher/webhook-runner.ts +2 -2
- package/packages/bundled-features/src/subscription-stripe/changes.json +6 -0
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -11
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +3 -1
- package/packages/bundled-features/src/template-resolver/README.md +4 -2
- package/packages/bundled-features/src/template-resolver/api.ts +3 -2
- package/packages/bundled-features/src/template-resolver/changes.json +149 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +148 -18
- package/packages/bundled-features/src/template-resolver/compile-steps-block.ts +68 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +23 -3
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +3 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +33 -5
- package/packages/bundled-features/src/template-resolver/document-steps.ts +114 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +16 -1
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +81 -39
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +4 -3
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +121 -53
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +6 -44
- package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +52 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +56 -1
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +6 -3
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +25 -15
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +12 -1
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +73 -30
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +14 -11
- package/packages/bundled-features/src/template-resolver/handlers/override-diff.ts +49 -0
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +102 -19
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +49 -10
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +39 -10
- package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +14 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +54 -4
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +96 -14
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +51 -24
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +16 -2
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +4 -1
- package/packages/bundled-features/src/template-resolver/index.ts +18 -2
- package/packages/bundled-features/src/template-resolver/layout-appearance.ts +56 -0
- package/packages/bundled-features/src/template-resolver/mail-constants.ts +4 -0
- package/packages/bundled-features/src/template-resolver/pdf-constants.ts +12 -0
- package/packages/bundled-features/src/template-resolver/qualified-names.ts +4 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +9 -16
- package/packages/bundled-features/src/template-resolver/seeding.ts +27 -4
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +390 -124
- package/packages/bundled-features/src/template-resolver/table.ts +13 -1
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +4 -0
- package/packages/bundled-features/src/template-resolver/template-draft.ts +106 -0
- package/packages/bundled-features/src/template-resolver/template-meta.ts +95 -24
- package/packages/bundled-features/src/template-resolver/template-preview.ts +160 -57
- package/packages/bundled-features/src/template-resolver/template-resources.ts +3 -4
- package/packages/bundled-features/src/template-resolver/template-usage.ts +31 -11
- package/packages/bundled-features/src/template-resolver/template-validation.ts +16 -9
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +12 -8
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +97 -10
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +15 -5
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +405 -145
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +17 -3
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +32 -4
- package/packages/bundled-features/src/template-resolver/web/index.ts +8 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +23 -7
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +3 -8
- package/packages/bundled-features/src/tenant/seeding.ts +33 -27
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +14 -3
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +9 -1
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +3 -2
- package/packages/bundled-features/src/tier-engine/changes.json +18 -0
- package/packages/bundled-features/src/tier-engine/constants.ts +9 -0
- package/packages/bundled-features/src/tier-engine/entity.ts +9 -4
- package/packages/bundled-features/src/tier-engine/feature.ts +11 -3
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -5
- package/packages/bundled-features/src/tier-engine/index.ts +2 -0
- package/packages/bundled-features/src/user/changes.json +6 -0
- package/packages/bundled-features/src/user/constants.ts +1 -0
- package/packages/bundled-features/src/user/invited-global-roles.ts +2 -4
- package/packages/bundled-features/src/user/resolve-display-names.ts +8 -15
- package/packages/bundled-features/src/user/seeding.ts +19 -5
- package/packages/bundled-features/src/user-data-rights/changes.json +12 -0
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +0 -1
- package/packages/bundled-features/src/user-data-rights/i18n.ts +5 -0
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +13 -2
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +7 -1
- package/packages/bundled-features/src/user-profile/changes.json +1 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/feature.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +35 -3
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +2 -17
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +33 -9
- package/packages/bundled-features/src/waitlist/i18n.ts +9 -0
- package/packages/bundled-features/src/waitlist/lib.ts +20 -0
- package/packages/bundled-features/src/waitlist/options.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +7 -1
- package/samples/apps/ui-walkthrough/package.json +9 -0
- package/samples/apps/ui-walkthrough/playwright.config.ts +40 -7
- package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +87 -2
- package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +246 -30
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +4 -0
- package/samples/apps/ui-walkthrough/src/run-config.ts +18 -1
- package/samples/apps/use-all-bundled/README.md +3 -13
- package/samples/apps/use-all-bundled/feature-manifest.json +7 -5
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +34 -118
- package/samples/apps/use-all-bundled/src/app/notes-feature.ts +1 -1
- package/samples/apps/use-all-bundled/src/run-config.ts +0 -3
- package/samples/recipes/auth-mfa/README.md +2 -1
- package/samples/recipes/auth-mfa/src/feature.ts +1 -0
- package/samples/recipes/custom-handlers/README.md +4 -3
- package/samples/recipes/custom-handlers/src/feature.ts +21 -12
- package/samples/recipes/dashboard-stats/README.md +38 -0
- package/samples/recipes/dashboard-stats/package.json +8 -0
- package/samples/recipes/dashboard-stats/src/feature.ts +104 -0
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/managed-pages/README.md +2 -2
- package/samples/recipes/pdf-audit-export/README.md +2 -1
- package/samples/recipes/pdf-audit-export/package.json +1 -1
- package/samples/recipes/pdf-audit-export/src/feature.ts +68 -48
- package/samples/recipes/workflow-engine/src/db/queries/suspended-runs.ts +2 -2
- package/samples/apps/use-all-bundled/schema/generate.ts +0 -617
- package/samples/apps/use-all-bundled/schema/migrations/meta/0000_snapshot.json +0 -3055
- package/samples/apps/use-all-bundled/schema/migrations/meta/_journal.json +0 -13
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +0 -94
- package/samples/apps/use-all-bundled/schema/schema.ts +0 -9
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.364.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Workflow run-failed and retry logs carry the original cause",
|
|
6
|
+
"detail": "The ops log used to print the generic `workflow step N failed` wrapper. It now prints the underlying error, while the persisted event still carries the error class only."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.353.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
describeWorkflowStepError,
|
|
23
23
|
getStep,
|
|
24
24
|
SYSTEM_TENANT_ID,
|
|
25
|
+
unwrapWorkflowStepError,
|
|
25
26
|
WORKFLOW_AGGREGATE_TYPE,
|
|
26
27
|
WORKFLOW_RUN_FAILED_TYPE,
|
|
27
28
|
WorkflowStepError,
|
|
@@ -167,7 +168,7 @@ export function registerEventTrigger(r: FeatureRegistrar, workflow: WorkflowDefi
|
|
|
167
168
|
runId,
|
|
168
169
|
workflowName: workflow.name,
|
|
169
170
|
stepIndex,
|
|
170
|
-
error: String(error),
|
|
171
|
+
error: String(unwrapWorkflowStepError(error)),
|
|
171
172
|
});
|
|
172
173
|
const failedPayload: WorkflowRunFailedPayload = {
|
|
173
174
|
workflowName: workflow.name,
|
|
@@ -32,7 +32,7 @@ import { workflowRunPendingTableMeta } from "./tables.js";
|
|
|
32
32
|
|
|
33
33
|
// Kebab-case matching the "./workflow-runner" export subpath — every other
|
|
34
34
|
// bundled feature's runtime name follows this convention, and
|
|
35
|
-
//
|
|
35
|
+
// use-all-bundled's check-coverage matches exports to mounted features by it.
|
|
36
36
|
const FEATURE_NAME = "workflow-runner";
|
|
37
37
|
|
|
38
38
|
export const workflowRunnerFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
runStepList,
|
|
41
41
|
type StepInstance,
|
|
42
42
|
SYSTEM_ROLE,
|
|
43
|
+
unwrapWorkflowStepError,
|
|
43
44
|
WORKFLOW_AGGREGATE_TYPE,
|
|
44
45
|
WORKFLOW_RESUMED_TYPE,
|
|
45
46
|
WORKFLOW_RETRY_SCHEDULED_TYPE,
|
|
@@ -392,7 +393,12 @@ async function runResumedPipeline(ctx: HandlerContext, input: ResumedPipelineInp
|
|
|
392
393
|
});
|
|
393
394
|
return { isSuccess: true as const, data: { outcome: "completed" as const } };
|
|
394
395
|
} catch (error) {
|
|
395
|
-
log.warn("workflow run failed", {
|
|
396
|
+
log.warn("workflow run failed", {
|
|
397
|
+
runId,
|
|
398
|
+
workflowName,
|
|
399
|
+
stepIndex,
|
|
400
|
+
error: String(unwrapWorkflowStepError(error)),
|
|
401
|
+
});
|
|
396
402
|
// The input stepIndex is the suspended step; the failure happened at the step the resumed pass reached.
|
|
397
403
|
const failedPayload: WorkflowRunFailedPayload = {
|
|
398
404
|
workflowName,
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@cosmicdrift/kumiko-framework": "workspace:*",
|
|
16
16
|
"@cosmicdrift/kumiko-headless": "workspace:*",
|
|
17
17
|
"@cosmicdrift/kumiko-renderer-mail-html": "workspace:*",
|
|
18
|
+
"@cosmicdrift/kumiko-renderer-puppeteer-client": "workspace:*",
|
|
18
19
|
"@cosmicdrift/kumiko-renderer-web": "workspace:*",
|
|
19
20
|
"@cosmicdrift/kumiko-server-runtime": "workspace:*",
|
|
20
21
|
"react": "^19.2.6",
|
|
@@ -24,5 +25,13 @@
|
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@cosmicdrift/kumiko-testing": "workspace:*"
|
|
28
|
+
},
|
|
29
|
+
"kumiko": {
|
|
30
|
+
"assets": [
|
|
31
|
+
{
|
|
32
|
+
"name": "logo-nordlicht.png",
|
|
33
|
+
"source": "src/app/assets/logo-nordlicht.png"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
27
36
|
}
|
|
28
37
|
}
|
|
@@ -7,18 +7,51 @@
|
|
|
7
7
|
// setupTestStack-Default ist ephemeral (fresh kumiko_test_<random> DB),
|
|
8
8
|
// deshalb braucht's keine DB-Reset-Logik hier.
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
defineAppE2eConfig,
|
|
12
|
+
E2E_TIMEOUT_MS,
|
|
13
|
+
PLAYWRIGHT_DEMO_ENV,
|
|
14
|
+
} from "@cosmicdrift/kumiko-testing/e2e";
|
|
15
|
+
import { chromium } from "@playwright/test";
|
|
11
16
|
import { E2E_PORTS } from "../../e2e/e2e-ports";
|
|
12
17
|
|
|
13
18
|
const PORT = E2E_PORTS["framework/ui-walkthrough"];
|
|
19
|
+
const PRINT_SERVICE_PORT = E2E_PORTS["framework/ui-walkthrough-puppeteer-service"];
|
|
20
|
+
// Both sides of the print service handshake; any value of 16+ characters will do for a local run.
|
|
21
|
+
const PRINT_SERVICE_TOKEN = "ui-walkthrough-e2e-print-service";
|
|
14
22
|
|
|
23
|
+
const appConfig = defineAppE2eConfig({
|
|
24
|
+
port: PORT,
|
|
25
|
+
serverEntry: "src/app/server.ts",
|
|
26
|
+
locale: "en-US",
|
|
27
|
+
env: {
|
|
28
|
+
...PLAYWRIGHT_DEMO_ENV,
|
|
29
|
+
PUPPETEER_SERVICE_URL: `http://127.0.0.1:${PRINT_SERVICE_PORT}`,
|
|
30
|
+
PUPPETEER_AUTH_TOKEN: PRINT_SERVICE_TOKEN,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// The PDF preview of the template editor prints through the real puppeteer-service,
|
|
35
|
+
// on the Chromium that `playwright install chromium` provides.
|
|
15
36
|
export default {
|
|
16
|
-
...
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
...appConfig,
|
|
38
|
+
webServer: [
|
|
39
|
+
{
|
|
40
|
+
command: "bun run src/index.ts",
|
|
41
|
+
cwd: "../../../apps/puppeteer-service",
|
|
42
|
+
url: `http://127.0.0.1:${PRINT_SERVICE_PORT}/healthz`,
|
|
43
|
+
env: {
|
|
44
|
+
PORT: String(PRINT_SERVICE_PORT),
|
|
45
|
+
PUPPETEER_AUTH_TOKEN: PRINT_SERVICE_TOKEN,
|
|
46
|
+
PUPPETEER_EXECUTABLE_PATH: chromium.executablePath(),
|
|
47
|
+
},
|
|
48
|
+
reuseExistingServer: false,
|
|
49
|
+
timeout: E2E_TIMEOUT_MS.webServer,
|
|
50
|
+
stdout: "pipe",
|
|
51
|
+
stderr: "pipe",
|
|
52
|
+
},
|
|
53
|
+
...[appConfig.webServer ?? []].flat(),
|
|
54
|
+
],
|
|
22
55
|
// globalSetup spawnt vor allen Tests einen bun-Subprozess der die
|
|
23
56
|
// Registry auswertet und `e2e/.e2e-data.json` schreibt. Der eigent-
|
|
24
57
|
// liche generated.spec.ts-Runner liest nur die JSON — framework-
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import type { SeedFn } from "@cosmicdrift/kumiko-dev-server";
|
|
13
13
|
import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
14
14
|
import { DEFAULT_MAIL_LAYOUT_MJML } from "@cosmicdrift/kumiko-renderer-mail-html";
|
|
15
|
+
import { readBundledAsset } from "@cosmicdrift/kumiko-server-runtime";
|
|
15
16
|
import {
|
|
16
17
|
NORDLICHT_TENANT_ID,
|
|
17
18
|
NORDLICHT_TENANT_NAME,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
import {
|
|
24
25
|
INVOICE_CORRECTED_DRAFT,
|
|
25
26
|
INVOICE_ISSUED_DRAFT_VERSIONS,
|
|
27
|
+
LOGO_RESOURCE_KEY,
|
|
26
28
|
PAYMENT_REMINDER_TEMPLATES,
|
|
27
29
|
type SeedTemplate,
|
|
28
30
|
SYSTEM_AUTH_MAILS,
|
|
@@ -30,12 +32,18 @@ import {
|
|
|
30
32
|
SYSTEM_INVOICE_ISSUED,
|
|
31
33
|
SYSTEM_PDF_LAYOUT,
|
|
32
34
|
SYSTEM_TENANCY_CERTIFICATE,
|
|
35
|
+
TENANT_INVOICE_OVERRIDES,
|
|
33
36
|
TENANT_MAIL_LAYOUT_VERSIONS,
|
|
34
37
|
TENANT_UTILITY_STATEMENT,
|
|
35
38
|
} from "./template-seed-data";
|
|
36
39
|
|
|
37
40
|
const NORDLICHT = NORDLICHT_TENANT_ID as TenantId;
|
|
38
41
|
|
|
42
|
+
// Declared under `kumiko.assets` in this sample's package.json; looked up from the
|
|
43
|
+
// package dir so the seed does not depend on the cwd the dev server was started in.
|
|
44
|
+
const LOGO_ASSET_NAME = "logo-nordlicht.png";
|
|
45
|
+
const SAMPLE_APP_DIR = new URL("../../", import.meta.url).pathname;
|
|
46
|
+
|
|
39
47
|
const BRANDING = [
|
|
40
48
|
[BRANDING_QN.title, "Nordlicht Mieterportal"],
|
|
41
49
|
[BRANDING_QN.accentColor, "#205265"],
|
|
@@ -108,6 +116,35 @@ export const seedTemplates: SeedFn = async (stack) => {
|
|
|
108
116
|
},
|
|
109
117
|
user,
|
|
110
118
|
);
|
|
119
|
+
// Real multipart upload; every resource link and the branding logo consume their own file.
|
|
120
|
+
const uploadLogo = async (user: SessionUser): Promise<string> => {
|
|
121
|
+
const logoPng = await readBundledAsset(LOGO_ASSET_NAME, { cwd: SAMPLE_APP_DIR });
|
|
122
|
+
const form = new FormData();
|
|
123
|
+
form.append(
|
|
124
|
+
"file",
|
|
125
|
+
new File([new Uint8Array(logoPng)], LOGO_RESOURCE_KEY, { type: "image/png" }),
|
|
126
|
+
);
|
|
127
|
+
const response = await stack.app.request("/api/files", {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: {
|
|
130
|
+
Authorization: `Bearer ${await stack.jwt.sign(user)}`,
|
|
131
|
+
},
|
|
132
|
+
body: form,
|
|
133
|
+
});
|
|
134
|
+
if (response.status !== 201) throw new Error(`logo upload failed: HTTP ${response.status}`);
|
|
135
|
+
return ((await response.json()) as { id: string }).id;
|
|
136
|
+
};
|
|
137
|
+
const linkInvoicePdf = (templateId: string): Promise<unknown> =>
|
|
138
|
+
stack.http.writeOk(
|
|
139
|
+
TemplateResolverHandlers.resourceLinkPdf,
|
|
140
|
+
{
|
|
141
|
+
templateId,
|
|
142
|
+
key: "rechnung",
|
|
143
|
+
templateSlug: "billing:invoice",
|
|
144
|
+
fileName: "rechnung-{{invoiceNumber}}.pdf",
|
|
145
|
+
},
|
|
146
|
+
tenantAdmin,
|
|
147
|
+
);
|
|
111
148
|
const upsertSystem = (template: SeedTemplate) =>
|
|
112
149
|
upsert(TemplateResolverHandlers.upsertSystem, template, platformAdmin);
|
|
113
150
|
const upsertTenant = (template: SeedTemplate, status: "draft" | "active") =>
|
|
@@ -139,16 +176,64 @@ export const seedTemplates: SeedFn = async (stack) => {
|
|
|
139
176
|
for (const [key, value] of BRANDING) {
|
|
140
177
|
await stack.http.writeOk("config:write:set", { key, value }, tenantAdmin);
|
|
141
178
|
}
|
|
179
|
+
// The built-in "logo" placeholder of the layouts resolves to this file.
|
|
180
|
+
await stack.http.writeOk(
|
|
181
|
+
"config:write:set",
|
|
182
|
+
{ key: BRANDING_QN.logoFile, value: await uploadLogo(tenantAdmin) },
|
|
183
|
+
tenantAdmin,
|
|
184
|
+
);
|
|
142
185
|
// Each save is a new version of the same row; the second one makes the
|
|
143
186
|
// layout "customised" for the overview and the first leaves history to diff.
|
|
144
187
|
for (const content of TENANT_MAIL_LAYOUT_VERSIONS) {
|
|
145
188
|
await upsertTenant(
|
|
146
|
-
{
|
|
189
|
+
{
|
|
190
|
+
slug: "default",
|
|
191
|
+
kind: "mail-layout",
|
|
192
|
+
locale: "en",
|
|
193
|
+
title: "Mail-Layout",
|
|
194
|
+
content,
|
|
195
|
+
meta: { accentLine: "branding" },
|
|
196
|
+
},
|
|
147
197
|
"active",
|
|
148
198
|
);
|
|
149
199
|
}
|
|
150
|
-
|
|
200
|
+
let invoiceDraftId: string | null = null;
|
|
201
|
+
for (const draft of INVOICE_ISSUED_DRAFT_VERSIONS) {
|
|
202
|
+
const saved = await upsertTenant(draft, "draft");
|
|
203
|
+
if (invoiceDraftId === null) {
|
|
204
|
+
invoiceDraftId = saved.id;
|
|
205
|
+
await linkInvoicePdf(saved.id);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
151
208
|
await upsertTenant(INVOICE_CORRECTED_DRAFT, "draft");
|
|
152
209
|
for (const reminder of PAYMENT_REMINDER_TEMPLATES) await upsertTenant(reminder, "active");
|
|
153
210
|
await upsertTenant(TENANT_UTILITY_STATEMENT, "active");
|
|
211
|
+
|
|
212
|
+
// Other tenants with their own invoice copy: what the SystemAdmin sees in the overrides drawer.
|
|
213
|
+
for (const override of TENANT_INVOICE_OVERRIDES) {
|
|
214
|
+
const account = await seedAdmin(stack.db, {
|
|
215
|
+
email: override.adminEmail,
|
|
216
|
+
password: TENANT_ADMIN_PASSWORD,
|
|
217
|
+
displayName: override.adminName,
|
|
218
|
+
emailVerified: true,
|
|
219
|
+
memberships: [
|
|
220
|
+
{
|
|
221
|
+
tenantId: override.tenantId as TenantId,
|
|
222
|
+
tenantKey: override.tenantKey,
|
|
223
|
+
tenantName: override.tenantName,
|
|
224
|
+
roles: ["TenantAdmin"],
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
});
|
|
228
|
+
const admin: SessionUser = {
|
|
229
|
+
id: account.id as SessionUser["id"],
|
|
230
|
+
tenantId: override.tenantId as TenantId,
|
|
231
|
+
roles: ["TenantAdmin"],
|
|
232
|
+
};
|
|
233
|
+
for (const version of override.versions) {
|
|
234
|
+
await upsert(TemplateResolverHandlers.upsertTenant, version, admin, {
|
|
235
|
+
status: override.status,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
154
239
|
};
|
|
@@ -16,6 +16,9 @@ export type SeedTemplate = {
|
|
|
16
16
|
// Document HTML marks a variable as an empty span; only link targets take {{name}}.
|
|
17
17
|
const v = (name: string): string => `<span data-variable="${name}"></span>`;
|
|
18
18
|
|
|
19
|
+
// Resource key of the logo image the invoice mail embeds (uploaded by seed-templates.ts).
|
|
20
|
+
export const LOGO_RESOURCE_KEY = "logo-nordlicht.png";
|
|
21
|
+
|
|
19
22
|
const AUTH_VARIABLE_SCHEMA = {
|
|
20
23
|
appName: { type: "text", required: true },
|
|
21
24
|
actionUrl: { type: "link", required: true },
|
|
@@ -44,16 +47,134 @@ const DOCUMENT_LINES_SCHEMA = {
|
|
|
44
47
|
},
|
|
45
48
|
} as const;
|
|
46
49
|
|
|
50
|
+
// Same variable names the billing notifications declare; labels follow the template language.
|
|
51
|
+
type InvoiceDocumentLabels = {
|
|
52
|
+
readonly recipientAddress: string;
|
|
53
|
+
readonly invoiceNumber: string;
|
|
54
|
+
readonly invoiceDate: string;
|
|
55
|
+
readonly invoiceTotal: string;
|
|
56
|
+
readonly netTotal: string;
|
|
57
|
+
readonly vatAmount: string;
|
|
58
|
+
readonly dueDate: string;
|
|
59
|
+
readonly lines: string;
|
|
60
|
+
readonly position: string;
|
|
61
|
+
readonly description: string;
|
|
62
|
+
readonly period: string;
|
|
63
|
+
readonly net: string;
|
|
64
|
+
readonly paymentReference: string;
|
|
65
|
+
readonly iban: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function invoiceDocumentSchema(labels: InvoiceDocumentLabels) {
|
|
69
|
+
return {
|
|
70
|
+
recipientAddress: {
|
|
71
|
+
type: "text",
|
|
72
|
+
required: true,
|
|
73
|
+
label: labels.recipientAddress,
|
|
74
|
+
example: "Familie Yilmaz, Lindenstraße 14, 24103 Kiel",
|
|
75
|
+
},
|
|
76
|
+
invoiceNumber: {
|
|
77
|
+
type: "text",
|
|
78
|
+
required: true,
|
|
79
|
+
label: labels.invoiceNumber,
|
|
80
|
+
example: "RE-2026-0142",
|
|
81
|
+
},
|
|
82
|
+
invoiceDate: { type: "date", required: true, label: labels.invoiceDate, example: "2026-10-01" },
|
|
83
|
+
dueDate: { type: "date", required: true, label: labels.dueDate, example: "2026-10-15" },
|
|
84
|
+
netTotal: {
|
|
85
|
+
type: "money",
|
|
86
|
+
label: labels.netTotal,
|
|
87
|
+
example: { amount: 107900, currency: "EUR" },
|
|
88
|
+
},
|
|
89
|
+
vatAmount: {
|
|
90
|
+
type: "money",
|
|
91
|
+
label: labels.vatAmount,
|
|
92
|
+
example: { amount: 20501, currency: "EUR" },
|
|
93
|
+
},
|
|
94
|
+
invoiceTotal: {
|
|
95
|
+
type: "money",
|
|
96
|
+
required: true,
|
|
97
|
+
label: labels.invoiceTotal,
|
|
98
|
+
example: { amount: 128401, currency: "EUR" },
|
|
99
|
+
},
|
|
100
|
+
lines: {
|
|
101
|
+
type: "list",
|
|
102
|
+
required: true,
|
|
103
|
+
label: labels.lines,
|
|
104
|
+
item: {
|
|
105
|
+
position: { type: "text", label: labels.position },
|
|
106
|
+
description: { type: "text", label: labels.description },
|
|
107
|
+
period: { type: "text", label: labels.period },
|
|
108
|
+
net: { type: "money", label: labels.net },
|
|
109
|
+
},
|
|
110
|
+
example: [
|
|
111
|
+
{
|
|
112
|
+
position: "1",
|
|
113
|
+
description: "Kaltmiete Lindenstraße 14, WE 3",
|
|
114
|
+
period: "Okt 2026",
|
|
115
|
+
net: { amount: 98000, currency: "EUR" },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
position: "2",
|
|
119
|
+
description: "Stellplatz 7",
|
|
120
|
+
period: "Okt 2026",
|
|
121
|
+
net: { amount: 9900, currency: "EUR" },
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
paymentReference: {
|
|
126
|
+
type: "text",
|
|
127
|
+
label: labels.paymentReference,
|
|
128
|
+
example: "RE-2026-0142 / Yilmaz",
|
|
129
|
+
},
|
|
130
|
+
iban: { type: "text", label: labels.iban, example: "DE12 2105 0170 0012 3456 78" },
|
|
131
|
+
} as const;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const INVOICE_DOCUMENT_SCHEMA_DE = invoiceDocumentSchema({
|
|
135
|
+
recipientAddress: "Empfängeradresse",
|
|
136
|
+
invoiceNumber: "Rechnungsnummer",
|
|
137
|
+
invoiceDate: "Rechnungsdatum",
|
|
138
|
+
invoiceTotal: "Rechnungsbetrag",
|
|
139
|
+
netTotal: "Nettobetrag",
|
|
140
|
+
vatAmount: "Umsatzsteuer",
|
|
141
|
+
dueDate: "Fälligkeitsdatum",
|
|
142
|
+
lines: "Rechnungspositionen",
|
|
143
|
+
position: "Nr.",
|
|
144
|
+
description: "Bezeichnung",
|
|
145
|
+
period: "Zeitraum",
|
|
146
|
+
net: "Netto",
|
|
147
|
+
paymentReference: "Zahlungsreferenz",
|
|
148
|
+
iban: "IBAN",
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const INVOICE_DOCUMENT_SCHEMA_EN = invoiceDocumentSchema({
|
|
152
|
+
recipientAddress: "Recipient address",
|
|
153
|
+
invoiceNumber: "Invoice number",
|
|
154
|
+
invoiceDate: "Invoice date",
|
|
155
|
+
invoiceTotal: "Invoice total",
|
|
156
|
+
netTotal: "Net amount",
|
|
157
|
+
vatAmount: "VAT",
|
|
158
|
+
dueDate: "Due date",
|
|
159
|
+
lines: "Invoice lines",
|
|
160
|
+
position: "No.",
|
|
161
|
+
description: "Description",
|
|
162
|
+
period: "Period",
|
|
163
|
+
net: "Net",
|
|
164
|
+
paymentReference: "Payment reference",
|
|
165
|
+
iban: "IBAN",
|
|
166
|
+
});
|
|
167
|
+
|
|
47
168
|
export const SYSTEM_PDF_LAYOUT: SeedTemplate = {
|
|
48
169
|
slug: "default",
|
|
49
170
|
kind: "pdf-layout",
|
|
50
171
|
locale: "en",
|
|
51
172
|
title: "Briefpapier",
|
|
52
173
|
content: [
|
|
53
|
-
`<div data-header><
|
|
174
|
+
`<div data-header><table><tbody><tr><td><img data-resource="logo" alt="Nordlicht"></td><td><p data-align="right" data-size="small">Am Hafen 12 · 24103 Kiel</p></td></tr></tbody></table></div>`,
|
|
54
175
|
"<div data-content></div>",
|
|
55
176
|
"<div data-signature></div>",
|
|
56
|
-
`<div data-footer><p
|
|
177
|
+
`<div data-footer><p>Seite ${v("page")} von ${v("pages")}</p><p>${v("tenantName")} · HRB 18422 Kiel · IBAN DE12 2105 0170 0012 3456 78</p></div>`,
|
|
57
178
|
].join(""),
|
|
58
179
|
};
|
|
59
180
|
|
|
@@ -65,14 +186,14 @@ export const TENANT_MAIL_LAYOUT_VERSIONS: readonly string[] = [
|
|
|
65
186
|
`<div data-footer><p>${v("tenantName")}</p></div>`,
|
|
66
187
|
].join(""),
|
|
67
188
|
[
|
|
68
|
-
|
|
189
|
+
'<div data-header><img data-resource="logo" alt="Nordlicht"></div>',
|
|
69
190
|
"<div data-content></div>",
|
|
70
191
|
`<div data-signature><p>Mit freundlichen Grüßen</p><p>${v("signature")}</p><p>${v("tenantName")}</p></div>`,
|
|
71
|
-
`<div data-footer><p>${v("imprint")}</p><p><a href="https://nordlicht-immobilien.de/datenschutz">Datenschutz</a> · <a href="{{unsubscribeUrl}}">
|
|
192
|
+
`<div data-footer><p>${v("imprint")}</p><p><a href="https://nordlicht-immobilien.de/datenschutz">Datenschutz</a> · <a href="{{unsubscribeUrl}}">Abmeldelink</a></p></div>`,
|
|
72
193
|
].join(""),
|
|
73
194
|
];
|
|
74
195
|
|
|
75
|
-
const LINES_TABLE = `<table><tbody><tr><th><p>Position</p></th><th><p>Betrag</p></th></tr><tr data-each="lines" data-repeat-header=""><td><p>${v("description")}</p></td><td><p>${v("amount")}</p></td></tr></tbody></table>`;
|
|
196
|
+
const LINES_TABLE = `<table><tbody><tr><th><p>Position</p></th><th><p data-align="right">Betrag</p></th></tr><tr data-each="lines" data-repeat-header=""><td><p>${v("description")}</p></td><td><p data-align="right">${v("amount")}</p></td></tr><tr><td><p><strong>Gesamt</strong></p></td><td><p data-align="right">${v("invoiceTotal")}</p></td></tr></tbody></table>`;
|
|
76
197
|
|
|
77
198
|
// The registry of the billing notifications declares the variables, so these
|
|
78
199
|
// templates carry no variableSchema of their own.
|
|
@@ -82,14 +203,14 @@ function invoiceIssuedBody(args: {
|
|
|
82
203
|
}): string {
|
|
83
204
|
const due = args.withDueDate ? ` Bitte überweisen Sie den Betrag bis zum ${v("dueDate")}.` : "";
|
|
84
205
|
return [
|
|
85
|
-
|
|
206
|
+
`<h2>Ihre Rechnung ${v("invoiceNumber")}</h2>`,
|
|
86
207
|
`<p>Guten Tag ${v("recipientName")},</p>`,
|
|
87
|
-
`<p>${args.intro}
|
|
88
|
-
'<a data-button href="{{invoiceUrl}}">Rechnung
|
|
208
|
+
`<p>${args.intro} ${v("invoiceNumber")} über ${v("invoiceTotal")}.${due} Die Rechnung finden Sie als PDF im Anhang und jederzeit online.</p>`,
|
|
209
|
+
'<a data-button href="{{invoiceUrl}}">Rechnung online ansehen</a>',
|
|
89
210
|
LINES_TABLE,
|
|
90
211
|
"<hr>",
|
|
91
|
-
"<p>
|
|
92
|
-
`<p>${v("
|
|
212
|
+
"<p>Bei Fragen zur Rechnung antworten Sie einfach auf diese E-Mail.</p>",
|
|
213
|
+
`<p>${v("signature")}</p>`,
|
|
93
214
|
].join("");
|
|
94
215
|
}
|
|
95
216
|
|
|
@@ -101,6 +222,12 @@ const invoiceIssuedMeta = (subject: string) => ({
|
|
|
101
222
|
previewText: "Rechnung {{invoiceNumber}} über {{invoiceTotal}}, online abrufbar",
|
|
102
223
|
});
|
|
103
224
|
|
|
225
|
+
const tenantInvoiceMeta = (subject: string) => ({
|
|
226
|
+
...invoiceIssuedMeta(subject),
|
|
227
|
+
senderName: "Nordlicht Immobilien GmbH",
|
|
228
|
+
replyTo: "buchhaltung@nordlicht-immobilien.de",
|
|
229
|
+
});
|
|
230
|
+
|
|
104
231
|
// The platform default has a due date; the tenant drafts below drop it, which
|
|
105
232
|
// is what blocks publishing in the walkthrough.
|
|
106
233
|
export const SYSTEM_INVOICE_ISSUED: SeedTemplate = {
|
|
@@ -123,7 +250,7 @@ export const INVOICE_ISSUED_DRAFT_VERSIONS: readonly SeedTemplate[] = [
|
|
|
123
250
|
intro: "anbei senden wir Ihnen die Rechnung",
|
|
124
251
|
withDueDate: false,
|
|
125
252
|
}),
|
|
126
|
-
meta:
|
|
253
|
+
meta: tenantInvoiceMeta("Ihre Rechnung {{invoiceNumber}}"),
|
|
127
254
|
},
|
|
128
255
|
{
|
|
129
256
|
slug: INVOICE_ISSUED_SLUG,
|
|
@@ -131,7 +258,7 @@ export const INVOICE_ISSUED_DRAFT_VERSIONS: readonly SeedTemplate[] = [
|
|
|
131
258
|
locale: "de",
|
|
132
259
|
title: "Rechnung versendet",
|
|
133
260
|
content: invoiceIssuedBody({ intro: "anbei erhalten Sie die Rechnung", withDueDate: false }),
|
|
134
|
-
meta:
|
|
261
|
+
meta: tenantInvoiceMeta("Ihre Rechnung {{invoiceNumber}} von {{tenantName}}"),
|
|
135
262
|
},
|
|
136
263
|
];
|
|
137
264
|
|
|
@@ -279,7 +406,22 @@ export const SYSTEM_AUTH_MAILS: readonly SeedTemplate[] = [
|
|
|
279
406
|
]),
|
|
280
407
|
];
|
|
281
408
|
|
|
282
|
-
const
|
|
409
|
+
const UTILITY_ROWS = `<table><tbody><tr><th><p>Leistung</p></th><th><p>Betrag</p></th></tr><tr data-each="lines" data-repeat-header=""><td><p>${v("description")}</p></td><td><p>${v("amount")}</p></td></tr></tbody></table>`;
|
|
410
|
+
|
|
411
|
+
const right = (inner: string): string => `<p data-align="right">${inner}</p>`;
|
|
412
|
+
|
|
413
|
+
function invoiceRows(labels: {
|
|
414
|
+
readonly service: string;
|
|
415
|
+
readonly period: string;
|
|
416
|
+
readonly net: string;
|
|
417
|
+
}): string {
|
|
418
|
+
return [
|
|
419
|
+
"<table><tbody>",
|
|
420
|
+
`<tr><th><p>#</p></th><th><p>${labels.service}</p></th><th><p>${labels.period}</p></th><th><p data-align="right">${labels.net}</p></th></tr>`,
|
|
421
|
+
`<tr data-each="lines" data-repeat-header=""><td><p>${v("position")}</p></td><td><p>${v("description")}</p></td><td><p>${v("period")}</p></td><td><p data-align="right">${v("net")}</p></td></tr>`,
|
|
422
|
+
"</tbody></table>",
|
|
423
|
+
].join("");
|
|
424
|
+
}
|
|
283
425
|
|
|
284
426
|
export const SYSTEM_INVOICE_DOCUMENTS: readonly SeedTemplate[] = [
|
|
285
427
|
{
|
|
@@ -288,16 +430,29 @@ export const SYSTEM_INVOICE_DOCUMENTS: readonly SeedTemplate[] = [
|
|
|
288
430
|
locale: "de",
|
|
289
431
|
title: "Rechnung",
|
|
290
432
|
content: [
|
|
433
|
+
`<p data-size="small"><u>Nordlicht Immobilien GmbH · Am Hafen 12 · 24103 Kiel</u></p>`,
|
|
434
|
+
`<p>${v("recipientAddress")}</p>`,
|
|
435
|
+
right(`Rechnungsnummer: ${v("invoiceNumber")}`),
|
|
436
|
+
right(`Rechnungsdatum: ${v("invoiceDate")}`),
|
|
437
|
+
right(`Fällig am: ${v("dueDate")}`),
|
|
291
438
|
`<h1>Rechnung ${v("invoiceNumber")}</h1>`,
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
439
|
+
"<p>Sehr geehrte Damen und Herren,</p>",
|
|
440
|
+
"<p>wir stellen Ihnen die folgenden Leistungen in Rechnung:</p>",
|
|
441
|
+
invoiceRows({ service: "Leistung", period: "Zeitraum", net: "Netto" }),
|
|
442
|
+
right(`Nettobetrag ${v("netTotal")}`),
|
|
443
|
+
right(`Umsatzsteuer 19 % ${v("vatAmount")}`),
|
|
444
|
+
right(`<strong>Rechnungsbetrag ${v("invoiceTotal")}</strong>`),
|
|
445
|
+
`<p>Bitte überweisen Sie den Rechnungsbetrag bis zum ${v("dueDate")} unter Angabe der Referenz ${v("paymentReference")} auf ${v("iban")}.</p>`,
|
|
446
|
+
"<div data-page-break></div>",
|
|
447
|
+
"<p>Zahlungsbedingungen und Hinweise auf Seite 2 …</p>",
|
|
298
448
|
].join(""),
|
|
299
|
-
meta: {
|
|
300
|
-
|
|
449
|
+
meta: {
|
|
450
|
+
layoutSlug: "default",
|
|
451
|
+
pageFormat: "A4",
|
|
452
|
+
fileName: "rechnung-{{invoiceNumber}}.pdf",
|
|
453
|
+
metadata: { title: "Rechnung {{invoiceNumber}}" },
|
|
454
|
+
},
|
|
455
|
+
variableSchema: INVOICE_DOCUMENT_SCHEMA_DE,
|
|
301
456
|
},
|
|
302
457
|
{
|
|
303
458
|
slug: "billing:invoice",
|
|
@@ -305,16 +460,29 @@ export const SYSTEM_INVOICE_DOCUMENTS: readonly SeedTemplate[] = [
|
|
|
305
460
|
locale: "en",
|
|
306
461
|
title: "Invoice",
|
|
307
462
|
content: [
|
|
463
|
+
`<p data-size="small"><u>Nordlicht Immobilien GmbH · Am Hafen 12 · 24103 Kiel</u></p>`,
|
|
464
|
+
`<p>${v("recipientAddress")}</p>`,
|
|
465
|
+
right(`Invoice number: ${v("invoiceNumber")}`),
|
|
466
|
+
right(`Invoice date: ${v("invoiceDate")}`),
|
|
467
|
+
right(`Due on: ${v("dueDate")}`),
|
|
308
468
|
`<h1>Invoice ${v("invoiceNumber")}</h1>`,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
469
|
+
"<p>Dear Sir or Madam,</p>",
|
|
470
|
+
"<p>we charge you the following services:</p>",
|
|
471
|
+
invoiceRows({ service: "Service", period: "Period", net: "Net" }),
|
|
472
|
+
right(`Net amount ${v("netTotal")}`),
|
|
473
|
+
right(`VAT 19 % ${v("vatAmount")}`),
|
|
474
|
+
right(`<strong>Total ${v("invoiceTotal")}</strong>`),
|
|
475
|
+
`<p>Please transfer the amount by ${v("dueDate")} quoting the reference ${v("paymentReference")} to ${v("iban")}.</p>`,
|
|
476
|
+
"<div data-page-break></div>",
|
|
477
|
+
"<p>Terms of payment and notes on page 2 …</p>",
|
|
315
478
|
].join(""),
|
|
316
|
-
meta: {
|
|
317
|
-
|
|
479
|
+
meta: {
|
|
480
|
+
layoutSlug: "default",
|
|
481
|
+
pageFormat: "A4",
|
|
482
|
+
fileName: "invoice-{{invoiceNumber}}.pdf",
|
|
483
|
+
metadata: { title: "Invoice {{invoiceNumber}}" },
|
|
484
|
+
},
|
|
485
|
+
variableSchema: INVOICE_DOCUMENT_SCHEMA_EN,
|
|
318
486
|
},
|
|
319
487
|
];
|
|
320
488
|
|
|
@@ -328,7 +496,7 @@ export const TENANT_UTILITY_STATEMENT: SeedTemplate = {
|
|
|
328
496
|
`<p>Abrechnungsdatum: ${v("invoiceDate")}</p>`,
|
|
329
497
|
`<p>Guten Tag ${v("customerName")},</p>`,
|
|
330
498
|
"<p>nachfolgend erhalten Sie die Abrechnung der Betriebskosten für den Abrechnungszeitraum.</p>",
|
|
331
|
-
|
|
499
|
+
UTILITY_ROWS,
|
|
332
500
|
`<p><strong>Ergebnis: ${v("total")}</strong></p>`,
|
|
333
501
|
"<p>Ein Guthaben überweisen wir Ihnen innerhalb von 30 Tagen, eine Nachzahlung ist zum Monatsende fällig.</p>",
|
|
334
502
|
].join(""),
|
|
@@ -352,3 +520,51 @@ export const SYSTEM_TENANCY_CERTIFICATE: SeedTemplate = {
|
|
|
352
520
|
invoiceDate: { type: "date", required: true },
|
|
353
521
|
},
|
|
354
522
|
};
|
|
523
|
+
|
|
524
|
+
// Two other tenants customise the invoice for the "Anpassungen der Mandanten" drawer: one
|
|
525
|
+
// published copy with a second save, one draft.
|
|
526
|
+
export type TenantInvoiceOverride = {
|
|
527
|
+
readonly tenantId: string;
|
|
528
|
+
readonly tenantKey: string;
|
|
529
|
+
readonly tenantName: string;
|
|
530
|
+
readonly adminEmail: string;
|
|
531
|
+
readonly adminName: string;
|
|
532
|
+
readonly status: "active" | "draft";
|
|
533
|
+
readonly versions: readonly SeedTemplate[];
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const SYSTEM_INVOICE_DE = SYSTEM_INVOICE_DOCUMENTS[0] as SeedTemplate;
|
|
537
|
+
const INVOICE_INTRO = "<p>wir stellen Ihnen die folgenden Leistungen in Rechnung:</p>";
|
|
538
|
+
|
|
539
|
+
function invoiceWithIntro(sentence: string): SeedTemplate {
|
|
540
|
+
return {
|
|
541
|
+
...SYSTEM_INVOICE_DE,
|
|
542
|
+
content: SYSTEM_INVOICE_DE.content.replace(INVOICE_INTRO, `<p>${sentence}</p>`),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export const TENANT_INVOICE_OVERRIDES: readonly TenantInvoiceOverride[] = [
|
|
547
|
+
{
|
|
548
|
+
tenantId: "00000000-0000-4000-8000-000000000004",
|
|
549
|
+
tenantKey: "kuestenwohnen",
|
|
550
|
+
tenantName: "Küstenwohnen Kiel GmbH",
|
|
551
|
+
adminEmail: "verwaltung@kuestenwohnen-kiel.de",
|
|
552
|
+
adminName: "M. Petersen",
|
|
553
|
+
status: "active",
|
|
554
|
+
versions: [
|
|
555
|
+
invoiceWithIntro("für den genannten Zeitraum stellen wir Ihnen Folgendes in Rechnung:"),
|
|
556
|
+
invoiceWithIntro(
|
|
557
|
+
"für den genannten Zeitraum stellen wir Ihnen Folgendes in Rechnung. Die Nebenkosten rechnen wir jährlich ab.",
|
|
558
|
+
),
|
|
559
|
+
],
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
tenantId: "00000000-0000-4000-8000-000000000005",
|
|
563
|
+
tenantKey: "hansa-wohnbau",
|
|
564
|
+
tenantName: "Hansa Wohnbau eG",
|
|
565
|
+
adminEmail: "rechnungswesen@hansa-wohnbau.de",
|
|
566
|
+
adminName: "K. Lorenzen",
|
|
567
|
+
status: "draft",
|
|
568
|
+
versions: [invoiceWithIntro("nachfolgend die Abrechnung für Ihre Wohnung:")],
|
|
569
|
+
},
|
|
570
|
+
];
|
|
@@ -10,6 +10,10 @@ const INVOICE_ISSUED_VARIABLES = {
|
|
|
10
10
|
required: true,
|
|
11
11
|
example: "Familie Yilmaz",
|
|
12
12
|
}),
|
|
13
|
+
recipientEmail: templateVariable.text({
|
|
14
|
+
label: "Empfänger-Adresse",
|
|
15
|
+
example: "yilmaz.familie@example.org",
|
|
16
|
+
}),
|
|
13
17
|
invoiceNumber: templateVariable.text({
|
|
14
18
|
label: "Rechnungsnummer",
|
|
15
19
|
required: true,
|