@open-mercato/core 0.10.1-develop.7782.1.91d78919ba → 0.10.1-develop.7784.1.80225b38e4
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/customer_accounts/api/admin/users.js +14 -1
- package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
- package/dist/modules/warranty_claims/commands/claim-lines.js +30 -0
- package/dist/modules/warranty_claims/commands/claim-lines.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customer_accounts/api/admin/users.ts +14 -1
- package/src/modules/warranty_claims/commands/claim-lines.ts +36 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -4,6 +4,7 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
4
4
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
5
|
import { CustomerUser, CustomerUserRole, CustomerRole } from "@open-mercato/core/modules/customer_accounts/data/entities";
|
|
6
6
|
import { Organization } from "@open-mercato/core/modules/directory/data/entities";
|
|
7
|
+
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
7
8
|
import { adminCreateUserSchema } from "@open-mercato/core/modules/customer_accounts/data/validators";
|
|
8
9
|
import { emitCustomerAccountsEvent } from "@open-mercato/core/modules/customer_accounts/events";
|
|
9
10
|
import { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
@@ -35,11 +36,23 @@ async function GET(req) {
|
|
|
35
36
|
const personEntityId = url.searchParams.get("personEntityId");
|
|
36
37
|
const roleId = url.searchParams.get("roleId");
|
|
37
38
|
const search = url.searchParams.get("search");
|
|
39
|
+
const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
38
40
|
const where = {
|
|
39
41
|
tenantId: auth.tenantId,
|
|
40
|
-
organizationId: auth.orgId,
|
|
41
42
|
deletedAt: null
|
|
42
43
|
};
|
|
44
|
+
if (Array.isArray(organizationScope.filterIds)) {
|
|
45
|
+
if (organizationScope.filterIds.length === 0) {
|
|
46
|
+
return NextResponse.json({
|
|
47
|
+
ok: true,
|
|
48
|
+
items: [],
|
|
49
|
+
total: 0,
|
|
50
|
+
totalPages: 1,
|
|
51
|
+
page
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
where.organizationId = { $in: organizationScope.filterIds };
|
|
55
|
+
}
|
|
43
56
|
if (status === "active") {
|
|
44
57
|
where.isActive = true;
|
|
45
58
|
where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: /* @__PURE__ */ new Date() } }];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/customer_accounts/api/admin/users.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { CustomerUser, CustomerUserRole, CustomerRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { adminCreateUserSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOwnedCompanyEntity } from '@open-mercato/core/modules/customer_accounts/lib/customerEntityOwnership'\nimport { lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\n\nconst EMAIL_LIKE_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport const metadata = {}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.view'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as EntityManager\n\n const url = new URL(req.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1'))\n const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get('pageSize') || '25')))\n const status = url.searchParams.get('status') as 'active' | 'inactive' | 'locked' | null\n const customerEntityId = url.searchParams.get('customerEntityId')\n const personEntityId = url.searchParams.get('personEntityId')\n const roleId = url.searchParams.get('roleId')\n const search = url.searchParams.get('search')\n\n const where: Record<string, unknown> = {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n }\n\n if (status === 'active') {\n where.isActive = true\n where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: new Date() } }]\n } else if (status === 'inactive') {\n where.isActive = false\n } else if (status === 'locked') {\n where.lockedUntil = { $gt: new Date() }\n }\n\n if (customerEntityId) {\n where.customerEntityId = customerEntityId\n }\n\n if (personEntityId) {\n where.personEntityId = personEntityId\n }\n\n if (search) {\n const trimmedSearch = search.trim()\n // email/displayName are stored encrypted, so SQL ILIKE on the ciphertext\n // never matches a plaintext search term. Use search_tokens table for partial\n // matches and emailHash for exact email lookups.\n const searchFilter: Record<string, unknown>[] = []\n\n // Search encrypted fields via search_tokens\n const matchedIds = await findCustomerUserIdsBySearchTokens(em, E.customer_accounts.customer_user, trimmedSearch, auth.tenantId)\n if (matchedIds && matchedIds.length > 0) {\n searchFilter.push({ id: { $in: matchedIds } })\n }\n\n // Also support exact email lookup via emailHash\n if (EMAIL_LIKE_PATTERN.test(search)) {\n searchFilter.push({ emailHash: { $in: lookupHashCandidates(search) } })\n }\n\n if (searchFilter.length > 0) {\n if (where.$or) {\n where.$and = [{ $or: where.$or }, { $or: searchFilter }]\n delete where.$or\n } else {\n where.$or = searchFilter\n }\n } else {\n // No search results found, return empty\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n }\n\n let userIds: string[] | null = null\n if (roleId) {\n // Validate the roleId against the scoped CustomerRole set before touching the\n // junction table. CustomerUserRole carries no tenant/org column of its own,\n // so an unscoped lookup here is a role-UUID existence oracle and is brittle\n // against future code that reads the link rows directly (#2693, defence-in-depth).\n const scopedRole = await findOneWithDecryption(\n em,\n CustomerRole,\n { id: roleId, tenantId: auth.tenantId, organizationId: auth.orgId, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (!scopedRole) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n const roleLinks = await findWithDecryption(\n em,\n CustomerUserRole,\n { role: roleId as any, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n userIds = roleLinks.map((link) => (link.user as any)?.id || (link.user as unknown as string))\n if (userIds.length === 0) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n where.id = { $in: userIds }\n }\n\n const offset = (page - 1) * pageSize\n const [users, total] = await findAndCountWithDecryption(\n em,\n CustomerUser,\n where as any,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset,\n },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n\n const pageUserIds = users.map((user) => user.id)\n const userRoleLinks = pageUserIds.length > 0\n ? await findWithDecryption(\n em,\n CustomerUserRole,\n { user: { $in: pageUserIds } as any, deletedAt: null } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n : []\n\n const rolesByUserId = new Map<string, Array<{ id: string; name: string; slug: string }>>()\n for (const link of userRoleLinks) {\n const linkUserId = (link.user as any)?.id ?? (link.user as unknown as string)\n const role = link.role as any\n const bucket = rolesByUserId.get(linkUserId)\n const entry = { id: role.id, name: role.name, slug: role.slug }\n if (bucket) bucket.push(entry)\n else rolesByUserId.set(linkUserId, [entry])\n }\n\n const pageOrganizationIds = Array.from(new Set(\n users.map((user) => user.organizationId).filter((organizationId): organizationId is string => !!organizationId),\n ))\n const organizations = pageOrganizationIds.length > 0\n ? await em.find(Organization, { id: { $in: pageOrganizationIds }, tenant: auth.tenantId, deletedAt: null })\n : []\n const organizationNameById = new Map(organizations.map((organization) => [organization.id, organization.name]))\n\n const items = users.map((user) => {\n const organizationId = user.organizationId ?? null\n return {\n id: user.id,\n email: user.email,\n displayName: user.displayName,\n emailVerified: !!user.emailVerifiedAt,\n isActive: user.isActive,\n lockedUntil: user.lockedUntil || null,\n lastLoginAt: user.lastLoginAt || null,\n customerEntityId: user.customerEntityId || null,\n personEntityId: user.personEntityId || null,\n organizationId,\n organizationName: organizationId ? organizationNameById.get(organizationId) ?? null : null,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt || null,\n roles: rolesByUserId.get(user.id) ?? [],\n }\n })\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n return NextResponse.json({\n ok: true,\n items,\n total,\n totalPages,\n page,\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = adminCreateUserSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const em = container.resolve('em') as EntityManager\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n\n const existing = await customerUserService.findByEmail(parsed.data.email, auth.tenantId!)\n if (existing) {\n return NextResponse.json({ ok: false, error: 'A user with this email already exists' }, { status: 409 })\n }\n\n // Resolve roles up front, scoped to the caller's tenant AND organization, and\n // reject the request if any requested role is missing from the scoped set.\n // CustomerRole is org-scoped, so omitting organizationId here would let an\n // admin in org A link roles from org B in the same tenant \u2014 a cross-org\n // privilege grant (#2693). Invalid IDs must be rejected, not silently dropped.\n let resolvedRoles: Array<{ id: string }> = []\n if (parsed.data.roleIds && parsed.data.roleIds.length > 0) {\n const requestedRoleIds = parsed.data.roleIds\n const validRoles = await findWithDecryption(\n em,\n CustomerRole,\n {\n id: { $in: requestedRoleIds } as any,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (validRoles.length !== requestedRoleIds.length) {\n const foundIds = new Set(validRoles.map((role) => role.id))\n const missingId = requestedRoleIds.find((roleId) => !foundIds.has(roleId))\n return NextResponse.json({ ok: false, error: `Role ${missingId} not found` }, { status: 400 })\n }\n resolvedRoles = validRoles\n }\n\n // Reject a customerEntityId the caller does not own. Without this check a\n // mislinked company FK persists indefinitely and cross-links the user into\n // another org/company's portal context (#2693).\n if (parsed.data.customerEntityId) {\n const owned = await isOwnedCompanyEntity(em, parsed.data.customerEntityId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n if (!owned) {\n return NextResponse.json({ ok: false, error: 'Company not found' }, { status: 400 })\n }\n }\n\n const user = await customerUserService.createUser(\n parsed.data.email,\n parsed.data.password,\n parsed.data.displayName,\n { tenantId: auth.tenantId!, organizationId: auth.orgId! },\n )\n user.emailVerifiedAt = new Date()\n\n // Persist the user, its company association, and its role links in one\n // transaction so a flush failure on the role loop cannot leave a roleless\n // user committed (privilege gap).\n await em.transactional(async (tx) => {\n tx.persist(user)\n await tx.flush()\n\n if (parsed.data.customerEntityId) {\n await tx.nativeUpdate(CustomerUser, { id: user.id }, { customerEntityId: parsed.data.customerEntityId })\n }\n\n for (const role of resolvedRoles) {\n const userRole = tx.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n tx.persist(userRole)\n }\n })\n\n void emitCustomerAccountsEvent('customer_accounts.user.created', {\n id: user.id,\n email: user.email,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub,\n }).catch(() => undefined)\n\n return NextResponse.json({\n ok: true,\n user: { id: user.id, email: user.email, displayName: user.displayName },\n }, { status: 201 })\n}\n\nconst roleSchema = z.object({ id: z.string().uuid(), name: z.string(), slug: z.string() })\nconst userSchema = z.object({\n id: z.string().uuid(),\n email: z.string(),\n displayName: z.string(),\n emailVerified: z.boolean(),\n isActive: z.boolean(),\n lockedUntil: z.string().datetime().nullable(),\n lastLoginAt: z.string().datetime().nullable(),\n customerEntityId: z.string().uuid().nullable(),\n personEntityId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable().describe('Organization the customer user belongs to'),\n organizationName: z.string().nullable().describe('Resolved organization display name'),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime().nullable(),\n roles: z.array(roleSchema),\n})\n\nconst successSchema = z.object({\n ok: z.literal(true),\n user: z.object({ id: z.string().uuid(), email: z.string(), displayName: z.string() }),\n})\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nasync function findCustomerUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n field?: string,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (field) {\n query = query.where('field', '=', field)\n }\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nconst getMethodDoc: OpenApiMethodDoc = {\n summary: 'List customer users (admin)',\n description: 'Returns a paginated list of customer users with roles. Supports filtering by status, company, role, and search.',\n tags: ['Customer Accounts Admin'],\n query: z.object({\n page: z.number().int().positive().optional(),\n pageSize: z.number().int().positive().max(100).optional(),\n status: z.enum(['active', 'inactive', 'locked']).optional(),\n customerEntityId: z.string().uuid().optional(),\n roleId: z.string().uuid().optional(),\n search: z.string().optional(),\n }),\n responses: [{\n status: 200,\n description: 'Paginated user list',\n schema: z.object({ ok: z.literal(true), items: z.array(userSchema), total: z.number(), totalPages: z.number(), page: z.number() }),\n }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n ],\n}\n\nconst postMethodDoc: OpenApiMethodDoc = {\n summary: 'Create customer user (admin)',\n description: 'Creates a new customer user directly. Staff-initiated, bypasses signup flow.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: adminCreateUserSchema },\n responses: [{ status: 201, description: 'User created', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 409, description: 'Email already exists', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer user management (admin)',\n methods: {\n GET: getMethodDoc,\n POST: postMethodDoc,\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc, OpenApiMethodDoc } from '@open-mercato/shared/lib/openapi'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'\nimport { CustomerUser, CustomerUserRole, CustomerRole } from '@open-mercato/core/modules/customer_accounts/data/entities'\nimport { Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { adminCreateUserSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'\nimport { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'\nimport { findAndCountWithDecryption, findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isOwnedCompanyEntity } from '@open-mercato/core/modules/customer_accounts/lib/customerEntityOwnership'\nimport { lookupHashCandidates } from '@open-mercato/shared/lib/encryption/aes'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\n\nconst EMAIL_LIKE_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport const metadata = {}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.view'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n const em = container.resolve('em') as EntityManager\n\n const url = new URL(req.url)\n const page = Math.max(1, parseInt(url.searchParams.get('page') || '1'))\n const pageSize = Math.min(100, Math.max(1, parseInt(url.searchParams.get('pageSize') || '25')))\n const status = url.searchParams.get('status') as 'active' | 'inactive' | 'locked' | null\n const customerEntityId = url.searchParams.get('customerEntityId')\n const personEntityId = url.searchParams.get('personEntityId')\n const roleId = url.searchParams.get('roleId')\n const search = url.searchParams.get('search')\n\n const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const where: Record<string, unknown> = {\n tenantId: auth.tenantId,\n deletedAt: null,\n }\n if (Array.isArray(organizationScope.filterIds)) {\n if (organizationScope.filterIds.length === 0) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n where.organizationId = { $in: organizationScope.filterIds }\n }\n\n if (status === 'active') {\n where.isActive = true\n where.$or = [{ lockedUntil: null }, { lockedUntil: { $lt: new Date() } }]\n } else if (status === 'inactive') {\n where.isActive = false\n } else if (status === 'locked') {\n where.lockedUntil = { $gt: new Date() }\n }\n\n if (customerEntityId) {\n where.customerEntityId = customerEntityId\n }\n\n if (personEntityId) {\n where.personEntityId = personEntityId\n }\n\n if (search) {\n const trimmedSearch = search.trim()\n // email/displayName are stored encrypted, so SQL ILIKE on the ciphertext\n // never matches a plaintext search term. Use search_tokens table for partial\n // matches and emailHash for exact email lookups.\n const searchFilter: Record<string, unknown>[] = []\n\n // Search encrypted fields via search_tokens\n const matchedIds = await findCustomerUserIdsBySearchTokens(em, E.customer_accounts.customer_user, trimmedSearch, auth.tenantId)\n if (matchedIds && matchedIds.length > 0) {\n searchFilter.push({ id: { $in: matchedIds } })\n }\n\n // Also support exact email lookup via emailHash\n if (EMAIL_LIKE_PATTERN.test(search)) {\n searchFilter.push({ emailHash: { $in: lookupHashCandidates(search) } })\n }\n\n if (searchFilter.length > 0) {\n if (where.$or) {\n where.$and = [{ $or: where.$or }, { $or: searchFilter }]\n delete where.$or\n } else {\n where.$or = searchFilter\n }\n } else {\n // No search results found, return empty\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n }\n\n let userIds: string[] | null = null\n if (roleId) {\n // Validate the roleId against the scoped CustomerRole set before touching the\n // junction table. CustomerUserRole carries no tenant/org column of its own,\n // so an unscoped lookup here is a role-UUID existence oracle and is brittle\n // against future code that reads the link rows directly (#2693, defence-in-depth).\n const scopedRole = await findOneWithDecryption(\n em,\n CustomerRole,\n { id: roleId, tenantId: auth.tenantId, organizationId: auth.orgId, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (!scopedRole) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n const roleLinks = await findWithDecryption(\n em,\n CustomerUserRole,\n { role: roleId as any, deletedAt: null } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n userIds = roleLinks.map((link) => (link.user as any)?.id || (link.user as unknown as string))\n if (userIds.length === 0) {\n return NextResponse.json({\n ok: true,\n items: [],\n total: 0,\n totalPages: 1,\n page,\n })\n }\n where.id = { $in: userIds }\n }\n\n const offset = (page - 1) * pageSize\n const [users, total] = await findAndCountWithDecryption(\n em,\n CustomerUser,\n where as any,\n {\n orderBy: { createdAt: 'DESC' },\n limit: pageSize,\n offset,\n },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n\n const pageUserIds = users.map((user) => user.id)\n const userRoleLinks = pageUserIds.length > 0\n ? await findWithDecryption(\n em,\n CustomerUserRole,\n { user: { $in: pageUserIds } as any, deletedAt: null } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n : []\n\n const rolesByUserId = new Map<string, Array<{ id: string; name: string; slug: string }>>()\n for (const link of userRoleLinks) {\n const linkUserId = (link.user as any)?.id ?? (link.user as unknown as string)\n const role = link.role as any\n const bucket = rolesByUserId.get(linkUserId)\n const entry = { id: role.id, name: role.name, slug: role.slug }\n if (bucket) bucket.push(entry)\n else rolesByUserId.set(linkUserId, [entry])\n }\n\n const pageOrganizationIds = Array.from(new Set(\n users.map((user) => user.organizationId).filter((organizationId): organizationId is string => !!organizationId),\n ))\n const organizations = pageOrganizationIds.length > 0\n ? await em.find(Organization, { id: { $in: pageOrganizationIds }, tenant: auth.tenantId, deletedAt: null })\n : []\n const organizationNameById = new Map(organizations.map((organization) => [organization.id, organization.name]))\n\n const items = users.map((user) => {\n const organizationId = user.organizationId ?? null\n return {\n id: user.id,\n email: user.email,\n displayName: user.displayName,\n emailVerified: !!user.emailVerifiedAt,\n isActive: user.isActive,\n lockedUntil: user.lockedUntil || null,\n lastLoginAt: user.lastLoginAt || null,\n customerEntityId: user.customerEntityId || null,\n personEntityId: user.personEntityId || null,\n organizationId,\n organizationName: organizationId ? organizationNameById.get(organizationId) ?? null : null,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt || null,\n roles: rolesByUserId.get(user.id) ?? [],\n }\n })\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize))\n\n return NextResponse.json({\n ok: true,\n items,\n total,\n totalPages,\n page,\n })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n return NextResponse.json({ ok: false, error: 'Authentication required' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const rbacService = container.resolve('rbacService') as RbacService\n const hasAccess = await rbacService.userHasAllFeatures(auth.sub, ['customer_accounts.manage'], { tenantId: auth.tenantId, organizationId: auth.orgId })\n if (!hasAccess) {\n return NextResponse.json({ ok: false, error: 'Insufficient permissions' }, { status: 403 })\n }\n\n let body: unknown\n try {\n body = await req.json()\n } catch {\n return NextResponse.json({ ok: false, error: 'Invalid request body' }, { status: 400 })\n }\n\n const parsed = adminCreateUserSchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: 'Validation failed', details: parsed.error.flatten().fieldErrors }, { status: 400 })\n }\n\n const em = container.resolve('em') as EntityManager\n const customerUserService = container.resolve('customerUserService') as CustomerUserService\n\n const existing = await customerUserService.findByEmail(parsed.data.email, auth.tenantId!)\n if (existing) {\n return NextResponse.json({ ok: false, error: 'A user with this email already exists' }, { status: 409 })\n }\n\n // Resolve roles up front, scoped to the caller's tenant AND organization, and\n // reject the request if any requested role is missing from the scoped set.\n // CustomerRole is org-scoped, so omitting organizationId here would let an\n // admin in org A link roles from org B in the same tenant \u2014 a cross-org\n // privilege grant (#2693). Invalid IDs must be rejected, not silently dropped.\n let resolvedRoles: Array<{ id: string }> = []\n if (parsed.data.roleIds && parsed.data.roleIds.length > 0) {\n const requestedRoleIds = parsed.data.roleIds\n const validRoles = await findWithDecryption(\n em,\n CustomerRole,\n {\n id: { $in: requestedRoleIds } as any,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n deletedAt: null,\n } as any,\n undefined,\n { tenantId: auth.tenantId, organizationId: auth.orgId },\n )\n if (validRoles.length !== requestedRoleIds.length) {\n const foundIds = new Set(validRoles.map((role) => role.id))\n const missingId = requestedRoleIds.find((roleId) => !foundIds.has(roleId))\n return NextResponse.json({ ok: false, error: `Role ${missingId} not found` }, { status: 400 })\n }\n resolvedRoles = validRoles\n }\n\n // Reject a customerEntityId the caller does not own. Without this check a\n // mislinked company FK persists indefinitely and cross-links the user into\n // another org/company's portal context (#2693).\n if (parsed.data.customerEntityId) {\n const owned = await isOwnedCompanyEntity(em, parsed.data.customerEntityId, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n })\n if (!owned) {\n return NextResponse.json({ ok: false, error: 'Company not found' }, { status: 400 })\n }\n }\n\n const user = await customerUserService.createUser(\n parsed.data.email,\n parsed.data.password,\n parsed.data.displayName,\n { tenantId: auth.tenantId!, organizationId: auth.orgId! },\n )\n user.emailVerifiedAt = new Date()\n\n // Persist the user, its company association, and its role links in one\n // transaction so a flush failure on the role loop cannot leave a roleless\n // user committed (privilege gap).\n await em.transactional(async (tx) => {\n tx.persist(user)\n await tx.flush()\n\n if (parsed.data.customerEntityId) {\n await tx.nativeUpdate(CustomerUser, { id: user.id }, { customerEntityId: parsed.data.customerEntityId })\n }\n\n for (const role of resolvedRoles) {\n const userRole = tx.create(CustomerUserRole, {\n user,\n role,\n createdAt: new Date(),\n } as any)\n tx.persist(userRole)\n }\n })\n\n void emitCustomerAccountsEvent('customer_accounts.user.created', {\n id: user.id,\n email: user.email,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n createdBy: auth.sub,\n }).catch(() => undefined)\n\n return NextResponse.json({\n ok: true,\n user: { id: user.id, email: user.email, displayName: user.displayName },\n }, { status: 201 })\n}\n\nconst roleSchema = z.object({ id: z.string().uuid(), name: z.string(), slug: z.string() })\nconst userSchema = z.object({\n id: z.string().uuid(),\n email: z.string(),\n displayName: z.string(),\n emailVerified: z.boolean(),\n isActive: z.boolean(),\n lockedUntil: z.string().datetime().nullable(),\n lastLoginAt: z.string().datetime().nullable(),\n customerEntityId: z.string().uuid().nullable(),\n personEntityId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable().describe('Organization the customer user belongs to'),\n organizationName: z.string().nullable().describe('Resolved organization display name'),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime().nullable(),\n roles: z.array(roleSchema),\n})\n\nconst successSchema = z.object({\n ok: z.literal(true),\n user: z.object({ id: z.string().uuid(), email: z.string(), displayName: z.string() }),\n})\nconst errorSchema = z.object({ ok: z.literal(false), error: z.string() })\n\nasync function findCustomerUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n field?: string,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (field) {\n query = query.where('field', '=', field)\n }\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nconst getMethodDoc: OpenApiMethodDoc = {\n summary: 'List customer users (admin)',\n description: 'Returns a paginated list of customer users with roles. Supports filtering by status, company, role, and search.',\n tags: ['Customer Accounts Admin'],\n query: z.object({\n page: z.number().int().positive().optional(),\n pageSize: z.number().int().positive().max(100).optional(),\n status: z.enum(['active', 'inactive', 'locked']).optional(),\n customerEntityId: z.string().uuid().optional(),\n roleId: z.string().uuid().optional(),\n search: z.string().optional(),\n }),\n responses: [{\n status: 200,\n description: 'Paginated user list',\n schema: z.object({ ok: z.literal(true), items: z.array(userSchema), total: z.number(), totalPages: z.number(), page: z.number() }),\n }],\n errors: [\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n ],\n}\n\nconst postMethodDoc: OpenApiMethodDoc = {\n summary: 'Create customer user (admin)',\n description: 'Creates a new customer user directly. Staff-initiated, bypasses signup flow.',\n tags: ['Customer Accounts Admin'],\n requestBody: { schema: adminCreateUserSchema },\n responses: [{ status: 201, description: 'User created', schema: successSchema }],\n errors: [\n { status: 400, description: 'Validation failed', schema: errorSchema },\n { status: 401, description: 'Not authenticated', schema: errorSchema },\n { status: 403, description: 'Insufficient permissions', schema: errorSchema },\n { status: 409, description: 'Email already exists', schema: errorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Customer user management (admin)',\n methods: {\n GET: getMethodDoc,\n POST: postMethodDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -404,6 +404,31 @@ async function requireCurrentLineUndoState(em, after, rule) {
|
|
|
404
404
|
if (!line || !after.updatedAt || toIso(line.updatedAt) !== after.updatedAt) return refuseLineUndoConflict();
|
|
405
405
|
return { claim, line };
|
|
406
406
|
}
|
|
407
|
+
async function validateLineRestore(em, ctx, state, before) {
|
|
408
|
+
if (!before.orderLineId || before.deletedAt) return;
|
|
409
|
+
const scope = { tenantId: state.claim.tenantId, organizationId: state.claim.organizationId };
|
|
410
|
+
try {
|
|
411
|
+
await validateClaimReferences(ctx, scope, {
|
|
412
|
+
lineOrderRefs: [{ orderLineId: before.orderLineId, orderId: state.claim.orderId ?? null }]
|
|
413
|
+
});
|
|
414
|
+
if (before.lineStatus === "rejected") return;
|
|
415
|
+
await assertPendingClaimQuantitiesWithinSold(em, scope, /* @__PURE__ */ new Map([[before.orderLineId, [{
|
|
416
|
+
id: state.line.id,
|
|
417
|
+
orderLineId: before.orderLineId,
|
|
418
|
+
qtyClaimed: before.qtyClaimed
|
|
419
|
+
}]]]), { currentClaimId: state.claim.id, excludedLineIds: /* @__PURE__ */ new Set([state.line.id]) });
|
|
420
|
+
} catch (error) {
|
|
421
|
+
if (error instanceof CrudHttpError) {
|
|
422
|
+
if (error.body.error === "warranty_claims.errors.qtyExceedsOrdered") {
|
|
423
|
+
return throwUndoRefusal("warranty_claims.errors.qtyExceedsOrdered", "Claimed quantity exceeds the quantity sold on the linked order line.");
|
|
424
|
+
}
|
|
425
|
+
if (error.body.error === "warranty_claims.errors.invalidReference") {
|
|
426
|
+
return throwUndoRefusal("warranty_claims.errors.invalidReference", "The referenced order, order line, return, or replacement order could not be found.");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
407
432
|
async function requireScopedLine(em, lineId, scope) {
|
|
408
433
|
const line = await findOneWithDecryption(
|
|
409
434
|
em,
|
|
@@ -645,6 +670,7 @@ const updateClaimLineCommand = {
|
|
|
645
670
|
async () => {
|
|
646
671
|
state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule);
|
|
647
672
|
await assertLineSnapshotNumberAvailable(em, state.claim, before);
|
|
673
|
+
await validateLineRestore(em, ctx, state, before);
|
|
648
674
|
},
|
|
649
675
|
() => restoreLineFromSnapshot(state.line, before),
|
|
650
676
|
() => recomputeClaimRollups(em, state.claim)
|
|
@@ -704,6 +730,7 @@ const deleteClaimLineCommand = {
|
|
|
704
730
|
async () => {
|
|
705
731
|
state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule);
|
|
706
732
|
await assertLineSnapshotNumberAvailable(em, state.claim, before);
|
|
733
|
+
await validateLineRestore(em, ctx, state, before);
|
|
707
734
|
},
|
|
708
735
|
() => {
|
|
709
736
|
restoreLineFromSnapshot(state.line, before);
|
|
@@ -804,6 +831,7 @@ const receiveClaimLineCommand = {
|
|
|
804
831
|
async () => {
|
|
805
832
|
state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule);
|
|
806
833
|
await assertLineSnapshotNumberAvailable(em, state.claim, before);
|
|
834
|
+
await validateLineRestore(em, ctx, state, before);
|
|
807
835
|
},
|
|
808
836
|
() => restoreLineFromSnapshot(state.line, before),
|
|
809
837
|
() => recomputeClaimRollups(em, state.claim)
|
|
@@ -870,6 +898,7 @@ const releaseClaimLineQuarantineCommand = {
|
|
|
870
898
|
async () => {
|
|
871
899
|
state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule);
|
|
872
900
|
await assertLineSnapshotNumberAvailable(em, state.claim, before);
|
|
901
|
+
await validateLineRestore(em, ctx, state, before);
|
|
873
902
|
},
|
|
874
903
|
() => restoreLineFromSnapshot(state.line, before),
|
|
875
904
|
() => recomputeClaimRollups(em, state.claim)
|
|
@@ -935,6 +964,7 @@ const setClaimLineAssessmentCommand = {
|
|
|
935
964
|
async () => {
|
|
936
965
|
state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule);
|
|
937
966
|
await assertLineSnapshotNumberAvailable(em, state.claim, before);
|
|
967
|
+
await validateLineRestore(em, ctx, state, before);
|
|
938
968
|
},
|
|
939
969
|
() => restoreLineFromSnapshot(state.line, before),
|
|
940
970
|
() => recomputeClaimRollups(em, state.claim)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/warranty_claims/commands/claim-lines.ts"],
|
|
4
|
-
"sourcesContent": ["import { randomUUID } from 'crypto'\nimport { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { registerCommand, type CommandHandler, type CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { E } from '#generated/entities.ids.generated'\nimport { WarrantyClaim, WarrantyClaimLine } from '../data/entities'\nimport {\n claimLineCreateSchema,\n claimLineReceiveSchema,\n claimLineReleaseQuarantineSchema,\n claimLineUpdateSchema,\n type ClaimLineCreateInput,\n type ClaimLineReceiveInput,\n type ClaimLineReleaseQuarantineInput,\n type ClaimLineUpdateInput,\n type WarrantyClaimDisposition,\n type WarrantyClaimLineStatus,\n type WarrantyClaimWarrantyStatus,\n} from '../data/validators'\nimport { emitWarrantyClaimsEvent } from '../events'\nimport {\n assertDispositionAllowedForGrade,\n suggestedDispositionForGrade,\n type ConditionGrade,\n} from '../lib/grading'\nimport { assertDispositionAllowedForType, resolveClaimTypeUiConfig } from '../lib/claimTypeConfig'\nimport { resolveEffectiveWarrantyClaimSettings } from '../lib/settings'\nimport { computeHeaderRollups, lineStatusGuards } from '../lib/stateMachine'\nimport {\n WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n WARRANTY_CLAIM_RESOURCE_KIND,\n appendClaimEvent,\n assertClaimLineNumberAvailable,\n assertUniqueClaimLineNumbers,\n enforceWarrantyClaimOptimisticLock,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n requireScopedClaim,\n throwUndoRefusal,\n type WarrantyClaimScope,\n} from './shared'\nimport { assertPendingClaimQuantitiesWithinSold, validateClaimReferences } from './claims'\n\nconst claimCrudEvents: CrudEventsConfig = {\n module: 'warranty_claims',\n entity: 'claim',\n persistent: true,\n}\n\nconst lineDeleteSchema = z\n .object({\n id: z.string().uuid(),\n claimId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n tenantId: z.string().uuid().optional(),\n })\n .strict()\n\ntype LineDeleteInput = z.infer<typeof lineDeleteSchema>\n\nconst lineSetAssessmentSchema = z\n .object({\n id: z.string().uuid(),\n organizationId: z.string().uuid().optional(),\n tenantId: z.string().uuid().optional(),\n assessmentPayload: z.record(z.string(), z.unknown()),\n updatedAt: z.union([z.string().datetime(), z.date()]).nullable().optional(),\n })\n .strict()\n\ntype LineSetAssessmentInput = z.infer<typeof lineSetAssessmentSchema>\n\ntype ScopeInput = {\n organizationId?: string | null\n tenantId?: string | null\n}\n\ntype LineSnapshot = {\n id: string\n claimId: string\n organizationId: string\n tenantId: string\n lineNo: number\n productId: string | null\n variantId: string | null\n sku: string | null\n productName: string | null\n orderLineId: string | null\n serialNumber: string | null\n lotNumber: string | null\n purchaseDate: string | null\n warrantyMonths: number | null\n warrantyExpiresAt: string | null\n warrantyStatus: WarrantyClaimWarrantyStatus\n faultCode: string | null\n faultDescription: string | null\n qtyClaimed: string\n qtyApproved: string | null\n qtyReceived: string | null\n conditionOnReceipt: string | null\n conditionGrade: string | null\n quarantineStatus: string\n inspectionNotes: string | null\n assessmentPayload: Record<string, unknown> | null\n disposition: WarrantyClaimDisposition | null\n lineStatus: WarrantyClaimLineStatus\n creditAmount: string | null\n restockingFee: string | null\n coreChargeAmount: string | null\n coreCreditAmount: string | null\n vendorClaimLineId: string | null\n vendorName: string | null\n deletedAt: string | null\n updatedAt: string | null\n}\n\ntype LineUndoPayload = {\n before?: LineSnapshot | null\n after?: LineSnapshot | null\n}\n\nconst mutableParentStatuses = new Set([\n 'draft',\n 'submitted',\n 'in_review',\n 'info_requested',\n 'approved',\n 'received',\n 'inspecting',\n])\n\nconst receivingParentStatuses = new Set(['received', 'inspecting'])\n\ntype LineUndoParentRule = {\n allowedParentStatuses: ReadonlySet<string>\n lockedKey: string\n lockedFallback: string\n}\n\ntype LineUndoState = {\n claim: WarrantyClaim\n line: WarrantyClaimLine\n}\n\nconst mutableParentUndoRule: LineUndoParentRule = {\n allowedParentStatuses: mutableParentStatuses,\n lockedKey: 'warranty_claims.errors.lineLocked',\n lockedFallback: 'This line is locked in the current claim status.',\n}\n\nconst receivingParentUndoRule: LineUndoParentRule = {\n allowedParentStatuses: receivingParentStatuses,\n lockedKey: 'warranty_claims.errors.receivingStatusInvalid',\n lockedFallback: 'Receiving details can only be saved when the claim is received or under inspection.',\n}\n\nfunction parseCommandInput<T>(schema: z.ZodType<T>, rawInput: unknown): T {\n const result = schema.safeParse(rawInput ?? {})\n if (!result.success) {\n throw new CrudHttpError(400, { error: '[internal] invalid warranty claim line command input' })\n }\n return result.data\n}\n\nfunction hasOwn(input: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(input, key)\n}\n\nfunction resolveScope(ctx: CommandRuntimeContext, input: ScopeInput): WarrantyClaimScope {\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n if (!tenantId) throw new CrudHttpError(400, { error: '[internal] tenant scope required for warranty claim line command' })\n if (!organizationId) throw new CrudHttpError(400, { error: '[internal] organization scope required for warranty claim line command' })\n ensureTenantScope(ctx, tenantId)\n ensureOrganizationScope(ctx, organizationId)\n return { tenantId, organizationId }\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? null : date.toISOString()\n}\n\nfunction toDate(value: string | null): Date | null {\n if (!value) return null\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction toDateOnlyIso(value: Date | string | null | undefined): string | null {\n const iso = toIso(value)\n return iso ? iso.slice(0, 10) : null\n}\n\nfunction toDateOnly(value: string | null): Date | null {\n if (!value) return null\n const date = new Date(`${value}T00:00:00.000Z`)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction amountString(value: number | string | null | undefined, fallback = '0'): string | null {\n if (value === null) return null\n if (value === undefined) return fallback\n const parsed = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(parsed)) return fallback\n return String(parsed)\n}\n\nfunction nullableAmountString(value: number | string | null | undefined): string | null {\n if (value === undefined || value === null) return null\n return amountString(value, '0')\n}\n\nfunction numberValue(value: string | number | null | undefined): number {\n if (value === null || value === undefined) return 0\n const parsed = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nfunction addMonths(date: Date, months: number): Date {\n const target = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, 1))\n const lastDay = new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth() + 1, 0)).getUTCDate()\n const copy = new Date(date.getTime())\n copy.setUTCDate(1)\n copy.setUTCFullYear(target.getUTCFullYear(), target.getUTCMonth(), Math.min(date.getUTCDate(), lastDay))\n return copy\n}\n\nfunction computeWarrantyDates(\n purchaseDate: Date | null | undefined,\n warrantyMonths: number | null | undefined,\n): { warrantyExpiresAt: Date | null; warrantyStatus: WarrantyClaimWarrantyStatus } {\n if (!purchaseDate || warrantyMonths === null || warrantyMonths === undefined) {\n return { warrantyExpiresAt: null, warrantyStatus: 'unknown' }\n }\n const warrantyExpiresAt = addMonths(purchaseDate, warrantyMonths)\n const warrantyStatus = warrantyExpiresAt.getTime() >= Date.now() ? 'in_warranty' : 'out_of_warranty'\n return { warrantyExpiresAt, warrantyStatus }\n}\n\nfunction claimIdOf(line: WarrantyClaimLine): string {\n const claim = line.claim\n if (typeof claim === 'object' && claim && 'id' in claim && typeof claim.id === 'string') return claim.id\n return String(claim)\n}\n\nfunction claimOf(line: WarrantyClaimLine): WarrantyClaim | null {\n const claim = line.claim\n if (typeof claim === 'object' && claim && 'id' in claim && typeof claim.id === 'string') return claim as WarrantyClaim\n return null\n}\n\nfunction sameDateValue(left: Date | null | undefined, right: Date | null | undefined): boolean {\n const leftTime = left instanceof Date ? left.getTime() : null\n const rightTime = right instanceof Date ? right.getTime() : null\n return leftTime === rightTime\n}\n\nfunction assertParentMutable(claim: WarrantyClaim): void {\n if (!mutableParentStatuses.has(claim.status)) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n}\n\nfunction assertReceivingStatus(claim: WarrantyClaim): void {\n if (claim.status !== 'received' && claim.status !== 'inspecting') {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.receivingStatusInvalid' })\n }\n}\n\nfunction toConditionGrade(value: string | null | undefined): ConditionGrade | null {\n if (value === 'A' || value === 'B' || value === 'C' || value === 'D') return value\n return null\n}\n\nfunction assertLineQuantities(line: WarrantyClaimLine): void {\n const qtyClaimed = numberValue(line.qtyClaimed)\n const qtyApproved = line.qtyApproved === null || line.qtyApproved === undefined ? null : numberValue(line.qtyApproved)\n const qtyReceived = line.qtyReceived === null || line.qtyReceived === undefined ? null : numberValue(line.qtyReceived)\n if (qtyClaimed < 0 || (qtyApproved !== null && qtyApproved < 0) || (qtyReceived !== null && qtyReceived < 0)) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n if (qtyApproved !== null && qtyApproved > qtyClaimed) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n if (qtyReceived !== null && qtyReceived > qtyClaimed) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n}\n\nfunction assertLineStatusMove(from: WarrantyClaimLineStatus, to: WarrantyClaimLineStatus): void {\n if (from === to) return\n if (lineStatusGuards[from].includes(to)) return\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.invalidTransition' })\n}\n\nfunction buildLineCreateData(\n claim: WarrantyClaim,\n input: ClaimLineCreateInput,\n lineNo: number,\n): Partial<WarrantyClaimLine> & {\n id: string\n claim: WarrantyClaim\n organizationId: string\n tenantId: string\n lineNo: number\n qtyClaimed: string\n lineStatus: WarrantyClaimLineStatus\n warrantyStatus: WarrantyClaimWarrantyStatus\n createdAt: Date\n updatedAt: Date\n} {\n const purchaseDate = input.purchaseDate ?? null\n const warrantyMonths = input.warrantyMonths ?? null\n const computedWarranty = computeWarrantyDates(purchaseDate, warrantyMonths)\n return {\n id: randomUUID(),\n claim,\n organizationId: claim.organizationId,\n tenantId: claim.tenantId,\n lineNo,\n productId: input.productId ?? null,\n variantId: input.variantId ?? null,\n sku: input.sku ?? null,\n productName: input.productName ?? null,\n orderLineId: input.orderLineId ?? null,\n serialNumber: input.serialNumber ?? null,\n lotNumber: input.lotNumber ?? null,\n purchaseDate,\n warrantyMonths,\n warrantyExpiresAt: input.warrantyExpiresAt ?? computedWarranty.warrantyExpiresAt,\n warrantyStatus: input.warrantyStatus ?? computedWarranty.warrantyStatus,\n faultCode: input.faultCode ?? null,\n faultDescription: input.faultDescription ?? null,\n qtyClaimed: amountString(input.qtyClaimed, '1') ?? '1',\n qtyApproved: nullableAmountString(input.qtyApproved),\n qtyReceived: nullableAmountString(input.qtyReceived),\n conditionOnReceipt: input.conditionOnReceipt ?? null,\n inspectionNotes: input.inspectionNotes ?? null,\n disposition: input.disposition ?? null,\n vendorName: input.vendorName ?? null,\n lineStatus: 'pending',\n creditAmount: nullableAmountString(input.creditAmount),\n restockingFee: nullableAmountString(input.restockingFee),\n coreChargeAmount: nullableAmountString(input.coreChargeAmount),\n coreCreditAmount: nullableAmountString(input.coreCreditAmount),\n createdAt: new Date(),\n updatedAt: new Date(),\n }\n}\n\nfunction applyLineUpdate(line: WarrantyClaimLine, input: ClaimLineUpdateInput): void {\n const purchaseDateChanged = hasOwn(input, 'purchaseDate') && !sameDateValue(line.purchaseDate ?? null, input.purchaseDate ?? null)\n const warrantyMonthsChanged = hasOwn(input, 'warrantyMonths') && (line.warrantyMonths ?? null) !== (input.warrantyMonths ?? null)\n const hasExplicitWarranty = hasOwn(input, 'warrantyExpiresAt') || hasOwn(input, 'warrantyStatus')\n if (hasOwn(input, 'lineNo') && input.lineNo !== undefined) line.lineNo = input.lineNo\n if (hasOwn(input, 'productId')) line.productId = input.productId ?? null\n if (hasOwn(input, 'variantId')) line.variantId = input.variantId ?? null\n if (hasOwn(input, 'sku')) line.sku = input.sku ?? null\n if (hasOwn(input, 'productName')) line.productName = input.productName ?? null\n if (hasOwn(input, 'orderLineId')) line.orderLineId = input.orderLineId ?? null\n if (hasOwn(input, 'serialNumber')) line.serialNumber = input.serialNumber ?? null\n if (hasOwn(input, 'lotNumber')) line.lotNumber = input.lotNumber ?? null\n if (hasOwn(input, 'purchaseDate')) line.purchaseDate = input.purchaseDate ?? null\n if (hasOwn(input, 'warrantyMonths')) line.warrantyMonths = input.warrantyMonths ?? null\n if (hasOwn(input, 'warrantyExpiresAt')) line.warrantyExpiresAt = input.warrantyExpiresAt ?? null\n if (hasOwn(input, 'warrantyStatus') && input.warrantyStatus) line.warrantyStatus = input.warrantyStatus\n if (hasOwn(input, 'faultCode')) line.faultCode = input.faultCode ?? null\n if (hasOwn(input, 'faultDescription')) line.faultDescription = input.faultDescription ?? null\n if (hasOwn(input, 'qtyClaimed') && input.qtyClaimed !== undefined) line.qtyClaimed = amountString(input.qtyClaimed, '1') ?? '1'\n if (hasOwn(input, 'qtyApproved')) line.qtyApproved = nullableAmountString(input.qtyApproved)\n if (hasOwn(input, 'qtyReceived')) line.qtyReceived = nullableAmountString(input.qtyReceived)\n if (hasOwn(input, 'conditionOnReceipt')) line.conditionOnReceipt = input.conditionOnReceipt ?? null\n if (hasOwn(input, 'inspectionNotes')) line.inspectionNotes = input.inspectionNotes ?? null\n if (hasOwn(input, 'disposition')) line.disposition = input.disposition ?? null\n if (hasOwn(input, 'vendorName')) line.vendorName = input.vendorName ?? null\n if (hasOwn(input, 'lineStatus') && input.lineStatus) {\n assertLineStatusMove(line.lineStatus, input.lineStatus)\n line.lineStatus = input.lineStatus\n }\n if (hasOwn(input, 'creditAmount')) line.creditAmount = nullableAmountString(input.creditAmount)\n if (hasOwn(input, 'restockingFee')) line.restockingFee = nullableAmountString(input.restockingFee)\n if (hasOwn(input, 'coreChargeAmount')) line.coreChargeAmount = nullableAmountString(input.coreChargeAmount)\n if (hasOwn(input, 'coreCreditAmount')) line.coreCreditAmount = nullableAmountString(input.coreCreditAmount)\n if ((purchaseDateChanged || warrantyMonthsChanged) && !hasExplicitWarranty) {\n const computedWarranty = computeWarrantyDates(line.purchaseDate ?? null, line.warrantyMonths ?? null)\n line.warrantyExpiresAt = computedWarranty.warrantyExpiresAt\n line.warrantyStatus = computedWarranty.warrantyStatus\n }\n line.updatedAt = new Date()\n}\n\nfunction snapshotLine(line: WarrantyClaimLine): LineSnapshot {\n return {\n id: line.id,\n claimId: claimIdOf(line),\n organizationId: line.organizationId,\n tenantId: line.tenantId,\n lineNo: line.lineNo,\n productId: line.productId ?? null,\n variantId: line.variantId ?? null,\n sku: line.sku ?? null,\n productName: line.productName ?? null,\n orderLineId: line.orderLineId ?? null,\n serialNumber: line.serialNumber ?? null,\n lotNumber: line.lotNumber ?? null,\n purchaseDate: toDateOnlyIso(line.purchaseDate),\n warrantyMonths: line.warrantyMonths ?? null,\n warrantyExpiresAt: toDateOnlyIso(line.warrantyExpiresAt),\n warrantyStatus: line.warrantyStatus,\n faultCode: line.faultCode ?? null,\n faultDescription: line.faultDescription ?? null,\n qtyClaimed: line.qtyClaimed,\n qtyApproved: line.qtyApproved ?? null,\n qtyReceived: line.qtyReceived ?? null,\n conditionOnReceipt: line.conditionOnReceipt ?? null,\n conditionGrade: line.conditionGrade ?? null,\n quarantineStatus: line.quarantineStatus,\n inspectionNotes: line.inspectionNotes ?? null,\n assessmentPayload: line.assessmentPayload ?? null,\n disposition: line.disposition ?? null,\n lineStatus: line.lineStatus,\n creditAmount: line.creditAmount ?? null,\n restockingFee: line.restockingFee ?? null,\n coreChargeAmount: line.coreChargeAmount ?? null,\n coreCreditAmount: line.coreCreditAmount ?? null,\n vendorClaimLineId: line.vendorClaimLineId ?? null,\n vendorName: line.vendorName ?? null,\n deletedAt: toIso(line.deletedAt),\n updatedAt: toIso(line.updatedAt),\n }\n}\n\nasync function loadLineSnapshot(\n em: EntityManager,\n lineId: string,\n scope: WarrantyClaimScope,\n): Promise<LineSnapshot | null> {\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n { id: lineId, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { populate: ['claim'] },\n scope,\n )\n return line ? snapshotLine(line) : null\n}\n\nasync function assertLineSnapshotNumberAvailable(em: EntityManager, claim: WarrantyClaim, snapshot: LineSnapshot): Promise<void> {\n if (!snapshot.deletedAt) await assertClaimLineNumberAvailable(em, claim, snapshot.lineNo, snapshot.id, true)\n else await requireScopedClaim(em, claim.id, { tenantId: claim.tenantId, organizationId: claim.organizationId }, { lockMode: LockMode.PESSIMISTIC_WRITE })\n}\n\nfunction restoreLineFromSnapshot(line: WarrantyClaimLine, snapshot: LineSnapshot): void {\n line.lineNo = snapshot.lineNo\n line.productId = snapshot.productId\n line.variantId = snapshot.variantId\n line.sku = snapshot.sku\n line.productName = snapshot.productName\n line.orderLineId = snapshot.orderLineId\n line.serialNumber = snapshot.serialNumber\n line.lotNumber = snapshot.lotNumber\n line.purchaseDate = toDateOnly(snapshot.purchaseDate)\n line.warrantyMonths = snapshot.warrantyMonths\n line.warrantyExpiresAt = toDateOnly(snapshot.warrantyExpiresAt)\n line.warrantyStatus = snapshot.warrantyStatus\n line.faultCode = snapshot.faultCode\n line.faultDescription = snapshot.faultDescription\n line.qtyClaimed = snapshot.qtyClaimed\n line.qtyApproved = snapshot.qtyApproved\n line.qtyReceived = snapshot.qtyReceived\n line.conditionOnReceipt = snapshot.conditionOnReceipt\n line.conditionGrade = snapshot.conditionGrade\n line.quarantineStatus = snapshot.quarantineStatus\n line.inspectionNotes = snapshot.inspectionNotes\n line.assessmentPayload = snapshot.assessmentPayload\n line.disposition = snapshot.disposition\n line.lineStatus = snapshot.lineStatus\n line.creditAmount = snapshot.creditAmount\n line.restockingFee = snapshot.restockingFee\n line.coreChargeAmount = snapshot.coreChargeAmount\n line.coreCreditAmount = snapshot.coreCreditAmount\n line.vendorClaimLineId = snapshot.vendorClaimLineId\n line.vendorName = snapshot.vendorName\n line.deletedAt = toDate(snapshot.deletedAt)\n line.updatedAt = new Date()\n}\n\nasync function recomputeClaimRollups(em: EntityManager, claim: WarrantyClaim): Promise<void> {\n const lines = await findWithDecryption(\n em,\n WarrantyClaimLine,\n {\n claim: claim.id,\n tenantId: claim.tenantId,\n organizationId: claim.organizationId,\n deletedAt: null,\n },\n {},\n { tenantId: claim.tenantId, organizationId: claim.organizationId },\n )\n const totals = computeHeaderRollups(lines, { claimType: claim.claimType })\n claim.totalClaimedAmount = String(totals.totalClaimedAmount)\n claim.totalApprovedAmount = String(totals.totalApprovedAmount)\n claim.updatedAt = new Date()\n}\n\nfunction refuseLineUndoConflict(): Promise<never> {\n return throwUndoRefusal('warranty_claims.errors.conflict', 'This claim changed while you were editing it.')\n}\n\nasync function requireCurrentLineUndoState(\n em: EntityManager,\n after: LineSnapshot | null | undefined,\n rule: LineUndoParentRule,\n): Promise<LineUndoState> {\n if (!after) return refuseLineUndoConflict()\n const scope: WarrantyClaimScope = { tenantId: after.tenantId, organizationId: after.organizationId }\n const claim = await findOneWithDecryption(\n em,\n WarrantyClaim,\n { id: after.claimId, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scope,\n )\n if (!claim) return refuseLineUndoConflict()\n if (!rule.allowedParentStatuses.has(claim.status)) return throwUndoRefusal(rule.lockedKey, rule.lockedFallback)\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n { id: after.id, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scope,\n )\n if (!line || !after.updatedAt || toIso(line.updatedAt) !== after.updatedAt) return refuseLineUndoConflict()\n return { claim, line }\n}\n\nasync function requireScopedLine(\n em: EntityManager,\n lineId: string,\n scope: WarrantyClaimScope,\n): Promise<WarrantyClaimLine> {\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n { id: lineId, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },\n { populate: ['claim'] },\n scope,\n )\n if (!line) throw new CrudHttpError(404, { error: 'warranty_claims.errors.notFound' })\n return line\n}\n\nasync function emitClaimUpdated(ctx: CommandRuntimeContext, claim: WarrantyClaim): Promise<void> {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: claim,\n identifiers: { id: claim.id, organizationId: claim.organizationId, tenantId: claim.tenantId },\n indexer: { entityType: E.warranty_claims.warranty_claim },\n events: claimCrudEvents,\n })\n await invalidateCrudCache(\n ctx.container,\n 'warranty_claims.claim',\n { id: claim.id, organizationId: claim.organizationId, tenantId: claim.tenantId },\n ctx.auth?.tenantId ?? null,\n 'warranty_claims.claim_line.rollup',\n )\n}\n\nasync function emitLineCrud(\n ctx: CommandRuntimeContext,\n action: 'created' | 'updated' | 'deleted',\n line: WarrantyClaimLine,\n): Promise<void> {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action,\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.warranty_claims.warranty_claim_line },\n })\n await invalidateCrudCache(\n ctx.container,\n 'warranty_claims.claim_line',\n { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n ctx.auth?.tenantId ?? null,\n `warranty_claims.claim_line.${action}`,\n )\n}\n\nasync function emitLineUndoCrud(\n ctx: CommandRuntimeContext,\n action: 'created' | 'updated' | 'deleted',\n line: WarrantyClaimLine,\n): Promise<void> {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action,\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.warranty_claims.warranty_claim_line },\n })\n await invalidateCrudCache(\n ctx.container,\n 'warranty_claims.claim_line',\n { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n ctx.auth?.tenantId ?? null,\n `warranty_claims.claim_line.undo.${action}`,\n )\n}\n\nasync function buildLineLog(\n actionLabelKey: string,\n actionLabelFallback: string,\n resourceId: string,\n snapshots: { before?: unknown; after?: unknown },\n): Promise<{\n actionLabel: string\n resourceKind: string\n resourceId: string\n parentResourceKind: string | null\n parentResourceId: string | null\n tenantId: string | null\n organizationId: string | null\n snapshotBefore: unknown\n snapshotAfter: unknown\n payload: { undo: LineUndoPayload }\n}> {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as LineSnapshot | null | undefined\n const before = snapshots.before as LineSnapshot | null | undefined\n return {\n actionLabel: translate(actionLabelKey, actionLabelFallback),\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId,\n parentResourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n parentResourceId: after?.claimId ?? before?.claimId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: { undo: { before: before ?? null, after: after ?? null } },\n }\n}\n\nconst createClaimLineCommand: CommandHandler<ClaimLineCreateInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.create',\n isUndoable: true,\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineCreateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const claim = await requireScopedClaim(em, input.claimId, scope)\n assertDispositionAllowedForType(claim.claimType, input.disposition ?? null)\n await enforceWarrantyClaimOptimisticLock(ctx, claim)\n assertParentMutable(claim)\n let line!: WarrantyClaimLine\n let nextLineNo = input.lineNo ?? 1\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true })\n assertParentMutable(claim)\n if (input.orderLineId) {\n await validateClaimReferences(ctx, scope, {\n lineOrderRefs: [{ orderLineId: input.orderLineId, orderId: claim.orderId ?? null }],\n })\n }\n const existingLines = await findWithDecryption(\n em,\n WarrantyClaimLine,\n {\n claim: claim.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n {},\n scope,\n )\n if (input.orderLineId) {\n await assertPendingClaimQuantitiesWithinSold(em, scope, new Map([[input.orderLineId, [{\n orderLineId: input.orderLineId,\n qtyClaimed: amountString(input.qtyClaimed, '1') ?? '1',\n }]]]), { currentClaimId: claim.id })\n }\n nextLineNo = input.lineNo ?? existingLines.reduce((max, entry) => Math.max(max, entry.lineNo), 0) + 1\n await assertUniqueClaimLineNumbers([nextLineNo, ...existingLines.filter((line) => line.lineNo === nextLineNo).map((line) => line.lineNo)])\n },\n () => {\n line = em.create(WarrantyClaimLine, buildLineCreateData(claim, input, nextLineNo))\n assertLineQuantities(line)\n em.persist(line)\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.create' })\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'created', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineCreateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.create', 'Add claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n },\n () => {\n state.line.deletedAt = new Date()\n state.line.updatedAt = new Date()\n },\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.create.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'deleted', state.line)\n },\n}\n\nconst updateClaimLineCommand: CommandHandler<ClaimLineUpdateInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.update',\n isUndoable: true,\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(claimLineUpdateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineUpdateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = claimOf(line) ?? await requireScopedClaim(em, input.claimId ?? claimIdOf(line), scope)\n if (input.claimId && input.claimId !== claim.id) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.notFound' })\n }\n assertParentMutable(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND)\n const effectiveOrderLineId = hasOwn(input, 'orderLineId') ? (input.orderLineId ?? null) : (line.orderLineId ?? null)\n if (hasOwn(input, 'disposition')) {\n assertDispositionAllowedForType(claim.claimType, input.disposition ?? null)\n assertDispositionAllowedForGrade(toConditionGrade(line.conditionGrade), input.disposition ?? null)\n }\n const guardsClaimedQty = hasOwn(input, 'qtyClaimed') || hasOwn(input, 'orderLineId')\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true })\n assertParentMutable(claim)\n if (input.lineNo !== undefined) await assertClaimLineNumberAvailable(em, claim, input.lineNo, line.id)\n if (effectiveOrderLineId && (claim.orderId || effectiveOrderLineId !== (line.orderLineId ?? null))) {\n await validateClaimReferences(ctx, scope, {\n lineOrderRefs: [{\n orderLineId: effectiveOrderLineId,\n orderId: claim.orderId ?? null,\n allowMissing: effectiveOrderLineId === (line.orderLineId ?? null),\n }],\n })\n }\n if (!guardsClaimedQty) return\n const orderLineId = hasOwn(input, 'orderLineId') ? (input.orderLineId ?? null) : (line.orderLineId ?? null)\n if (orderLineId) {\n await assertPendingClaimQuantitiesWithinSold(\n em,\n scope,\n new Map([[orderLineId, [{\n id: line.id,\n orderLineId,\n qtyClaimed: hasOwn(input, 'qtyClaimed')\n ? (amountString(input.qtyClaimed, '1') ?? '1')\n : line.qtyClaimed,\n }]]]),\n { currentClaimId: claim.id, excludedLineIds: new Set([line.id]) },\n )\n }\n },\n () => {\n applyLineUpdate(line, input)\n assertLineQuantities(line)\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.update' })\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineUpdateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.update', 'Update claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.update.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nconst deleteClaimLineCommand: CommandHandler<LineDeleteInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.delete',\n isUndoable: true,\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(lineDeleteSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(lineDeleteSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = claimOf(line) ?? await requireScopedClaim(em, input.claimId ?? claimIdOf(line), scope)\n if (input.claimId && input.claimId !== claim.id) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.notFound' })\n }\n assertParentMutable(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND)\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.deletedAt = new Date()\n line.updatedAt = new Date()\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.delete' })\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'deleted', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(lineDeleteSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.delete', 'Delete claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n },\n () => {\n restoreLineFromSnapshot(state.line, before)\n state.line.deletedAt = null\n },\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.delete.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'created', state.line)\n },\n}\n\nconst receiveClaimLineCommand: CommandHandler<ClaimLineReceiveInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.receive',\n isUndoable: true,\n redo: ({ input, ctx }) => receiveClaimLineCommand.execute({ ...input, updatedAt: undefined }, ctx),\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(claimLineReceiveSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineReceiveSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = await requireScopedClaim(em, claimIdOf(line), scope)\n assertReceivingStatus(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND, input.updatedAt ?? undefined)\n\n const grade: ConditionGrade = input.conditionGrade\n const effectiveSettings = await resolveEffectiveWarrantyClaimSettings(em, scope)\n const shouldQuarantine = Boolean(effectiveSettings.quarantineGrades?.includes(grade))\n\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.conditionGrade = grade\n if (input.inspectionNotes !== undefined) line.inspectionNotes = input.inspectionNotes ?? null\n\n const suggestedDisposition = suggestedDispositionForGrade(grade)\n if (!line.disposition && suggestedDisposition && resolveClaimTypeUiConfig(claim.claimType).allowedDispositions.includes(suggestedDisposition)) {\n assertDispositionAllowedForGrade(grade, suggestedDisposition)\n if (suggestedDisposition === 'restock' || suggestedDisposition === 'repair' || suggestedDisposition === 'scrap') {\n line.disposition = suggestedDisposition\n }\n }\n assertDispositionAllowedForGrade(grade, line.disposition ?? null)\n\n if (shouldQuarantine) line.quarantineStatus = 'held'\n line.updatedAt = new Date()\n appendClaimEvent(em, claim, 'system', {\n visibility: 'internal',\n payload: {\n action: 'line_received',\n lineId: line.id,\n conditionGrade: grade,\n disposition: line.disposition ?? null,\n quarantineStatus: line.quarantineStatus,\n quarantineHeld: shouldQuarantine,\n },\n actorUserId: ctx.auth?.sub ?? null,\n })\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.receive' })\n\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n if (shouldQuarantine) {\n await emitWarrantyClaimsEvent('warranty_claims.claim_line.quarantined', {\n id: line.id,\n claimId: claim.id,\n lineId: line.id,\n grade,\n scope,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }, {\n persistent: true,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n }\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineReceiveSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.receive', 'Receive claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.receive.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nconst releaseClaimLineQuarantineCommand: CommandHandler<ClaimLineReleaseQuarantineInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.release_quarantine',\n isUndoable: true,\n redo: ({ input, ctx }) => releaseClaimLineQuarantineCommand.execute({ ...input, updatedAt: undefined }, ctx),\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(claimLineReleaseQuarantineSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineReleaseQuarantineSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = await requireScopedClaim(em, claimIdOf(line), scope)\n assertReceivingStatus(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND, input.updatedAt ?? undefined)\n\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.quarantineStatus = 'released'\n line.updatedAt = new Date()\n appendClaimEvent(em, claim, 'system', {\n visibility: 'internal',\n payload: {\n action: 'line_quarantine_released',\n lineId: line.id,\n quarantineStatus: line.quarantineStatus,\n },\n actorUserId: ctx.auth?.sub ?? null,\n })\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.release_quarantine' })\n\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineReleaseQuarantineSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.release_quarantine', 'Release line from quarantine', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.release_quarantine.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nconst setClaimLineAssessmentCommand: CommandHandler<LineSetAssessmentInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.set_assessment',\n isUndoable: true,\n redo: ({ input, ctx }) => setClaimLineAssessmentCommand.execute({ ...input, updatedAt: undefined }, ctx),\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(lineSetAssessmentSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(lineSetAssessmentSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = claimOf(line) ?? await requireScopedClaim(em, claimIdOf(line), scope)\n assertParentMutable(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND, input.updatedAt ?? undefined)\n\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.assessmentPayload = input.assessmentPayload\n line.updatedAt = new Date()\n claim.updatedAt = new Date()\n appendClaimEvent(em, claim, 'system', {\n visibility: 'internal',\n payload: {\n action: 'ai_assessment_recorded',\n lineId: line.id,\n },\n actorUserId: ctx.auth?.sub ?? null,\n })\n },\n ], { transaction: true, label: 'warranty_claims.claim_line.set_assessment' })\n\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(lineSetAssessmentSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.set_assessment', 'Record line assessment', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.set_assessment.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nregisterCommand(createClaimLineCommand)\nregisterCommand(updateClaimLineCommand)\nregisterCommand(deleteClaimLineCommand)\nregisterCommand(receiveClaimLineCommand)\nregisterCommand(releaseClaimLineQuarantineCommand)\nregisterCommand(setClaimLineAssessmentCommand)\n\nexport const claimLineCommands = [\n createClaimLineCommand,\n updateClaimLineCommand,\n deleteClaimLineCommand,\n receiveClaimLineCommand,\n releaseClaimLineQuarantineCommand,\n setClaimLineAssessmentCommand,\n]\n\nexport {\n createClaimLineCommand,\n updateClaimLineCommand,\n deleteClaimLineCommand,\n receiveClaimLineCommand,\n releaseClaimLineQuarantineCommand,\n setClaimLineAssessmentCommand,\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AAEzB,SAAS,SAAS;AAClB,SAAS,uBAAwE;AACjF,SAAS,uBAAuB;AAChC,SAAS,qBAAqB,+BAA+B;AAG7D,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,SAAS;AAClB,SAAS,eAAe,yBAAyB;AACjD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAQK;AACP,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,iCAAiC,gCAAgC;AAC1E,SAAS,6CAA6C;AACtD,SAAS,sBAAsB,wBAAwB;AACvD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,wCAAwC,+BAA+B;AAEhF,MAAM,kBAAoC;AAAA,EACxC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AACd;AAEA,MAAM,mBAAmB,EACtB,OAAO;AAAA,EACN,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACvC,CAAC,EACA,OAAO;AAIV,MAAM,0BAA0B,EAC7B,OAAO;AAAA,EACN,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACrC,mBAAmB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACnD,WAAW,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,GAAG,EAAE,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS;AAC5E,CAAC,EACA,OAAO;AAqDV,MAAM,wBAAwB,oBAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,MAAM,0BAA0B,oBAAI,IAAI,CAAC,YAAY,YAAY,CAAC;AAalE,MAAM,wBAA4C;AAAA,EAChD,uBAAuB;AAAA,EACvB,WAAW;AAAA,EACX,gBAAgB;AAClB;AAEA,MAAM,0BAA8C;AAAA,EAClD,uBAAuB;AAAA,EACvB,WAAW;AAAA,EACX,gBAAgB;AAClB;AAEA,SAAS,kBAAqB,QAAsB,UAAsB;AACxE,QAAM,SAAS,OAAO,UAAU,YAAY,CAAC,CAAC;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,uDAAuD,CAAC;AAAA,EAChG;AACA,SAAO,OAAO;AAChB;AAEA,SAAS,OAAO,OAAe,KAAsB;AACnD,SAAO,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG;AACxD;AAEA,SAAS,aAAa,KAA4B,OAAuC;AACvF,QAAM,WAAW,MAAM,YAAY,IAAI,MAAM,YAAY;AACzD,QAAM,iBAAiB,MAAM,kBAAkB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAChG,MAAI,CAAC,SAAU,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,mEAAmE,CAAC;AACzH,MAAI,CAAC,eAAgB,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,yEAAyE,CAAC;AACrI,oBAAkB,KAAK,QAAQ;AAC/B,0BAAwB,KAAK,cAAc;AAC3C,SAAO,EAAE,UAAU,eAAe;AACpC;AAEA,SAAS,MAAM,OAAwD;AACrE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,iBAAiB,OAAO,QAAQ,IAAI,KAAK,KAAK;AAC3D,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,YAAY;AAChE;AAEA,SAAS,OAAO,OAAmC;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAEA,SAAS,cAAc,OAAwD;AAC7E,QAAM,MAAM,MAAM,KAAK;AACvB,SAAO,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI;AAClC;AAEA,SAAS,WAAW,OAAmC;AACrD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,oBAAI,KAAK,GAAG,KAAK,gBAAgB;AAC9C,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO;AAC/C;AAEA,SAAS,aAAa,OAA2C,WAAW,KAAoB;AAC9F,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC/D,MAAI,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACrC,SAAO,OAAO,MAAM;AACtB;AAEA,SAAS,qBAAqB,OAA0D;AACtF,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,SAAO,aAAa,OAAO,GAAG;AAChC;AAEA,SAAS,YAAY,OAAmD;AACtE,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC/D,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAEA,SAAS,UAAU,MAAY,QAAsB;AACnD,QAAM,SAAS,IAAI,KAAK,KAAK,IAAI,KAAK,eAAe,GAAG,KAAK,YAAY,IAAI,QAAQ,CAAC,CAAC;AACvF,QAAM,UAAU,IAAI,KAAK,KAAK,IAAI,OAAO,eAAe,GAAG,OAAO,YAAY,IAAI,GAAG,CAAC,CAAC,EAAE,WAAW;AACpG,QAAM,OAAO,IAAI,KAAK,KAAK,QAAQ,CAAC;AACpC,OAAK,WAAW,CAAC;AACjB,OAAK,eAAe,OAAO,eAAe,GAAG,OAAO,YAAY,GAAG,KAAK,IAAI,KAAK,WAAW,GAAG,OAAO,CAAC;AACvG,SAAO;AACT;AAEA,SAAS,qBACP,cACA,gBACiF;AACjF,MAAI,CAAC,gBAAgB,mBAAmB,QAAQ,mBAAmB,QAAW;AAC5E,WAAO,EAAE,mBAAmB,MAAM,gBAAgB,UAAU;AAAA,EAC9D;AACA,QAAM,oBAAoB,UAAU,cAAc,cAAc;AAChE,QAAM,iBAAiB,kBAAkB,QAAQ,KAAK,KAAK,IAAI,IAAI,gBAAgB;AACnF,SAAO,EAAE,mBAAmB,eAAe;AAC7C;AAEA,SAAS,UAAU,MAAiC;AAClD,QAAM,QAAQ,KAAK;AACnB,MAAI,OAAO,UAAU,YAAY,SAAS,QAAQ,SAAS,OAAO,MAAM,OAAO,SAAU,QAAO,MAAM;AACtG,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,QAAQ,MAA+C;AAC9D,QAAM,QAAQ,KAAK;AACnB,MAAI,OAAO,UAAU,YAAY,SAAS,QAAQ,SAAS,OAAO,MAAM,OAAO,SAAU,QAAO;AAChG,SAAO;AACT;AAEA,SAAS,cAAc,MAA+B,OAAyC;AAC7F,QAAM,WAAW,gBAAgB,OAAO,KAAK,QAAQ,IAAI;AACzD,QAAM,YAAY,iBAAiB,OAAO,MAAM,QAAQ,IAAI;AAC5D,SAAO,aAAa;AACtB;AAEA,SAAS,oBAAoB,OAA4B;AACvD,MAAI,CAAC,sBAAsB,IAAI,MAAM,MAAM,GAAG;AAC5C,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,oCAAoC,CAAC;AAAA,EAC7E;AACF;AAEA,SAAS,sBAAsB,OAA4B;AACzD,MAAI,MAAM,WAAW,cAAc,MAAM,WAAW,cAAc;AAChE,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,gDAAgD,CAAC;AAAA,EACzF;AACF;AAEA,SAAS,iBAAiB,OAAyD;AACjF,MAAI,UAAU,OAAO,UAAU,OAAO,UAAU,OAAO,UAAU,IAAK,QAAO;AAC7E,SAAO;AACT;AAEA,SAAS,qBAAqB,MAA+B;AAC3D,QAAM,aAAa,YAAY,KAAK,UAAU;AAC9C,QAAM,cAAc,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,SAAY,OAAO,YAAY,KAAK,WAAW;AACrH,QAAM,cAAc,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,SAAY,OAAO,YAAY,KAAK,WAAW;AACrH,MAAI,aAAa,KAAM,gBAAgB,QAAQ,cAAc,KAAO,gBAAgB,QAAQ,cAAc,GAAI;AAC5G,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,oCAAoC,CAAC;AAAA,EAC7E;AACA,MAAI,gBAAgB,QAAQ,cAAc,YAAY;AACpD,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,oCAAoC,CAAC;AAAA,EAC7E;AACA,MAAI,gBAAgB,QAAQ,cAAc,YAAY;AACpD,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,oCAAoC,CAAC;AAAA,EAC7E;AACF;AAEA,SAAS,qBAAqB,MAA+B,IAAmC;AAC9F,MAAI,SAAS,GAAI;AACjB,MAAI,iBAAiB,IAAI,EAAE,SAAS,EAAE,EAAG;AACzC,QAAM,IAAI,cAAc,KAAK,EAAE,OAAO,2CAA2C,CAAC;AACpF;AAEA,SAAS,oBACP,OACA,OACA,QAYA;AACA,QAAM,eAAe,MAAM,gBAAgB;AAC3C,QAAM,iBAAiB,MAAM,kBAAkB;AAC/C,QAAM,mBAAmB,qBAAqB,cAAc,cAAc;AAC1E,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,UAAU,MAAM;AAAA,IAChB;AAAA,IACA,WAAW,MAAM,aAAa;AAAA,IAC9B,WAAW,MAAM,aAAa;AAAA,IAC9B,KAAK,MAAM,OAAO;AAAA,IAClB,aAAa,MAAM,eAAe;AAAA,IAClC,aAAa,MAAM,eAAe;AAAA,IAClC,cAAc,MAAM,gBAAgB;AAAA,IACpC,WAAW,MAAM,aAAa;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,mBAAmB,MAAM,qBAAqB,iBAAiB;AAAA,IAC/D,gBAAgB,MAAM,kBAAkB,iBAAiB;AAAA,IACzD,WAAW,MAAM,aAAa;AAAA,IAC9B,kBAAkB,MAAM,oBAAoB;AAAA,IAC5C,YAAY,aAAa,MAAM,YAAY,GAAG,KAAK;AAAA,IACnD,aAAa,qBAAqB,MAAM,WAAW;AAAA,IACnD,aAAa,qBAAqB,MAAM,WAAW;AAAA,IACnD,oBAAoB,MAAM,sBAAsB;AAAA,IAChD,iBAAiB,MAAM,mBAAmB;AAAA,IAC1C,aAAa,MAAM,eAAe;AAAA,IAClC,YAAY,MAAM,cAAc;AAAA,IAChC,YAAY;AAAA,IACZ,cAAc,qBAAqB,MAAM,YAAY;AAAA,IACrD,eAAe,qBAAqB,MAAM,aAAa;AAAA,IACvD,kBAAkB,qBAAqB,MAAM,gBAAgB;AAAA,IAC7D,kBAAkB,qBAAqB,MAAM,gBAAgB;AAAA,IAC7D,WAAW,oBAAI,KAAK;AAAA,IACpB,WAAW,oBAAI,KAAK;AAAA,EACtB;AACF;AAEA,SAAS,gBAAgB,MAAyB,OAAmC;AACnF,QAAM,sBAAsB,OAAO,OAAO,cAAc,KAAK,CAAC,cAAc,KAAK,gBAAgB,MAAM,MAAM,gBAAgB,IAAI;AACjI,QAAM,wBAAwB,OAAO,OAAO,gBAAgB,MAAM,KAAK,kBAAkB,WAAW,MAAM,kBAAkB;AAC5H,QAAM,sBAAsB,OAAO,OAAO,mBAAmB,KAAK,OAAO,OAAO,gBAAgB;AAChG,MAAI,OAAO,OAAO,QAAQ,KAAK,MAAM,WAAW,OAAW,MAAK,SAAS,MAAM;AAC/E,MAAI,OAAO,OAAO,WAAW,EAAG,MAAK,YAAY,MAAM,aAAa;AACpE,MAAI,OAAO,OAAO,WAAW,EAAG,MAAK,YAAY,MAAM,aAAa;AACpE,MAAI,OAAO,OAAO,KAAK,EAAG,MAAK,MAAM,MAAM,OAAO;AAClD,MAAI,OAAO,OAAO,aAAa,EAAG,MAAK,cAAc,MAAM,eAAe;AAC1E,MAAI,OAAO,OAAO,aAAa,EAAG,MAAK,cAAc,MAAM,eAAe;AAC1E,MAAI,OAAO,OAAO,cAAc,EAAG,MAAK,eAAe,MAAM,gBAAgB;AAC7E,MAAI,OAAO,OAAO,WAAW,EAAG,MAAK,YAAY,MAAM,aAAa;AACpE,MAAI,OAAO,OAAO,cAAc,EAAG,MAAK,eAAe,MAAM,gBAAgB;AAC7E,MAAI,OAAO,OAAO,gBAAgB,EAAG,MAAK,iBAAiB,MAAM,kBAAkB;AACnF,MAAI,OAAO,OAAO,mBAAmB,EAAG,MAAK,oBAAoB,MAAM,qBAAqB;AAC5F,MAAI,OAAO,OAAO,gBAAgB,KAAK,MAAM,eAAgB,MAAK,iBAAiB,MAAM;AACzF,MAAI,OAAO,OAAO,WAAW,EAAG,MAAK,YAAY,MAAM,aAAa;AACpE,MAAI,OAAO,OAAO,kBAAkB,EAAG,MAAK,mBAAmB,MAAM,oBAAoB;AACzF,MAAI,OAAO,OAAO,YAAY,KAAK,MAAM,eAAe,OAAW,MAAK,aAAa,aAAa,MAAM,YAAY,GAAG,KAAK;AAC5H,MAAI,OAAO,OAAO,aAAa,EAAG,MAAK,cAAc,qBAAqB,MAAM,WAAW;AAC3F,MAAI,OAAO,OAAO,aAAa,EAAG,MAAK,cAAc,qBAAqB,MAAM,WAAW;AAC3F,MAAI,OAAO,OAAO,oBAAoB,EAAG,MAAK,qBAAqB,MAAM,sBAAsB;AAC/F,MAAI,OAAO,OAAO,iBAAiB,EAAG,MAAK,kBAAkB,MAAM,mBAAmB;AACtF,MAAI,OAAO,OAAO,aAAa,EAAG,MAAK,cAAc,MAAM,eAAe;AAC1E,MAAI,OAAO,OAAO,YAAY,EAAG,MAAK,aAAa,MAAM,cAAc;AACvE,MAAI,OAAO,OAAO,YAAY,KAAK,MAAM,YAAY;AACnD,yBAAqB,KAAK,YAAY,MAAM,UAAU;AACtD,SAAK,aAAa,MAAM;AAAA,EAC1B;AACA,MAAI,OAAO,OAAO,cAAc,EAAG,MAAK,eAAe,qBAAqB,MAAM,YAAY;AAC9F,MAAI,OAAO,OAAO,eAAe,EAAG,MAAK,gBAAgB,qBAAqB,MAAM,aAAa;AACjG,MAAI,OAAO,OAAO,kBAAkB,EAAG,MAAK,mBAAmB,qBAAqB,MAAM,gBAAgB;AAC1G,MAAI,OAAO,OAAO,kBAAkB,EAAG,MAAK,mBAAmB,qBAAqB,MAAM,gBAAgB;AAC1G,OAAK,uBAAuB,0BAA0B,CAAC,qBAAqB;AAC1E,UAAM,mBAAmB,qBAAqB,KAAK,gBAAgB,MAAM,KAAK,kBAAkB,IAAI;AACpG,SAAK,oBAAoB,iBAAiB;AAC1C,SAAK,iBAAiB,iBAAiB;AAAA,EACzC;AACA,OAAK,YAAY,oBAAI,KAAK;AAC5B;AAEA,SAAS,aAAa,MAAuC;AAC3D,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,SAAS,UAAU,IAAI;AAAA,IACvB,gBAAgB,KAAK;AAAA,IACrB,UAAU,KAAK;AAAA,IACf,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK,aAAa;AAAA,IAC7B,WAAW,KAAK,aAAa;AAAA,IAC7B,KAAK,KAAK,OAAO;AAAA,IACjB,aAAa,KAAK,eAAe;AAAA,IACjC,aAAa,KAAK,eAAe;AAAA,IACjC,cAAc,KAAK,gBAAgB;AAAA,IACnC,WAAW,KAAK,aAAa;AAAA,IAC7B,cAAc,cAAc,KAAK,YAAY;AAAA,IAC7C,gBAAgB,KAAK,kBAAkB;AAAA,IACvC,mBAAmB,cAAc,KAAK,iBAAiB;AAAA,IACvD,gBAAgB,KAAK;AAAA,IACrB,WAAW,KAAK,aAAa;AAAA,IAC7B,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK,eAAe;AAAA,IACjC,aAAa,KAAK,eAAe;AAAA,IACjC,oBAAoB,KAAK,sBAAsB;AAAA,IAC/C,gBAAgB,KAAK,kBAAkB;AAAA,IACvC,kBAAkB,KAAK;AAAA,IACvB,iBAAiB,KAAK,mBAAmB;AAAA,IACzC,mBAAmB,KAAK,qBAAqB;AAAA,IAC7C,aAAa,KAAK,eAAe;AAAA,IACjC,YAAY,KAAK;AAAA,IACjB,cAAc,KAAK,gBAAgB;AAAA,IACnC,eAAe,KAAK,iBAAiB;AAAA,IACrC,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,mBAAmB,KAAK,qBAAqB;AAAA,IAC7C,YAAY,KAAK,cAAc;AAAA,IAC/B,WAAW,MAAM,KAAK,SAAS;AAAA,IAC/B,WAAW,MAAM,KAAK,SAAS;AAAA,EACjC;AACF;AAEA,eAAe,iBACb,IACA,QACA,OAC8B;AAC9B,QAAM,OAAO,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,QAAQ,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IAC7E,EAAE,UAAU,CAAC,OAAO,EAAE;AAAA,IACtB;AAAA,EACF;AACA,SAAO,OAAO,aAAa,IAAI,IAAI;AACrC;AAEA,eAAe,kCAAkC,IAAmB,OAAsB,UAAuC;AAC/H,MAAI,CAAC,SAAS,UAAW,OAAM,+BAA+B,IAAI,OAAO,SAAS,QAAQ,SAAS,IAAI,IAAI;AAAA,MACtG,OAAM,mBAAmB,IAAI,MAAM,IAAI,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe,GAAG,EAAE,UAAU,SAAS,kBAAkB,CAAC;AAC1J;AAEA,SAAS,wBAAwB,MAAyB,UAA8B;AACtF,OAAK,SAAS,SAAS;AACvB,OAAK,YAAY,SAAS;AAC1B,OAAK,YAAY,SAAS;AAC1B,OAAK,MAAM,SAAS;AACpB,OAAK,cAAc,SAAS;AAC5B,OAAK,cAAc,SAAS;AAC5B,OAAK,eAAe,SAAS;AAC7B,OAAK,YAAY,SAAS;AAC1B,OAAK,eAAe,WAAW,SAAS,YAAY;AACpD,OAAK,iBAAiB,SAAS;AAC/B,OAAK,oBAAoB,WAAW,SAAS,iBAAiB;AAC9D,OAAK,iBAAiB,SAAS;AAC/B,OAAK,YAAY,SAAS;AAC1B,OAAK,mBAAmB,SAAS;AACjC,OAAK,aAAa,SAAS;AAC3B,OAAK,cAAc,SAAS;AAC5B,OAAK,cAAc,SAAS;AAC5B,OAAK,qBAAqB,SAAS;AACnC,OAAK,iBAAiB,SAAS;AAC/B,OAAK,mBAAmB,SAAS;AACjC,OAAK,kBAAkB,SAAS;AAChC,OAAK,oBAAoB,SAAS;AAClC,OAAK,cAAc,SAAS;AAC5B,OAAK,aAAa,SAAS;AAC3B,OAAK,eAAe,SAAS;AAC7B,OAAK,gBAAgB,SAAS;AAC9B,OAAK,mBAAmB,SAAS;AACjC,OAAK,mBAAmB,SAAS;AACjC,OAAK,oBAAoB,SAAS;AAClC,OAAK,aAAa,SAAS;AAC3B,OAAK,YAAY,OAAO,SAAS,SAAS;AAC1C,OAAK,YAAY,oBAAI,KAAK;AAC5B;AAEA,eAAe,sBAAsB,IAAmB,OAAqC;AAC3F,QAAM,QAAQ,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,WAAW;AAAA,IACb;AAAA,IACA,CAAC;AAAA,IACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,EACnE;AACA,QAAM,SAAS,qBAAqB,OAAO,EAAE,WAAW,MAAM,UAAU,CAAC;AACzE,QAAM,qBAAqB,OAAO,OAAO,kBAAkB;AAC3D,QAAM,sBAAsB,OAAO,OAAO,mBAAmB;AAC7D,QAAM,YAAY,oBAAI,KAAK;AAC7B;AAEA,SAAS,yBAAyC;AAChD,SAAO,iBAAiB,mCAAmC,+CAA+C;AAC5G;AAEA,eAAe,4BACb,IACA,OACA,MACwB;AACxB,MAAI,CAAC,MAAO,QAAO,uBAAuB;AAC1C,QAAM,QAA4B,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AACnG,QAAM,QAAQ,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,MAAM,SAAS,UAAU,MAAM,UAAU,gBAAgB,MAAM,gBAAgB,WAAW,KAAK;AAAA,IACrG,EAAE,UAAU,SAAS,kBAAkB;AAAA,IACvC;AAAA,EACF;AACA,MAAI,CAAC,MAAO,QAAO,uBAAuB;AAC1C,MAAI,CAAC,KAAK,sBAAsB,IAAI,MAAM,MAAM,EAAG,QAAO,iBAAiB,KAAK,WAAW,KAAK,cAAc;AAC9G,QAAM,OAAO,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,MAAM,IAAI,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IAC/E,EAAE,UAAU,SAAS,kBAAkB;AAAA,IACvC;AAAA,EACF;AACA,MAAI,CAAC,QAAQ,CAAC,MAAM,aAAa,MAAM,KAAK,SAAS,MAAM,MAAM,UAAW,QAAO,uBAAuB;AAC1G,SAAO,EAAE,OAAO,KAAK;AACvB;AAEA,eAAe,kBACb,IACA,QACA,OAC4B;AAC5B,QAAM,OAAO,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,QAAQ,UAAU,MAAM,UAAU,gBAAgB,MAAM,gBAAgB,WAAW,KAAK;AAAA,IAC9F,EAAE,UAAU,CAAC,OAAO,EAAE;AAAA,IACtB;AAAA,EACF;AACA,MAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,kCAAkC,CAAC;AACpF,SAAO;AACT;AAEA,eAAe,iBAAiB,KAA4B,OAAqC;AAC/F,QAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AACrD,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa,EAAE,IAAI,MAAM,IAAI,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,SAAS;AAAA,IAC5F,SAAS,EAAE,YAAY,EAAE,gBAAgB,eAAe;AAAA,IACxD,QAAQ;AAAA,EACV,CAAC;AACD,QAAM;AAAA,IACJ,IAAI;AAAA,IACJ;AAAA,IACA,EAAE,IAAI,MAAM,IAAI,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,SAAS;AAAA,IAC/E,IAAI,MAAM,YAAY;AAAA,IACtB;AAAA,EACF;AACF;AAEA,eAAe,aACb,KACA,QACA,MACe;AACf,QAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AACrD,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,aAAa,EAAE,IAAI,KAAK,IAAI,gBAAgB,KAAK,gBAAgB,UAAU,KAAK,SAAS;AAAA,IACzF,SAAS,EAAE,YAAY,EAAE,gBAAgB,oBAAoB;AAAA,EAC/D,CAAC;AACD,QAAM;AAAA,IACJ,IAAI;AAAA,IACJ;AAAA,IACA,EAAE,IAAI,KAAK,IAAI,gBAAgB,KAAK,gBAAgB,UAAU,KAAK,SAAS;AAAA,IAC5E,IAAI,MAAM,YAAY;AAAA,IACtB,8BAA8B,MAAM;AAAA,EACtC;AACF;AAEA,eAAe,iBACb,KACA,QACA,MACe;AACf,QAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AACrD,QAAM,wBAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,aAAa,EAAE,IAAI,KAAK,IAAI,gBAAgB,KAAK,gBAAgB,UAAU,KAAK,SAAS;AAAA,IACzF,SAAS,EAAE,YAAY,EAAE,gBAAgB,oBAAoB;AAAA,EAC/D,CAAC;AACD,QAAM;AAAA,IACJ,IAAI;AAAA,IACJ;AAAA,IACA,EAAE,IAAI,KAAK,IAAI,gBAAgB,KAAK,gBAAgB,UAAU,KAAK,SAAS;AAAA,IAC5E,IAAI,MAAM,YAAY;AAAA,IACtB,mCAAmC,MAAM;AAAA,EAC3C;AACF;AAEA,eAAe,aACb,gBACA,qBACA,YACA,WAYC;AACD,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAM,QAAQ,UAAU;AACxB,QAAM,SAAS,UAAU;AACzB,SAAO;AAAA,IACL,aAAa,UAAU,gBAAgB,mBAAmB;AAAA,IAC1D,cAAc;AAAA,IACd;AAAA,IACA,oBAAoB;AAAA,IACpB,kBAAkB,OAAO,WAAW,QAAQ,WAAW;AAAA,IACvD,UAAU,OAAO,YAAY,QAAQ,YAAY;AAAA,IACjD,gBAAgB,OAAO,kBAAkB,QAAQ,kBAAkB;AAAA,IACnE,gBAAgB,UAAU;AAAA,IAC1B,eAAe,SAAS;AAAA,IACxB,SAAS,EAAE,MAAM,EAAE,QAAQ,UAAU,MAAM,OAAO,SAAS,KAAK,EAAE;AAAA,EACpE;AACF;AAEA,MAAM,yBAAoG;AAAA,EACxG,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,kBAAkB,uBAAuB,QAAQ;AAC/D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,MAAM,mBAAmB,IAAI,MAAM,SAAS,KAAK;AAC/D,oCAAgC,MAAM,WAAW,MAAM,eAAe,IAAI;AAC1E,UAAM,mCAAmC,KAAK,KAAK;AACnD,wBAAoB,KAAK;AACzB,QAAI;AACJ,QAAI,aAAa,MAAM,UAAU;AACjC,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,mBAAmB,IAAI,MAAM,IAAI,OAAO,EAAE,UAAU,SAAS,mBAAmB,SAAS,KAAK,CAAC;AACrG,4BAAoB,KAAK;AACzB,YAAI,MAAM,aAAa;AACrB,gBAAM,wBAAwB,KAAK,OAAO;AAAA,YACxC,eAAe,CAAC,EAAE,aAAa,MAAM,aAAa,SAAS,MAAM,WAAW,KAAK,CAAC;AAAA,UACpF,CAAC;AAAA,QACH;AACA,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,YACE,OAAO,MAAM;AAAA,YACb,UAAU,MAAM;AAAA,YAChB,gBAAgB,MAAM;AAAA,YACtB,WAAW;AAAA,UACb;AAAA,UACA,CAAC;AAAA,UACD;AAAA,QACF;AACA,YAAI,MAAM,aAAa;AACrB,gBAAM,uCAAuC,IAAI,OAAO,oBAAI,IAAI,CAAC,CAAC,MAAM,aAAa,CAAC;AAAA,YACpF,aAAa,MAAM;AAAA,YACnB,YAAY,aAAa,MAAM,YAAY,GAAG,KAAK;AAAA,UACrD,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,gBAAgB,MAAM,GAAG,CAAC;AAAA,QACrC;AACA,qBAAa,MAAM,UAAU,cAAc,OAAO,CAAC,KAAK,UAAU,KAAK,IAAI,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI;AACpG,cAAM,6BAA6B,CAAC,YAAY,GAAG,cAAc,OAAO,CAACA,UAASA,MAAK,WAAW,UAAU,EAAE,IAAI,CAACA,UAASA,MAAK,MAAM,CAAC,CAAC;AAAA,MAC3I;AAAA,MACA,MAAM;AACJ,eAAO,GAAG,OAAO,mBAAmB,oBAAoB,OAAO,OAAO,UAAU,CAAC;AACjF,6BAAqB,IAAI;AACzB,WAAG,QAAQ,IAAI;AAAA,MACjB;AAAA,MACA,MAAM,sBAAsB,IAAI,KAAK;AAAA,IACvC,GAAG,EAAE,aAAa,MAAM,OAAO,oCAAoC,CAAC;AACpE,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,aAAa,KAAK,WAAW,IAAI;AACvC,WAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,MAAM,GAAG;AAAA,EAC9C;AAAA,EACA,cAAc,OAAO,UAAU,QAAQ,QAAQ;AAC7C,UAAM,QAAQ,kBAAkB,uBAAuB,QAAQ;AAC/D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,iBAAiB,IAAI,OAAO,QAAQ,KAAK;AAAA,EAClD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM,aAAa,2CAA2C,kBAAkB,OAAO,QAAQ,SAAS;AAAA,EAC7I,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI;AACJ,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,gBAAQ,MAAM,4BAA4B,IAAI,SAAS,OAAO,qBAAqB;AAAA,MACrF;AAAA,MACA,MAAM;AACJ,cAAM,KAAK,YAAY,oBAAI,KAAK;AAChC,cAAM,KAAK,YAAY,oBAAI,KAAK;AAAA,MAClC;AAAA,MACA,MAAM,sBAAsB,IAAI,MAAM,KAAK;AAAA,IAC7C,GAAG,EAAE,aAAa,MAAM,OAAO,yCAAyC,CAAC;AACzE,UAAM,iBAAiB,KAAK,MAAM,KAAK;AACvC,UAAM,iBAAiB,KAAK,WAAW,MAAM,IAAI;AAAA,EACnD;AACF;AAEA,MAAM,yBAAoG;AAAA,EACxG,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,QAAQ,kBAAkB,uBAAuB,QAAQ;AAC/D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,EAAE,QAAQ,MAAM,iBAAiB,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,EAC/D;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,kBAAkB,uBAAuB,QAAQ;AAC/D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,kBAAkB,IAAI,MAAM,IAAI,KAAK;AACxD,UAAM,QAAQ,QAAQ,IAAI,KAAK,MAAM,mBAAmB,IAAI,MAAM,WAAW,UAAU,IAAI,GAAG,KAAK;AACnG,QAAI,MAAM,WAAW,MAAM,YAAY,MAAM,IAAI;AAC/C,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,kCAAkC,CAAC;AAAA,IAC3E;AACA,wBAAoB,KAAK;AACzB,UAAM,mCAAmC,KAAK,MAAM,iCAAiC;AACrF,UAAM,uBAAuB,OAAO,OAAO,aAAa,IAAK,MAAM,eAAe,OAAS,KAAK,eAAe;AAC/G,QAAI,OAAO,OAAO,aAAa,GAAG;AAChC,sCAAgC,MAAM,WAAW,MAAM,eAAe,IAAI;AAC1E,uCAAiC,iBAAiB,KAAK,cAAc,GAAG,MAAM,eAAe,IAAI;AAAA,IACnG;AACA,UAAM,mBAAmB,OAAO,OAAO,YAAY,KAAK,OAAO,OAAO,aAAa;AACnF,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,mBAAmB,IAAI,MAAM,IAAI,OAAO,EAAE,UAAU,SAAS,mBAAmB,SAAS,KAAK,CAAC;AACrG,4BAAoB,KAAK;AACzB,YAAI,MAAM,WAAW,OAAW,OAAM,+BAA+B,IAAI,OAAO,MAAM,QAAQ,KAAK,EAAE;AACrG,YAAI,yBAAyB,MAAM,WAAW,0BAA0B,KAAK,eAAe,QAAQ;AAClG,gBAAM,wBAAwB,KAAK,OAAO;AAAA,YACxC,eAAe,CAAC;AAAA,cACd,aAAa;AAAA,cACb,SAAS,MAAM,WAAW;AAAA,cAC1B,cAAc,0BAA0B,KAAK,eAAe;AAAA,YAC9D,CAAC;AAAA,UACH,CAAC;AAAA,QACH;AACA,YAAI,CAAC,iBAAkB;AACvB,cAAM,cAAc,OAAO,OAAO,aAAa,IAAK,MAAM,eAAe,OAAS,KAAK,eAAe;AACtG,YAAI,aAAa;AACf,gBAAM;AAAA,YACJ;AAAA,YACA;AAAA,YACA,oBAAI,IAAI,CAAC,CAAC,aAAa,CAAC;AAAA,cACtB,IAAI,KAAK;AAAA,cACT;AAAA,cACA,YAAY,OAAO,OAAO,YAAY,IACjC,aAAa,MAAM,YAAY,GAAG,KAAK,MACxC,KAAK;AAAA,YACX,CAAC,CAAC,CAAC,CAAC;AAAA,YACJ,EAAE,gBAAgB,MAAM,IAAI,iBAAiB,oBAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,MACA,MAAM;AACJ,wBAAgB,MAAM,KAAK;AAC3B,6BAAqB,IAAI;AAAA,MAC3B;AAAA,MACA,MAAM,sBAAsB,IAAI,KAAK;AAAA,IACvC,GAAG,EAAE,aAAa,MAAM,OAAO,oCAAoC,CAAC;AACpE,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,aAAa,KAAK,WAAW,IAAI;AACvC,WAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,MAAM,GAAG;AAAA,EAC9C;AAAA,EACA,cAAc,OAAO,UAAU,QAAQ,QAAQ;AAC7C,UAAM,QAAQ,kBAAkB,uBAAuB,QAAQ;AAC/D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,iBAAiB,IAAI,OAAO,QAAQ,KAAK;AAAA,EAClD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM,aAAa,2CAA2C,qBAAqB,OAAO,QAAQ,SAAS;AAAA,EAChJ,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI;AACJ,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,gBAAQ,MAAM,4BAA4B,IAAI,SAAS,OAAO,qBAAqB;AACnF,cAAM,kCAAkC,IAAI,MAAM,OAAO,MAAM;AAAA,MACjE;AAAA,MACA,MAAM,wBAAwB,MAAM,MAAM,MAAM;AAAA,MAChD,MAAM,sBAAsB,IAAI,MAAM,KAAK;AAAA,IAC7C,GAAG,EAAE,aAAa,MAAM,OAAO,yCAAyC,CAAC;AACzE,UAAM,iBAAiB,KAAK,MAAM,KAAK;AACvC,UAAM,iBAAiB,KAAK,WAAW,MAAM,IAAI;AAAA,EACnD;AACF;AAEA,MAAM,yBAA+F;AAAA,EACnG,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,QAAQ,kBAAkB,kBAAkB,QAAQ;AAC1D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,EAAE,QAAQ,MAAM,iBAAiB,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,EAC/D;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,kBAAkB,kBAAkB,QAAQ;AAC1D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,kBAAkB,IAAI,MAAM,IAAI,KAAK;AACxD,UAAM,QAAQ,QAAQ,IAAI,KAAK,MAAM,mBAAmB,IAAI,MAAM,WAAW,UAAU,IAAI,GAAG,KAAK;AACnG,QAAI,MAAM,WAAW,MAAM,YAAY,MAAM,IAAI;AAC/C,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,kCAAkC,CAAC;AAAA,IAC3E;AACA,wBAAoB,KAAK;AACzB,UAAM,mCAAmC,KAAK,MAAM,iCAAiC;AACrF,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,mBAAmB,IAAI,MAAM,IAAI,OAAO,EAAE,UAAU,SAAS,kBAAkB,CAAC;AAAA,MACxF;AAAA,MACA,MAAM;AACJ,aAAK,YAAY,oBAAI,KAAK;AAC1B,aAAK,YAAY,oBAAI,KAAK;AAAA,MAC5B;AAAA,MACA,MAAM,sBAAsB,IAAI,KAAK;AAAA,IACvC,GAAG,EAAE,aAAa,MAAM,OAAO,oCAAoC,CAAC;AACpE,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,aAAa,KAAK,WAAW,IAAI;AACvC,WAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,MAAM,GAAG;AAAA,EAC9C;AAAA,EACA,cAAc,OAAO,UAAU,QAAQ,QAAQ;AAC7C,UAAM,QAAQ,kBAAkB,kBAAkB,QAAQ;AAC1D,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,iBAAiB,IAAI,OAAO,QAAQ,KAAK;AAAA,EAClD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM,aAAa,2CAA2C,qBAAqB,OAAO,QAAQ,SAAS;AAAA,EAChJ,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI;AACJ,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,gBAAQ,MAAM,4BAA4B,IAAI,SAAS,OAAO,qBAAqB;AACnF,cAAM,kCAAkC,IAAI,MAAM,OAAO,MAAM;AAAA,MACjE;AAAA,MACA,MAAM;AACJ,gCAAwB,MAAM,MAAM,MAAM;AAC1C,cAAM,KAAK,YAAY;AAAA,MACzB;AAAA,MACA,MAAM,sBAAsB,IAAI,MAAM,KAAK;AAAA,IAC7C,GAAG,EAAE,aAAa,MAAM,OAAO,yCAAyC,CAAC;AACzE,UAAM,iBAAiB,KAAK,MAAM,KAAK;AACvC,UAAM,iBAAiB,KAAK,WAAW,MAAM,IAAI;AAAA,EACnD;AACF;AAEA,MAAM,0BAAsG;AAAA,EAC1G,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,MAAM,CAAC,EAAE,OAAO,IAAI,MAAM,wBAAwB,QAAQ,EAAE,GAAG,OAAO,WAAW,OAAU,GAAG,GAAG;AAAA,EACjG,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,QAAQ,kBAAkB,wBAAwB,QAAQ;AAChE,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,EAAE,QAAQ,MAAM,iBAAiB,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,EAC/D;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,kBAAkB,wBAAwB,QAAQ;AAChE,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,kBAAkB,IAAI,MAAM,IAAI,KAAK;AACxD,UAAM,QAAQ,MAAM,mBAAmB,IAAI,UAAU,IAAI,GAAG,KAAK;AACjE,0BAAsB,KAAK;AAC3B,UAAM,mCAAmC,KAAK,MAAM,mCAAmC,MAAM,aAAa,MAAS;AAEnH,UAAM,QAAwB,MAAM;AACpC,UAAM,oBAAoB,MAAM,sCAAsC,IAAI,KAAK;AAC/E,UAAM,mBAAmB,QAAQ,kBAAkB,kBAAkB,SAAS,KAAK,CAAC;AAEpF,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,mBAAmB,IAAI,MAAM,IAAI,OAAO,EAAE,UAAU,SAAS,kBAAkB,CAAC;AAAA,MACxF;AAAA,MACA,MAAM;AACJ,aAAK,iBAAiB;AACtB,YAAI,MAAM,oBAAoB,OAAW,MAAK,kBAAkB,MAAM,mBAAmB;AAEzF,cAAM,uBAAuB,6BAA6B,KAAK;AAC/D,YAAI,CAAC,KAAK,eAAe,wBAAwB,yBAAyB,MAAM,SAAS,EAAE,oBAAoB,SAAS,oBAAoB,GAAG;AAC7I,2CAAiC,OAAO,oBAAoB;AAC5D,cAAI,yBAAyB,aAAa,yBAAyB,YAAY,yBAAyB,SAAS;AAC/G,iBAAK,cAAc;AAAA,UACrB;AAAA,QACF;AACA,yCAAiC,OAAO,KAAK,eAAe,IAAI;AAEhE,YAAI,iBAAkB,MAAK,mBAAmB;AAC9C,aAAK,YAAY,oBAAI,KAAK;AAC1B,yBAAiB,IAAI,OAAO,UAAU;AAAA,UACpC,YAAY;AAAA,UACZ,SAAS;AAAA,YACP,QAAQ;AAAA,YACR,QAAQ,KAAK;AAAA,YACb,gBAAgB;AAAA,YAChB,aAAa,KAAK,eAAe;AAAA,YACjC,kBAAkB,KAAK;AAAA,YACvB,gBAAgB;AAAA,UAClB;AAAA,UACA,aAAa,IAAI,MAAM,OAAO;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,MACA,MAAM,sBAAsB,IAAI,KAAK;AAAA,IACvC,GAAG,EAAE,aAAa,MAAM,OAAO,qCAAqC,CAAC;AAErE,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,aAAa,KAAK,WAAW,IAAI;AACvC,QAAI,kBAAkB;AACpB,YAAM,wBAAwB,0CAA0C;AAAA,QACtE,IAAI,KAAK;AAAA,QACT,SAAS,MAAM;AAAA,QACf,QAAQ,KAAK;AAAA,QACb;AAAA,QACA;AAAA,QACA,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,MAClB,GAAG;AAAA,QACD,YAAY;AAAA,QACZ,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AAAA,IACH;AACA,WAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,MAAM,GAAG;AAAA,EAC9C;AAAA,EACA,cAAc,OAAO,UAAU,QAAQ,QAAQ;AAC7C,UAAM,QAAQ,kBAAkB,wBAAwB,QAAQ;AAChE,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,iBAAiB,IAAI,OAAO,QAAQ,KAAK;AAAA,EAClD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM,aAAa,4CAA4C,sBAAsB,OAAO,QAAQ,SAAS;AAAA,EAClJ,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI;AACJ,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,gBAAQ,MAAM,4BAA4B,IAAI,SAAS,OAAO,uBAAuB;AACrF,cAAM,kCAAkC,IAAI,MAAM,OAAO,MAAM;AAAA,MACjE;AAAA,MACA,MAAM,wBAAwB,MAAM,MAAM,MAAM;AAAA,MAChD,MAAM,sBAAsB,IAAI,MAAM,KAAK;AAAA,IAC7C,GAAG,EAAE,aAAa,MAAM,OAAO,0CAA0C,CAAC;AAC1E,UAAM,iBAAiB,KAAK,MAAM,KAAK;AACvC,UAAM,iBAAiB,KAAK,WAAW,MAAM,IAAI;AAAA,EACnD;AACF;AAEA,MAAM,oCAA0H;AAAA,EAC9H,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,MAAM,CAAC,EAAE,OAAO,IAAI,MAAM,kCAAkC,QAAQ,EAAE,GAAG,OAAO,WAAW,OAAU,GAAG,GAAG;AAAA,EAC3G,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,QAAQ,kBAAkB,kCAAkC,QAAQ;AAC1E,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,EAAE,QAAQ,MAAM,iBAAiB,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,EAC/D;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,kBAAkB,kCAAkC,QAAQ;AAC1E,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,kBAAkB,IAAI,MAAM,IAAI,KAAK;AACxD,UAAM,QAAQ,MAAM,mBAAmB,IAAI,UAAU,IAAI,GAAG,KAAK;AACjE,0BAAsB,KAAK;AAC3B,UAAM,mCAAmC,KAAK,MAAM,mCAAmC,MAAM,aAAa,MAAS;AAEnH,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,mBAAmB,IAAI,MAAM,IAAI,OAAO,EAAE,UAAU,SAAS,kBAAkB,CAAC;AAAA,MACxF;AAAA,MACA,MAAM;AACJ,aAAK,mBAAmB;AACxB,aAAK,YAAY,oBAAI,KAAK;AAC1B,yBAAiB,IAAI,OAAO,UAAU;AAAA,UACpC,YAAY;AAAA,UACZ,SAAS;AAAA,YACP,QAAQ;AAAA,YACR,QAAQ,KAAK;AAAA,YACb,kBAAkB,KAAK;AAAA,UACzB;AAAA,UACA,aAAa,IAAI,MAAM,OAAO;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,MACA,MAAM,sBAAsB,IAAI,KAAK;AAAA,IACvC,GAAG,EAAE,aAAa,MAAM,OAAO,gDAAgD,CAAC;AAEhF,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,aAAa,KAAK,WAAW,IAAI;AACvC,WAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,MAAM,GAAG;AAAA,EAC9C;AAAA,EACA,cAAc,OAAO,UAAU,QAAQ,QAAQ;AAC7C,UAAM,QAAQ,kBAAkB,kCAAkC,QAAQ;AAC1E,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,iBAAiB,IAAI,OAAO,QAAQ,KAAK;AAAA,EAClD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM,aAAa,uDAAuD,gCAAgC,OAAO,QAAQ,SAAS;AAAA,EACvK,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI;AACJ,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,gBAAQ,MAAM,4BAA4B,IAAI,SAAS,OAAO,uBAAuB;AACrF,cAAM,kCAAkC,IAAI,MAAM,OAAO,MAAM;AAAA,MACjE;AAAA,MACA,MAAM,wBAAwB,MAAM,MAAM,MAAM;AAAA,MAChD,MAAM,sBAAsB,IAAI,MAAM,KAAK;AAAA,IAC7C,GAAG,EAAE,aAAa,MAAM,OAAO,qDAAqD,CAAC;AACrF,UAAM,iBAAiB,KAAK,MAAM,KAAK;AACvC,UAAM,iBAAiB,KAAK,WAAW,MAAM,IAAI;AAAA,EACnD;AACF;AAEA,MAAM,gCAA6G;AAAA,EACjH,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,MAAM,CAAC,EAAE,OAAO,IAAI,MAAM,8BAA8B,QAAQ,EAAE,GAAG,OAAO,WAAW,OAAU,GAAG,GAAG;AAAA,EACvG,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,QAAQ,kBAAkB,yBAAyB,QAAQ;AACjE,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,EAAE,QAAQ,MAAM,iBAAiB,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,EAC/D;AAAA,EACA,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,kBAAkB,yBAAyB,QAAQ;AACjE,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,OAAO,MAAM,kBAAkB,IAAI,MAAM,IAAI,KAAK;AACxD,UAAM,QAAQ,QAAQ,IAAI,KAAK,MAAM,mBAAmB,IAAI,UAAU,IAAI,GAAG,KAAK;AAClF,wBAAoB,KAAK;AACzB,UAAM,mCAAmC,KAAK,MAAM,mCAAmC,MAAM,aAAa,MAAS;AAEnH,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,cAAM,mBAAmB,IAAI,MAAM,IAAI,OAAO,EAAE,UAAU,SAAS,kBAAkB,CAAC;AAAA,MACxF;AAAA,MACA,MAAM;AACJ,aAAK,oBAAoB,MAAM;AAC/B,aAAK,YAAY,oBAAI,KAAK;AAC1B,cAAM,YAAY,oBAAI,KAAK;AAC3B,yBAAiB,IAAI,OAAO,UAAU;AAAA,UACpC,YAAY;AAAA,UACZ,SAAS;AAAA,YACP,QAAQ;AAAA,YACR,QAAQ,KAAK;AAAA,UACf;AAAA,UACA,aAAa,IAAI,MAAM,OAAO;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,IACF,GAAG,EAAE,aAAa,MAAM,OAAO,4CAA4C,CAAC;AAE5E,UAAM,iBAAiB,KAAK,KAAK;AACjC,UAAM,aAAa,KAAK,WAAW,IAAI;AACvC,WAAO,EAAE,QAAQ,KAAK,IAAI,SAAS,MAAM,GAAG;AAAA,EAC9C;AAAA,EACA,cAAc,OAAO,UAAU,QAAQ,QAAQ;AAC7C,UAAM,QAAQ,kBAAkB,yBAAyB,QAAQ;AACjE,UAAM,QAAQ,aAAa,KAAK,KAAK;AACrC,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,iBAAiB,IAAI,OAAO,QAAQ,KAAK;AAAA,EAClD;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM,aAAa,mDAAmD,0BAA0B,OAAO,QAAQ,SAAS;AAAA,EAC7J,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAoC,QAAQ;AAC5D,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,QAAI;AACJ,UAAM,gBAAgB,IAAI;AAAA,MACxB,YAAY;AACV,gBAAQ,MAAM,4BAA4B,IAAI,SAAS,OAAO,qBAAqB;AACnF,cAAM,kCAAkC,IAAI,MAAM,OAAO,MAAM;AAAA,MACjE;AAAA,MACA,MAAM,wBAAwB,MAAM,MAAM,MAAM;AAAA,MAChD,MAAM,sBAAsB,IAAI,MAAM,KAAK;AAAA,IAC7C,GAAG,EAAE,aAAa,MAAM,OAAO,iDAAiD,CAAC;AACjF,UAAM,iBAAiB,KAAK,MAAM,KAAK;AACvC,UAAM,iBAAiB,KAAK,WAAW,MAAM,IAAI;AAAA,EACnD;AACF;AAEA,gBAAgB,sBAAsB;AACtC,gBAAgB,sBAAsB;AACtC,gBAAgB,sBAAsB;AACtC,gBAAgB,uBAAuB;AACvC,gBAAgB,iCAAiC;AACjD,gBAAgB,6BAA6B;AAEtC,MAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;",
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from 'crypto'\nimport { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { registerCommand, type CommandHandler, type CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { E } from '#generated/entities.ids.generated'\nimport { WarrantyClaim, WarrantyClaimLine } from '../data/entities'\nimport {\n claimLineCreateSchema,\n claimLineReceiveSchema,\n claimLineReleaseQuarantineSchema,\n claimLineUpdateSchema,\n type ClaimLineCreateInput,\n type ClaimLineReceiveInput,\n type ClaimLineReleaseQuarantineInput,\n type ClaimLineUpdateInput,\n type WarrantyClaimDisposition,\n type WarrantyClaimLineStatus,\n type WarrantyClaimWarrantyStatus,\n} from '../data/validators'\nimport { emitWarrantyClaimsEvent } from '../events'\nimport {\n assertDispositionAllowedForGrade,\n suggestedDispositionForGrade,\n type ConditionGrade,\n} from '../lib/grading'\nimport { assertDispositionAllowedForType, resolveClaimTypeUiConfig } from '../lib/claimTypeConfig'\nimport { resolveEffectiveWarrantyClaimSettings } from '../lib/settings'\nimport { computeHeaderRollups, lineStatusGuards } from '../lib/stateMachine'\nimport {\n WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n WARRANTY_CLAIM_RESOURCE_KIND,\n appendClaimEvent,\n assertClaimLineNumberAvailable,\n assertUniqueClaimLineNumbers,\n enforceWarrantyClaimOptimisticLock,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n requireScopedClaim,\n throwUndoRefusal,\n type WarrantyClaimScope,\n} from './shared'\nimport { assertPendingClaimQuantitiesWithinSold, validateClaimReferences } from './claims'\n\nconst claimCrudEvents: CrudEventsConfig = {\n module: 'warranty_claims',\n entity: 'claim',\n persistent: true,\n}\n\nconst lineDeleteSchema = z\n .object({\n id: z.string().uuid(),\n claimId: z.string().uuid().optional(),\n organizationId: z.string().uuid().optional(),\n tenantId: z.string().uuid().optional(),\n })\n .strict()\n\ntype LineDeleteInput = z.infer<typeof lineDeleteSchema>\n\nconst lineSetAssessmentSchema = z\n .object({\n id: z.string().uuid(),\n organizationId: z.string().uuid().optional(),\n tenantId: z.string().uuid().optional(),\n assessmentPayload: z.record(z.string(), z.unknown()),\n updatedAt: z.union([z.string().datetime(), z.date()]).nullable().optional(),\n })\n .strict()\n\ntype LineSetAssessmentInput = z.infer<typeof lineSetAssessmentSchema>\n\ntype ScopeInput = {\n organizationId?: string | null\n tenantId?: string | null\n}\n\ntype LineSnapshot = {\n id: string\n claimId: string\n organizationId: string\n tenantId: string\n lineNo: number\n productId: string | null\n variantId: string | null\n sku: string | null\n productName: string | null\n orderLineId: string | null\n serialNumber: string | null\n lotNumber: string | null\n purchaseDate: string | null\n warrantyMonths: number | null\n warrantyExpiresAt: string | null\n warrantyStatus: WarrantyClaimWarrantyStatus\n faultCode: string | null\n faultDescription: string | null\n qtyClaimed: string\n qtyApproved: string | null\n qtyReceived: string | null\n conditionOnReceipt: string | null\n conditionGrade: string | null\n quarantineStatus: string\n inspectionNotes: string | null\n assessmentPayload: Record<string, unknown> | null\n disposition: WarrantyClaimDisposition | null\n lineStatus: WarrantyClaimLineStatus\n creditAmount: string | null\n restockingFee: string | null\n coreChargeAmount: string | null\n coreCreditAmount: string | null\n vendorClaimLineId: string | null\n vendorName: string | null\n deletedAt: string | null\n updatedAt: string | null\n}\n\ntype LineUndoPayload = {\n before?: LineSnapshot | null\n after?: LineSnapshot | null\n}\n\nconst mutableParentStatuses = new Set([\n 'draft',\n 'submitted',\n 'in_review',\n 'info_requested',\n 'approved',\n 'received',\n 'inspecting',\n])\n\nconst receivingParentStatuses = new Set(['received', 'inspecting'])\n\ntype LineUndoParentRule = {\n allowedParentStatuses: ReadonlySet<string>\n lockedKey: string\n lockedFallback: string\n}\n\ntype LineUndoState = {\n claim: WarrantyClaim\n line: WarrantyClaimLine\n}\n\nconst mutableParentUndoRule: LineUndoParentRule = {\n allowedParentStatuses: mutableParentStatuses,\n lockedKey: 'warranty_claims.errors.lineLocked',\n lockedFallback: 'This line is locked in the current claim status.',\n}\n\nconst receivingParentUndoRule: LineUndoParentRule = {\n allowedParentStatuses: receivingParentStatuses,\n lockedKey: 'warranty_claims.errors.receivingStatusInvalid',\n lockedFallback: 'Receiving details can only be saved when the claim is received or under inspection.',\n}\n\nfunction parseCommandInput<T>(schema: z.ZodType<T>, rawInput: unknown): T {\n const result = schema.safeParse(rawInput ?? {})\n if (!result.success) {\n throw new CrudHttpError(400, { error: '[internal] invalid warranty claim line command input' })\n }\n return result.data\n}\n\nfunction hasOwn(input: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(input, key)\n}\n\nfunction resolveScope(ctx: CommandRuntimeContext, input: ScopeInput): WarrantyClaimScope {\n const tenantId = input.tenantId ?? ctx.auth?.tenantId ?? null\n const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null\n if (!tenantId) throw new CrudHttpError(400, { error: '[internal] tenant scope required for warranty claim line command' })\n if (!organizationId) throw new CrudHttpError(400, { error: '[internal] organization scope required for warranty claim line command' })\n ensureTenantScope(ctx, tenantId)\n ensureOrganizationScope(ctx, organizationId)\n return { tenantId, organizationId }\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? null : date.toISOString()\n}\n\nfunction toDate(value: string | null): Date | null {\n if (!value) return null\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction toDateOnlyIso(value: Date | string | null | undefined): string | null {\n const iso = toIso(value)\n return iso ? iso.slice(0, 10) : null\n}\n\nfunction toDateOnly(value: string | null): Date | null {\n if (!value) return null\n const date = new Date(`${value}T00:00:00.000Z`)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction amountString(value: number | string | null | undefined, fallback = '0'): string | null {\n if (value === null) return null\n if (value === undefined) return fallback\n const parsed = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(parsed)) return fallback\n return String(parsed)\n}\n\nfunction nullableAmountString(value: number | string | null | undefined): string | null {\n if (value === undefined || value === null) return null\n return amountString(value, '0')\n}\n\nfunction numberValue(value: string | number | null | undefined): number {\n if (value === null || value === undefined) return 0\n const parsed = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nfunction addMonths(date: Date, months: number): Date {\n const target = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, 1))\n const lastDay = new Date(Date.UTC(target.getUTCFullYear(), target.getUTCMonth() + 1, 0)).getUTCDate()\n const copy = new Date(date.getTime())\n copy.setUTCDate(1)\n copy.setUTCFullYear(target.getUTCFullYear(), target.getUTCMonth(), Math.min(date.getUTCDate(), lastDay))\n return copy\n}\n\nfunction computeWarrantyDates(\n purchaseDate: Date | null | undefined,\n warrantyMonths: number | null | undefined,\n): { warrantyExpiresAt: Date | null; warrantyStatus: WarrantyClaimWarrantyStatus } {\n if (!purchaseDate || warrantyMonths === null || warrantyMonths === undefined) {\n return { warrantyExpiresAt: null, warrantyStatus: 'unknown' }\n }\n const warrantyExpiresAt = addMonths(purchaseDate, warrantyMonths)\n const warrantyStatus = warrantyExpiresAt.getTime() >= Date.now() ? 'in_warranty' : 'out_of_warranty'\n return { warrantyExpiresAt, warrantyStatus }\n}\n\nfunction claimIdOf(line: WarrantyClaimLine): string {\n const claim = line.claim\n if (typeof claim === 'object' && claim && 'id' in claim && typeof claim.id === 'string') return claim.id\n return String(claim)\n}\n\nfunction claimOf(line: WarrantyClaimLine): WarrantyClaim | null {\n const claim = line.claim\n if (typeof claim === 'object' && claim && 'id' in claim && typeof claim.id === 'string') return claim as WarrantyClaim\n return null\n}\n\nfunction sameDateValue(left: Date | null | undefined, right: Date | null | undefined): boolean {\n const leftTime = left instanceof Date ? left.getTime() : null\n const rightTime = right instanceof Date ? right.getTime() : null\n return leftTime === rightTime\n}\n\nfunction assertParentMutable(claim: WarrantyClaim): void {\n if (!mutableParentStatuses.has(claim.status)) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n}\n\nfunction assertReceivingStatus(claim: WarrantyClaim): void {\n if (claim.status !== 'received' && claim.status !== 'inspecting') {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.receivingStatusInvalid' })\n }\n}\n\nfunction toConditionGrade(value: string | null | undefined): ConditionGrade | null {\n if (value === 'A' || value === 'B' || value === 'C' || value === 'D') return value\n return null\n}\n\nfunction assertLineQuantities(line: WarrantyClaimLine): void {\n const qtyClaimed = numberValue(line.qtyClaimed)\n const qtyApproved = line.qtyApproved === null || line.qtyApproved === undefined ? null : numberValue(line.qtyApproved)\n const qtyReceived = line.qtyReceived === null || line.qtyReceived === undefined ? null : numberValue(line.qtyReceived)\n if (qtyClaimed < 0 || (qtyApproved !== null && qtyApproved < 0) || (qtyReceived !== null && qtyReceived < 0)) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n if (qtyApproved !== null && qtyApproved > qtyClaimed) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n if (qtyReceived !== null && qtyReceived > qtyClaimed) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.lineLocked' })\n }\n}\n\nfunction assertLineStatusMove(from: WarrantyClaimLineStatus, to: WarrantyClaimLineStatus): void {\n if (from === to) return\n if (lineStatusGuards[from].includes(to)) return\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.invalidTransition' })\n}\n\nfunction buildLineCreateData(\n claim: WarrantyClaim,\n input: ClaimLineCreateInput,\n lineNo: number,\n): Partial<WarrantyClaimLine> & {\n id: string\n claim: WarrantyClaim\n organizationId: string\n tenantId: string\n lineNo: number\n qtyClaimed: string\n lineStatus: WarrantyClaimLineStatus\n warrantyStatus: WarrantyClaimWarrantyStatus\n createdAt: Date\n updatedAt: Date\n} {\n const purchaseDate = input.purchaseDate ?? null\n const warrantyMonths = input.warrantyMonths ?? null\n const computedWarranty = computeWarrantyDates(purchaseDate, warrantyMonths)\n return {\n id: randomUUID(),\n claim,\n organizationId: claim.organizationId,\n tenantId: claim.tenantId,\n lineNo,\n productId: input.productId ?? null,\n variantId: input.variantId ?? null,\n sku: input.sku ?? null,\n productName: input.productName ?? null,\n orderLineId: input.orderLineId ?? null,\n serialNumber: input.serialNumber ?? null,\n lotNumber: input.lotNumber ?? null,\n purchaseDate,\n warrantyMonths,\n warrantyExpiresAt: input.warrantyExpiresAt ?? computedWarranty.warrantyExpiresAt,\n warrantyStatus: input.warrantyStatus ?? computedWarranty.warrantyStatus,\n faultCode: input.faultCode ?? null,\n faultDescription: input.faultDescription ?? null,\n qtyClaimed: amountString(input.qtyClaimed, '1') ?? '1',\n qtyApproved: nullableAmountString(input.qtyApproved),\n qtyReceived: nullableAmountString(input.qtyReceived),\n conditionOnReceipt: input.conditionOnReceipt ?? null,\n inspectionNotes: input.inspectionNotes ?? null,\n disposition: input.disposition ?? null,\n vendorName: input.vendorName ?? null,\n lineStatus: 'pending',\n creditAmount: nullableAmountString(input.creditAmount),\n restockingFee: nullableAmountString(input.restockingFee),\n coreChargeAmount: nullableAmountString(input.coreChargeAmount),\n coreCreditAmount: nullableAmountString(input.coreCreditAmount),\n createdAt: new Date(),\n updatedAt: new Date(),\n }\n}\n\nfunction applyLineUpdate(line: WarrantyClaimLine, input: ClaimLineUpdateInput): void {\n const purchaseDateChanged = hasOwn(input, 'purchaseDate') && !sameDateValue(line.purchaseDate ?? null, input.purchaseDate ?? null)\n const warrantyMonthsChanged = hasOwn(input, 'warrantyMonths') && (line.warrantyMonths ?? null) !== (input.warrantyMonths ?? null)\n const hasExplicitWarranty = hasOwn(input, 'warrantyExpiresAt') || hasOwn(input, 'warrantyStatus')\n if (hasOwn(input, 'lineNo') && input.lineNo !== undefined) line.lineNo = input.lineNo\n if (hasOwn(input, 'productId')) line.productId = input.productId ?? null\n if (hasOwn(input, 'variantId')) line.variantId = input.variantId ?? null\n if (hasOwn(input, 'sku')) line.sku = input.sku ?? null\n if (hasOwn(input, 'productName')) line.productName = input.productName ?? null\n if (hasOwn(input, 'orderLineId')) line.orderLineId = input.orderLineId ?? null\n if (hasOwn(input, 'serialNumber')) line.serialNumber = input.serialNumber ?? null\n if (hasOwn(input, 'lotNumber')) line.lotNumber = input.lotNumber ?? null\n if (hasOwn(input, 'purchaseDate')) line.purchaseDate = input.purchaseDate ?? null\n if (hasOwn(input, 'warrantyMonths')) line.warrantyMonths = input.warrantyMonths ?? null\n if (hasOwn(input, 'warrantyExpiresAt')) line.warrantyExpiresAt = input.warrantyExpiresAt ?? null\n if (hasOwn(input, 'warrantyStatus') && input.warrantyStatus) line.warrantyStatus = input.warrantyStatus\n if (hasOwn(input, 'faultCode')) line.faultCode = input.faultCode ?? null\n if (hasOwn(input, 'faultDescription')) line.faultDescription = input.faultDescription ?? null\n if (hasOwn(input, 'qtyClaimed') && input.qtyClaimed !== undefined) line.qtyClaimed = amountString(input.qtyClaimed, '1') ?? '1'\n if (hasOwn(input, 'qtyApproved')) line.qtyApproved = nullableAmountString(input.qtyApproved)\n if (hasOwn(input, 'qtyReceived')) line.qtyReceived = nullableAmountString(input.qtyReceived)\n if (hasOwn(input, 'conditionOnReceipt')) line.conditionOnReceipt = input.conditionOnReceipt ?? null\n if (hasOwn(input, 'inspectionNotes')) line.inspectionNotes = input.inspectionNotes ?? null\n if (hasOwn(input, 'disposition')) line.disposition = input.disposition ?? null\n if (hasOwn(input, 'vendorName')) line.vendorName = input.vendorName ?? null\n if (hasOwn(input, 'lineStatus') && input.lineStatus) {\n assertLineStatusMove(line.lineStatus, input.lineStatus)\n line.lineStatus = input.lineStatus\n }\n if (hasOwn(input, 'creditAmount')) line.creditAmount = nullableAmountString(input.creditAmount)\n if (hasOwn(input, 'restockingFee')) line.restockingFee = nullableAmountString(input.restockingFee)\n if (hasOwn(input, 'coreChargeAmount')) line.coreChargeAmount = nullableAmountString(input.coreChargeAmount)\n if (hasOwn(input, 'coreCreditAmount')) line.coreCreditAmount = nullableAmountString(input.coreCreditAmount)\n if ((purchaseDateChanged || warrantyMonthsChanged) && !hasExplicitWarranty) {\n const computedWarranty = computeWarrantyDates(line.purchaseDate ?? null, line.warrantyMonths ?? null)\n line.warrantyExpiresAt = computedWarranty.warrantyExpiresAt\n line.warrantyStatus = computedWarranty.warrantyStatus\n }\n line.updatedAt = new Date()\n}\n\nfunction snapshotLine(line: WarrantyClaimLine): LineSnapshot {\n return {\n id: line.id,\n claimId: claimIdOf(line),\n organizationId: line.organizationId,\n tenantId: line.tenantId,\n lineNo: line.lineNo,\n productId: line.productId ?? null,\n variantId: line.variantId ?? null,\n sku: line.sku ?? null,\n productName: line.productName ?? null,\n orderLineId: line.orderLineId ?? null,\n serialNumber: line.serialNumber ?? null,\n lotNumber: line.lotNumber ?? null,\n purchaseDate: toDateOnlyIso(line.purchaseDate),\n warrantyMonths: line.warrantyMonths ?? null,\n warrantyExpiresAt: toDateOnlyIso(line.warrantyExpiresAt),\n warrantyStatus: line.warrantyStatus,\n faultCode: line.faultCode ?? null,\n faultDescription: line.faultDescription ?? null,\n qtyClaimed: line.qtyClaimed,\n qtyApproved: line.qtyApproved ?? null,\n qtyReceived: line.qtyReceived ?? null,\n conditionOnReceipt: line.conditionOnReceipt ?? null,\n conditionGrade: line.conditionGrade ?? null,\n quarantineStatus: line.quarantineStatus,\n inspectionNotes: line.inspectionNotes ?? null,\n assessmentPayload: line.assessmentPayload ?? null,\n disposition: line.disposition ?? null,\n lineStatus: line.lineStatus,\n creditAmount: line.creditAmount ?? null,\n restockingFee: line.restockingFee ?? null,\n coreChargeAmount: line.coreChargeAmount ?? null,\n coreCreditAmount: line.coreCreditAmount ?? null,\n vendorClaimLineId: line.vendorClaimLineId ?? null,\n vendorName: line.vendorName ?? null,\n deletedAt: toIso(line.deletedAt),\n updatedAt: toIso(line.updatedAt),\n }\n}\n\nasync function loadLineSnapshot(\n em: EntityManager,\n lineId: string,\n scope: WarrantyClaimScope,\n): Promise<LineSnapshot | null> {\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n { id: lineId, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { populate: ['claim'] },\n scope,\n )\n return line ? snapshotLine(line) : null\n}\n\nasync function assertLineSnapshotNumberAvailable(em: EntityManager, claim: WarrantyClaim, snapshot: LineSnapshot): Promise<void> {\n if (!snapshot.deletedAt) await assertClaimLineNumberAvailable(em, claim, snapshot.lineNo, snapshot.id, true)\n else await requireScopedClaim(em, claim.id, { tenantId: claim.tenantId, organizationId: claim.organizationId }, { lockMode: LockMode.PESSIMISTIC_WRITE })\n}\n\nfunction restoreLineFromSnapshot(line: WarrantyClaimLine, snapshot: LineSnapshot): void {\n line.lineNo = snapshot.lineNo\n line.productId = snapshot.productId\n line.variantId = snapshot.variantId\n line.sku = snapshot.sku\n line.productName = snapshot.productName\n line.orderLineId = snapshot.orderLineId\n line.serialNumber = snapshot.serialNumber\n line.lotNumber = snapshot.lotNumber\n line.purchaseDate = toDateOnly(snapshot.purchaseDate)\n line.warrantyMonths = snapshot.warrantyMonths\n line.warrantyExpiresAt = toDateOnly(snapshot.warrantyExpiresAt)\n line.warrantyStatus = snapshot.warrantyStatus\n line.faultCode = snapshot.faultCode\n line.faultDescription = snapshot.faultDescription\n line.qtyClaimed = snapshot.qtyClaimed\n line.qtyApproved = snapshot.qtyApproved\n line.qtyReceived = snapshot.qtyReceived\n line.conditionOnReceipt = snapshot.conditionOnReceipt\n line.conditionGrade = snapshot.conditionGrade\n line.quarantineStatus = snapshot.quarantineStatus\n line.inspectionNotes = snapshot.inspectionNotes\n line.assessmentPayload = snapshot.assessmentPayload\n line.disposition = snapshot.disposition\n line.lineStatus = snapshot.lineStatus\n line.creditAmount = snapshot.creditAmount\n line.restockingFee = snapshot.restockingFee\n line.coreChargeAmount = snapshot.coreChargeAmount\n line.coreCreditAmount = snapshot.coreCreditAmount\n line.vendorClaimLineId = snapshot.vendorClaimLineId\n line.vendorName = snapshot.vendorName\n line.deletedAt = toDate(snapshot.deletedAt)\n line.updatedAt = new Date()\n}\n\nasync function recomputeClaimRollups(em: EntityManager, claim: WarrantyClaim): Promise<void> {\n const lines = await findWithDecryption(\n em,\n WarrantyClaimLine,\n {\n claim: claim.id,\n tenantId: claim.tenantId,\n organizationId: claim.organizationId,\n deletedAt: null,\n },\n {},\n { tenantId: claim.tenantId, organizationId: claim.organizationId },\n )\n const totals = computeHeaderRollups(lines, { claimType: claim.claimType })\n claim.totalClaimedAmount = String(totals.totalClaimedAmount)\n claim.totalApprovedAmount = String(totals.totalApprovedAmount)\n claim.updatedAt = new Date()\n}\n\nfunction refuseLineUndoConflict(): Promise<never> {\n return throwUndoRefusal('warranty_claims.errors.conflict', 'This claim changed while you were editing it.')\n}\n\nasync function requireCurrentLineUndoState(\n em: EntityManager,\n after: LineSnapshot | null | undefined,\n rule: LineUndoParentRule,\n): Promise<LineUndoState> {\n if (!after) return refuseLineUndoConflict()\n const scope: WarrantyClaimScope = { tenantId: after.tenantId, organizationId: after.organizationId }\n const claim = await findOneWithDecryption(\n em,\n WarrantyClaim,\n { id: after.claimId, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scope,\n )\n if (!claim) return refuseLineUndoConflict()\n if (!rule.allowedParentStatuses.has(claim.status)) return throwUndoRefusal(rule.lockedKey, rule.lockedFallback)\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n { id: after.id, tenantId: scope.tenantId, organizationId: scope.organizationId },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n scope,\n )\n if (!line || !after.updatedAt || toIso(line.updatedAt) !== after.updatedAt) return refuseLineUndoConflict()\n return { claim, line }\n}\n\nasync function validateLineRestore(\n em: EntityManager,\n ctx: CommandRuntimeContext,\n state: LineUndoState,\n before: LineSnapshot,\n): Promise<void> {\n if (!before.orderLineId || before.deletedAt) return\n const scope = { tenantId: state.claim.tenantId, organizationId: state.claim.organizationId }\n try {\n await validateClaimReferences(ctx, scope, {\n lineOrderRefs: [{ orderLineId: before.orderLineId, orderId: state.claim.orderId ?? null }],\n })\n if (before.lineStatus === 'rejected') return\n await assertPendingClaimQuantitiesWithinSold(em, scope, new Map([[before.orderLineId, [{\n id: state.line.id,\n orderLineId: before.orderLineId,\n qtyClaimed: before.qtyClaimed,\n }]]]), { currentClaimId: state.claim.id, excludedLineIds: new Set([state.line.id]) })\n } catch (error) {\n if (error instanceof CrudHttpError) {\n if (error.body.error === 'warranty_claims.errors.qtyExceedsOrdered') {\n return throwUndoRefusal('warranty_claims.errors.qtyExceedsOrdered', 'Claimed quantity exceeds the quantity sold on the linked order line.')\n }\n if (error.body.error === 'warranty_claims.errors.invalidReference') {\n return throwUndoRefusal('warranty_claims.errors.invalidReference', 'The referenced order, order line, return, or replacement order could not be found.')\n }\n }\n throw error\n }\n}\n\nasync function requireScopedLine(\n em: EntityManager,\n lineId: string,\n scope: WarrantyClaimScope,\n): Promise<WarrantyClaimLine> {\n const line = await findOneWithDecryption(\n em,\n WarrantyClaimLine,\n { id: lineId, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },\n { populate: ['claim'] },\n scope,\n )\n if (!line) throw new CrudHttpError(404, { error: 'warranty_claims.errors.notFound' })\n return line\n}\n\nasync function emitClaimUpdated(ctx: CommandRuntimeContext, claim: WarrantyClaim): Promise<void> {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: claim,\n identifiers: { id: claim.id, organizationId: claim.organizationId, tenantId: claim.tenantId },\n indexer: { entityType: E.warranty_claims.warranty_claim },\n events: claimCrudEvents,\n })\n await invalidateCrudCache(\n ctx.container,\n 'warranty_claims.claim',\n { id: claim.id, organizationId: claim.organizationId, tenantId: claim.tenantId },\n ctx.auth?.tenantId ?? null,\n 'warranty_claims.claim_line.rollup',\n )\n}\n\nasync function emitLineCrud(\n ctx: CommandRuntimeContext,\n action: 'created' | 'updated' | 'deleted',\n line: WarrantyClaimLine,\n): Promise<void> {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action,\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.warranty_claims.warranty_claim_line },\n })\n await invalidateCrudCache(\n ctx.container,\n 'warranty_claims.claim_line',\n { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n ctx.auth?.tenantId ?? null,\n `warranty_claims.claim_line.${action}`,\n )\n}\n\nasync function emitLineUndoCrud(\n ctx: CommandRuntimeContext,\n action: 'created' | 'updated' | 'deleted',\n line: WarrantyClaimLine,\n): Promise<void> {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudUndoSideEffects({\n dataEngine,\n action,\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.warranty_claims.warranty_claim_line },\n })\n await invalidateCrudCache(\n ctx.container,\n 'warranty_claims.claim_line',\n { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n ctx.auth?.tenantId ?? null,\n `warranty_claims.claim_line.undo.${action}`,\n )\n}\n\nasync function buildLineLog(\n actionLabelKey: string,\n actionLabelFallback: string,\n resourceId: string,\n snapshots: { before?: unknown; after?: unknown },\n): Promise<{\n actionLabel: string\n resourceKind: string\n resourceId: string\n parentResourceKind: string | null\n parentResourceId: string | null\n tenantId: string | null\n organizationId: string | null\n snapshotBefore: unknown\n snapshotAfter: unknown\n payload: { undo: LineUndoPayload }\n}> {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as LineSnapshot | null | undefined\n const before = snapshots.before as LineSnapshot | null | undefined\n return {\n actionLabel: translate(actionLabelKey, actionLabelFallback),\n resourceKind: WARRANTY_CLAIM_LINE_RESOURCE_KIND,\n resourceId,\n parentResourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n parentResourceId: after?.claimId ?? before?.claimId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: { undo: { before: before ?? null, after: after ?? null } },\n }\n}\n\nconst createClaimLineCommand: CommandHandler<ClaimLineCreateInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.create',\n isUndoable: true,\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineCreateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const claim = await requireScopedClaim(em, input.claimId, scope)\n assertDispositionAllowedForType(claim.claimType, input.disposition ?? null)\n await enforceWarrantyClaimOptimisticLock(ctx, claim)\n assertParentMutable(claim)\n let line!: WarrantyClaimLine\n let nextLineNo = input.lineNo ?? 1\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true })\n assertParentMutable(claim)\n if (input.orderLineId) {\n await validateClaimReferences(ctx, scope, {\n lineOrderRefs: [{ orderLineId: input.orderLineId, orderId: claim.orderId ?? null }],\n })\n }\n const existingLines = await findWithDecryption(\n em,\n WarrantyClaimLine,\n {\n claim: claim.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n },\n {},\n scope,\n )\n if (input.orderLineId) {\n await assertPendingClaimQuantitiesWithinSold(em, scope, new Map([[input.orderLineId, [{\n orderLineId: input.orderLineId,\n qtyClaimed: amountString(input.qtyClaimed, '1') ?? '1',\n }]]]), { currentClaimId: claim.id })\n }\n nextLineNo = input.lineNo ?? existingLines.reduce((max, entry) => Math.max(max, entry.lineNo), 0) + 1\n await assertUniqueClaimLineNumbers([nextLineNo, ...existingLines.filter((line) => line.lineNo === nextLineNo).map((line) => line.lineNo)])\n },\n () => {\n line = em.create(WarrantyClaimLine, buildLineCreateData(claim, input, nextLineNo))\n assertLineQuantities(line)\n em.persist(line)\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.create' })\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'created', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineCreateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.create', 'Add claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n },\n () => {\n state.line.deletedAt = new Date()\n state.line.updatedAt = new Date()\n },\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.create.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'deleted', state.line)\n },\n}\n\nconst updateClaimLineCommand: CommandHandler<ClaimLineUpdateInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.update',\n isUndoable: true,\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(claimLineUpdateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineUpdateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = claimOf(line) ?? await requireScopedClaim(em, input.claimId ?? claimIdOf(line), scope)\n if (input.claimId && input.claimId !== claim.id) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.notFound' })\n }\n assertParentMutable(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND)\n const effectiveOrderLineId = hasOwn(input, 'orderLineId') ? (input.orderLineId ?? null) : (line.orderLineId ?? null)\n if (hasOwn(input, 'disposition')) {\n assertDispositionAllowedForType(claim.claimType, input.disposition ?? null)\n assertDispositionAllowedForGrade(toConditionGrade(line.conditionGrade), input.disposition ?? null)\n }\n const guardsClaimedQty = hasOwn(input, 'qtyClaimed') || hasOwn(input, 'orderLineId')\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE, refresh: true })\n assertParentMutable(claim)\n if (input.lineNo !== undefined) await assertClaimLineNumberAvailable(em, claim, input.lineNo, line.id)\n if (effectiveOrderLineId && (claim.orderId || effectiveOrderLineId !== (line.orderLineId ?? null))) {\n await validateClaimReferences(ctx, scope, {\n lineOrderRefs: [{\n orderLineId: effectiveOrderLineId,\n orderId: claim.orderId ?? null,\n allowMissing: effectiveOrderLineId === (line.orderLineId ?? null),\n }],\n })\n }\n if (!guardsClaimedQty) return\n const orderLineId = hasOwn(input, 'orderLineId') ? (input.orderLineId ?? null) : (line.orderLineId ?? null)\n if (orderLineId) {\n await assertPendingClaimQuantitiesWithinSold(\n em,\n scope,\n new Map([[orderLineId, [{\n id: line.id,\n orderLineId,\n qtyClaimed: hasOwn(input, 'qtyClaimed')\n ? (amountString(input.qtyClaimed, '1') ?? '1')\n : line.qtyClaimed,\n }]]]),\n { currentClaimId: claim.id, excludedLineIds: new Set([line.id]) },\n )\n }\n },\n () => {\n applyLineUpdate(line, input)\n assertLineQuantities(line)\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.update' })\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineUpdateSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.update', 'Update claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n await validateLineRestore(em, ctx, state, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.update.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nconst deleteClaimLineCommand: CommandHandler<LineDeleteInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.delete',\n isUndoable: true,\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(lineDeleteSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(lineDeleteSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = claimOf(line) ?? await requireScopedClaim(em, input.claimId ?? claimIdOf(line), scope)\n if (input.claimId && input.claimId !== claim.id) {\n throw new CrudHttpError(400, { error: 'warranty_claims.errors.notFound' })\n }\n assertParentMutable(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND)\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.deletedAt = new Date()\n line.updatedAt = new Date()\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.delete' })\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'deleted', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(lineDeleteSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.delete', 'Delete claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n await validateLineRestore(em, ctx, state, before)\n },\n () => {\n restoreLineFromSnapshot(state.line, before)\n state.line.deletedAt = null\n },\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.delete.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'created', state.line)\n },\n}\n\nconst receiveClaimLineCommand: CommandHandler<ClaimLineReceiveInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.receive',\n isUndoable: true,\n redo: ({ input, ctx }) => receiveClaimLineCommand.execute({ ...input, updatedAt: undefined }, ctx),\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(claimLineReceiveSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineReceiveSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = await requireScopedClaim(em, claimIdOf(line), scope)\n assertReceivingStatus(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND, input.updatedAt ?? undefined)\n\n const grade: ConditionGrade = input.conditionGrade\n const effectiveSettings = await resolveEffectiveWarrantyClaimSettings(em, scope)\n const shouldQuarantine = Boolean(effectiveSettings.quarantineGrades?.includes(grade))\n\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.conditionGrade = grade\n if (input.inspectionNotes !== undefined) line.inspectionNotes = input.inspectionNotes ?? null\n\n const suggestedDisposition = suggestedDispositionForGrade(grade)\n if (!line.disposition && suggestedDisposition && resolveClaimTypeUiConfig(claim.claimType).allowedDispositions.includes(suggestedDisposition)) {\n assertDispositionAllowedForGrade(grade, suggestedDisposition)\n if (suggestedDisposition === 'restock' || suggestedDisposition === 'repair' || suggestedDisposition === 'scrap') {\n line.disposition = suggestedDisposition\n }\n }\n assertDispositionAllowedForGrade(grade, line.disposition ?? null)\n\n if (shouldQuarantine) line.quarantineStatus = 'held'\n line.updatedAt = new Date()\n appendClaimEvent(em, claim, 'system', {\n visibility: 'internal',\n payload: {\n action: 'line_received',\n lineId: line.id,\n conditionGrade: grade,\n disposition: line.disposition ?? null,\n quarantineStatus: line.quarantineStatus,\n quarantineHeld: shouldQuarantine,\n },\n actorUserId: ctx.auth?.sub ?? null,\n })\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.receive' })\n\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n if (shouldQuarantine) {\n await emitWarrantyClaimsEvent('warranty_claims.claim_line.quarantined', {\n id: line.id,\n claimId: claim.id,\n lineId: line.id,\n grade,\n scope,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n }, {\n persistent: true,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n }\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineReceiveSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.receive', 'Receive claim line', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n await validateLineRestore(em, ctx, state, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.receive.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nconst releaseClaimLineQuarantineCommand: CommandHandler<ClaimLineReleaseQuarantineInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.release_quarantine',\n isUndoable: true,\n redo: ({ input, ctx }) => releaseClaimLineQuarantineCommand.execute({ ...input, updatedAt: undefined }, ctx),\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(claimLineReleaseQuarantineSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(claimLineReleaseQuarantineSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = await requireScopedClaim(em, claimIdOf(line), scope)\n assertReceivingStatus(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND, input.updatedAt ?? undefined)\n\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.quarantineStatus = 'released'\n line.updatedAt = new Date()\n appendClaimEvent(em, claim, 'system', {\n visibility: 'internal',\n payload: {\n action: 'line_quarantine_released',\n lineId: line.id,\n quarantineStatus: line.quarantineStatus,\n },\n actorUserId: ctx.auth?.sub ?? null,\n })\n },\n () => recomputeClaimRollups(em, claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.release_quarantine' })\n\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(claimLineReleaseQuarantineSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.release_quarantine', 'Release line from quarantine', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n await validateLineRestore(em, ctx, state, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.release_quarantine.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nconst setClaimLineAssessmentCommand: CommandHandler<LineSetAssessmentInput, { lineId: string; claimId: string }> = {\n id: 'warranty_claims.claim_line.set_assessment',\n isUndoable: true,\n redo: ({ input, ctx }) => setClaimLineAssessmentCommand.execute({ ...input, updatedAt: undefined }, ctx),\n prepare: async (rawInput, ctx) => {\n const input = parseCommandInput(lineSetAssessmentSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return { before: await loadLineSnapshot(em, input.id, scope) }\n },\n async execute(rawInput, ctx) {\n const input = parseCommandInput(lineSetAssessmentSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const line = await requireScopedLine(em, input.id, scope)\n const claim = claimOf(line) ?? await requireScopedClaim(em, claimIdOf(line), scope)\n assertParentMutable(claim)\n await enforceWarrantyClaimOptimisticLock(ctx, line, WARRANTY_CLAIM_LINE_RESOURCE_KIND, input.updatedAt ?? undefined)\n\n await withAtomicFlush(em, [\n async () => {\n await requireScopedClaim(em, claim.id, scope, { lockMode: LockMode.PESSIMISTIC_WRITE })\n },\n () => {\n line.assessmentPayload = input.assessmentPayload\n line.updatedAt = new Date()\n claim.updatedAt = new Date()\n appendClaimEvent(em, claim, 'system', {\n visibility: 'internal',\n payload: {\n action: 'ai_assessment_recorded',\n lineId: line.id,\n },\n actorUserId: ctx.auth?.sub ?? null,\n })\n },\n ], { transaction: true, label: 'warranty_claims.claim_line.set_assessment' })\n\n await emitClaimUpdated(ctx, claim)\n await emitLineCrud(ctx, 'updated', line)\n return { lineId: line.id, claimId: claim.id }\n },\n captureAfter: async (rawInput, result, ctx) => {\n const input = parseCommandInput(lineSetAssessmentSchema, rawInput)\n const scope = resolveScope(ctx, input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadLineSnapshot(em, result.lineId, scope)\n },\n buildLog: async ({ result, snapshots }) => buildLineLog('warranty_claims.audit.claim_line.set_assessment', 'Record line assessment', result.lineId, snapshots),\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<LineUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let state!: LineUndoState\n await withAtomicFlush(em, [\n async () => {\n state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)\n await assertLineSnapshotNumberAvailable(em, state.claim, before)\n await validateLineRestore(em, ctx, state, before)\n },\n () => restoreLineFromSnapshot(state.line, before),\n () => recomputeClaimRollups(em, state.claim),\n ], { transaction: true, label: 'warranty_claims.claim_line.set_assessment.undo' })\n await emitClaimUpdated(ctx, state.claim)\n await emitLineUndoCrud(ctx, 'updated', state.line)\n },\n}\n\nregisterCommand(createClaimLineCommand)\nregisterCommand(updateClaimLineCommand)\nregisterCommand(deleteClaimLineCommand)\nregisterCommand(receiveClaimLineCommand)\nregisterCommand(releaseClaimLineQuarantineCommand)\nregisterCommand(setClaimLineAssessmentCommand)\n\nexport const claimLineCommands = [\n createClaimLineCommand,\n updateClaimLineCommand,\n deleteClaimLineCommand,\n receiveClaimLineCommand,\n releaseClaimLineQuarantineCommand,\n setClaimLineAssessmentCommand,\n]\n\nexport {\n createClaimLineCommand,\n updateClaimLineCommand,\n deleteClaimLineCommand,\n receiveClaimLineCommand,\n releaseClaimLineQuarantineCommand,\n setClaimLineAssessmentCommand,\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["line"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.7784.1.80225b38e4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -266,16 +266,16 @@
|
|
|
266
266
|
"zod": "^4.4.3"
|
|
267
267
|
},
|
|
268
268
|
"peerDependencies": {
|
|
269
|
-
"@open-mercato/ai-assistant": "0.10.1-develop.
|
|
270
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
271
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
269
|
+
"@open-mercato/ai-assistant": "0.10.1-develop.7784.1.80225b38e4",
|
|
270
|
+
"@open-mercato/shared": "0.10.1-develop.7784.1.80225b38e4",
|
|
271
|
+
"@open-mercato/ui": "0.10.1-develop.7784.1.80225b38e4",
|
|
272
272
|
"react": "^19.0.0",
|
|
273
273
|
"react-dom": "^19.0.0"
|
|
274
274
|
},
|
|
275
275
|
"devDependencies": {
|
|
276
|
-
"@open-mercato/ai-assistant": "0.10.1-develop.
|
|
277
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
278
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
276
|
+
"@open-mercato/ai-assistant": "0.10.1-develop.7784.1.80225b38e4",
|
|
277
|
+
"@open-mercato/shared": "0.10.1-develop.7784.1.80225b38e4",
|
|
278
|
+
"@open-mercato/ui": "0.10.1-develop.7784.1.80225b38e4",
|
|
279
279
|
"@testing-library/dom": "^10.4.1",
|
|
280
280
|
"@testing-library/jest-dom": "^7.0.1",
|
|
281
281
|
"@testing-library/react": "^16.3.3",
|
|
@@ -7,6 +7,7 @@ import { RbacService } from '@open-mercato/core/modules/auth/services/rbacServic
|
|
|
7
7
|
import { CustomerUserService } from '@open-mercato/core/modules/customer_accounts/services/customerUserService'
|
|
8
8
|
import { CustomerUser, CustomerUserRole, CustomerRole } from '@open-mercato/core/modules/customer_accounts/data/entities'
|
|
9
9
|
import { Organization } from '@open-mercato/core/modules/directory/data/entities'
|
|
10
|
+
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
10
11
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
11
12
|
import { adminCreateUserSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'
|
|
12
13
|
import { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'
|
|
@@ -46,11 +47,23 @@ export async function GET(req: Request) {
|
|
|
46
47
|
const roleId = url.searchParams.get('roleId')
|
|
47
48
|
const search = url.searchParams.get('search')
|
|
48
49
|
|
|
50
|
+
const organizationScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })
|
|
49
51
|
const where: Record<string, unknown> = {
|
|
50
52
|
tenantId: auth.tenantId,
|
|
51
|
-
organizationId: auth.orgId,
|
|
52
53
|
deletedAt: null,
|
|
53
54
|
}
|
|
55
|
+
if (Array.isArray(organizationScope.filterIds)) {
|
|
56
|
+
if (organizationScope.filterIds.length === 0) {
|
|
57
|
+
return NextResponse.json({
|
|
58
|
+
ok: true,
|
|
59
|
+
items: [],
|
|
60
|
+
total: 0,
|
|
61
|
+
totalPages: 1,
|
|
62
|
+
page,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
where.organizationId = { $in: organizationScope.filterIds }
|
|
66
|
+
}
|
|
54
67
|
|
|
55
68
|
if (status === 'active') {
|
|
56
69
|
where.isActive = true
|
|
@@ -547,6 +547,37 @@ async function requireCurrentLineUndoState(
|
|
|
547
547
|
return { claim, line }
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
+
async function validateLineRestore(
|
|
551
|
+
em: EntityManager,
|
|
552
|
+
ctx: CommandRuntimeContext,
|
|
553
|
+
state: LineUndoState,
|
|
554
|
+
before: LineSnapshot,
|
|
555
|
+
): Promise<void> {
|
|
556
|
+
if (!before.orderLineId || before.deletedAt) return
|
|
557
|
+
const scope = { tenantId: state.claim.tenantId, organizationId: state.claim.organizationId }
|
|
558
|
+
try {
|
|
559
|
+
await validateClaimReferences(ctx, scope, {
|
|
560
|
+
lineOrderRefs: [{ orderLineId: before.orderLineId, orderId: state.claim.orderId ?? null }],
|
|
561
|
+
})
|
|
562
|
+
if (before.lineStatus === 'rejected') return
|
|
563
|
+
await assertPendingClaimQuantitiesWithinSold(em, scope, new Map([[before.orderLineId, [{
|
|
564
|
+
id: state.line.id,
|
|
565
|
+
orderLineId: before.orderLineId,
|
|
566
|
+
qtyClaimed: before.qtyClaimed,
|
|
567
|
+
}]]]), { currentClaimId: state.claim.id, excludedLineIds: new Set([state.line.id]) })
|
|
568
|
+
} catch (error) {
|
|
569
|
+
if (error instanceof CrudHttpError) {
|
|
570
|
+
if (error.body.error === 'warranty_claims.errors.qtyExceedsOrdered') {
|
|
571
|
+
return throwUndoRefusal('warranty_claims.errors.qtyExceedsOrdered', 'Claimed quantity exceeds the quantity sold on the linked order line.')
|
|
572
|
+
}
|
|
573
|
+
if (error.body.error === 'warranty_claims.errors.invalidReference') {
|
|
574
|
+
return throwUndoRefusal('warranty_claims.errors.invalidReference', 'The referenced order, order line, return, or replacement order could not be found.')
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
throw error
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
550
581
|
async function requireScopedLine(
|
|
551
582
|
em: EntityManager,
|
|
552
583
|
lineId: string,
|
|
@@ -824,6 +855,7 @@ const updateClaimLineCommand: CommandHandler<ClaimLineUpdateInput, { lineId: str
|
|
|
824
855
|
async () => {
|
|
825
856
|
state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)
|
|
826
857
|
await assertLineSnapshotNumberAvailable(em, state.claim, before)
|
|
858
|
+
await validateLineRestore(em, ctx, state, before)
|
|
827
859
|
},
|
|
828
860
|
() => restoreLineFromSnapshot(state.line, before),
|
|
829
861
|
() => recomputeClaimRollups(em, state.claim),
|
|
@@ -884,6 +916,7 @@ const deleteClaimLineCommand: CommandHandler<LineDeleteInput, { lineId: string;
|
|
|
884
916
|
async () => {
|
|
885
917
|
state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)
|
|
886
918
|
await assertLineSnapshotNumberAvailable(em, state.claim, before)
|
|
919
|
+
await validateLineRestore(em, ctx, state, before)
|
|
887
920
|
},
|
|
888
921
|
() => {
|
|
889
922
|
restoreLineFromSnapshot(state.line, before)
|
|
@@ -990,6 +1023,7 @@ const receiveClaimLineCommand: CommandHandler<ClaimLineReceiveInput, { lineId: s
|
|
|
990
1023
|
async () => {
|
|
991
1024
|
state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule)
|
|
992
1025
|
await assertLineSnapshotNumberAvailable(em, state.claim, before)
|
|
1026
|
+
await validateLineRestore(em, ctx, state, before)
|
|
993
1027
|
},
|
|
994
1028
|
() => restoreLineFromSnapshot(state.line, before),
|
|
995
1029
|
() => recomputeClaimRollups(em, state.claim),
|
|
@@ -1059,6 +1093,7 @@ const releaseClaimLineQuarantineCommand: CommandHandler<ClaimLineReleaseQuaranti
|
|
|
1059
1093
|
async () => {
|
|
1060
1094
|
state = await requireCurrentLineUndoState(em, payload?.after, receivingParentUndoRule)
|
|
1061
1095
|
await assertLineSnapshotNumberAvailable(em, state.claim, before)
|
|
1096
|
+
await validateLineRestore(em, ctx, state, before)
|
|
1062
1097
|
},
|
|
1063
1098
|
() => restoreLineFromSnapshot(state.line, before),
|
|
1064
1099
|
() => recomputeClaimRollups(em, state.claim),
|
|
@@ -1127,6 +1162,7 @@ const setClaimLineAssessmentCommand: CommandHandler<LineSetAssessmentInput, { li
|
|
|
1127
1162
|
async () => {
|
|
1128
1163
|
state = await requireCurrentLineUndoState(em, payload?.after, mutableParentUndoRule)
|
|
1129
1164
|
await assertLineSnapshotNumberAvailable(em, state.claim, before)
|
|
1165
|
+
await validateLineRestore(em, ctx, state, before)
|
|
1130
1166
|
},
|
|
1131
1167
|
() => restoreLineFromSnapshot(state.line, before),
|
|
1132
1168
|
() => recomputeClaimRollups(em, state.claim),
|