@open-mercato/core 0.4.5-develop-3ce83a8b24 → 0.4.5-develop-539cff4960
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/dist/generated/entities/catalog_product/index.js +16 -0
- package/dist/generated/entities/catalog_product/index.js.map +2 -2
- package/dist/generated/entities/catalog_product_unit_conversion/index.js +27 -0
- package/dist/generated/entities/catalog_product_unit_conversion/index.js.map +7 -0
- package/dist/generated/entities/sales_credit_memo_line/index.js +7 -1
- package/dist/generated/entities/sales_credit_memo_line/index.js.map +2 -2
- package/dist/generated/entities/sales_invoice_line/index.js +7 -1
- package/dist/generated/entities/sales_invoice_line/index.js.map +2 -2
- package/dist/generated/entities/sales_order_line/index.js +6 -0
- package/dist/generated/entities/sales_order_line/index.js.map +2 -2
- package/dist/generated/entities/sales_quote_line/index.js +6 -0
- package/dist/generated/entities/sales_quote_line/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/api/prices/route.js +123 -8
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/product-unit-conversions/route.js +194 -0
- package/dist/modules/catalog/api/product-unit-conversions/route.js.map +7 -0
- package/dist/modules/catalog/api/products/route.js +351 -201
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +1267 -497
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +733 -210
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/commands/index.js +1 -0
- package/dist/modules/catalog/commands/index.js.map +2 -2
- package/dist/modules/catalog/commands/productUnitConversions.js +503 -0
- package/dist/modules/catalog/commands/productUnitConversions.js.map +7 -0
- package/dist/modules/catalog/commands/products.js +355 -73
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/shared.js +18 -4
- package/dist/modules/catalog/commands/shared.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductUomSection.js +591 -0
- package/dist/modules/catalog/components/products/ProductUomSection.js.map +7 -0
- package/dist/modules/catalog/components/products/productForm.js +66 -5
- package/dist/modules/catalog/components/products/productForm.js.map +2 -2
- package/dist/modules/catalog/components/products/productFormUtils.js +68 -0
- package/dist/modules/catalog/components/products/productFormUtils.js.map +7 -0
- package/dist/modules/catalog/data/entities.js +86 -0
- package/dist/modules/catalog/data/entities.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +65 -3
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/events.js +3 -0
- package/dist/modules/catalog/events.js.map +2 -2
- package/dist/modules/catalog/lib/unitCodes.js +7 -0
- package/dist/modules/catalog/lib/unitCodes.js.map +7 -0
- package/dist/modules/catalog/lib/unitResolution.js +53 -0
- package/dist/modules/catalog/lib/unitResolution.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260218225422.js +19 -0
- package/dist/modules/catalog/migrations/Migration20260218225422.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260219084500.js +27 -0
- package/dist/modules/catalog/migrations/Migration20260219084500.js.map +7 -0
- package/dist/modules/catalog/search.js +69 -1
- package/dist/modules/catalog/search.js.map +2 -2
- package/dist/modules/catalog/seed/examples.js +91 -42
- package/dist/modules/catalog/seed/examples.js.map +2 -2
- package/dist/modules/dashboards/seed/analytics.js +3 -0
- package/dist/modules/dashboards/seed/analytics.js.map +2 -2
- package/dist/modules/sales/api/order-lines/route.js +98 -15
- package/dist/modules/sales/api/order-lines/route.js.map +2 -2
- package/dist/modules/sales/api/quote-lines/route.js +101 -14
- package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js +87 -12
- package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +1424 -260
- package/dist/modules/sales/commands/documents.js.map +3 -3
- package/dist/modules/sales/commands/shared.js +6 -2
- package/dist/modules/sales/commands/shared.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +216 -86
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +913 -241
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +3 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js +15 -3
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/data/entities.js +59 -3
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/data/validators.js +35 -0
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/sales/frontend/quote/[token]/page.js +15 -1
- package/dist/modules/sales/frontend/quote/[token]/page.js.map +2 -2
- package/dist/modules/sales/migrations/Migration20260218225423.js +31 -0
- package/dist/modules/sales/migrations/Migration20260218225423.js.map +7 -0
- package/dist/modules/sales/migrations/Migration20260219084501.js +71 -0
- package/dist/modules/sales/migrations/Migration20260219084501.js.map +7 -0
- package/dist/modules/sales/search.js +28 -0
- package/dist/modules/sales/search.js.map +2 -2
- package/dist/modules/sales/seed/examples.js +14 -1
- package/dist/modules/sales/seed/examples.js.map +2 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js +1 -1
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +28 -15
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/translations.js +9 -0
- package/dist/modules/staff/translations.js.map +7 -0
- package/dist/modules/translations/components/TranslationDrawerAction.js +97 -0
- package/dist/modules/translations/components/TranslationDrawerAction.js.map +7 -0
- package/dist/modules/translations/lib/extract-record-id.js +31 -2
- package/dist/modules/translations/lib/extract-record-id.js.map +2 -2
- package/dist/modules/translations/lib/resolve-field-list.js +3 -0
- package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
- package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js +105 -36
- package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map +2 -2
- package/dist/modules/translations/widgets/injection-table.js +18 -29
- package/dist/modules/translations/widgets/injection-table.js.map +2 -2
- package/generated/entities/catalog_product/index.ts +8 -0
- package/generated/entities/catalog_product_unit_conversion/index.ts +12 -0
- package/generated/entities/sales_credit_memo_line/index.ts +3 -0
- package/generated/entities/sales_invoice_line/index.ts +3 -0
- package/generated/entities/sales_order_line/index.ts +3 -0
- package/generated/entities/sales_quote_line/index.ts +3 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +2 -2
- package/src/modules/auth/i18n/de.json +1 -1
- package/src/modules/auth/i18n/en.json +1 -1
- package/src/modules/auth/i18n/es.json +1 -1
- package/src/modules/auth/i18n/pl.json +1 -1
- package/src/modules/catalog/api/prices/route.ts +213 -81
- package/src/modules/catalog/api/product-unit-conversions/route.ts +195 -0
- package/src/modules/catalog/api/products/route.ts +638 -402
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2085 -1072
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +1288 -593
- package/src/modules/catalog/commands/index.ts +1 -0
- package/src/modules/catalog/commands/productUnitConversions.ts +626 -0
- package/src/modules/catalog/commands/products.ts +1151 -693
- package/src/modules/catalog/commands/shared.ts +19 -5
- package/src/modules/catalog/components/products/ProductUomSection.tsx +745 -0
- package/src/modules/catalog/components/products/productForm.ts +369 -256
- package/src/modules/catalog/components/products/productFormUtils.ts +82 -0
- package/src/modules/catalog/data/entities.ts +82 -1
- package/src/modules/catalog/data/validators.ts +118 -34
- package/src/modules/catalog/events.ts +3 -0
- package/src/modules/catalog/i18n/de.json +56 -0
- package/src/modules/catalog/i18n/en.json +56 -0
- package/src/modules/catalog/i18n/es.json +56 -0
- package/src/modules/catalog/i18n/pl.json +56 -0
- package/src/modules/catalog/lib/unitCodes.ts +1 -0
- package/src/modules/catalog/lib/unitResolution.ts +62 -0
- package/src/modules/catalog/migrations/.snapshot-open-mercato.json +245 -0
- package/src/modules/catalog/migrations/Migration20260218225422.ts +21 -0
- package/src/modules/catalog/migrations/Migration20260219084500.ts +26 -0
- package/src/modules/catalog/search.ts +73 -1
- package/src/modules/catalog/seed/examples.ts +552 -479
- package/src/modules/dashboards/i18n/de.json +1 -1
- package/src/modules/dashboards/i18n/en.json +1 -1
- package/src/modules/dashboards/i18n/es.json +1 -1
- package/src/modules/dashboards/i18n/pl.json +1 -1
- package/src/modules/dashboards/seed/analytics.ts +3 -0
- package/src/modules/sales/api/order-lines/route.ts +158 -68
- package/src/modules/sales/api/quote-lines/route.ts +161 -67
- package/src/modules/sales/api/quotes/public/[token]/route.ts +122 -36
- package/src/modules/sales/commands/documents.ts +4250 -2424
- package/src/modules/sales/commands/shared.ts +7 -2
- package/src/modules/sales/components/documents/ItemsSection.tsx +580 -310
- package/src/modules/sales/components/documents/LineItemDialog.tsx +1988 -833
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +17 -3
- package/src/modules/sales/components/documents/lineItemTypes.ts +6 -0
- package/src/modules/sales/data/entities.ts +53 -0
- package/src/modules/sales/data/validators.ts +36 -0
- package/src/modules/sales/frontend/quote/[token]/page.tsx +25 -1
- package/src/modules/sales/i18n/de.json +23 -3
- package/src/modules/sales/i18n/en.json +23 -3
- package/src/modules/sales/i18n/es.json +23 -3
- package/src/modules/sales/i18n/pl.json +23 -3
- package/src/modules/sales/lib/types.ts +30 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +172 -0
- package/src/modules/sales/migrations/Migration20260218225423.ts +37 -0
- package/src/modules/sales/migrations/Migration20260219084501.ts +73 -0
- package/src/modules/sales/search.ts +28 -0
- package/src/modules/sales/seed/examples.ts +20 -1
- package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +1 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +8 -0
- package/src/modules/staff/translations.ts +5 -0
- package/src/modules/translations/components/TranslationDrawerAction.tsx +107 -0
- package/src/modules/translations/lib/extract-record-id.ts +47 -3
- package/src/modules/translations/lib/resolve-field-list.ts +4 -0
- package/src/modules/translations/widgets/injection/translation-manager/widget.client.tsx +108 -36
- package/src/modules/translations/widgets/injection-table.ts +19 -33
- package/src/modules/workflows/i18n/de.json +4 -4
- package/src/modules/workflows/i18n/en.json +4 -4
- package/src/modules/workflows/i18n/es.json +4 -4
- package/src/modules/workflows/i18n/pl.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/api/order-lines/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { SalesOrderLine } from '../../data/entities'\nimport { orderLineCreateSchema } from '../../data/validators'\nimport { createPagedListResponseSchema, createSalesCrudOpenApi, defaultOkResponseSchema } from '../openapi'\nimport { withScopedPayload } from '../utils'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/sales_order_line'\n\nconst rawBodySchema = z.object({}).passthrough()\nconst resolveRawBody = (raw: unknown) => (raw && typeof raw === 'object' && 'body' in raw ? (raw as any).body : raw)\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(200).default(50),\n id: z.string().uuid().optional(),\n orderId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.orders.view'] },\n POST: { requireAuth: true, requireFeatures: ['sales.orders.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.orders.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.orders.manage'] },\n}\n\nconst upsertSchema = orderLineCreateSchema.extend({ id: z.string().uuid().optional() })\nconst deleteSchema = z.object({\n id: z.string().uuid(),\n orderId: z.string().uuid(),\n})\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesOrderLine,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: E.sales.sales_order_line,\n },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_order_line,\n fields: [\n F.id,\n 'order_id',\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.organization_id,\n F.tenant_id,\n F.quantity,\n F.quantity_unit,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query, ctx) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query.orderId) filters.order_id = { $eq: query.orderId }\n try {\n const em = ctx.container.resolve('em')\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId: E.sales.sales_order_line,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch {\n // ignore\n }\n return filters\n },\n transformItem: (item: any) => {\n if (!item) return item\n const normalized = { ...item }\n const cfEntries = extractAllCustomFieldEntries(item)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) delete normalized[key]\n }\n return { ...normalized, ...cfEntries }\n },\n },\n actions: {\n create: {\n commandId: 'sales.orders.lines.upsert',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n console.log('CREATE order line raw input:', raw)\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))\n return { body: payload }\n },\n response: ({ result }) => ({ id: result?.lineId ?? null, orderId: result?.orderId ?? null }),\n status: 201,\n },\n update: {\n commandId: 'sales.orders.lines.upsert',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))\n return { body: payload }\n },\n response: ({ result }) => ({ id: result?.lineId ?? null, orderId: result?.orderId ?? null }),\n },\n delete: {\n commandId: 'sales.orders.lines.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const payload = deleteSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))\n if (!payload.id || !payload.orderId) {\n throw new CrudHttpError(400, { error: translate('sales.documents.detail.error', 'Document not found or inaccessible.') })\n }\n return { body: payload }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst { GET, POST, PUT, DELETE } = crud\n\nexport { GET, POST, PUT, DELETE }\n\nconst orderLineSchema = z.object({\n id: z.string().uuid(),\n order_id: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n})\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Order line',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(orderLineSchema),\n create: {\n schema: upsertSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable(), orderId: z.string().uuid().nullable() }),\n description: 'Creates an order line and recalculates totals.',\n },\n update: {\n schema: upsertSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable(), orderId: z.string().uuid().nullable() }),\n description: 'Updates an order line and recalculates totals.',\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes an order line and recalculates totals.',\n },\n})\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAC9B,
|
|
4
|
+
"sourcesContent": ["import { z } from \"zod\";\nimport { makeCrudRoute } from \"@open-mercato/shared/lib/crud/factory\";\nimport { resolveTranslations } from \"@open-mercato/shared/lib/i18n/server\";\nimport { CrudHttpError } from \"@open-mercato/shared/lib/crud/errors\";\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from \"@open-mercato/shared/lib/crud/custom-fields\";\nimport { SalesOrderLine } from \"../../data/entities\";\nimport { orderLineCreateSchema } from \"../../data/validators\";\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from \"../openapi\";\nimport { withScopedPayload } from \"../utils\";\nimport { E } from \"#generated/entities.ids.generated\";\nimport * as F from \"#generated/entities/sales_order_line\";\nimport { canonicalizeUnitCode, REFERENCE_UNIT_CODES } from \"@open-mercato/shared/lib/units/unitCodes\";\n\nconst rawBodySchema = z.object({}).passthrough();\nconst resolveRawBody = (raw: unknown): Record<string, unknown> => {\n if (!raw || typeof raw !== \"object\") return {};\n if (\"body\" in raw) {\n const payload = raw as { body?: unknown };\n if (payload.body && typeof payload.body === \"object\") {\n return payload.body as Record<string, unknown>;\n }\n }\n return raw as Record<string, unknown>;\n};\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n orderId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum([\"asc\", \"desc\"]).optional(),\n })\n .passthrough();\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [\"sales.orders.view\"] },\n POST: { requireAuth: true, requireFeatures: [\"sales.orders.manage\"] },\n PUT: { requireAuth: true, requireFeatures: [\"sales.orders.manage\"] },\n DELETE: { requireAuth: true, requireFeatures: [\"sales.orders.manage\"] },\n};\n\nconst upsertSchema = orderLineCreateSchema.extend({\n id: z.string().uuid().optional(),\n});\nconst deleteSchema = z.object({\n id: z.string().uuid(),\n orderId: z.string().uuid(),\n});\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesOrderLine,\n idField: \"id\",\n orgField: \"organizationId\",\n tenantField: \"tenantId\",\n softDeleteField: \"deletedAt\",\n },\n indexer: {\n entityType: E.sales.sales_order_line,\n },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_order_line,\n fields: [\n F.id,\n \"order_id\",\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.organization_id,\n F.tenant_id,\n F.quantity,\n F.quantity_unit,\n F.normalized_quantity,\n F.normalized_unit,\n F.uom_snapshot,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query, ctx) => {\n const filters: Record<string, unknown> = {};\n if (query.id) filters.id = { $eq: query.id };\n if (query.orderId) filters.order_id = { $eq: query.orderId };\n try {\n const em = ctx.container.resolve(\"em\");\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId: E.sales.sales_order_line,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n });\n Object.assign(filters, cfFilters);\n } catch {\n // ignore\n }\n return filters;\n },\n transformItem: (item: Record<string, unknown> | null | undefined) => {\n if (!item) return item;\n const normalized = { ...item };\n const cfEntries = extractAllCustomFieldEntries(item);\n for (const key of Object.keys(normalized)) {\n if (key.startsWith(\"cf:\")) delete normalized[key];\n }\n const quantityUnit = canonicalizeUnitCode(\n normalized[\"quantity_unit\"] ?? normalized[\"quantityUnit\"],\n );\n const normalizedUnit =\n canonicalizeUnitCode(\n normalized[\"normalized_unit\"] ?? normalized[\"normalizedUnit\"],\n ) ?? quantityUnit;\n return {\n ...normalized,\n quantity_unit: quantityUnit,\n normalized_unit: normalizedUnit,\n ...cfEntries,\n };\n },\n },\n actions: {\n create: {\n commandId: \"sales.orders.lines.upsert\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n );\n return { body: payload };\n },\n response: ({ result }) => ({\n id: result?.lineId ?? null,\n orderId: result?.orderId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: \"sales.orders.lines.upsert\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n );\n return { body: payload };\n },\n response: ({ result }) => ({\n id: result?.lineId ?? null,\n orderId: result?.orderId ?? null,\n }),\n },\n delete: {\n commandId: \"sales.orders.lines.delete\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const payload = deleteSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n );\n if (!payload.id || !payload.orderId) {\n throw new CrudHttpError(400, {\n error: translate(\n \"sales.documents.detail.error\",\n \"Document not found or inaccessible.\",\n ),\n });\n }\n return { body: payload };\n },\n response: () => ({ ok: true }),\n },\n },\n});\n\nconst { GET, POST, PUT, DELETE } = crud;\n\nexport { GET, POST, PUT, DELETE };\n\nconst uomSnapshotOpenApiSchema = z\n .object({\n version: z.literal(1),\n productId: z.string().nullable(),\n productVariantId: z.string().nullable(),\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n enteredQuantity: z.string(),\n toBaseFactor: z.string(),\n normalizedQuantity: z.string(),\n rounding: z.object({\n mode: z.enum([\"half_up\", \"down\", \"up\"]),\n scale: z.number().int(),\n }),\n source: z.object({\n conversionId: z.string().nullable(),\n resolvedAt: z.string(),\n }),\n unitPriceReference: z\n .object({\n enabled: z.boolean(),\n referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),\n baseQuantity: z.string().nullable(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .optional(),\n })\n .nullable()\n .optional();\n\nconst orderLineSchema = z.object({\n id: z.string().uuid(),\n order_id: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n normalized_quantity: z.number(),\n normalized_unit: z.string().nullable().optional(),\n uom_snapshot: uomSnapshotOpenApiSchema,\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n});\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: \"Order line\",\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(orderLineSchema),\n create: {\n schema: upsertSchema,\n responseSchema: z.object({\n id: z.string().uuid().nullable(),\n orderId: z.string().uuid().nullable(),\n }),\n description: \"Creates an order line and recalculates totals.\",\n },\n update: {\n schema: upsertSchema,\n responseSchema: z.object({\n id: z.string().uuid().nullable(),\n orderId: z.string().uuid().nullable(),\n }),\n description: \"Updates an order line and recalculates totals.\",\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: \"Deletes an order line and recalculates totals.\",\n },\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,yBAAyB;AAClC,SAAS,SAAS;AAClB,YAAY,OAAO;AACnB,SAAS,sBAAsB,4BAA4B;AAE3D,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAC/C,MAAM,iBAAiB,CAAC,QAA0C;AAChE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,CAAC;AAC7C,MAAI,UAAU,KAAK;AACjB,UAAM,UAAU;AAChB,QAAI,QAAQ,QAAQ,OAAO,QAAQ,SAAS,UAAU;AACpD,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,aAAa,EAChB,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACpC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAC5C,CAAC,EACA,YAAY;AAEf,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AAAA,EACjE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AAAA,EACpE,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AAAA,EACnE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACxE;AAEA,MAAM,eAAe,sBAAsB,OAAO;AAAA,EAChD,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACjC,CAAC;AACD,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,SAAS,EAAE,OAAO,EAAE,KAAK;AAC3B,CAAC;AAED,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,SAAS;AAAA,IACP,YAAY,EAAE,MAAM;AAAA,EACtB;AAAA,EACA,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,UAAU,EAAE,MAAM;AAAA,IAClB,QAAQ;AAAA,MACN,EAAE;AAAA,MACF;AAAA,MACA,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,MACF,EAAE;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,MACZ,WAAW,EAAE;AAAA,MACb,WAAW,EAAE;AAAA,MACb,YAAY,EAAE;AAAA,IAChB;AAAA,IACA,cAAc,OAAO,OAAO,QAAQ;AAClC,YAAM,UAAmC,CAAC;AAC1C,UAAI,MAAM,GAAI,SAAQ,KAAK,EAAE,KAAK,MAAM,GAAG;AAC3C,UAAI,MAAM,QAAS,SAAQ,WAAW,EAAE,KAAK,MAAM,QAAQ;AAC3D,UAAI;AACF,cAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,cAAM,YAAY,MAAM,iCAAiC;AAAA,UACvD,UAAU,EAAE,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA,UAAU,IAAI,MAAM,YAAY;AAAA,QAClC,CAAC;AACD,eAAO,OAAO,SAAS,SAAS;AAAA,MAClC,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT;AAAA,IACA,eAAe,CAAC,SAAqD;AACnE,UAAI,CAAC,KAAM,QAAO;AAClB,YAAM,aAAa,EAAE,GAAG,KAAK;AAC7B,YAAM,YAAY,6BAA6B,IAAI;AACnD,iBAAW,OAAO,OAAO,KAAK,UAAU,GAAG;AACzC,YAAI,IAAI,WAAW,KAAK,EAAG,QAAO,WAAW,GAAG;AAAA,MAClD;AACA,YAAM,eAAe;AAAA,QACnB,WAAW,eAAe,KAAK,WAAW,cAAc;AAAA,MAC1D;AACA,YAAM,iBACJ;AAAA,QACE,WAAW,iBAAiB,KAAK,WAAW,gBAAgB;AAAA,MAC9D,KAAK;AACP,aAAO;AAAA,QACL,GAAG;AAAA,QACH,eAAe;AAAA,QACf,iBAAiB;AAAA,QACjB,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,UAAU,aAAa;AAAA,UAC3B,kBAAkB,eAAe,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS;AAAA,QAC7D;AACA,eAAO,EAAE,MAAM,QAAQ;AAAA,MACzB;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,IAAI,QAAQ,UAAU;AAAA,QACtB,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,UAAU,aAAa;AAAA,UAC3B,kBAAkB,eAAe,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS;AAAA,QAC7D;AACA,eAAO,EAAE,MAAM,QAAQ;AAAA,MACzB;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,IAAI,QAAQ,UAAU;AAAA,QACtB,SAAS,QAAQ,WAAW;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,UAAU,aAAa;AAAA,UAC3B,kBAAkB,eAAe,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS;AAAA,QAC7D;AACA,YAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,SAAS;AACnC,gBAAM,IAAI,cAAc,KAAK;AAAA,YAC3B,OAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AACA,eAAO,EAAE,MAAM,QAAQ;AAAA,MACzB;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AACF,CAAC;AAED,MAAM,EAAE,KAAK,MAAM,KAAK,OAAO,IAAI;AAInC,MAAM,2BAA2B,EAC9B,OAAO;AAAA,EACN,SAAS,EAAE,QAAQ,CAAC;AAAA,EACpB,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACtC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,iBAAiB,EAAE,OAAO;AAAA,EAC1B,cAAc,EAAE,OAAO;AAAA,EACvB,oBAAoB,EAAE,OAAO;AAAA,EAC7B,UAAU,EAAE,OAAO;AAAA,IACjB,MAAM,EAAE,KAAK,CAAC,WAAW,QAAQ,IAAI,CAAC;AAAA,IACtC,OAAO,EAAE,OAAO,EAAE,IAAI;AAAA,EACxB,CAAC;AAAA,EACD,QAAQ,EAAE,OAAO;AAAA,IACf,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,IAClC,YAAY,EAAE,OAAO;AAAA,EACvB,CAAC;AAAA,EACD,oBAAoB,EACjB,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ;AAAA,IACnB,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,SAAS;AAAA,IACzD,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,IAClC,mBAAmB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,IAClD,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,CAAC,EACA,SAAS;AACd,CAAC,EACA,SAAS,EACT,SAAS;AAEZ,MAAM,kBAAkB,EAAE,OAAO;AAAA,EAC/B,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,UAAU,EAAE,OAAO,EAAE,KAAK;AAAA,EAC1B,aAAa,EAAE,OAAO;AAAA,EACtB,MAAM,EAAE,OAAO;AAAA,EACf,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAClD,oBAAoB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1D,kBAAkB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACxE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACrC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxC,UAAU,EAAE,OAAO;AAAA,EACnB,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,qBAAqB,EAAE,OAAO;AAAA,EAC9B,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,cAAc;AAAA,EACd,eAAe,EAAE,OAAO;AAAA,EACxB,gBAAgB,EAAE,OAAO;AAAA,EACzB,kBAAkB,EAAE,OAAO;AAAA,EAC3B,iBAAiB,EAAE,OAAO;AAAA,EAC1B,kBAAkB,EAAE,OAAO;AAAA,EAC3B,UAAU,EAAE,OAAO;AAAA,EACnB,YAAY,EAAE,OAAO;AAAA,EACrB,kBAAkB,EAAE,OAAO;AAAA,EAC3B,oBAAoB,EAAE,OAAO;AAAA,EAC7B,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EACrE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC/C,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EAC1E,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS;AAAA,EAChE,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EAC3D,YAAY,EAAE,OAAO;AAAA,EACrB,YAAY,EAAE,OAAO;AACvB,CAAC;AAEM,MAAM,UAAU,uBAAuB;AAAA,EAC5C,cAAc;AAAA,EACd,aAAa;AAAA,EACb,oBAAoB,8BAA8B,eAAe;AAAA,EACjE,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB,EAAE,OAAO;AAAA,MACvB,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,MAC/B,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,IACtC,CAAC;AAAA,IACD,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB,EAAE,OAAO;AAAA,MACvB,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,MAC/B,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,IACtC,CAAC;AAAA,IACD,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,18 +2,35 @@ import { z } from "zod";
|
|
|
2
2
|
import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
|
|
3
3
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
4
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
buildCustomFieldFiltersFromQuery,
|
|
7
|
+
extractAllCustomFieldEntries
|
|
8
|
+
} from "@open-mercato/shared/lib/crud/custom-fields";
|
|
6
9
|
import { SalesQuoteLine } from "../../data/entities.js";
|
|
7
10
|
import { quoteLineCreateSchema } from "../../data/validators.js";
|
|
8
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
createPagedListResponseSchema,
|
|
13
|
+
createSalesCrudOpenApi,
|
|
14
|
+
defaultOkResponseSchema
|
|
15
|
+
} from "../openapi.js";
|
|
9
16
|
import { withScopedPayload } from "../utils.js";
|
|
10
17
|
import { E } from "../../../../generated/entities.ids.generated.js";
|
|
11
18
|
import * as F from "../../../../generated/entities/sales_quote_line/index.js";
|
|
19
|
+
import { canonicalizeUnitCode, REFERENCE_UNIT_CODES } from "@open-mercato/shared/lib/units/unitCodes";
|
|
12
20
|
const rawBodySchema = z.object({}).passthrough();
|
|
13
|
-
const resolveRawBody = (raw) =>
|
|
21
|
+
const resolveRawBody = (raw) => {
|
|
22
|
+
if (!raw || typeof raw !== "object") return {};
|
|
23
|
+
if ("body" in raw) {
|
|
24
|
+
const payload = raw;
|
|
25
|
+
if (payload.body && typeof payload.body === "object") {
|
|
26
|
+
return payload.body;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return raw;
|
|
30
|
+
};
|
|
14
31
|
const listSchema = z.object({
|
|
15
32
|
page: z.coerce.number().min(1).default(1),
|
|
16
|
-
pageSize: z.coerce.number().min(1).max(
|
|
33
|
+
pageSize: z.coerce.number().min(1).max(100).default(50),
|
|
17
34
|
id: z.string().uuid().optional(),
|
|
18
35
|
quoteId: z.string().uuid().optional(),
|
|
19
36
|
sortField: z.string().optional(),
|
|
@@ -25,7 +42,9 @@ const routeMetadata = {
|
|
|
25
42
|
PUT: { requireAuth: true, requireFeatures: ["sales.quotes.manage"] },
|
|
26
43
|
DELETE: { requireAuth: true, requireFeatures: ["sales.quotes.manage"] }
|
|
27
44
|
};
|
|
28
|
-
const upsertSchema = quoteLineCreateSchema.extend({
|
|
45
|
+
const upsertSchema = quoteLineCreateSchema.extend({
|
|
46
|
+
id: z.string().uuid().optional()
|
|
47
|
+
});
|
|
29
48
|
const deleteSchema = z.object({
|
|
30
49
|
id: z.string().uuid(),
|
|
31
50
|
quoteId: z.string().uuid()
|
|
@@ -39,6 +58,9 @@ const crud = makeCrudRoute({
|
|
|
39
58
|
tenantField: "tenantId",
|
|
40
59
|
softDeleteField: "deletedAt"
|
|
41
60
|
},
|
|
61
|
+
indexer: {
|
|
62
|
+
entityType: E.sales.sales_quote_line
|
|
63
|
+
},
|
|
42
64
|
list: {
|
|
43
65
|
schema: listSchema,
|
|
44
66
|
entityId: E.sales.sales_quote_line,
|
|
@@ -57,6 +79,9 @@ const crud = makeCrudRoute({
|
|
|
57
79
|
F.comment,
|
|
58
80
|
F.quantity,
|
|
59
81
|
F.quantity_unit,
|
|
82
|
+
F.normalized_quantity,
|
|
83
|
+
F.normalized_unit,
|
|
84
|
+
F.uom_snapshot,
|
|
60
85
|
F.currency_code,
|
|
61
86
|
F.unit_price_net,
|
|
62
87
|
F.unit_price_gross,
|
|
@@ -103,7 +128,18 @@ const crud = makeCrudRoute({
|
|
|
103
128
|
for (const key of Object.keys(normalized)) {
|
|
104
129
|
if (key.startsWith("cf:")) delete normalized[key];
|
|
105
130
|
}
|
|
106
|
-
|
|
131
|
+
const quantityUnit = canonicalizeUnitCode(
|
|
132
|
+
normalized["quantity_unit"] ?? normalized["quantityUnit"]
|
|
133
|
+
);
|
|
134
|
+
const normalizedUnit = canonicalizeUnitCode(
|
|
135
|
+
normalized["normalized_unit"] ?? normalized["normalizedUnit"]
|
|
136
|
+
) ?? quantityUnit;
|
|
137
|
+
return {
|
|
138
|
+
...normalized,
|
|
139
|
+
quantity_unit: quantityUnit,
|
|
140
|
+
normalized_unit: normalizedUnit,
|
|
141
|
+
...cfEntries
|
|
142
|
+
};
|
|
107
143
|
}
|
|
108
144
|
},
|
|
109
145
|
actions: {
|
|
@@ -112,10 +148,15 @@ const crud = makeCrudRoute({
|
|
|
112
148
|
schema: rawBodySchema,
|
|
113
149
|
mapInput: async ({ raw, ctx }) => {
|
|
114
150
|
const { translate } = await resolveTranslations();
|
|
115
|
-
const payload = upsertSchema.parse(
|
|
151
|
+
const payload = upsertSchema.parse(
|
|
152
|
+
withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate)
|
|
153
|
+
);
|
|
116
154
|
return { body: payload };
|
|
117
155
|
},
|
|
118
|
-
response: ({ result }) => ({
|
|
156
|
+
response: ({ result }) => ({
|
|
157
|
+
id: result?.lineId ?? null,
|
|
158
|
+
quoteId: result?.quoteId ?? null
|
|
159
|
+
}),
|
|
119
160
|
status: 201
|
|
120
161
|
},
|
|
121
162
|
update: {
|
|
@@ -123,19 +164,31 @@ const crud = makeCrudRoute({
|
|
|
123
164
|
schema: rawBodySchema,
|
|
124
165
|
mapInput: async ({ raw, ctx }) => {
|
|
125
166
|
const { translate } = await resolveTranslations();
|
|
126
|
-
const payload = upsertSchema.parse(
|
|
167
|
+
const payload = upsertSchema.parse(
|
|
168
|
+
withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate)
|
|
169
|
+
);
|
|
127
170
|
return { body: payload };
|
|
128
171
|
},
|
|
129
|
-
response: ({ result }) => ({
|
|
172
|
+
response: ({ result }) => ({
|
|
173
|
+
id: result?.lineId ?? null,
|
|
174
|
+
quoteId: result?.quoteId ?? null
|
|
175
|
+
})
|
|
130
176
|
},
|
|
131
177
|
delete: {
|
|
132
178
|
commandId: "sales.quotes.lines.delete",
|
|
133
179
|
schema: rawBodySchema,
|
|
134
180
|
mapInput: async ({ raw, ctx }) => {
|
|
135
181
|
const { translate } = await resolveTranslations();
|
|
136
|
-
const payload = deleteSchema.parse(
|
|
182
|
+
const payload = deleteSchema.parse(
|
|
183
|
+
withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate)
|
|
184
|
+
);
|
|
137
185
|
if (!payload.id || !payload.quoteId) {
|
|
138
|
-
throw new CrudHttpError(400, {
|
|
186
|
+
throw new CrudHttpError(400, {
|
|
187
|
+
error: translate(
|
|
188
|
+
"sales.documents.detail.error",
|
|
189
|
+
"Document not found or inaccessible."
|
|
190
|
+
)
|
|
191
|
+
});
|
|
139
192
|
}
|
|
140
193
|
return { body: payload };
|
|
141
194
|
},
|
|
@@ -144,6 +197,31 @@ const crud = makeCrudRoute({
|
|
|
144
197
|
}
|
|
145
198
|
});
|
|
146
199
|
const { GET, POST, PUT, DELETE } = crud;
|
|
200
|
+
const uomSnapshotOpenApiSchema = z.object({
|
|
201
|
+
version: z.literal(1),
|
|
202
|
+
productId: z.string().nullable(),
|
|
203
|
+
productVariantId: z.string().nullable(),
|
|
204
|
+
baseUnitCode: z.string().nullable(),
|
|
205
|
+
enteredUnitCode: z.string().nullable(),
|
|
206
|
+
enteredQuantity: z.string(),
|
|
207
|
+
toBaseFactor: z.string(),
|
|
208
|
+
normalizedQuantity: z.string(),
|
|
209
|
+
rounding: z.object({
|
|
210
|
+
mode: z.enum(["half_up", "down", "up"]),
|
|
211
|
+
scale: z.number().int()
|
|
212
|
+
}),
|
|
213
|
+
source: z.object({
|
|
214
|
+
conversionId: z.string().nullable(),
|
|
215
|
+
resolvedAt: z.string()
|
|
216
|
+
}),
|
|
217
|
+
unitPriceReference: z.object({
|
|
218
|
+
enabled: z.boolean(),
|
|
219
|
+
referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),
|
|
220
|
+
baseQuantity: z.string().nullable(),
|
|
221
|
+
grossPerReference: z.string().nullable().optional(),
|
|
222
|
+
netPerReference: z.string().nullable().optional()
|
|
223
|
+
}).optional()
|
|
224
|
+
}).nullable().optional();
|
|
147
225
|
const quoteLineSchema = z.object({
|
|
148
226
|
id: z.string().uuid(),
|
|
149
227
|
quote_id: z.string().uuid(),
|
|
@@ -159,6 +237,9 @@ const quoteLineSchema = z.object({
|
|
|
159
237
|
comment: z.string().nullable().optional(),
|
|
160
238
|
quantity: z.number(),
|
|
161
239
|
quantity_unit: z.string().nullable().optional(),
|
|
240
|
+
normalized_quantity: z.number(),
|
|
241
|
+
normalized_unit: z.string().nullable().optional(),
|
|
242
|
+
uom_snapshot: uomSnapshotOpenApiSchema,
|
|
162
243
|
currency_code: z.string(),
|
|
163
244
|
unit_price_net: z.number(),
|
|
164
245
|
unit_price_gross: z.number(),
|
|
@@ -182,12 +263,18 @@ const openApi = createSalesCrudOpenApi({
|
|
|
182
263
|
listResponseSchema: createPagedListResponseSchema(quoteLineSchema),
|
|
183
264
|
create: {
|
|
184
265
|
schema: upsertSchema,
|
|
185
|
-
responseSchema: z.object({
|
|
266
|
+
responseSchema: z.object({
|
|
267
|
+
id: z.string().uuid().nullable(),
|
|
268
|
+
quoteId: z.string().uuid().nullable()
|
|
269
|
+
}),
|
|
186
270
|
description: "Creates a quote line and recalculates totals."
|
|
187
271
|
},
|
|
188
272
|
update: {
|
|
189
273
|
schema: upsertSchema,
|
|
190
|
-
responseSchema: z.object({
|
|
274
|
+
responseSchema: z.object({
|
|
275
|
+
id: z.string().uuid().nullable(),
|
|
276
|
+
quoteId: z.string().uuid().nullable()
|
|
277
|
+
}),
|
|
191
278
|
description: "Updates a quote line and recalculates totals."
|
|
192
279
|
},
|
|
193
280
|
del: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/api/quote-lines/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { SalesQuoteLine } from '../../data/entities'\nimport { quoteLineCreateSchema } from '../../data/validators'\nimport { createPagedListResponseSchema, createSalesCrudOpenApi, defaultOkResponseSchema } from '../openapi'\nimport { withScopedPayload } from '../utils'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/sales_quote_line'\n\nconst rawBodySchema = z.object({}).passthrough()\nconst resolveRawBody = (raw: unknown) => (raw && typeof raw === 'object' && 'body' in raw ? (raw as any).body : raw)\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(200).default(50),\n id: z.string().uuid().optional(),\n quoteId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['sales.quotes.view'] },\n POST: { requireAuth: true, requireFeatures: ['sales.quotes.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['sales.quotes.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['sales.quotes.manage'] },\n}\n\nconst upsertSchema = quoteLineCreateSchema.extend({ id: z.string().uuid().optional() })\nconst deleteSchema = z.object({\n id: z.string().uuid(),\n quoteId: z.string().uuid(),\n})\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesQuoteLine,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_quote_line,\n fields: [\n F.id,\n 'quote_id',\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.quantity,\n F.quantity_unit,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query, ctx) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query.quoteId) filters.quote_id = { $eq: query.quoteId }\n try {\n const em = ctx.container.resolve('em')\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId: E.sales.sales_quote_line,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch {\n // ignore\n }\n return filters\n },\n transformItem: (item: any) => {\n if (!item) return item\n const normalized = { ...item }\n const cfEntries = extractAllCustomFieldEntries(item)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) delete normalized[key]\n }\n return { ...normalized, ...cfEntries }\n },\n },\n actions: {\n create: {\n commandId: 'sales.quotes.lines.upsert',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))\n return { body: payload }\n },\n response: ({ result }) => ({ id: result?.lineId ?? null, quoteId: result?.quoteId ?? null }),\n status: 201,\n },\n update: {\n commandId: 'sales.quotes.lines.upsert',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))\n return { body: payload }\n },\n response: ({ result }) => ({ id: result?.lineId ?? null, quoteId: result?.quoteId ?? null }),\n },\n delete: {\n commandId: 'sales.quotes.lines.delete',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const payload = deleteSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))\n if (!payload.id || !payload.quoteId) {\n throw new CrudHttpError(400, { error: translate('sales.documents.detail.error', 'Document not found or inaccessible.') })\n }\n return { body: payload }\n },\n response: () => ({ ok: true }),\n },\n },\n})\n\nconst { GET, POST, PUT, DELETE } = crud\n\nexport { GET, POST, PUT, DELETE }\n\nconst quoteLineSchema = z.object({\n id: z.string().uuid(),\n quote_id: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n})\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: 'Quote line',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(quoteLineSchema),\n create: {\n schema: upsertSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable(), quoteId: z.string().uuid().nullable() }),\n description: 'Creates a quote line and recalculates totals.',\n },\n update: {\n schema: upsertSchema,\n responseSchema: z.object({ id: z.string().uuid().nullable(), quoteId: z.string().uuid().nullable() }),\n description: 'Updates a quote line and recalculates totals.',\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a quote line and recalculates totals.',\n },\n})\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAC9B,
|
|
4
|
+
"sourcesContent": ["import { z } from \"zod\";\nimport { makeCrudRoute } from \"@open-mercato/shared/lib/crud/factory\";\nimport { resolveTranslations } from \"@open-mercato/shared/lib/i18n/server\";\nimport { CrudHttpError } from \"@open-mercato/shared/lib/crud/errors\";\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from \"@open-mercato/shared/lib/crud/custom-fields\";\nimport { SalesQuoteLine } from \"../../data/entities\";\nimport { quoteLineCreateSchema } from \"../../data/validators\";\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from \"../openapi\";\nimport { withScopedPayload } from \"../utils\";\nimport { E } from \"#generated/entities.ids.generated\";\nimport * as F from \"#generated/entities/sales_quote_line\";\nimport { canonicalizeUnitCode, REFERENCE_UNIT_CODES } from \"@open-mercato/shared/lib/units/unitCodes\";\n\nconst rawBodySchema = z.object({}).passthrough();\nconst resolveRawBody = (raw: unknown): Record<string, unknown> => {\n if (!raw || typeof raw !== \"object\") return {};\n if (\"body\" in raw) {\n const payload = raw as { body?: unknown };\n if (payload.body && typeof payload.body === \"object\") {\n return payload.body as Record<string, unknown>;\n }\n }\n return raw as Record<string, unknown>;\n};\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n quoteId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum([\"asc\", \"desc\"]).optional(),\n })\n .passthrough();\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [\"sales.quotes.view\"] },\n POST: { requireAuth: true, requireFeatures: [\"sales.quotes.manage\"] },\n PUT: { requireAuth: true, requireFeatures: [\"sales.quotes.manage\"] },\n DELETE: { requireAuth: true, requireFeatures: [\"sales.quotes.manage\"] },\n};\n\nconst upsertSchema = quoteLineCreateSchema.extend({\n id: z.string().uuid().optional(),\n});\nconst deleteSchema = z.object({\n id: z.string().uuid(),\n quoteId: z.string().uuid(),\n});\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: SalesQuoteLine,\n idField: \"id\",\n orgField: \"organizationId\",\n tenantField: \"tenantId\",\n softDeleteField: \"deletedAt\",\n },\n indexer: {\n entityType: E.sales.sales_quote_line,\n },\n list: {\n schema: listSchema,\n entityId: E.sales.sales_quote_line,\n fields: [\n F.id,\n \"quote_id\",\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.quantity,\n F.quantity_unit,\n F.normalized_quantity,\n F.normalized_unit,\n F.uom_snapshot,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query, ctx) => {\n const filters: Record<string, unknown> = {};\n if (query.id) filters.id = { $eq: query.id };\n if (query.quoteId) filters.quote_id = { $eq: query.quoteId };\n try {\n const em = ctx.container.resolve(\"em\");\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId: E.sales.sales_quote_line,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n });\n Object.assign(filters, cfFilters);\n } catch {\n // ignore\n }\n return filters;\n },\n transformItem: (item: Record<string, unknown> | null | undefined) => {\n if (!item) return item;\n const normalized = { ...item };\n const cfEntries = extractAllCustomFieldEntries(item);\n for (const key of Object.keys(normalized)) {\n if (key.startsWith(\"cf:\")) delete normalized[key];\n }\n const quantityUnit = canonicalizeUnitCode(\n normalized[\"quantity_unit\"] ?? normalized[\"quantityUnit\"],\n );\n const normalizedUnit =\n canonicalizeUnitCode(\n normalized[\"normalized_unit\"] ?? normalized[\"normalizedUnit\"],\n ) ?? quantityUnit;\n return {\n ...normalized,\n quantity_unit: quantityUnit,\n normalized_unit: normalizedUnit,\n ...cfEntries,\n };\n },\n },\n actions: {\n create: {\n commandId: \"sales.quotes.lines.upsert\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n );\n return { body: payload };\n },\n response: ({ result }) => ({\n id: result?.lineId ?? null,\n quoteId: result?.quoteId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: \"sales.quotes.lines.upsert\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n );\n return { body: payload };\n },\n response: ({ result }) => ({\n id: result?.lineId ?? null,\n quoteId: result?.quoteId ?? null,\n }),\n },\n delete: {\n commandId: \"sales.quotes.lines.delete\",\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations();\n const payload = deleteSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n );\n if (!payload.id || !payload.quoteId) {\n throw new CrudHttpError(400, {\n error: translate(\n \"sales.documents.detail.error\",\n \"Document not found or inaccessible.\",\n ),\n });\n }\n return { body: payload };\n },\n response: () => ({ ok: true }),\n },\n },\n});\n\nconst { GET, POST, PUT, DELETE } = crud;\n\nexport { GET, POST, PUT, DELETE };\n\nconst uomSnapshotOpenApiSchema = z\n .object({\n version: z.literal(1),\n productId: z.string().nullable(),\n productVariantId: z.string().nullable(),\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n enteredQuantity: z.string(),\n toBaseFactor: z.string(),\n normalizedQuantity: z.string(),\n rounding: z.object({\n mode: z.enum([\"half_up\", \"down\", \"up\"]),\n scale: z.number().int(),\n }),\n source: z.object({\n conversionId: z.string().nullable(),\n resolvedAt: z.string(),\n }),\n unitPriceReference: z\n .object({\n enabled: z.boolean(),\n referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),\n baseQuantity: z.string().nullable(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .optional(),\n })\n .nullable()\n .optional();\n\nconst quoteLineSchema = z.object({\n id: z.string().uuid(),\n quote_id: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n normalized_quantity: z.number(),\n normalized_unit: z.string().nullable().optional(),\n uom_snapshot: uomSnapshotOpenApiSchema,\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n});\n\nexport const openApi = createSalesCrudOpenApi({\n resourceName: \"Quote line\",\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(quoteLineSchema),\n create: {\n schema: upsertSchema,\n responseSchema: z.object({\n id: z.string().uuid().nullable(),\n quoteId: z.string().uuid().nullable(),\n }),\n description: \"Creates a quote line and recalculates totals.\",\n },\n update: {\n schema: upsertSchema,\n responseSchema: z.object({\n id: z.string().uuid().nullable(),\n quoteId: z.string().uuid().nullable(),\n }),\n description: \"Updates a quote line and recalculates totals.\",\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: \"Deletes a quote line and recalculates totals.\",\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,7 +3,13 @@ import { z } from "zod";
|
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
4
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
5
5
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
6
|
-
import {
|
|
6
|
+
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
7
|
+
import {
|
|
8
|
+
SalesQuote,
|
|
9
|
+
SalesQuoteLine,
|
|
10
|
+
SalesQuoteAdjustment
|
|
11
|
+
} from "../../../../data/entities.js";
|
|
12
|
+
import { canonicalizeUnitCode } from "@open-mercato/shared/lib/units/unitCodes";
|
|
7
13
|
const paramsSchema = z.object({
|
|
8
14
|
token: z.string().uuid()
|
|
9
15
|
});
|
|
@@ -15,16 +21,31 @@ async function GET(_req, ctx) {
|
|
|
15
21
|
const { token } = paramsSchema.parse(ctx.params ?? {});
|
|
16
22
|
const container = await createRequestContainer();
|
|
17
23
|
const em = container.resolve("em");
|
|
18
|
-
const quote = await em
|
|
24
|
+
const quote = await findOneWithDecryption(em, SalesQuote, {
|
|
25
|
+
acceptanceToken: token,
|
|
26
|
+
deletedAt: null
|
|
27
|
+
});
|
|
19
28
|
const { translate } = await resolveTranslations();
|
|
20
29
|
if (!quote) {
|
|
21
|
-
throw new CrudHttpError(404, {
|
|
30
|
+
throw new CrudHttpError(404, {
|
|
31
|
+
error: translate("sales.quotes.public.notFound", "Quote not found.")
|
|
32
|
+
});
|
|
22
33
|
}
|
|
23
34
|
const now = /* @__PURE__ */ new Date();
|
|
24
35
|
const isExpired = !!quote.validUntil && quote.validUntil.getTime() < now.getTime();
|
|
25
36
|
const [lines, adjustments] = await Promise.all([
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
findWithDecryption(
|
|
38
|
+
em,
|
|
39
|
+
SalesQuoteLine,
|
|
40
|
+
{ quote: quote.id, organizationId: quote.organizationId, tenantId: quote.tenantId, deletedAt: null },
|
|
41
|
+
{ orderBy: { lineNumber: "asc" } }
|
|
42
|
+
),
|
|
43
|
+
findWithDecryption(
|
|
44
|
+
em,
|
|
45
|
+
SalesQuoteAdjustment,
|
|
46
|
+
{ quote: quote.id, organizationId: quote.organizationId, tenantId: quote.tenantId },
|
|
47
|
+
{ orderBy: { position: "asc" } }
|
|
48
|
+
)
|
|
28
49
|
]);
|
|
29
50
|
return NextResponse.json({
|
|
30
51
|
quote: {
|
|
@@ -46,7 +67,13 @@ async function GET(_req, ctx) {
|
|
|
46
67
|
name: line.name ?? null,
|
|
47
68
|
description: line.description ?? null,
|
|
48
69
|
quantity: line.quantity,
|
|
49
|
-
quantityUnit: line.quantityUnit ?? null,
|
|
70
|
+
quantityUnit: canonicalizeUnitCode(line.quantityUnit) ?? null,
|
|
71
|
+
normalizedQuantity: line.normalizedQuantity ?? line.quantity,
|
|
72
|
+
normalizedUnit: canonicalizeUnitCode(line.normalizedUnit ?? line.quantityUnit) ?? null,
|
|
73
|
+
uomSnapshot: line.uomSnapshot ? {
|
|
74
|
+
baseUnitCode: line.uomSnapshot.baseUnitCode ?? null,
|
|
75
|
+
enteredUnitCode: line.uomSnapshot.enteredUnitCode ?? null
|
|
76
|
+
} : null,
|
|
50
77
|
currencyCode: line.currencyCode,
|
|
51
78
|
unitPriceNet: line.unitPriceNet,
|
|
52
79
|
unitPriceGross: line.unitPriceGross,
|
|
@@ -55,7 +82,19 @@ async function GET(_req, ctx) {
|
|
|
55
82
|
taxRate: line.taxRate,
|
|
56
83
|
taxAmount: line.taxAmount,
|
|
57
84
|
totalNetAmount: line.totalNetAmount,
|
|
58
|
-
totalGrossAmount: line.totalGrossAmount
|
|
85
|
+
totalGrossAmount: line.totalGrossAmount,
|
|
86
|
+
unitPriceReference: (() => {
|
|
87
|
+
if (!line.uomSnapshot) return null;
|
|
88
|
+
const ref = line.uomSnapshot.unitPriceReference;
|
|
89
|
+
if (!ref) return null;
|
|
90
|
+
return {
|
|
91
|
+
enabled: ref.enabled ?? null,
|
|
92
|
+
referenceUnitCode: ref.referenceUnitCode ?? null,
|
|
93
|
+
baseQuantity: ref.baseQuantity ?? null,
|
|
94
|
+
grossPerReference: ref.grossPerReference ?? null,
|
|
95
|
+
netPerReference: ref.netPerReference ?? null
|
|
96
|
+
};
|
|
97
|
+
})()
|
|
59
98
|
})),
|
|
60
99
|
adjustments: adjustments.map((adj) => ({
|
|
61
100
|
scope: adj.scope,
|
|
@@ -76,7 +115,12 @@ async function GET(_req, ctx) {
|
|
|
76
115
|
}
|
|
77
116
|
const { translate } = await resolveTranslations();
|
|
78
117
|
console.error("sales.quotes.public failed", err);
|
|
79
|
-
return NextResponse.json(
|
|
118
|
+
return NextResponse.json(
|
|
119
|
+
{
|
|
120
|
+
error: translate("sales.quotes.public.failed", "Failed to load quote.")
|
|
121
|
+
},
|
|
122
|
+
{ status: 400 }
|
|
123
|
+
);
|
|
80
124
|
}
|
|
81
125
|
}
|
|
82
126
|
const publicQuoteResponseSchema = z.object({
|
|
@@ -101,8 +145,28 @@ const publicQuoteResponseSchema = z.object({
|
|
|
101
145
|
description: z.string().nullable(),
|
|
102
146
|
quantity: z.string(),
|
|
103
147
|
quantityUnit: z.string().nullable(),
|
|
148
|
+
normalizedQuantity: z.string(),
|
|
149
|
+
normalizedUnit: z.string().nullable(),
|
|
150
|
+
uomSnapshot: z.object({
|
|
151
|
+
baseUnitCode: z.string().nullable(),
|
|
152
|
+
enteredUnitCode: z.string().nullable()
|
|
153
|
+
}).nullable().optional(),
|
|
104
154
|
currencyCode: z.string(),
|
|
105
|
-
|
|
155
|
+
unitPriceNet: z.string(),
|
|
156
|
+
unitPriceGross: z.string(),
|
|
157
|
+
discountAmount: z.string(),
|
|
158
|
+
discountPercent: z.string(),
|
|
159
|
+
taxRate: z.string(),
|
|
160
|
+
taxAmount: z.string(),
|
|
161
|
+
totalNetAmount: z.string(),
|
|
162
|
+
totalGrossAmount: z.string(),
|
|
163
|
+
unitPriceReference: z.object({
|
|
164
|
+
enabled: z.boolean().nullable().optional(),
|
|
165
|
+
referenceUnitCode: z.string().nullable().optional(),
|
|
166
|
+
baseQuantity: z.string().nullable().optional(),
|
|
167
|
+
grossPerReference: z.string().nullable().optional(),
|
|
168
|
+
netPerReference: z.string().nullable().optional()
|
|
169
|
+
}).nullable().optional()
|
|
106
170
|
})
|
|
107
171
|
),
|
|
108
172
|
adjustments: z.array(
|
|
@@ -112,7 +176,10 @@ const publicQuoteResponseSchema = z.object({
|
|
|
112
176
|
label: z.string().nullable(),
|
|
113
177
|
rate: z.string().nullable(),
|
|
114
178
|
amountNet: z.string().nullable(),
|
|
115
|
-
amountGross: z.string().nullable()
|
|
179
|
+
amountGross: z.string().nullable(),
|
|
180
|
+
currencyCode: z.string().nullable(),
|
|
181
|
+
position: z.number().nullable(),
|
|
182
|
+
quoteLineId: z.string().uuid().nullable()
|
|
116
183
|
})
|
|
117
184
|
),
|
|
118
185
|
isExpired: z.boolean()
|
|
@@ -125,8 +192,16 @@ const openApi = {
|
|
|
125
192
|
GET: {
|
|
126
193
|
summary: "Get quote details by acceptance token",
|
|
127
194
|
responses: [
|
|
128
|
-
{
|
|
129
|
-
|
|
195
|
+
{
|
|
196
|
+
status: 200,
|
|
197
|
+
description: "Quote details",
|
|
198
|
+
schema: publicQuoteResponseSchema
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
status: 404,
|
|
202
|
+
description: "Quote not found",
|
|
203
|
+
schema: z.object({ error: z.string() })
|
|
204
|
+
}
|
|
130
205
|
]
|
|
131
206
|
}
|
|
132
207
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/sales/api/quotes/public/%5Btoken%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,qBAAqB;AAG9B,SAAS,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from \"next/server\";\nimport { z } from \"zod\";\nimport { createRequestContainer } from \"@open-mercato/shared/lib/di/container\";\nimport { resolveTranslations } from \"@open-mercato/shared/lib/i18n/server\";\nimport { CrudHttpError } from \"@open-mercato/shared/lib/crud/errors\";\nimport type { OpenApiRouteDoc } from \"@open-mercato/shared/lib/openapi\";\nimport type { EntityManager } from \"@mikro-orm/postgresql\";\nimport { findOneWithDecryption, findWithDecryption } from \"@open-mercato/shared/lib/encryption/find\";\nimport {\n SalesQuote,\n SalesQuoteLine,\n SalesQuoteAdjustment,\n} from \"../../../../data/entities\";\nimport { canonicalizeUnitCode } from \"@open-mercato/shared/lib/units/unitCodes\";\n\nconst paramsSchema = z.object({\n token: z.string().uuid(),\n});\n\nexport const metadata = {\n GET: { requireAuth: false },\n};\n\nexport async function GET(_req: Request, ctx: { params: { token: string } }) {\n try {\n const { token } = paramsSchema.parse(ctx.params ?? {});\n const container = await createRequestContainer();\n const em = container.resolve(\"em\") as EntityManager;\n const quote = await findOneWithDecryption(em, SalesQuote, {\n acceptanceToken: token,\n deletedAt: null,\n });\n const { translate } = await resolveTranslations();\n if (!quote) {\n throw new CrudHttpError(404, {\n error: translate(\"sales.quotes.public.notFound\", \"Quote not found.\"),\n });\n }\n\n const now = new Date();\n const isExpired =\n !!quote.validUntil && quote.validUntil.getTime() < now.getTime();\n\n const [lines, adjustments] = await Promise.all([\n findWithDecryption(\n em,\n SalesQuoteLine,\n { quote: quote.id, organizationId: quote.organizationId, tenantId: quote.tenantId, deletedAt: null },\n { orderBy: { lineNumber: \"asc\" } },\n ),\n findWithDecryption(\n em,\n SalesQuoteAdjustment,\n { quote: quote.id, organizationId: quote.organizationId, tenantId: quote.tenantId },\n { orderBy: { position: \"asc\" } },\n ),\n ]);\n\n return NextResponse.json({\n quote: {\n quoteNumber: quote.quoteNumber,\n currencyCode: quote.currencyCode,\n validFrom: quote.validFrom?.toISOString() ?? null,\n validUntil: quote.validUntil?.toISOString() ?? null,\n status: quote.status ?? null,\n subtotalNetAmount: quote.subtotalNetAmount,\n subtotalGrossAmount: quote.subtotalGrossAmount,\n discountTotalAmount: quote.discountTotalAmount,\n taxTotalAmount: quote.taxTotalAmount,\n grandTotalNetAmount: quote.grandTotalNetAmount,\n grandTotalGrossAmount: quote.grandTotalGrossAmount,\n },\n lines: lines.map((line) => ({\n lineNumber: line.lineNumber ?? null,\n kind: line.kind,\n name: line.name ?? null,\n description: line.description ?? null,\n quantity: line.quantity,\n quantityUnit: canonicalizeUnitCode(line.quantityUnit) ?? null,\n normalizedQuantity: line.normalizedQuantity ?? line.quantity,\n normalizedUnit:\n canonicalizeUnitCode(line.normalizedUnit ?? line.quantityUnit) ??\n null,\n uomSnapshot: line.uomSnapshot\n ? {\n baseUnitCode: line.uomSnapshot.baseUnitCode ?? null,\n enteredUnitCode: line.uomSnapshot.enteredUnitCode ?? null,\n }\n : null,\n currencyCode: line.currencyCode,\n unitPriceNet: line.unitPriceNet,\n unitPriceGross: line.unitPriceGross,\n discountAmount: line.discountAmount,\n discountPercent: line.discountPercent,\n taxRate: line.taxRate,\n taxAmount: line.taxAmount,\n totalNetAmount: line.totalNetAmount,\n totalGrossAmount: line.totalGrossAmount,\n unitPriceReference: (() => {\n if (!line.uomSnapshot) return null;\n const ref = line.uomSnapshot.unitPriceReference;\n if (!ref) return null;\n return {\n enabled: ref.enabled ?? null,\n referenceUnitCode: ref.referenceUnitCode ?? null,\n baseQuantity: ref.baseQuantity ?? null,\n grossPerReference: ref.grossPerReference ?? null,\n netPerReference: ref.netPerReference ?? null,\n };\n })(),\n })),\n adjustments: adjustments.map((adj) => ({\n scope: adj.scope,\n kind: adj.kind,\n label: adj.label ?? adj.code ?? null,\n rate: adj.rate,\n amountNet: adj.amountNet,\n amountGross: adj.amountGross,\n currencyCode: adj.currencyCode ?? null,\n position: adj.position ?? null,\n quoteLineId: adj.quoteLine?.id ?? null,\n })),\n isExpired,\n });\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status });\n }\n const { translate } = await resolveTranslations();\n console.error(\"sales.quotes.public failed\", err);\n return NextResponse.json(\n {\n error: translate(\"sales.quotes.public.failed\", \"Failed to load quote.\"),\n },\n { status: 400 },\n );\n }\n}\n\nconst publicQuoteResponseSchema = z.object({\n quote: z.object({\n quoteNumber: z.string(),\n currencyCode: z.string(),\n validFrom: z.string().nullable(),\n validUntil: z.string().nullable(),\n status: z.string().nullable(),\n subtotalNetAmount: z.string(),\n subtotalGrossAmount: z.string(),\n discountTotalAmount: z.string(),\n taxTotalAmount: z.string(),\n grandTotalNetAmount: z.string(),\n grandTotalGrossAmount: z.string(),\n }),\n lines: z.array(\n z.object({\n lineNumber: z.number().nullable(),\n kind: z.string(),\n name: z.string().nullable(),\n description: z.string().nullable(),\n quantity: z.string(),\n quantityUnit: z.string().nullable(),\n normalizedQuantity: z.string(),\n normalizedUnit: z.string().nullable(),\n uomSnapshot: z\n .object({\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n })\n .nullable()\n .optional(),\n currencyCode: z.string(),\n unitPriceNet: z.string(),\n unitPriceGross: z.string(),\n discountAmount: z.string(),\n discountPercent: z.string(),\n taxRate: z.string(),\n taxAmount: z.string(),\n totalNetAmount: z.string(),\n totalGrossAmount: z.string(),\n unitPriceReference: z\n .object({\n enabled: z.boolean().nullable().optional(),\n referenceUnitCode: z.string().nullable().optional(),\n baseQuantity: z.string().nullable().optional(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .nullable()\n .optional(),\n }),\n ),\n adjustments: z.array(\n z.object({\n scope: z.string().nullable(),\n kind: z.string().nullable(),\n label: z.string().nullable(),\n rate: z.string().nullable(),\n amountNet: z.string().nullable(),\n amountGross: z.string().nullable(),\n currencyCode: z.string().nullable(),\n position: z.number().nullable(),\n quoteLineId: z.string().uuid().nullable(),\n }),\n ),\n isExpired: z.boolean(),\n});\n\nexport const openApi: OpenApiRouteDoc = {\n tag: \"Sales\",\n summary: \"View a quote (public)\",\n pathParams: z.object({ token: z.string().uuid() }),\n methods: {\n GET: {\n summary: \"Get quote details by acceptance token\",\n responses: [\n {\n status: 200,\n description: \"Quote details\",\n schema: publicQuoteResponseSchema,\n },\n {\n status: 404,\n description: \"Quote not found\",\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|