@open-mercato/core 0.8.1-develop.7458.1.3847638f67 → 0.9.1-develop.7459.1.99e3accd30

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.
Files changed (109) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/availability/api/policies/route.js +6 -6
  3. package/dist/modules/availability/api/policies/route.js.map +2 -2
  4. package/dist/modules/availability/backend/availability/check/page.js +4 -3
  5. package/dist/modules/availability/backend/availability/check/page.js.map +2 -2
  6. package/dist/modules/availability/backend/availability/policies/create/page.js +3 -0
  7. package/dist/modules/availability/backend/availability/policies/create/page.js.map +2 -2
  8. package/dist/modules/availability/lib/organizationScope.js +24 -1
  9. package/dist/modules/availability/lib/organizationScope.js.map +2 -2
  10. package/dist/modules/catalog/api/categories/bulk-create/route.js +16 -2
  11. package/dist/modules/catalog/api/categories/bulk-create/route.js.map +2 -2
  12. package/dist/modules/catalog/api/products/bulk-create/route.js +16 -2
  13. package/dist/modules/catalog/api/products/bulk-create/route.js.map +2 -2
  14. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +1 -0
  15. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  16. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +2 -2
  17. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
  18. package/dist/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].js +78 -0
  19. package/dist/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].js.map +7 -0
  20. package/dist/modules/customer_groups/api/customer-groups/price-kinds/route.js +90 -0
  21. package/dist/modules/customer_groups/api/customer-groups/price-kinds/route.js.map +7 -0
  22. package/dist/modules/customer_groups/backend/customer-groups/page.js +5 -1
  23. package/dist/modules/customer_groups/backend/customer-groups/page.js.map +2 -2
  24. package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js +2 -2
  25. package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js.map +2 -2
  26. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +3 -1
  27. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
  28. package/dist/modules/customer_groups/lib/priceKindScope.js +35 -1
  29. package/dist/modules/customer_groups/lib/priceKindScope.js.map +2 -2
  30. package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js +1 -1
  31. package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js.map +2 -2
  32. package/dist/modules/customers/commands/companies.js +14 -1
  33. package/dist/modules/customers/commands/companies.js.map +2 -2
  34. package/dist/modules/dictionaries/components/DictionariesManager.js +98 -87
  35. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  36. package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js +3 -3
  37. package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js.map +2 -2
  38. package/dist/modules/forms/backend/forms/[id]/submissions/components/formSummary.js +9 -0
  39. package/dist/modules/forms/backend/forms/[id]/submissions/components/formSummary.js.map +7 -0
  40. package/dist/modules/forms/backend/forms/[id]/submissions/page.js +3 -6
  41. package/dist/modules/forms/backend/forms/[id]/submissions/page.js.map +2 -2
  42. package/dist/modules/forms/commands/distribution.js +7 -3
  43. package/dist/modules/forms/commands/distribution.js.map +2 -2
  44. package/dist/modules/forms/commands/form-version.js +9 -4
  45. package/dist/modules/forms/commands/form-version.js.map +2 -2
  46. package/dist/modules/forms/commands/form.js +9 -4
  47. package/dist/modules/forms/commands/form.js.map +2 -2
  48. package/dist/modules/forms/commands/invitation.js +7 -3
  49. package/dist/modules/forms/commands/invitation.js.map +2 -2
  50. package/dist/modules/forms/commands/submission.js +13 -6
  51. package/dist/modules/forms/commands/submission.js.map +2 -2
  52. package/package.json +8 -8
  53. package/src/modules/availability/api/policies/route.ts +10 -11
  54. package/src/modules/availability/backend/availability/check/page.tsx +38 -35
  55. package/src/modules/availability/backend/availability/policies/create/page.tsx +3 -0
  56. package/src/modules/availability/i18n/de.json +1 -0
  57. package/src/modules/availability/i18n/en.json +1 -0
  58. package/src/modules/availability/i18n/es.json +1 -0
  59. package/src/modules/availability/i18n/ko.json +1 -0
  60. package/src/modules/availability/i18n/pl.json +1 -0
  61. package/src/modules/availability/lib/organizationScope.ts +37 -0
  62. package/src/modules/catalog/api/categories/bulk-create/route.ts +16 -2
  63. package/src/modules/catalog/api/products/bulk-create/route.ts +16 -2
  64. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +1 -0
  65. package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +2 -2
  66. package/src/modules/customer_accounts/AGENTS.md +1 -0
  67. package/src/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].ts +89 -0
  68. package/src/modules/customer_groups/api/customer-groups/price-kinds/route.ts +99 -0
  69. package/src/modules/customer_groups/backend/customer-groups/page.tsx +7 -1
  70. package/src/modules/customer_groups/components/CustomerGroupTermsPriceKindField.tsx +5 -4
  71. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +8 -4
  72. package/src/modules/customer_groups/lib/priceKindScope.ts +43 -0
  73. package/src/modules/customer_groups/widgets/injection/person-groups-explain-terms.tsx +2 -2
  74. package/src/modules/customers/commands/companies.ts +16 -1
  75. package/src/modules/dictionaries/components/DictionariesManager.tsx +8 -1
  76. package/src/modules/eudr/i18n/de.json +1 -0
  77. package/src/modules/eudr/i18n/en.json +1 -0
  78. package/src/modules/eudr/i18n/es.json +1 -0
  79. package/src/modules/eudr/i18n/ko.json +1 -0
  80. package/src/modules/eudr/i18n/pl.json +1 -0
  81. package/src/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.tsx +6 -5
  82. package/src/modules/forms/backend/forms/[id]/submissions/components/formSummary.ts +19 -0
  83. package/src/modules/forms/backend/forms/[id]/submissions/page.tsx +4 -16
  84. package/src/modules/forms/commands/distribution.ts +7 -3
  85. package/src/modules/forms/commands/form-version.ts +9 -4
  86. package/src/modules/forms/commands/form.ts +9 -4
  87. package/src/modules/forms/commands/invitation.ts +7 -3
  88. package/src/modules/forms/commands/submission.ts +13 -6
  89. package/src/modules/forms/i18n/de.json +21 -0
  90. package/src/modules/forms/i18n/en.json +21 -0
  91. package/src/modules/forms/i18n/es.json +21 -0
  92. package/src/modules/forms/i18n/ko.json +21 -0
  93. package/src/modules/forms/i18n/pl.json +21 -0
  94. package/src/modules/notifications/AGENTS.md +1 -1
  95. package/src/modules/push_notifications/i18n/de.json +1 -0
  96. package/src/modules/push_notifications/i18n/en.json +1 -0
  97. package/src/modules/push_notifications/i18n/es.json +1 -0
  98. package/src/modules/push_notifications/i18n/ko.json +1 -0
  99. package/src/modules/push_notifications/i18n/pl.json +1 -0
  100. package/src/modules/warranty_claims/i18n/de.json +1 -0
  101. package/src/modules/warranty_claims/i18n/en.json +1 -0
  102. package/src/modules/warranty_claims/i18n/es.json +1 -0
  103. package/src/modules/warranty_claims/i18n/ko.json +1 -0
  104. package/src/modules/warranty_claims/i18n/pl.json +1 -0
  105. package/src/modules/wms/i18n/de.json +1 -0
  106. package/src/modules/wms/i18n/en.json +1 -0
  107. package/src/modules/wms/i18n/es.json +1 -0
  108. package/src/modules/wms/i18n/ko.json +1 -0
  109. package/src/modules/wms/i18n/pl.json +1 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customer_groups/components/CustomerGroupTermsSection.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileText } from 'lucide-react'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { SectionHeader } from '@open-mercato/ui/backend/SectionHeader'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CustomerGroupTermsPriceKindField } from './CustomerGroupTermsPriceKindField'\n\nexport type CustomerGroupTermsDTO = {\n id: string\n groupId: string\n organizationId: string | null\n tenantId: string\n priceKindId: string | null\n paymentTermsDays: number | null\n allowPurchaseOnAccount: boolean | null\n defaultCreditLimit: number | null\n creditCurrencyCode: string | null\n approvalRequiredAbove: number | null\n minOrderValue: number | null\n metadata: Record<string, unknown> | null\n createdAt: string\n updatedAt: string\n}\n\n// `inherit` maps to `null` on the wire: the field is left unset on this group and\n// resolves from the parent chain / tenant default, like the other terms fields.\nconst PURCHASE_ON_ACCOUNT_VALUES = ['inherit', 'allow', 'deny'] as const\ntype PurchaseOnAccountValue = (typeof PURCHASE_ON_ACCOUNT_VALUES)[number]\n\nfunction toPurchaseOnAccountValue(value: boolean | null | undefined): PurchaseOnAccountValue {\n if (value === true) return 'allow'\n if (value === false) return 'deny'\n return 'inherit'\n}\n\nfunction fromPurchaseOnAccountValue(value: string | undefined): boolean | null {\n if (value === 'allow') return true\n if (value === 'deny') return false\n return null\n}\n\ntype CustomerGroupTermsFormValues = {\n priceKindId: string\n paymentTermsDays?: number\n allowPurchaseOnAccount: PurchaseOnAccountValue\n defaultCreditLimit?: number\n creditCurrencyCode: string\n approvalRequiredAbove?: number\n minOrderValue?: number\n}\n\nfunction mapTermsToFormValues(terms: CustomerGroupTermsDTO | null): CustomerGroupTermsFormValues {\n return {\n priceKindId: terms?.priceKindId ?? '',\n paymentTermsDays: terms?.paymentTermsDays ?? undefined,\n allowPurchaseOnAccount: toPurchaseOnAccountValue(terms?.allowPurchaseOnAccount),\n defaultCreditLimit: terms?.defaultCreditLimit ?? undefined,\n creditCurrencyCode: terms?.creditCurrencyCode ?? '',\n approvalRequiredAbove: terms?.approvalRequiredAbove ?? undefined,\n minOrderValue: terms?.minOrderValue ?? undefined,\n }\n}\n\nexport function CustomerGroupTermsSection({\n groupId,\n terms,\n loading,\n loadError,\n canManage,\n onSaved,\n}: {\n groupId: string\n // `undefined` = still loading, `null` = confirmed no terms row for this group\n // (spec: \"No terms set \u2014 inheriting from parent / tenant defaults\"), object = loaded row.\n terms: CustomerGroupTermsDTO | null | undefined\n loading: boolean\n // Set when the terms GET failed for a reason other than \"no row\" \u2014 the form is\n // then withheld so a save can never overwrite terms it could not read.\n loadError?: string | null\n // Without `customer_groups.terms.manage` the terms stay viewable but the form is\n // read-only and the empty state offers no \"set terms\" action \u2014 a save would 403.\n canManage: boolean\n onSaved: (next: CustomerGroupTermsDTO) => void\n}) {\n const t = useT()\n // Once the user starts filling out terms for a group that has none yet, keep the\n // form mounted even while the in-flight save briefly leaves `terms` as `null` \u2014\n // otherwise the empty state would flash back before `onSaved` lands.\n const [editing, setEditing] = React.useState(false)\n const hasTerms = terms != null\n const showForm = hasTerms || editing\n\n const negativeMessage = t('customer_groups.groups.form.terms.errors.negative', 'Must be zero or greater.')\n\n const schema = React.useMemo(\n () =>\n z.object({\n priceKindId: z.string().trim(),\n paymentTermsDays: z.coerce.number().int().min(0, negativeMessage).optional(),\n allowPurchaseOnAccount: z.enum(PURCHASE_ON_ACCOUNT_VALUES),\n defaultCreditLimit: z.coerce.number().min(0, negativeMessage).optional(),\n creditCurrencyCode: z\n .string()\n .trim()\n .max(4, t('customer_groups.groups.form.terms.errors.currencyLength', 'Use up to 4 characters.')),\n approvalRequiredAbove: z.coerce.number().min(0, negativeMessage).optional(),\n minOrderValue: z.coerce.number().min(0, negativeMessage).optional(),\n }),\n [negativeMessage, t],\n )\n\n const fields = React.useMemo<CrudField[]>(\n () => [\n {\n id: 'priceKindId',\n label: t('customer_groups.groups.form.terms.field.priceKind', 'Price kind'),\n type: 'custom',\n component: ({ value, setValue, disabled }) => (\n <CustomerGroupTermsPriceKindField\n value={typeof value === 'string' ? value : ''}\n onChange={setValue}\n disabled={disabled}\n />\n ),\n },\n {\n id: 'paymentTermsDays',\n label: t('customer_groups.groups.form.terms.field.paymentTermsDays', 'Payment terms (days)'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'allowPurchaseOnAccount',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccount', 'Allow purchase on account'),\n type: 'select',\n layout: 'half',\n required: true,\n options: [\n {\n value: 'inherit',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccountOptions.inherit', 'Inherit'),\n },\n {\n value: 'allow',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccountOptions.allow', 'Allow'),\n },\n {\n value: 'deny',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccountOptions.deny', 'Do not allow'),\n },\n ],\n },\n {\n id: 'defaultCreditLimit',\n label: t('customer_groups.groups.form.terms.field.defaultCreditLimit', 'Default credit limit'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'creditCurrencyCode',\n label: t('customer_groups.groups.form.terms.field.creditCurrencyCode', 'Credit currency'),\n type: 'text',\n layout: 'half',\n placeholder: t('customer_groups.groups.form.terms.field.creditCurrencyCodePlaceholder', 'e.g., USD'),\n maxLength: 4,\n },\n {\n id: 'approvalRequiredAbove',\n label: t('customer_groups.groups.form.terms.field.approvalRequiredAbove', 'Approval required above'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'minOrderValue',\n label: t('customer_groups.groups.form.terms.field.minOrderValue', 'Minimum order value'),\n type: 'number',\n layout: 'half',\n },\n ],\n [t],\n )\n\n const initialValues = React.useMemo(() => mapTermsToFormValues(terms ?? null), [terms])\n\n const handleSubmit = React.useCallback(\n async (values: CustomerGroupTermsFormValues) => {\n const payload: Record<string, unknown> = {\n priceKindId: values.priceKindId && values.priceKindId.trim().length ? values.priceKindId : null,\n paymentTermsDays: typeof values.paymentTermsDays === 'number' ? values.paymentTermsDays : null,\n allowPurchaseOnAccount: fromPurchaseOnAccountValue(values.allowPurchaseOnAccount),\n defaultCreditLimit: typeof values.defaultCreditLimit === 'number' ? values.defaultCreditLimit : null,\n creditCurrencyCode:\n values.creditCurrencyCode && values.creditCurrencyCode.trim().length\n ? values.creditCurrencyCode.trim().toUpperCase()\n : null,\n approvalRequiredAbove:\n typeof values.approvalRequiredAbove === 'number' ? values.approvalRequiredAbove : null,\n minOrderValue: typeof values.minOrderValue === 'number' ? values.minOrderValue : null,\n }\n // `updateCrud` PUTs to `/api/customer_groups/customer-groups/{groupId}/terms`, matching the\n // upsert route (Step 2.4). `optimisticLockUpdatedAt` below is `null` on the\n // first save (no `terms` row yet), so CrudForm skips the lock header entirely \u2014\n // mirrors the route's own \"no lock check on creation\" behavior.\n const call = await updateCrud<{ terms: CustomerGroupTermsDTO }>(`customer_groups/customer-groups/${groupId}/terms`, payload, {\n errorMessage: t('customer_groups.groups.form.terms.errors.save', 'Failed to save commercial terms.'),\n })\n if (call.result?.terms) onSaved(call.result.terms)\n flash(t('customer_groups.groups.form.terms.flash.saved', 'Commercial terms saved.'), 'success')\n },\n [groupId, onSaved, t],\n )\n\n return (\n <div className=\"rounded-md border border-border bg-card p-4 space-y-4\">\n <SectionHeader title={t('customer_groups.groups.form.terms.title', 'Commercial terms')} />\n {loading ? (\n <LoadingMessage label={t('customer_groups.groups.form.terms.loading', 'Loading commercial terms\u2026')} />\n ) : loadError ? (\n <ErrorMessage label={loadError} />\n ) : !showForm ? (\n <EmptyState\n size=\"sm\"\n variant=\"subtle\"\n icon={<FileText className=\"h-5 w-5\" aria-hidden />}\n title={t(\n 'customer_groups.groups.form.terms.empty.title',\n 'No terms set \u2014 inheriting from parent / tenant defaults',\n )}\n description={t(\n 'customer_groups.groups.form.terms.empty.description',\n 'This group currently uses commercial terms inherited from its parent group or the tenant defaults. Set explicit terms to override them for this group.',\n )}\n actions={canManage ? (\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => setEditing(true)}>\n {t('customer_groups.groups.form.terms.empty.action', 'Set terms for this group')}\n </Button>\n ) : undefined}\n className=\"border border-dashed border-border\"\n />\n ) : (\n <CrudForm<CustomerGroupTermsFormValues>\n key={terms?.updatedAt ?? 'new'}\n embedded\n schema={schema}\n fields={fields}\n initialValues={initialValues}\n optimisticLockUpdatedAt={terms?.updatedAt ?? null}\n readOnly={!canManage}\n readOnlyOverlay={(\n <div className=\"rounded-xl border border-border/70 bg-background/95 px-4 py-3 text-sm text-muted-foreground shadow-sm\">\n {t(\n 'customer_groups.groups.form.terms.readOnly',\n 'You do not have permission to change commercial terms.',\n )}\n </div>\n )}\n submitLabel={t('customer_groups.groups.form.terms.action.save', 'Save terms')}\n onSubmit={handleSubmit}\n />\n )}\n </div>\n )\n}\n\nexport default CustomerGroupTermsSection\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport { FileText } from 'lucide-react'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { SectionHeader } from '@open-mercato/ui/backend/SectionHeader'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { CustomerGroupTermsPriceKindField } from './CustomerGroupTermsPriceKindField'\n\nexport type CustomerGroupTermsDTO = {\n id: string\n groupId: string\n organizationId: string | null\n tenantId: string\n priceKindId: string | null\n paymentTermsDays: number | null\n allowPurchaseOnAccount: boolean | null\n defaultCreditLimit: number | null\n creditCurrencyCode: string | null\n approvalRequiredAbove: number | null\n minOrderValue: number | null\n metadata: Record<string, unknown> | null\n createdAt: string\n updatedAt: string\n}\n\n// Lock token sent with the first save of a group that has no terms row yet. No real\n// row can carry this `updated_at`, so if another editor created the row meanwhile the\n// route's lock check against that row answers 409 instead of silently overwriting it.\nexport const TERMS_NOT_YET_CREATED_LOCK_TOKEN = new Date(0).toISOString()\n\n// `inherit` maps to `null` on the wire: the field is left unset on this group and\n// resolves from the parent chain / tenant default, like the other terms fields.\nconst PURCHASE_ON_ACCOUNT_VALUES = ['inherit', 'allow', 'deny'] as const\ntype PurchaseOnAccountValue = (typeof PURCHASE_ON_ACCOUNT_VALUES)[number]\n\nfunction toPurchaseOnAccountValue(value: boolean | null | undefined): PurchaseOnAccountValue {\n if (value === true) return 'allow'\n if (value === false) return 'deny'\n return 'inherit'\n}\n\nfunction fromPurchaseOnAccountValue(value: string | undefined): boolean | null {\n if (value === 'allow') return true\n if (value === 'deny') return false\n return null\n}\n\ntype CustomerGroupTermsFormValues = {\n priceKindId: string\n paymentTermsDays?: number\n allowPurchaseOnAccount: PurchaseOnAccountValue\n defaultCreditLimit?: number\n creditCurrencyCode: string\n approvalRequiredAbove?: number\n minOrderValue?: number\n}\n\nfunction mapTermsToFormValues(terms: CustomerGroupTermsDTO | null): CustomerGroupTermsFormValues {\n return {\n priceKindId: terms?.priceKindId ?? '',\n paymentTermsDays: terms?.paymentTermsDays ?? undefined,\n allowPurchaseOnAccount: toPurchaseOnAccountValue(terms?.allowPurchaseOnAccount),\n defaultCreditLimit: terms?.defaultCreditLimit ?? undefined,\n creditCurrencyCode: terms?.creditCurrencyCode ?? '',\n approvalRequiredAbove: terms?.approvalRequiredAbove ?? undefined,\n minOrderValue: terms?.minOrderValue ?? undefined,\n }\n}\n\nexport function CustomerGroupTermsSection({\n groupId,\n terms,\n loading,\n loadError,\n canManage,\n onSaved,\n}: {\n groupId: string\n // `undefined` = still loading, `null` = confirmed no terms row for this group\n // (spec: \"No terms set \u2014 inheriting from parent / tenant defaults\"), object = loaded row.\n terms: CustomerGroupTermsDTO | null | undefined\n loading: boolean\n // Set when the terms GET failed for a reason other than \"no row\" \u2014 the form is\n // then withheld so a save can never overwrite terms it could not read.\n loadError?: string | null\n // Without `customer_groups.terms.manage` the terms stay viewable but the form is\n // read-only and the empty state offers no \"set terms\" action \u2014 a save would 403.\n canManage: boolean\n onSaved: (next: CustomerGroupTermsDTO) => void\n}) {\n const t = useT()\n // Once the user starts filling out terms for a group that has none yet, keep the\n // form mounted even while the in-flight save briefly leaves `terms` as `null` \u2014\n // otherwise the empty state would flash back before `onSaved` lands.\n const [editing, setEditing] = React.useState(false)\n const hasTerms = terms != null\n const showForm = hasTerms || editing\n\n const negativeMessage = t('customer_groups.groups.form.terms.errors.negative', 'Must be zero or greater.')\n\n const schema = React.useMemo(\n () =>\n z.object({\n priceKindId: z.string().trim(),\n paymentTermsDays: z.coerce.number().int().min(0, negativeMessage).optional(),\n allowPurchaseOnAccount: z.enum(PURCHASE_ON_ACCOUNT_VALUES),\n defaultCreditLimit: z.coerce.number().min(0, negativeMessage).optional(),\n creditCurrencyCode: z\n .string()\n .trim()\n .max(4, t('customer_groups.groups.form.terms.errors.currencyLength', 'Use up to 4 characters.')),\n approvalRequiredAbove: z.coerce.number().min(0, negativeMessage).optional(),\n minOrderValue: z.coerce.number().min(0, negativeMessage).optional(),\n }),\n [negativeMessage, t],\n )\n\n const fields = React.useMemo<CrudField[]>(\n () => [\n {\n id: 'priceKindId',\n label: t('customer_groups.groups.form.terms.field.priceKind', 'Price kind'),\n type: 'custom',\n component: ({ value, setValue, disabled }) => (\n <CustomerGroupTermsPriceKindField\n value={typeof value === 'string' ? value : ''}\n onChange={setValue}\n disabled={disabled}\n />\n ),\n },\n {\n id: 'paymentTermsDays',\n label: t('customer_groups.groups.form.terms.field.paymentTermsDays', 'Payment terms (days)'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'allowPurchaseOnAccount',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccount', 'Allow purchase on account'),\n type: 'select',\n layout: 'half',\n required: true,\n options: [\n {\n value: 'inherit',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccountOptions.inherit', 'Inherit'),\n },\n {\n value: 'allow',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccountOptions.allow', 'Allow'),\n },\n {\n value: 'deny',\n label: t('customer_groups.groups.form.terms.field.allowPurchaseOnAccountOptions.deny', 'Do not allow'),\n },\n ],\n },\n {\n id: 'defaultCreditLimit',\n label: t('customer_groups.groups.form.terms.field.defaultCreditLimit', 'Default credit limit'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'creditCurrencyCode',\n label: t('customer_groups.groups.form.terms.field.creditCurrencyCode', 'Credit currency'),\n type: 'text',\n layout: 'half',\n placeholder: t('customer_groups.groups.form.terms.field.creditCurrencyCodePlaceholder', 'e.g., USD'),\n maxLength: 4,\n },\n {\n id: 'approvalRequiredAbove',\n label: t('customer_groups.groups.form.terms.field.approvalRequiredAbove', 'Approval required above'),\n type: 'number',\n layout: 'half',\n },\n {\n id: 'minOrderValue',\n label: t('customer_groups.groups.form.terms.field.minOrderValue', 'Minimum order value'),\n type: 'number',\n layout: 'half',\n },\n ],\n [t],\n )\n\n const initialValues = React.useMemo(() => mapTermsToFormValues(terms ?? null), [terms])\n\n const handleSubmit = React.useCallback(\n async (values: CustomerGroupTermsFormValues) => {\n const payload: Record<string, unknown> = {\n priceKindId: values.priceKindId && values.priceKindId.trim().length ? values.priceKindId : null,\n paymentTermsDays: typeof values.paymentTermsDays === 'number' ? values.paymentTermsDays : null,\n allowPurchaseOnAccount: fromPurchaseOnAccountValue(values.allowPurchaseOnAccount),\n defaultCreditLimit: typeof values.defaultCreditLimit === 'number' ? values.defaultCreditLimit : null,\n creditCurrencyCode:\n values.creditCurrencyCode && values.creditCurrencyCode.trim().length\n ? values.creditCurrencyCode.trim().toUpperCase()\n : null,\n approvalRequiredAbove:\n typeof values.approvalRequiredAbove === 'number' ? values.approvalRequiredAbove : null,\n minOrderValue: typeof values.minOrderValue === 'number' ? values.minOrderValue : null,\n }\n // `updateCrud` PUTs to `/api/customer_groups/customer-groups/{groupId}/terms`, matching the\n // upsert route (Step 2.4). `optimisticLockUpdatedAt` below is\n // `TERMS_NOT_YET_CREATED_LOCK_TOKEN` on the first save (no `terms` row yet).\n const call = await updateCrud<{ terms: CustomerGroupTermsDTO }>(`customer_groups/customer-groups/${groupId}/terms`, payload, {\n errorMessage: t('customer_groups.groups.form.terms.errors.save', 'Failed to save commercial terms.'),\n })\n if (call.result?.terms) onSaved(call.result.terms)\n flash(t('customer_groups.groups.form.terms.flash.saved', 'Commercial terms saved.'), 'success')\n },\n [groupId, onSaved, t],\n )\n\n return (\n <div className=\"rounded-md border border-border bg-card p-4 space-y-4\">\n <SectionHeader title={t('customer_groups.groups.form.terms.title', 'Commercial terms')} />\n {loading ? (\n <LoadingMessage label={t('customer_groups.groups.form.terms.loading', 'Loading commercial terms\u2026')} />\n ) : loadError ? (\n <ErrorMessage label={loadError} />\n ) : !showForm ? (\n <EmptyState\n size=\"sm\"\n variant=\"subtle\"\n icon={<FileText className=\"h-5 w-5\" aria-hidden />}\n title={t(\n 'customer_groups.groups.form.terms.empty.title',\n 'No terms set \u2014 inheriting from parent / tenant defaults',\n )}\n description={t(\n 'customer_groups.groups.form.terms.empty.description',\n 'This group currently uses commercial terms inherited from its parent group or the tenant defaults. Set explicit terms to override them for this group.',\n )}\n actions={canManage ? (\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => setEditing(true)}>\n {t('customer_groups.groups.form.terms.empty.action', 'Set terms for this group')}\n </Button>\n ) : undefined}\n className=\"border border-dashed border-border\"\n />\n ) : (\n <CrudForm<CustomerGroupTermsFormValues>\n key={terms?.updatedAt ?? 'new'}\n embedded\n schema={schema}\n fields={fields}\n initialValues={initialValues}\n optimisticLockUpdatedAt={terms?.updatedAt ?? TERMS_NOT_YET_CREATED_LOCK_TOKEN}\n readOnly={!canManage}\n readOnlyOverlay={(\n <div className=\"rounded-xl border border-border/70 bg-background/95 px-4 py-3 text-sm text-muted-foreground shadow-sm\">\n {t(\n 'customer_groups.groups.form.terms.readOnly',\n 'You do not have permission to change commercial terms.',\n )}\n </div>\n )}\n submitLabel={t('customer_groups.groups.form.terms.action.save', 'Save terms')}\n onSubmit={handleSubmit}\n />\n )}\n </div>\n )\n}\n\nexport default CustomerGroupTermsSection\n"],
5
+ "mappings": ";AAmIU,cA8FN,YA9FM;AAjIV,YAAY,WAAW;AACvB,SAAS,SAAS;AAClB,SAAS,gBAAgB;AACzB,SAAS,gBAAgC;AACzC,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,cAAc,sBAAsB;AAC7C,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,wCAAwC;AAsB1C,MAAM,oCAAmC,oBAAI,KAAK,CAAC,GAAE,YAAY;AAIxE,MAAM,6BAA6B,CAAC,WAAW,SAAS,MAAM;AAG9D,SAAS,yBAAyB,OAA2D;AAC3F,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,MAAO,QAAO;AAC5B,SAAO;AACT;AAEA,SAAS,2BAA2B,OAA2C;AAC7E,MAAI,UAAU,QAAS,QAAO;AAC9B,MAAI,UAAU,OAAQ,QAAO;AAC7B,SAAO;AACT;AAYA,SAAS,qBAAqB,OAAmE;AAC/F,SAAO;AAAA,IACL,aAAa,OAAO,eAAe;AAAA,IACnC,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,wBAAwB,yBAAyB,OAAO,sBAAsB;AAAA,IAC9E,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,oBAAoB,OAAO,sBAAsB;AAAA,IACjD,uBAAuB,OAAO,yBAAyB;AAAA,IACvD,eAAe,OAAO,iBAAiB;AAAA,EACzC;AACF;AAEO,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAaG;AACD,QAAM,IAAI,KAAK;AAIf,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,KAAK;AAClD,QAAM,WAAW,SAAS;AAC1B,QAAM,WAAW,YAAY;AAE7B,QAAM,kBAAkB,EAAE,qDAAqD,0BAA0B;AAEzG,QAAM,SAAS,MAAM;AAAA,IACnB,MACE,EAAE,OAAO;AAAA,MACP,aAAa,EAAE,OAAO,EAAE,KAAK;AAAA,MAC7B,kBAAkB,EAAE,OAAO,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,eAAe,EAAE,SAAS;AAAA,MAC3E,wBAAwB,EAAE,KAAK,0BAA0B;AAAA,MACzD,oBAAoB,EAAE,OAAO,OAAO,EAAE,IAAI,GAAG,eAAe,EAAE,SAAS;AAAA,MACvE,oBAAoB,EACjB,OAAO,EACP,KAAK,EACL,IAAI,GAAG,EAAE,2DAA2D,yBAAyB,CAAC;AAAA,MACjG,uBAAuB,EAAE,OAAO,OAAO,EAAE,IAAI,GAAG,eAAe,EAAE,SAAS;AAAA,MAC1E,eAAe,EAAE,OAAO,OAAO,EAAE,IAAI,GAAG,eAAe,EAAE,SAAS;AAAA,IACpE,CAAC;AAAA,IACH,CAAC,iBAAiB,CAAC;AAAA,EACrB;AAEA,QAAM,SAAS,MAAM;AAAA,IACnB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,qDAAqD,YAAY;AAAA,QAC1E,MAAM;AAAA,QACN,WAAW,CAAC,EAAE,OAAO,UAAU,SAAS,MACtC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,YAC3C,UAAU;AAAA,YACV;AAAA;AAAA,QACF;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,4DAA4D,sBAAsB;AAAA,QAC3F,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,kEAAkE,2BAA2B;AAAA,QACtG,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,SAAS;AAAA,UACP;AAAA,YACE,OAAO;AAAA,YACP,OAAO,EAAE,iFAAiF,SAAS;AAAA,UACrG;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,OAAO,EAAE,+EAA+E,OAAO;AAAA,UACjG;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,OAAO,EAAE,8EAA8E,cAAc;AAAA,UACvG;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,8DAA8D,sBAAsB;AAAA,QAC7F,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,8DAA8D,iBAAiB;AAAA,QACxF,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,aAAa,EAAE,yEAAyE,WAAW;AAAA,QACnG,WAAW;AAAA,MACb;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,iEAAiE,yBAAyB;AAAA,QACnG,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,yDAAyD,qBAAqB;AAAA,QACvF,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,gBAAgB,MAAM,QAAQ,MAAM,qBAAqB,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC;AAEtF,QAAM,eAAe,MAAM;AAAA,IACzB,OAAO,WAAyC;AAC9C,YAAM,UAAmC;AAAA,QACvC,aAAa,OAAO,eAAe,OAAO,YAAY,KAAK,EAAE,SAAS,OAAO,cAAc;AAAA,QAC3F,kBAAkB,OAAO,OAAO,qBAAqB,WAAW,OAAO,mBAAmB;AAAA,QAC1F,wBAAwB,2BAA2B,OAAO,sBAAsB;AAAA,QAChF,oBAAoB,OAAO,OAAO,uBAAuB,WAAW,OAAO,qBAAqB;AAAA,QAChG,oBACE,OAAO,sBAAsB,OAAO,mBAAmB,KAAK,EAAE,SAC1D,OAAO,mBAAmB,KAAK,EAAE,YAAY,IAC7C;AAAA,QACN,uBACE,OAAO,OAAO,0BAA0B,WAAW,OAAO,wBAAwB;AAAA,QACpF,eAAe,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;AAAA,MACnF;AAIA,YAAM,OAAO,MAAM,WAA6C,mCAAmC,OAAO,UAAU,SAAS;AAAA,QAC3H,cAAc,EAAE,iDAAiD,kCAAkC;AAAA,MACrG,CAAC;AACD,UAAI,KAAK,QAAQ,MAAO,SAAQ,KAAK,OAAO,KAAK;AACjD,YAAM,EAAE,iDAAiD,yBAAyB,GAAG,SAAS;AAAA,IAChG;AAAA,IACA,CAAC,SAAS,SAAS,CAAC;AAAA,EACtB;AAEA,SACE,qBAAC,SAAI,WAAU,yDACb;AAAA,wBAAC,iBAAc,OAAO,EAAE,2CAA2C,kBAAkB,GAAG;AAAA,IACvF,UACC,oBAAC,kBAAe,OAAO,EAAE,6CAA6C,gCAA2B,GAAG,IAClG,YACF,oBAAC,gBAAa,OAAO,WAAW,IAC9B,CAAC,WACH;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,MAAM,oBAAC,YAAS,WAAU,WAAU,eAAW,MAAC;AAAA,QAChD,OAAO;AAAA,UACL;AAAA,UACA;AAAA,QACF;AAAA,QACA,aAAa;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAAS,YACP,oBAAC,UAAO,MAAK,UAAS,MAAK,MAAK,SAAQ,WAAU,SAAS,MAAM,WAAW,IAAI,GAC7E,YAAE,kDAAkD,0BAA0B,GACjF,IACE;AAAA,QACJ,WAAU;AAAA;AAAA,IACZ,IAEA;AAAA,MAAC;AAAA;AAAA,QAEC,UAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,yBAAyB,OAAO,aAAa;AAAA,QAC7C,UAAU,CAAC;AAAA,QACX,iBACE,oBAAC,SAAI,WAAU,yGACZ;AAAA,UACC;AAAA,UACA;AAAA,QACF,GACF;AAAA,QAEF,aAAa,EAAE,iDAAiD,YAAY;AAAA,QAC5E,UAAU;AAAA;AAAA,MAhBL,OAAO,aAAa;AAAA,IAiB3B;AAAA,KAEJ;AAEJ;AAEA,IAAO,oCAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
1
2
  const PRICE_KIND_TABLE = "catalog_price_kinds";
2
3
  async function isPriceKindInTenant(em, priceKindId, tenantId) {
3
4
  const connection = em.getConnection();
@@ -10,7 +11,40 @@ async function isPriceKindInTenant(em, priceKindId, tenantId) {
10
11
  );
11
12
  return Array.isArray(rows) && rows.length > 0;
12
13
  }
14
+ async function listTenantPriceKinds(em, tenantId, options) {
15
+ const connection = em.getConnection();
16
+ const tableRows = await connection.execute("select to_regclass(?) is not null as present", [PRICE_KIND_TABLE]);
17
+ const tablePresent = Array.isArray(tableRows) && tableRows[0]?.present === true;
18
+ if (!tablePresent) return [];
19
+ const conditions = ["tenant_id = ?", "deleted_at is null"];
20
+ const params = [tenantId];
21
+ if (options.ids && options.ids.length) {
22
+ conditions.push(`id in (${options.ids.map(() => "?").join(", ")})`);
23
+ params.push(...options.ids);
24
+ }
25
+ const search = options.search?.trim();
26
+ if (search) {
27
+ const like = `%${escapeLikePattern(search)}%`;
28
+ conditions.push("(code ilike ? or title ilike ?)");
29
+ params.push(like, like);
30
+ }
31
+ params.push(options.limit);
32
+ const rows = await connection.execute(
33
+ `select id, code, title from ${PRICE_KIND_TABLE} where ${conditions.join(" and ")} order by title asc, code asc limit ?`,
34
+ params
35
+ );
36
+ if (!Array.isArray(rows)) return [];
37
+ return rows.map((row) => {
38
+ const record = row;
39
+ return {
40
+ id: String(record.id),
41
+ code: typeof record.code === "string" ? record.code : "",
42
+ title: typeof record.title === "string" ? record.title : ""
43
+ };
44
+ });
45
+ }
13
46
  export {
14
- isPriceKindInTenant
47
+ isPriceKindInTenant,
48
+ listTenantPriceKinds
15
49
  };
16
50
  //# sourceMappingURL=priceKindScope.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/customer_groups/lib/priceKindScope.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\n\n// Terms reference a `catalog` price kind by id only (FK-id coupling, no module\n// import). `catalog` is optional, so its table may be absent; the id is then only\n// shape-checked, as before, since there is nothing it could resolve against.\nconst PRICE_KIND_TABLE = 'catalog_price_kinds'\n\nexport async function isPriceKindInTenant(\n em: EntityManager,\n priceKindId: string,\n tenantId: string,\n): Promise<boolean> {\n const connection = em.getConnection()\n const tableRows = await connection.execute('select to_regclass(?) is not null as present', [PRICE_KIND_TABLE])\n const tablePresent = Array.isArray(tableRows) && (tableRows[0] as { present?: unknown } | undefined)?.present === true\n if (!tablePresent) return true\n const rows = await connection.execute(\n `select 1 from ${PRICE_KIND_TABLE} where id = ? and tenant_id = ? and deleted_at is null limit 1`,\n [priceKindId, tenantId],\n )\n return Array.isArray(rows) && rows.length > 0\n}\n"],
5
- "mappings": "AAKA,MAAM,mBAAmB;AAEzB,eAAsB,oBACpB,IACA,aACA,UACkB;AAClB,QAAM,aAAa,GAAG,cAAc;AACpC,QAAM,YAAY,MAAM,WAAW,QAAQ,gDAAgD,CAAC,gBAAgB,CAAC;AAC7G,QAAM,eAAe,MAAM,QAAQ,SAAS,KAAM,UAAU,CAAC,GAAyC,YAAY;AAClH,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,OAAO,MAAM,WAAW;AAAA,IAC5B,iBAAiB,gBAAgB;AAAA,IACjC,CAAC,aAAa,QAAQ;AAAA,EACxB;AACA,SAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS;AAC9C;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\n\n// Terms reference a `catalog` price kind by id only (FK-id coupling, no module\n// import). `catalog` is optional, so its table may be absent; the id is then only\n// shape-checked, as before, since there is nothing it could resolve against.\nconst PRICE_KIND_TABLE = 'catalog_price_kinds'\n\nexport async function isPriceKindInTenant(\n em: EntityManager,\n priceKindId: string,\n tenantId: string,\n): Promise<boolean> {\n const connection = em.getConnection()\n const tableRows = await connection.execute('select to_regclass(?) is not null as present', [PRICE_KIND_TABLE])\n const tablePresent = Array.isArray(tableRows) && (tableRows[0] as { present?: unknown } | undefined)?.present === true\n if (!tablePresent) return true\n const rows = await connection.execute(\n `select 1 from ${PRICE_KIND_TABLE} where id = ? and tenant_id = ? and deleted_at is null limit 1`,\n [priceKindId, tenantId],\n )\n return Array.isArray(rows) && rows.length > 0\n}\n\nexport type TenantPriceKindOption = { id: string; code: string; title: string }\n\n// Terms managers pick a price kind without holding `catalog.settings.manage` (which\n// gates `/api/catalog/price-kinds`), so the picker reads the same tenant-scoped rows\n// through `customer_groups`' own route. Only id/code/title leave this helper.\nexport async function listTenantPriceKinds(\n em: EntityManager,\n tenantId: string,\n options: { search?: string | null; ids?: string[] | null; limit: number },\n): Promise<TenantPriceKindOption[]> {\n const connection = em.getConnection()\n const tableRows = await connection.execute('select to_regclass(?) is not null as present', [PRICE_KIND_TABLE])\n const tablePresent = Array.isArray(tableRows) && (tableRows[0] as { present?: unknown } | undefined)?.present === true\n if (!tablePresent) return []\n const conditions = ['tenant_id = ?', 'deleted_at is null']\n const params: unknown[] = [tenantId]\n if (options.ids && options.ids.length) {\n conditions.push(`id in (${options.ids.map(() => '?').join(', ')})`)\n params.push(...options.ids)\n }\n const search = options.search?.trim()\n if (search) {\n const like = `%${escapeLikePattern(search)}%`\n conditions.push('(code ilike ? or title ilike ?)')\n params.push(like, like)\n }\n params.push(options.limit)\n const rows = await connection.execute(\n `select id, code, title from ${PRICE_KIND_TABLE} where ${conditions.join(' and ')} order by title asc, code asc limit ?`,\n params,\n )\n if (!Array.isArray(rows)) return []\n return rows.map((row) => {\n const record = row as { id?: unknown; code?: unknown; title?: unknown }\n return {\n id: String(record.id),\n code: typeof record.code === 'string' ? record.code : '',\n title: typeof record.title === 'string' ? record.title : '',\n }\n })\n}\n"],
5
+ "mappings": "AACA,SAAS,yBAAyB;AAKlC,MAAM,mBAAmB;AAEzB,eAAsB,oBACpB,IACA,aACA,UACkB;AAClB,QAAM,aAAa,GAAG,cAAc;AACpC,QAAM,YAAY,MAAM,WAAW,QAAQ,gDAAgD,CAAC,gBAAgB,CAAC;AAC7G,QAAM,eAAe,MAAM,QAAQ,SAAS,KAAM,UAAU,CAAC,GAAyC,YAAY;AAClH,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,OAAO,MAAM,WAAW;AAAA,IAC5B,iBAAiB,gBAAgB;AAAA,IACjC,CAAC,aAAa,QAAQ;AAAA,EACxB;AACA,SAAO,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS;AAC9C;AAOA,eAAsB,qBACpB,IACA,UACA,SACkC;AAClC,QAAM,aAAa,GAAG,cAAc;AACpC,QAAM,YAAY,MAAM,WAAW,QAAQ,gDAAgD,CAAC,gBAAgB,CAAC;AAC7G,QAAM,eAAe,MAAM,QAAQ,SAAS,KAAM,UAAU,CAAC,GAAyC,YAAY;AAClH,MAAI,CAAC,aAAc,QAAO,CAAC;AAC3B,QAAM,aAAa,CAAC,iBAAiB,oBAAoB;AACzD,QAAM,SAAoB,CAAC,QAAQ;AACnC,MAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ;AACrC,eAAW,KAAK,UAAU,QAAQ,IAAI,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI,CAAC,GAAG;AAClE,WAAO,KAAK,GAAG,QAAQ,GAAG;AAAA,EAC5B;AACA,QAAM,SAAS,QAAQ,QAAQ,KAAK;AACpC,MAAI,QAAQ;AACV,UAAM,OAAO,IAAI,kBAAkB,MAAM,CAAC;AAC1C,eAAW,KAAK,iCAAiC;AACjD,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB;AACA,SAAO,KAAK,QAAQ,KAAK;AACzB,QAAM,OAAO,MAAM,WAAW;AAAA,IAC5B,+BAA+B,gBAAgB,UAAU,WAAW,KAAK,OAAO,CAAC;AAAA,IACjF;AAAA,EACF;AACA,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAClC,SAAO,KAAK,IAAI,CAAC,QAAQ;AACvB,UAAM,SAAS;AACf,WAAO;AAAA,MACL,IAAI,OAAO,OAAO,EAAE;AAAA,MACpB,MAAM,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,MACtD,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,IAC3D;AAAA,EACF,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -38,7 +38,7 @@ async function resolvePriceKindLabel(id) {
38
38
  try {
39
39
  const params = new URLSearchParams({ ids: id, pageSize: "1" });
40
40
  const payload = await readApiResultOrThrow(
41
- `/api/catalog/price-kinds?${params.toString()}`,
41
+ `/api/customer_groups/customer-groups/price-kinds?${params.toString()}`,
42
42
  void 0,
43
43
  { fallback: { items: [] } }
44
44
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customer_groups/widgets/injection/person-groups-explain-terms.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { SectionHeader } from '@open-mercato/ui/backend/SectionHeader'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customer_groups').child({ component: 'PersonGroupsExplainTerms' })\n\ntype GroupSummary = { id: string; code: string; name: string }\n\ntype ExplainTermsField = {\n value: string | number | boolean | null\n sourceGroupId: string | null\n path: GroupSummary[]\n}\n\n// Mirrors the `GET /api/customer_groups/customer-groups/explain-terms` response shape (see the\n// route's own `ExplainTermsResponse`/`ExplainTermsField` types) \u2014 kept as a local\n// client-side copy since API route modules are server-only and not importable here.\ntype ExplainTermsResponse = {\n groups: GroupSummary[]\n fields: {\n priceKindId: ExplainTermsField\n paymentTermsDays: ExplainTermsField\n allowPurchaseOnAccount: ExplainTermsField\n approvalRequiredAbove: ExplainTermsField\n minOrderValue: ExplainTermsField\n }\n}\n\nconst TERMS_FIELD_ORDER = [\n 'priceKindId',\n 'paymentTermsDays',\n 'allowPurchaseOnAccount',\n 'approvalRequiredAbove',\n 'minOrderValue',\n] as const\n\ntype TermsFieldKey = (typeof TERMS_FIELD_ORDER)[number]\n\nconst FIELD_LABEL_KEYS: Record<TermsFieldKey, [string, string]> = {\n priceKindId: ['customer_groups.groups.personTab.explainTerms.field.priceKindId', 'Price kind'],\n paymentTermsDays: ['customer_groups.groups.personTab.explainTerms.field.paymentTermsDays', 'Payment terms'],\n allowPurchaseOnAccount: [\n 'customer_groups.groups.personTab.explainTerms.field.allowPurchaseOnAccount',\n 'Allow purchase on account',\n ],\n approvalRequiredAbove: [\n 'customer_groups.groups.personTab.explainTerms.field.approvalRequiredAbove',\n 'Approval required above',\n ],\n minOrderValue: ['customer_groups.groups.personTab.explainTerms.field.minOrderValue', 'Minimum order value'],\n}\n\nfunction pickString(item: Record<string, unknown>, ...keys: string[]): string {\n for (const key of keys) {\n const value = item[key]\n if (typeof value === 'string' && value.length) return value\n }\n return ''\n}\n\n// Mirrors `resolvePriceKindLabel` in `../../components/CustomerGroupTermsPriceKindField.tsx`\n// \u2014 that file's helper is an unexported implementation detail of its combobox, and\n// this panel is read-only display (no combobox), so the same `/api/catalog/price-kinds?ids=`\n// lookup is re-implemented locally rather than imported.\nasync function resolvePriceKindLabel(id: string): Promise<string> {\n try {\n const params = new URLSearchParams({ ids: id, pageSize: '1' })\n const payload = await readApiResultOrThrow<{ items?: Record<string, unknown>[] }>(\n `/api/catalog/price-kinds?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n const item = items[0]\n if (!item) return id\n const title = pickString(item, 'title') || id\n const code = pickString(item, 'code')\n return code ? `${title} (${code})` : title\n } catch (err) {\n logger.error('customer_groups.explainTerms.priceKind.resolve', { err })\n return id\n }\n}\n\nfunction formatFieldValue(\n field: TermsFieldKey,\n entry: ExplainTermsField,\n priceKindLabels: Record<string, string>,\n t: TranslateFn,\n): string {\n const notSet = t('customer_groups.groups.personTab.explainTerms.notSet', 'Not set')\n if (field === 'priceKindId') {\n if (typeof entry.value !== 'string' || !entry.value) return notSet\n return priceKindLabels[entry.value] ?? entry.value\n }\n if (field === 'paymentTermsDays') {\n if (typeof entry.value !== 'number') return notSet\n return t('customer_groups.groups.personTab.explainTerms.value.days', '{count} days', { count: entry.value })\n }\n if (field === 'allowPurchaseOnAccount') {\n if (typeof entry.value !== 'boolean') return notSet\n return entry.value\n ? t('customer_groups.groups.personTab.explainTerms.value.yes', 'Yes')\n : t('customer_groups.groups.personTab.explainTerms.value.no', 'No')\n }\n // approvalRequiredAbove / minOrderValue\n if (typeof entry.value !== 'number') return notSet\n return String(entry.value)\n}\n\nfunction formatSourceLabel(entry: ExplainTermsField, t: TranslateFn): string {\n if (entry.sourceGroupId == null) {\n return t('customer_groups.groups.personTab.explainTerms.tenantDefault', 'Tenant default')\n }\n const path = entry.path.map((group) => `${group.name} (${group.code})`).join(' \u2192 ')\n return t('customer_groups.groups.personTab.explainTerms.viaPath', 'via {path}', { path })\n}\n\nfunction ExplainTermsRow({\n field,\n entry,\n priceKindLabels,\n}: {\n field: TermsFieldKey\n entry: ExplainTermsField\n priceKindLabels: Record<string, string>\n}) {\n const t = useT()\n const [labelKey, labelFallback] = FIELD_LABEL_KEYS[field]\n const fieldLabel = t(labelKey, labelFallback)\n const valueLabel = formatFieldValue(field, entry, priceKindLabels, t)\n const sourceLabel = formatSourceLabel(entry, t)\n\n return (\n <li className=\"rounded-md border border-border bg-card px-3 py-2\">\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"text-sm font-medium text-foreground\">{fieldLabel}</span>\n <span className=\"text-sm text-foreground\">{valueLabel}</span>\n </div>\n <div className=\"text-xs text-muted-foreground\">{sourceLabel}</div>\n </li>\n )\n}\n\nexport function PersonGroupsExplainTerms({\n customerId,\n refreshKey = 0,\n}: {\n customerId: string\n refreshKey?: number\n}) {\n const t = useT()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [data, setData] = React.useState<ExplainTermsResponse | null>(null)\n const [priceKindLabels, setPriceKindLabels] = React.useState<Record<string, string>>({})\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n apiCall<ExplainTermsResponse>(`/api/customer_groups/customer-groups/explain-terms?customerId=${encodeURIComponent(customerId)}`)\n .then((res) => {\n if (cancelled) return\n if (res.ok && res.result) {\n setData(res.result)\n } else if (res.status === 403) {\n setData(null)\n } else {\n throw new Error('[internal] explain-terms')\n }\n })\n .catch(() => {\n if (!cancelled) {\n setError(\n t('customer_groups.groups.personTab.explainTerms.errors.load', 'Failed to load resolved terms.'),\n )\n }\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [customerId, refreshKey, t])\n\n const priceKindId = data?.fields.priceKindId.value\n React.useEffect(() => {\n let cancelled = false\n if (typeof priceKindId !== 'string' || !priceKindId) return\n resolvePriceKindLabel(priceKindId).then((label) => {\n if (!cancelled) setPriceKindLabels((prev) => (prev[priceKindId] ? prev : { ...prev, [priceKindId]: label }))\n })\n return () => {\n cancelled = true\n }\n }, [priceKindId])\n\n if (loading) {\n return <LoadingMessage label={t('customer_groups.groups.personTab.explainTerms.loading', 'Loading resolved terms\u2026')} />\n }\n if (error) return <ErrorMessage label={error} />\n if (!data) return null\n\n return (\n <div className=\"space-y-3 border-t border-border pt-3\">\n <SectionHeader title={t('customer_groups.groups.personTab.explainTerms.title', 'Explain terms')} />\n <ul\n className=\"space-y-2\"\n aria-label={t('customer_groups.groups.personTab.explainTerms.listLabel', 'Resolved commercial terms')}\n >\n {TERMS_FIELD_ORDER.map((field) => (\n <ExplainTermsRow key={field} field={field} entry={data.fields[field]} priceKindLabels={priceKindLabels} />\n ))}\n </ul>\n </div>\n )\n}\n\nexport default PersonGroupsExplainTerms\n"],
5
- "mappings": ";AA6IM,SACE,KADF;AA3IN,YAAY,WAAW;AACvB,SAAS,YAAY;AAErB,SAAS,SAAS,4BAA4B;AAC9C,SAAS,cAAc,sBAAsB;AAC7C,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,iBAAiB,EAAE,MAAM,EAAE,WAAW,2BAA2B,CAAC;AAwB9F,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,MAAM,mBAA4D;AAAA,EAChE,aAAa,CAAC,mEAAmE,YAAY;AAAA,EAC7F,kBAAkB,CAAC,wEAAwE,eAAe;AAAA,EAC1G,wBAAwB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AAAA,EACA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAe,CAAC,qEAAqE,qBAAqB;AAC5G;AAEA,SAAS,WAAW,SAAkC,MAAwB;AAC5E,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,KAAK,GAAG;AACtB,QAAI,OAAO,UAAU,YAAY,MAAM,OAAQ,QAAO;AAAA,EACxD;AACA,SAAO;AACT;AAMA,eAAe,sBAAsB,IAA6B;AAChE,MAAI;AACF,UAAM,SAAS,IAAI,gBAAgB,EAAE,KAAK,IAAI,UAAU,IAAI,CAAC;AAC7D,UAAM,UAAU,MAAM;AAAA,MACpB,4BAA4B,OAAO,SAAS,CAAC;AAAA,MAC7C;AAAA,MACA,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,IAC5B;AACA,UAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC9D,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,WAAW,MAAM,OAAO,KAAK;AAC3C,UAAM,OAAO,WAAW,MAAM,MAAM;AACpC,WAAO,OAAO,GAAG,KAAK,KAAK,IAAI,MAAM;AAAA,EACvC,SAAS,KAAK;AACZ,WAAO,MAAM,kDAAkD,EAAE,IAAI,CAAC;AACtE,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBACP,OACA,OACA,iBACA,GACQ;AACR,QAAM,SAAS,EAAE,wDAAwD,SAAS;AAClF,MAAI,UAAU,eAAe;AAC3B,QAAI,OAAO,MAAM,UAAU,YAAY,CAAC,MAAM,MAAO,QAAO;AAC5D,WAAO,gBAAgB,MAAM,KAAK,KAAK,MAAM;AAAA,EAC/C;AACA,MAAI,UAAU,oBAAoB;AAChC,QAAI,OAAO,MAAM,UAAU,SAAU,QAAO;AAC5C,WAAO,EAAE,4DAA4D,gBAAgB,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,EAC7G;AACA,MAAI,UAAU,0BAA0B;AACtC,QAAI,OAAO,MAAM,UAAU,UAAW,QAAO;AAC7C,WAAO,MAAM,QACT,EAAE,2DAA2D,KAAK,IAClE,EAAE,0DAA0D,IAAI;AAAA,EACtE;AAEA,MAAI,OAAO,MAAM,UAAU,SAAU,QAAO;AAC5C,SAAO,OAAO,MAAM,KAAK;AAC3B;AAEA,SAAS,kBAAkB,OAA0B,GAAwB;AAC3E,MAAI,MAAM,iBAAiB,MAAM;AAC/B,WAAO,EAAE,+DAA+D,gBAAgB;AAAA,EAC1F;AACA,QAAM,OAAO,MAAM,KAAK,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG,EAAE,KAAK,UAAK;AAClF,SAAO,EAAE,yDAAyD,cAAc,EAAE,KAAK,CAAC;AAC1F;AAEA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,UAAU,aAAa,IAAI,iBAAiB,KAAK;AACxD,QAAM,aAAa,EAAE,UAAU,aAAa;AAC5C,QAAM,aAAa,iBAAiB,OAAO,OAAO,iBAAiB,CAAC;AACpE,QAAM,cAAc,kBAAkB,OAAO,CAAC;AAE9C,SACE,qBAAC,QAAG,WAAU,qDACZ;AAAA,yBAAC,SAAI,WAAU,uDACb;AAAA,0BAAC,UAAK,WAAU,uCAAuC,sBAAW;AAAA,MAClE,oBAAC,UAAK,WAAU,2BAA2B,sBAAW;AAAA,OACxD;AAAA,IACA,oBAAC,SAAI,WAAU,iCAAiC,uBAAY;AAAA,KAC9D;AAEJ;AAEO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA,aAAa;AACf,GAGG;AACD,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAsC,IAAI;AACxE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAiC,CAAC,CAAC;AAEvF,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,eAAW,IAAI;AACf,aAAS,IAAI;AACb,YAA8B,iEAAiE,mBAAmB,UAAU,CAAC,EAAE,EAC5H,KAAK,CAAC,QAAQ;AACb,UAAI,UAAW;AACf,UAAI,IAAI,MAAM,IAAI,QAAQ;AACxB,gBAAQ,IAAI,MAAM;AAAA,MACpB,WAAW,IAAI,WAAW,KAAK;AAC7B,gBAAQ,IAAI;AAAA,MACd,OAAO;AACL,cAAM,IAAI,MAAM,0BAA0B;AAAA,MAC5C;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AACX,UAAI,CAAC,WAAW;AACd;AAAA,UACE,EAAE,6DAA6D,gCAAgC;AAAA,QACjG;AAAA,MACF;AAAA,IACF,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,UAAW,YAAW,KAAK;AAAA,IAClC,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,YAAY,YAAY,CAAC,CAAC;AAE9B,QAAM,cAAc,MAAM,OAAO,YAAY;AAC7C,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,QAAI,OAAO,gBAAgB,YAAY,CAAC,YAAa;AACrD,0BAAsB,WAAW,EAAE,KAAK,CAAC,UAAU;AACjD,UAAI,CAAC,UAAW,oBAAmB,CAAC,SAAU,KAAK,WAAW,IAAI,OAAO,EAAE,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,CAAE;AAAA,IAC7G,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,MAAI,SAAS;AACX,WAAO,oBAAC,kBAAe,OAAO,EAAE,yDAAyD,8BAAyB,GAAG;AAAA,EACvH;AACA,MAAI,MAAO,QAAO,oBAAC,gBAAa,OAAO,OAAO;AAC9C,MAAI,CAAC,KAAM,QAAO;AAElB,SACE,qBAAC,SAAI,WAAU,yCACb;AAAA,wBAAC,iBAAc,OAAO,EAAE,uDAAuD,eAAe,GAAG;AAAA,IACjG;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,cAAY,EAAE,2DAA2D,2BAA2B;AAAA,QAEnG,4BAAkB,IAAI,CAAC,UACtB,oBAAC,mBAA4B,OAAc,OAAO,KAAK,OAAO,KAAK,GAAG,mBAAhD,KAAkF,CACzG;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,sCAAQ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { SectionHeader } from '@open-mercato/ui/backend/SectionHeader'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('customer_groups').child({ component: 'PersonGroupsExplainTerms' })\n\ntype GroupSummary = { id: string; code: string; name: string }\n\ntype ExplainTermsField = {\n value: string | number | boolean | null\n sourceGroupId: string | null\n path: GroupSummary[]\n}\n\n// Mirrors the `GET /api/customer_groups/customer-groups/explain-terms` response shape (see the\n// route's own `ExplainTermsResponse`/`ExplainTermsField` types) \u2014 kept as a local\n// client-side copy since API route modules are server-only and not importable here.\ntype ExplainTermsResponse = {\n groups: GroupSummary[]\n fields: {\n priceKindId: ExplainTermsField\n paymentTermsDays: ExplainTermsField\n allowPurchaseOnAccount: ExplainTermsField\n approvalRequiredAbove: ExplainTermsField\n minOrderValue: ExplainTermsField\n }\n}\n\nconst TERMS_FIELD_ORDER = [\n 'priceKindId',\n 'paymentTermsDays',\n 'allowPurchaseOnAccount',\n 'approvalRequiredAbove',\n 'minOrderValue',\n] as const\n\ntype TermsFieldKey = (typeof TERMS_FIELD_ORDER)[number]\n\nconst FIELD_LABEL_KEYS: Record<TermsFieldKey, [string, string]> = {\n priceKindId: ['customer_groups.groups.personTab.explainTerms.field.priceKindId', 'Price kind'],\n paymentTermsDays: ['customer_groups.groups.personTab.explainTerms.field.paymentTermsDays', 'Payment terms'],\n allowPurchaseOnAccount: [\n 'customer_groups.groups.personTab.explainTerms.field.allowPurchaseOnAccount',\n 'Allow purchase on account',\n ],\n approvalRequiredAbove: [\n 'customer_groups.groups.personTab.explainTerms.field.approvalRequiredAbove',\n 'Approval required above',\n ],\n minOrderValue: ['customer_groups.groups.personTab.explainTerms.field.minOrderValue', 'Minimum order value'],\n}\n\nfunction pickString(item: Record<string, unknown>, ...keys: string[]): string {\n for (const key of keys) {\n const value = item[key]\n if (typeof value === 'string' && value.length) return value\n }\n return ''\n}\n\n// Mirrors `resolvePriceKindLabel` in `../../components/CustomerGroupTermsPriceKindField.tsx`\n// \u2014 that file's helper is an unexported implementation detail of its combobox, and\n// this panel is read-only display (no combobox), so the same `/api/customer_groups/customer-groups/price-kinds?ids=`\n// lookup is re-implemented locally rather than imported.\nasync function resolvePriceKindLabel(id: string): Promise<string> {\n try {\n const params = new URLSearchParams({ ids: id, pageSize: '1' })\n const payload = await readApiResultOrThrow<{ items?: Record<string, unknown>[] }>(\n `/api/customer_groups/customer-groups/price-kinds?${params.toString()}`,\n undefined,\n { fallback: { items: [] } },\n )\n const items = Array.isArray(payload.items) ? payload.items : []\n const item = items[0]\n if (!item) return id\n const title = pickString(item, 'title') || id\n const code = pickString(item, 'code')\n return code ? `${title} (${code})` : title\n } catch (err) {\n logger.error('customer_groups.explainTerms.priceKind.resolve', { err })\n return id\n }\n}\n\nfunction formatFieldValue(\n field: TermsFieldKey,\n entry: ExplainTermsField,\n priceKindLabels: Record<string, string>,\n t: TranslateFn,\n): string {\n const notSet = t('customer_groups.groups.personTab.explainTerms.notSet', 'Not set')\n if (field === 'priceKindId') {\n if (typeof entry.value !== 'string' || !entry.value) return notSet\n return priceKindLabels[entry.value] ?? entry.value\n }\n if (field === 'paymentTermsDays') {\n if (typeof entry.value !== 'number') return notSet\n return t('customer_groups.groups.personTab.explainTerms.value.days', '{count} days', { count: entry.value })\n }\n if (field === 'allowPurchaseOnAccount') {\n if (typeof entry.value !== 'boolean') return notSet\n return entry.value\n ? t('customer_groups.groups.personTab.explainTerms.value.yes', 'Yes')\n : t('customer_groups.groups.personTab.explainTerms.value.no', 'No')\n }\n // approvalRequiredAbove / minOrderValue\n if (typeof entry.value !== 'number') return notSet\n return String(entry.value)\n}\n\nfunction formatSourceLabel(entry: ExplainTermsField, t: TranslateFn): string {\n if (entry.sourceGroupId == null) {\n return t('customer_groups.groups.personTab.explainTerms.tenantDefault', 'Tenant default')\n }\n const path = entry.path.map((group) => `${group.name} (${group.code})`).join(' \u2192 ')\n return t('customer_groups.groups.personTab.explainTerms.viaPath', 'via {path}', { path })\n}\n\nfunction ExplainTermsRow({\n field,\n entry,\n priceKindLabels,\n}: {\n field: TermsFieldKey\n entry: ExplainTermsField\n priceKindLabels: Record<string, string>\n}) {\n const t = useT()\n const [labelKey, labelFallback] = FIELD_LABEL_KEYS[field]\n const fieldLabel = t(labelKey, labelFallback)\n const valueLabel = formatFieldValue(field, entry, priceKindLabels, t)\n const sourceLabel = formatSourceLabel(entry, t)\n\n return (\n <li className=\"rounded-md border border-border bg-card px-3 py-2\">\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"text-sm font-medium text-foreground\">{fieldLabel}</span>\n <span className=\"text-sm text-foreground\">{valueLabel}</span>\n </div>\n <div className=\"text-xs text-muted-foreground\">{sourceLabel}</div>\n </li>\n )\n}\n\nexport function PersonGroupsExplainTerms({\n customerId,\n refreshKey = 0,\n}: {\n customerId: string\n refreshKey?: number\n}) {\n const t = useT()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [data, setData] = React.useState<ExplainTermsResponse | null>(null)\n const [priceKindLabels, setPriceKindLabels] = React.useState<Record<string, string>>({})\n\n React.useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(null)\n apiCall<ExplainTermsResponse>(`/api/customer_groups/customer-groups/explain-terms?customerId=${encodeURIComponent(customerId)}`)\n .then((res) => {\n if (cancelled) return\n if (res.ok && res.result) {\n setData(res.result)\n } else if (res.status === 403) {\n setData(null)\n } else {\n throw new Error('[internal] explain-terms')\n }\n })\n .catch(() => {\n if (!cancelled) {\n setError(\n t('customer_groups.groups.personTab.explainTerms.errors.load', 'Failed to load resolved terms.'),\n )\n }\n })\n .finally(() => {\n if (!cancelled) setLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [customerId, refreshKey, t])\n\n const priceKindId = data?.fields.priceKindId.value\n React.useEffect(() => {\n let cancelled = false\n if (typeof priceKindId !== 'string' || !priceKindId) return\n resolvePriceKindLabel(priceKindId).then((label) => {\n if (!cancelled) setPriceKindLabels((prev) => (prev[priceKindId] ? prev : { ...prev, [priceKindId]: label }))\n })\n return () => {\n cancelled = true\n }\n }, [priceKindId])\n\n if (loading) {\n return <LoadingMessage label={t('customer_groups.groups.personTab.explainTerms.loading', 'Loading resolved terms\u2026')} />\n }\n if (error) return <ErrorMessage label={error} />\n if (!data) return null\n\n return (\n <div className=\"space-y-3 border-t border-border pt-3\">\n <SectionHeader title={t('customer_groups.groups.personTab.explainTerms.title', 'Explain terms')} />\n <ul\n className=\"space-y-2\"\n aria-label={t('customer_groups.groups.personTab.explainTerms.listLabel', 'Resolved commercial terms')}\n >\n {TERMS_FIELD_ORDER.map((field) => (\n <ExplainTermsRow key={field} field={field} entry={data.fields[field]} priceKindLabels={priceKindLabels} />\n ))}\n </ul>\n </div>\n )\n}\n\nexport default PersonGroupsExplainTerms\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -544,7 +544,7 @@ const updateCompanyCommand = {
544
544
  id: "customers.companies.update",
545
545
  async prepare(rawInput, ctx) {
546
546
  const { parsed } = parseWithCustomFields(companyUpdateSchema, rawInput);
547
- const em = ctx.container.resolve("em");
547
+ const em = ctx.container.resolve("em").fork();
548
548
  const snapshot = await loadCompanySnapshot(em, parsed.id);
549
549
  return snapshot ? { before: snapshot } : {};
550
550
  },
@@ -592,6 +592,18 @@ const updateCompanyCommand = {
592
592
  if (parsed.annualRevenue !== void 0) {
593
593
  profile.annualRevenue = parsed.annualRevenue !== null && parsed.annualRevenue !== void 0 ? String(parsed.annualRevenue) : null;
594
594
  }
595
+ const profileFieldsUpdated = [
596
+ parsed.legalName,
597
+ parsed.brandName,
598
+ parsed.domain,
599
+ parsed.websiteUrl,
600
+ parsed.industry,
601
+ parsed.sizeBucket,
602
+ parsed.annualRevenue
603
+ ].some((value) => value !== void 0);
604
+ if (profileFieldsUpdated || Object.keys(custom).length > 0) {
605
+ record.updatedAt = /* @__PURE__ */ new Date();
606
+ }
595
607
  },
596
608
  () => syncEntityTags(em, record, parsed.tags)
597
609
  ], { transaction: true });
@@ -687,6 +699,7 @@ const updateCompanyCommand = {
687
699
  entity.nextInteractionIcon = before.entity.nextInteractionIcon;
688
700
  entity.nextInteractionColor = before.entity.nextInteractionColor;
689
701
  entity.isActive = before.entity.isActive;
702
+ entity.updatedAt = /* @__PURE__ */ new Date();
690
703
  }
691
704
  await em.flush();
692
705
  let profile = await em.findOne(CustomerCompanyProfile, { entity });