@open-mercato/core 0.6.7-develop.6726.1.983ae8a07e → 0.6.7-develop.6749.1.6b54c56dfe
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/resources_resource/index.js +2 -0
- package/dist/generated/entities/resources_resource/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/salesFixtures.js +31 -2
- package/dist/helpers/integration/salesFixtures.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +5 -0
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/auth/api/profile/route.js +8 -2
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/auth/backend/auth/profile/page.js.map +2 -2
- package/dist/modules/auth/backend/profile/change-password/page.js.map +2 -2
- package/dist/modules/communication_channels/data/enrichers.js +4 -4
- package/dist/modules/communication_channels/data/enrichers.js.map +2 -2
- package/dist/modules/customers/analytics.js +1 -0
- package/dist/modules/customers/analytics.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +2 -1
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +1 -1
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/data/validators.js +3 -1
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js +52 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +42 -26
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +2 -2
- package/dist/modules/data_sync/lib/queue-policy.js +15 -0
- package/dist/modules/data_sync/lib/queue-policy.js.map +7 -0
- package/dist/modules/data_sync/lib/queue.js +16 -1
- package/dist/modules/data_sync/lib/queue.js.map +2 -2
- package/dist/modules/data_sync/lib/start-run.js +2 -1
- package/dist/modules/data_sync/lib/start-run.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +35 -2
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +70 -2
- package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-export.js +9 -2
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +9 -2
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +34 -12
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/openapi.js +15 -2
- package/dist/modules/dictionaries/api/openapi.js.map +2 -2
- package/dist/modules/dictionaries/components/hooks/useDictionaryEntries.js +30 -29
- package/dist/modules/dictionaries/components/hooks/useDictionaryEntries.js.map +2 -2
- package/dist/modules/dictionaries/data/validators.js +7 -0
- package/dist/modules/dictionaries/data/validators.js.map +2 -2
- package/dist/modules/dictionaries/lib/clientEntries.js +4 -3
- package/dist/modules/dictionaries/lib/clientEntries.js.map +2 -2
- package/dist/modules/dictionaries/lib/fetchAllEntries.js +43 -0
- package/dist/modules/dictionaries/lib/fetchAllEntries.js.map +7 -0
- package/dist/modules/entities/api/definitions.js +51 -3
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/entities.js +21 -2
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +3 -24
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/api/sidebar-entities.js +34 -12
- package/dist/modules/entities/api/sidebar-entities.js.map +2 -2
- package/dist/modules/entities/lib/entityAcl.js +46 -0
- package/dist/modules/entities/lib/entityAcl.js.map +2 -2
- package/dist/modules/messages/api/route.js +2 -5
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/lib/participantScope.js +15 -0
- package/dist/modules/messages/lib/participantScope.js.map +7 -0
- package/dist/modules/notifications/api/route.js +3 -1
- package/dist/modules/notifications/api/route.js.map +2 -2
- package/dist/modules/notifications/api/unread-count/route.js +25 -6
- package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
- package/dist/modules/notifications/lib/notificationScope.js +26 -0
- package/dist/modules/notifications/lib/notificationScope.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +7 -3
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/notifications/lib/routeHelpers.js +16 -2
- package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
- package/dist/modules/planner/components/unavailabilityReasons.js +3 -3
- package/dist/modules/planner/components/unavailabilityReasons.js.map +2 -2
- package/dist/modules/progress/lib/progressService.js +2 -0
- package/dist/modules/progress/lib/progressService.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +322 -104
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/resources/api/resources.js +2 -0
- package/dist/modules/resources/api/resources.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +3 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
- package/dist/modules/resources/commands/resources.js +9 -0
- package/dist/modules/resources/commands/resources.js.map +2 -2
- package/dist/modules/resources/components/detail/dictionaries.js +3 -3
- package/dist/modules/resources/components/detail/dictionaries.js.map +2 -2
- package/dist/modules/resources/data/entities.js +3 -0
- package/dist/modules/resources/data/entities.js.map +2 -2
- package/dist/modules/resources/data/validators.js +6 -2
- package/dist/modules/resources/data/validators.js.map +2 -2
- package/dist/modules/resources/lib/seeds.js +16 -9
- package/dist/modules/resources/lib/seeds.js.map +2 -2
- package/dist/modules/resources/migrations/Migration20260608231000.js +13 -0
- package/dist/modules/resources/migrations/Migration20260608231000.js.map +7 -0
- package/dist/modules/sales/backend/sales/documents/create/page.js +11 -14
- package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +8 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +18 -9
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +12 -5
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +48 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +167 -0
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +7 -0
- package/dist/modules/sales/data/validators.js +5 -1
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/staff/components/detail/dictionaries.js +3 -3
- package/dist/modules/staff/components/detail/dictionaries.js.map +2 -2
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +11 -4
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +2 -2
- package/generated/entities/resources_resource/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +7 -7
- package/src/helpers/integration/salesFixtures.ts +34 -2
- package/src/helpers/integration/salesUi.ts +8 -0
- package/src/modules/auth/api/profile/route.ts +8 -2
- package/src/modules/auth/backend/auth/profile/page.tsx +1 -0
- package/src/modules/auth/backend/profile/change-password/page.tsx +1 -0
- package/src/modules/communication_channels/data/enrichers.ts +6 -26
- package/src/modules/customers/analytics.ts +1 -0
- package/src/modules/customers/components/detail/DealForm.tsx +5 -1
- package/src/modules/customers/components/detail/DealsSection.tsx +1 -1
- package/src/modules/customers/data/validators.ts +5 -1
- package/src/modules/dashboards/i18n/de.json +3 -0
- package/src/modules/dashboards/i18n/en.json +3 -0
- package/src/modules/dashboards/i18n/es.json +3 -0
- package/src/modules/dashboards/i18n/pl.json +3 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +86 -0
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +33 -18
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +2 -2
- package/src/modules/data_sync/lib/adapter.ts +18 -0
- package/src/modules/data_sync/lib/queue-policy.ts +30 -0
- package/src/modules/data_sync/lib/queue.ts +19 -1
- package/src/modules/data_sync/lib/start-run.ts +2 -1
- package/src/modules/data_sync/lib/sync-engine.ts +46 -0
- package/src/modules/data_sync/lib/sync-run-service.ts +82 -1
- package/src/modules/data_sync/workers/sync-export.ts +8 -1
- package/src/modules/data_sync/workers/sync-import.ts +8 -1
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +39 -11
- package/src/modules/dictionaries/api/openapi.ts +19 -0
- package/src/modules/dictionaries/components/hooks/useDictionaryEntries.ts +35 -36
- package/src/modules/dictionaries/data/validators.ts +14 -0
- package/src/modules/dictionaries/lib/clientEntries.ts +4 -3
- package/src/modules/dictionaries/lib/fetchAllEntries.ts +72 -0
- package/src/modules/entities/api/definitions.ts +51 -3
- package/src/modules/entities/api/entities.ts +30 -2
- package/src/modules/entities/api/records.ts +3 -27
- package/src/modules/entities/api/sidebar-entities.ts +43 -14
- package/src/modules/entities/lib/entityAcl.ts +58 -0
- package/src/modules/messages/api/route.ts +4 -5
- package/src/modules/messages/lib/participantScope.ts +64 -0
- package/src/modules/notifications/api/route.ts +2 -0
- package/src/modules/notifications/api/unread-count/route.ts +42 -5
- package/src/modules/notifications/lib/notificationScope.ts +40 -0
- package/src/modules/notifications/lib/notificationService.ts +5 -0
- package/src/modules/notifications/lib/routeHelpers.ts +19 -2
- package/src/modules/planner/components/unavailabilityReasons.ts +3 -3
- package/src/modules/progress/AGENTS.md +11 -1
- package/src/modules/progress/lib/progressService.ts +1 -0
- package/src/modules/progress/lib/progressServiceImpl.ts +385 -117
- package/src/modules/resources/api/resources.ts +2 -0
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +9 -3
- package/src/modules/resources/commands/resources.ts +10 -0
- package/src/modules/resources/components/detail/dictionaries.ts +3 -3
- package/src/modules/resources/data/entities.ts +3 -0
- package/src/modules/resources/data/validators.ts +5 -0
- package/src/modules/resources/lib/seeds.ts +16 -9
- package/src/modules/resources/migrations/.snapshot-open-mercato.json +17 -1
- package/src/modules/resources/migrations/Migration20260608231000.ts +13 -0
- package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -4
- package/src/modules/sales/commands/documents.ts +8 -0
- package/src/modules/sales/components/documents/ItemsSection.tsx +19 -10
- package/src/modules/sales/components/documents/LineItemDialog.tsx +13 -5
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +48 -6
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +198 -0
- package/src/modules/sales/data/validators.ts +7 -1
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/staff/components/detail/dictionaries.ts +3 -3
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +12 -5
|
@@ -86,6 +86,7 @@ const crud = makeCrudRoute({
|
|
|
86
86
|
'appearance_color',
|
|
87
87
|
F.is_active,
|
|
88
88
|
'availability_rule_set_id',
|
|
89
|
+
'custom_fieldset_code',
|
|
89
90
|
F.created_at,
|
|
90
91
|
F.updated_at,
|
|
91
92
|
],
|
|
@@ -242,6 +243,7 @@ const resourceListItemSchema = z.object({
|
|
|
242
243
|
appearance_color: z.string().nullable().optional(),
|
|
243
244
|
is_active: z.boolean().nullable().optional(),
|
|
244
245
|
availability_rule_set_id: z.string().uuid().nullable().optional(),
|
|
246
|
+
custom_fieldset_code: z.string().nullable().optional(),
|
|
245
247
|
created_at: z.string().nullable().optional(),
|
|
246
248
|
updated_at: z.string().nullable().optional(),
|
|
247
249
|
tags: z.array(resourceTagListItemSchema).optional(),
|
|
@@ -59,6 +59,8 @@ type ResourceRecord = {
|
|
|
59
59
|
is_active?: boolean
|
|
60
60
|
availabilityRuleSetId?: string | null
|
|
61
61
|
availability_rule_set_id?: string | null
|
|
62
|
+
customFieldsetCode?: string | null
|
|
63
|
+
custom_fieldset_code?: string | null
|
|
62
64
|
} & Record<string, unknown>
|
|
63
65
|
|
|
64
66
|
type ResourceResponse = {
|
|
@@ -85,6 +87,7 @@ function normalizeResourceRecord(record: ResourceRecord): ResourceRecord {
|
|
|
85
87
|
appearanceIcon: record.appearanceIcon ?? record.appearance_icon ?? null,
|
|
86
88
|
appearanceColor: record.appearanceColor ?? record.appearance_color ?? null,
|
|
87
89
|
isActive: record.isActive ?? record.is_active ?? true,
|
|
90
|
+
customFieldsetCode: record.customFieldsetCode ?? record.custom_fieldset_code ?? null,
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
|
|
@@ -547,9 +550,12 @@ export default function ResourcesResourceDetailPage({ params }: { params?: { id?
|
|
|
547
550
|
: typeof resource.updated_at === 'string'
|
|
548
551
|
? resource.updated_at
|
|
549
552
|
: null,
|
|
550
|
-
customFieldsetCode:
|
|
551
|
-
|
|
552
|
-
|
|
553
|
+
customFieldsetCode:
|
|
554
|
+
typeof resource.customFieldsetCode === 'string' && resource.customFieldsetCode.trim().length
|
|
555
|
+
? resource.customFieldsetCode.trim()
|
|
556
|
+
: resource.resourceTypeId
|
|
557
|
+
? resolveFieldsetCode(resource.resourceTypeId)
|
|
558
|
+
: RESOURCES_RESOURCE_FIELDSET_DEFAULT,
|
|
553
559
|
...customValues,
|
|
554
560
|
})
|
|
555
561
|
}
|
|
@@ -50,6 +50,7 @@ type ResourceSnapshot = {
|
|
|
50
50
|
appearanceColor: string | null
|
|
51
51
|
isActive: boolean
|
|
52
52
|
availabilityRuleSetId: string | null
|
|
53
|
+
customFieldsetCode: string | null
|
|
53
54
|
tags: string[]
|
|
54
55
|
deletedAt: string | null
|
|
55
56
|
customFields?: CustomFieldSnapshot | null
|
|
@@ -154,6 +155,7 @@ async function loadResourceSnapshot(em: EntityManager, id: string): Promise<Reso
|
|
|
154
155
|
appearanceColor: resource.appearanceColor ?? null,
|
|
155
156
|
isActive: resource.isActive,
|
|
156
157
|
availabilityRuleSetId: resource.availabilityRuleSetId ?? null,
|
|
158
|
+
customFieldsetCode: resource.customFieldsetCode ?? null,
|
|
157
159
|
tags,
|
|
158
160
|
deletedAt: resource.deletedAt ? resource.deletedAt.toISOString() : null,
|
|
159
161
|
}
|
|
@@ -235,6 +237,7 @@ const createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { reso
|
|
|
235
237
|
appearanceColor: parsed.appearanceColor ?? null,
|
|
236
238
|
isActive: parsed.isActive ?? true,
|
|
237
239
|
availabilityRuleSetId: parsed.availabilityRuleSetId ?? null,
|
|
240
|
+
customFieldsetCode: parsed.customFieldsetCode ?? null,
|
|
238
241
|
createdAt: now,
|
|
239
242
|
updatedAt: now,
|
|
240
243
|
})
|
|
@@ -358,6 +361,7 @@ const createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { reso
|
|
|
358
361
|
appearanceColor: after.appearanceColor ?? null,
|
|
359
362
|
isActive: after.isActive,
|
|
360
363
|
availabilityRuleSetId: after.availabilityRuleSetId ?? null,
|
|
364
|
+
customFieldsetCode: after.customFieldsetCode ?? null,
|
|
361
365
|
deletedAt: null,
|
|
362
366
|
createdAt: new Date(),
|
|
363
367
|
updatedAt: new Date(),
|
|
@@ -376,6 +380,7 @@ const createResourceCommand: CommandHandler<ResourcesResourceCreateInput, { reso
|
|
|
376
380
|
record.appearanceColor = after.appearanceColor ?? null
|
|
377
381
|
record.isActive = after.isActive
|
|
378
382
|
record.availabilityRuleSetId = after.availabilityRuleSetId ?? null
|
|
383
|
+
record.customFieldsetCode = after.customFieldsetCode ?? null
|
|
379
384
|
record.deletedAt = null
|
|
380
385
|
record.updatedAt = new Date()
|
|
381
386
|
}
|
|
@@ -471,6 +476,7 @@ const updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { reso
|
|
|
471
476
|
if (parsed.appearanceIcon !== undefined) record.appearanceIcon = parsed.appearanceIcon ?? null
|
|
472
477
|
if (parsed.appearanceColor !== undefined) record.appearanceColor = parsed.appearanceColor ?? null
|
|
473
478
|
if (parsed.availabilityRuleSetId !== undefined) record.availabilityRuleSetId = parsed.availabilityRuleSetId ?? null
|
|
479
|
+
if (parsed.customFieldsetCode !== undefined) record.customFieldsetCode = parsed.customFieldsetCode ?? null
|
|
474
480
|
record.updatedAt = new Date()
|
|
475
481
|
if (parsed.isActive !== undefined) record.isActive = parsed.isActive
|
|
476
482
|
},
|
|
@@ -531,6 +537,7 @@ const updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { reso
|
|
|
531
537
|
'appearanceColor',
|
|
532
538
|
'isActive',
|
|
533
539
|
'availabilityRuleSetId',
|
|
540
|
+
'customFieldsetCode',
|
|
534
541
|
'deletedAt',
|
|
535
542
|
])
|
|
536
543
|
if (before.tags.join(',') !== after.tags.join(',')) {
|
|
@@ -583,6 +590,7 @@ const updateResourceCommand: CommandHandler<ResourcesResourceUpdateInput, { reso
|
|
|
583
590
|
record.appearanceColor = before.appearanceColor ?? null
|
|
584
591
|
record.isActive = before.isActive
|
|
585
592
|
record.availabilityRuleSetId = before.availabilityRuleSetId ?? null
|
|
593
|
+
record.customFieldsetCode = before.customFieldsetCode ?? null
|
|
586
594
|
record.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null
|
|
587
595
|
record.updatedAt = new Date()
|
|
588
596
|
},
|
|
@@ -716,6 +724,7 @@ const deleteResourceCommand: CommandHandler<{ id?: string }, { resourceId: strin
|
|
|
716
724
|
appearanceColor: before.appearanceColor ?? null,
|
|
717
725
|
isActive: before.isActive,
|
|
718
726
|
availabilityRuleSetId: before.availabilityRuleSetId ?? null,
|
|
727
|
+
customFieldsetCode: before.customFieldsetCode ?? null,
|
|
719
728
|
deletedAt: null,
|
|
720
729
|
createdAt: new Date(),
|
|
721
730
|
updatedAt: new Date(),
|
|
@@ -734,6 +743,7 @@ const deleteResourceCommand: CommandHandler<{ id?: string }, { resourceId: strin
|
|
|
734
743
|
record.appearanceColor = before.appearanceColor ?? null
|
|
735
744
|
record.isActive = before.isActive
|
|
736
745
|
record.availabilityRuleSetId = before.availabilityRuleSetId ?? null
|
|
746
|
+
record.customFieldsetCode = before.customFieldsetCode ?? null
|
|
737
747
|
record.deletedAt = null
|
|
738
748
|
record.updatedAt = new Date()
|
|
739
749
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
4
|
+
import { fetchAllDictionaryEntries } from '@open-mercato/core/modules/dictionaries/lib/fetchAllEntries'
|
|
4
5
|
|
|
5
6
|
export type DictionaryEntryOption = {
|
|
6
7
|
value: string
|
|
@@ -52,10 +53,9 @@ export async function loadResourceDictionary(
|
|
|
52
53
|
const name = 'Resource activity types'
|
|
53
54
|
const dictionary = await ensureDictionary(key, name)
|
|
54
55
|
if (!dictionary) return { dictionary: null, entries: [] }
|
|
55
|
-
const entriesCall = await
|
|
56
|
+
const entriesCall = await fetchAllDictionaryEntries(dictionary.id)
|
|
56
57
|
if (!entriesCall.ok) return { dictionary, entries: [] }
|
|
57
|
-
const
|
|
58
|
-
const entries = items
|
|
58
|
+
const entries = entriesCall.items
|
|
59
59
|
.map((entry) => {
|
|
60
60
|
if (!entry || typeof entry !== 'object') return null
|
|
61
61
|
const record = entry as Record<string, unknown>
|
|
@@ -83,6 +83,9 @@ export class ResourcesResource {
|
|
|
83
83
|
@Property({ name: 'availability_rule_set_id', type: 'uuid', nullable: true })
|
|
84
84
|
availabilityRuleSetId?: string | null
|
|
85
85
|
|
|
86
|
+
@Property({ name: 'custom_fieldset_code', type: 'text', nullable: true })
|
|
87
|
+
customFieldsetCode?: string | null
|
|
88
|
+
|
|
86
89
|
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
87
90
|
createdAt: Date = new Date()
|
|
88
91
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import { fieldsetCodeRegex } from '@open-mercato/shared/modules/entities/validators'
|
|
3
|
+
|
|
4
|
+
const customFieldsetCodeSchema = z.string().trim().regex(fieldsetCodeRegex).max(100)
|
|
2
5
|
|
|
3
6
|
const scopedCreateFields = {
|
|
4
7
|
tenantId: z.string().uuid(),
|
|
@@ -52,6 +55,7 @@ export const resourcesResourceCreateSchema = z.object({
|
|
|
52
55
|
.nullable(),
|
|
53
56
|
isActive: z.boolean().optional(),
|
|
54
57
|
availabilityRuleSetId: z.string().uuid().optional().nullable(),
|
|
58
|
+
customFieldsetCode: customFieldsetCodeSchema.optional().nullable(),
|
|
55
59
|
})
|
|
56
60
|
|
|
57
61
|
export const resourcesResourceUpdateSchema = z.object({
|
|
@@ -71,6 +75,7 @@ export const resourcesResourceUpdateSchema = z.object({
|
|
|
71
75
|
.nullable(),
|
|
72
76
|
isActive: z.boolean().optional(),
|
|
73
77
|
availabilityRuleSetId: z.string().uuid().optional().nullable(),
|
|
78
|
+
customFieldsetCode: customFieldsetCodeSchema.optional().nullable(),
|
|
74
79
|
})
|
|
75
80
|
|
|
76
81
|
export const resourcesResourceTagCreateSchema = z.object({
|
|
@@ -707,25 +707,32 @@ export async function seedResourcesResourceExamples(
|
|
|
707
707
|
const resourceByName = new Map(existingResources.map((resource) => [resource.name.toLowerCase(), resource]))
|
|
708
708
|
const resourceByKey = new Map<string, ResourcesResource>()
|
|
709
709
|
for (const seed of RESOURCE_SEEDS) {
|
|
710
|
+
const seedType = seed.typeKey ? typeByKey.get(seed.typeKey) ?? null : null
|
|
711
|
+
const seedFieldsetCode = resolveResourcesResourceFieldsetCode(seedType?.name ?? seed.name)
|
|
710
712
|
const existing = resourceByName.get(seed.name.toLowerCase())
|
|
711
713
|
if (existing) {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
714
|
+
let touched = false
|
|
715
|
+
if (!existing.resourceTypeId && seedType?.id) {
|
|
716
|
+
existing.resourceTypeId = seedType.id
|
|
717
|
+
touched = true
|
|
718
|
+
}
|
|
719
|
+
if (!existing.customFieldsetCode) {
|
|
720
|
+
existing.customFieldsetCode = seedFieldsetCode
|
|
721
|
+
touched = true
|
|
722
|
+
}
|
|
723
|
+
if (touched) {
|
|
724
|
+
existing.updatedAt = now
|
|
725
|
+
em.persist(existing)
|
|
719
726
|
}
|
|
720
727
|
resourceByKey.set(seed.key, existing)
|
|
721
728
|
continue
|
|
722
729
|
}
|
|
723
|
-
const resourceTypeId = seed.typeKey ? typeByKey.get(seed.typeKey)?.id ?? null : null
|
|
724
730
|
const resource = em.create(ResourcesResource, {
|
|
725
731
|
tenantId: scope.tenantId,
|
|
726
732
|
organizationId: scope.organizationId,
|
|
727
733
|
name: seed.name,
|
|
728
|
-
resourceTypeId,
|
|
734
|
+
resourceTypeId: seedType?.id ?? null,
|
|
735
|
+
customFieldsetCode: seedFieldsetCode,
|
|
729
736
|
isActive: true,
|
|
730
737
|
createdAt: now,
|
|
731
738
|
updatedAt: now,
|
|
@@ -1118,6 +1118,22 @@
|
|
|
1118
1118
|
"enumItems": [],
|
|
1119
1119
|
"mappedType": "uuid"
|
|
1120
1120
|
},
|
|
1121
|
+
"custom_fieldset_code": {
|
|
1122
|
+
"name": "custom_fieldset_code",
|
|
1123
|
+
"type": "text",
|
|
1124
|
+
"unsigned": false,
|
|
1125
|
+
"autoincrement": false,
|
|
1126
|
+
"primary": false,
|
|
1127
|
+
"nullable": true,
|
|
1128
|
+
"unique": false,
|
|
1129
|
+
"length": null,
|
|
1130
|
+
"precision": null,
|
|
1131
|
+
"scale": null,
|
|
1132
|
+
"default": null,
|
|
1133
|
+
"comment": null,
|
|
1134
|
+
"enumItems": [],
|
|
1135
|
+
"mappedType": "text"
|
|
1136
|
+
},
|
|
1121
1137
|
"capacity": {
|
|
1122
1138
|
"name": "capacity",
|
|
1123
1139
|
"type": "int",
|
|
@@ -1389,4 +1405,4 @@
|
|
|
1389
1405
|
],
|
|
1390
1406
|
"views": [],
|
|
1391
1407
|
"nativeEnums": {}
|
|
1392
|
-
}
|
|
1408
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
export class Migration20260608231000 extends Migration {
|
|
4
|
+
|
|
5
|
+
override async up(): Promise<void> {
|
|
6
|
+
this.addSql(`alter table "resources_resources" add column "custom_fieldset_code" text null;`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
override async down(): Promise<void> {
|
|
10
|
+
this.addSql(`alter table "resources_resources" drop column "custom_fieldset_code";`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
@@ -58,7 +58,7 @@ export default function CreateSalesDocumentPage() {
|
|
|
58
58
|
} catch { /* ignore */ }
|
|
59
59
|
|
|
60
60
|
// Auto-add line items from the inbox draft
|
|
61
|
-
const lineItems = Array.isArray(inboxDraft.payload.lineItems)
|
|
61
|
+
const lineItems = kind === 'quote' && Array.isArray(inboxDraft.payload.lineItems)
|
|
62
62
|
? (inboxDraft.payload.lineItems as Record<string, unknown>[])
|
|
63
63
|
: []
|
|
64
64
|
const lineEndpoint = kind === 'order' ? '/api/sales/order-lines' : '/api/sales/quote-lines'
|
|
@@ -123,9 +123,7 @@ export default function CreateSalesDocumentPage() {
|
|
|
123
123
|
initialKind={initialKind}
|
|
124
124
|
inboxPreFill={inboxPreFill}
|
|
125
125
|
/>
|
|
126
|
-
|
|
127
|
-
{t('sales.documents.form.nextStep', 'After creation you will add items, prices, and fulfillment details.')}
|
|
128
|
-
</p>
|
|
126
|
+
|
|
129
127
|
</PageBody>
|
|
130
128
|
</Page>
|
|
131
129
|
)
|
|
@@ -7066,6 +7066,14 @@ const orderLineDeleteCommand: CommandHandler<
|
|
|
7066
7066
|
"Document not found or inaccessible.",
|
|
7067
7067
|
));
|
|
7068
7068
|
}
|
|
7069
|
+
if (filtered.length === 0) {
|
|
7070
|
+
throw new CrudHttpError(409, {
|
|
7071
|
+
error: translate(
|
|
7072
|
+
"sales.documents.items.errorDeleteLast",
|
|
7073
|
+
"An order must contain at least one line item.",
|
|
7074
|
+
),
|
|
7075
|
+
});
|
|
7076
|
+
}
|
|
7069
7077
|
const sourceInputs = filtered.map((line, index) => ({
|
|
7070
7078
|
...mapOrderLineEntityToSnapshot(line),
|
|
7071
7079
|
statusEntryId: line.statusEntryId ?? null,
|
|
@@ -833,6 +833,7 @@ export function SalesDocumentItemsSection({
|
|
|
833
833
|
size="icon"
|
|
834
834
|
variant="ghost"
|
|
835
835
|
className="h-8 w-8"
|
|
836
|
+
aria-label={t('ui.actions.edit', 'Edit')}
|
|
836
837
|
onClick={(event) => {
|
|
837
838
|
event.stopPropagation();
|
|
838
839
|
handleEdit(item);
|
|
@@ -840,17 +841,25 @@ export function SalesDocumentItemsSection({
|
|
|
840
841
|
>
|
|
841
842
|
<Pencil className="h-4 w-4" />
|
|
842
843
|
</Button>
|
|
843
|
-
<
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
onClick={(event) => {
|
|
848
|
-
event.stopPropagation();
|
|
849
|
-
void handleDelete(item);
|
|
850
|
-
}}
|
|
844
|
+
<span
|
|
845
|
+
title={kind === 'order' && items.length === 1
|
|
846
|
+
? t('sales.documents.items.errorDeleteLast', 'An order must contain at least one line item.')
|
|
847
|
+
: undefined}
|
|
851
848
|
>
|
|
852
|
-
<
|
|
853
|
-
|
|
849
|
+
<Button
|
|
850
|
+
size="icon"
|
|
851
|
+
variant="ghost"
|
|
852
|
+
className="h-8 w-8 text-destructive"
|
|
853
|
+
aria-label={t('ui.actions.delete', 'Delete')}
|
|
854
|
+
disabled={kind === 'order' && items.length === 1}
|
|
855
|
+
onClick={(event) => {
|
|
856
|
+
event.stopPropagation();
|
|
857
|
+
void handleDelete(item);
|
|
858
|
+
}}
|
|
859
|
+
>
|
|
860
|
+
<Trash2 className="h-4 w-4" />
|
|
861
|
+
</Button>
|
|
862
|
+
</span>
|
|
854
863
|
</div>
|
|
855
864
|
</td>
|
|
856
865
|
</tr>
|
|
@@ -272,7 +272,7 @@ type SnapshotEntity = {
|
|
|
272
272
|
type SalesLineDialogProps = {
|
|
273
273
|
open: boolean;
|
|
274
274
|
kind: "order" | "quote";
|
|
275
|
-
documentId
|
|
275
|
+
documentId?: string;
|
|
276
276
|
currencyCode: string | null | undefined;
|
|
277
277
|
documentUpdatedAt?: string | null;
|
|
278
278
|
organizationId: string | null;
|
|
@@ -281,6 +281,7 @@ type SalesLineDialogProps = {
|
|
|
281
281
|
shippedQuantity?: number;
|
|
282
282
|
onOpenChange: (open: boolean) => void;
|
|
283
283
|
onSaved?: () => Promise<void> | void;
|
|
284
|
+
onDraftSaved?: (payload: Record<string, unknown>, lineId: string | null) => Promise<void> | void;
|
|
284
285
|
};
|
|
285
286
|
|
|
286
287
|
const defaultForm = (currencyCode?: string | null): LineFormState => ({
|
|
@@ -476,6 +477,7 @@ export function LineItemDialog({
|
|
|
476
477
|
shippedQuantity = 0,
|
|
477
478
|
onOpenChange,
|
|
478
479
|
onSaved,
|
|
480
|
+
onDraftSaved,
|
|
479
481
|
}: SalesLineDialogProps) {
|
|
480
482
|
const t = useT();
|
|
481
483
|
const scope = useOrganizationScopeDetail();
|
|
@@ -1273,7 +1275,7 @@ export function LineItemDialog({
|
|
|
1273
1275
|
const resolvedOrg = resolvedOrganizationId;
|
|
1274
1276
|
const resolvedTenant = resolvedTenantId;
|
|
1275
1277
|
|
|
1276
|
-
if (!resolvedOrg || !resolvedTenant || !resolvedDocumentId) {
|
|
1278
|
+
if (!onDraftSaved && (!resolvedOrg || !resolvedTenant || !resolvedDocumentId)) {
|
|
1277
1279
|
throw createCrudFormError(
|
|
1278
1280
|
t(
|
|
1279
1281
|
"sales.documents.items.errorScope",
|
|
@@ -1466,9 +1468,9 @@ export function LineItemDialog({
|
|
|
1466
1468
|
};
|
|
1467
1469
|
|
|
1468
1470
|
const payload: Record<string, unknown> = {
|
|
1469
|
-
[documentKey]:
|
|
1470
|
-
organizationId:
|
|
1471
|
-
tenantId:
|
|
1471
|
+
...(resolvedDocumentId ? { [documentKey]: resolvedDocumentId } : {}),
|
|
1472
|
+
...(resolvedOrg ? { organizationId: resolvedOrg } : {}),
|
|
1473
|
+
...(resolvedTenant ? { tenantId: resolvedTenant } : {}),
|
|
1472
1474
|
productId: isCustomLine
|
|
1473
1475
|
? undefined
|
|
1474
1476
|
: values.productId
|
|
@@ -1508,6 +1510,11 @@ export function LineItemDialog({
|
|
|
1508
1510
|
if (resolvedName) payload.name = resolvedName;
|
|
1509
1511
|
|
|
1510
1512
|
try {
|
|
1513
|
+
if (onDraftSaved) {
|
|
1514
|
+
await onDraftSaved(payload, editingId);
|
|
1515
|
+
closeDialog();
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1511
1518
|
const action = editingId ? updateCrud : createCrud;
|
|
1512
1519
|
const result = await withScopedApiRequestHeaders(
|
|
1513
1520
|
buildOptimisticLockHeader(documentUpdatedAt),
|
|
@@ -1545,6 +1552,7 @@ export function LineItemDialog({
|
|
|
1545
1552
|
documentKey,
|
|
1546
1553
|
documentUpdatedAt,
|
|
1547
1554
|
editingId,
|
|
1555
|
+
onDraftSaved,
|
|
1548
1556
|
priceOptions,
|
|
1549
1557
|
productOption,
|
|
1550
1558
|
resourcePath,
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
import { AddressEditor, type AddressEditorDraft } from '@open-mercato/core/modules/customers/components/AddressEditor'
|
|
60
60
|
import { useSalesChannelsEnabled } from '../useSalesChannelsEnabled'
|
|
61
61
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
62
|
+
import { SalesOrderDraftLines, createSalesOrderLineDraft, type SalesOrderLineDraft } from './SalesOrderDraftLines'
|
|
62
63
|
|
|
63
64
|
const logger = createLogger('sales')
|
|
64
65
|
|
|
@@ -102,6 +103,7 @@ export type SalesDocumentFormValues = {
|
|
|
102
103
|
shippingAddressDraft?: AddressDraft
|
|
103
104
|
billingAddressDraft?: AddressDraft
|
|
104
105
|
comments?: string | null
|
|
106
|
+
lines?: SalesOrderLineDraft[]
|
|
105
107
|
} & Record<string, unknown>
|
|
106
108
|
|
|
107
109
|
type InboxPreFill = {
|
|
@@ -805,6 +807,7 @@ function CustomerGroupComponent({ values, setValue, t, customers, setCustomers,
|
|
|
805
807
|
|
|
806
808
|
export function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind, inboxPreFill }: SalesDocumentFormProps) {
|
|
807
809
|
const t = useT()
|
|
810
|
+
const { organizationId, tenantId } = useOrganizationScopeDetail()
|
|
808
811
|
const { enabled: channelsEnabled } = useSalesChannelsEnabled()
|
|
809
812
|
const [customers, setCustomers] = React.useState<CustomerOption[]>([])
|
|
810
813
|
const [customerLoading, setCustomerLoading] = React.useState(false)
|
|
@@ -1305,14 +1308,19 @@ export function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind
|
|
|
1305
1308
|
type: 'textarea',
|
|
1306
1309
|
},
|
|
1307
1310
|
{
|
|
1308
|
-
id: '
|
|
1311
|
+
id: 'lines',
|
|
1309
1312
|
label: '',
|
|
1310
1313
|
type: 'custom',
|
|
1311
|
-
component: () => (
|
|
1312
|
-
<
|
|
1313
|
-
{
|
|
1314
|
-
|
|
1315
|
-
|
|
1314
|
+
component: ({ value, error, values, setValue }) => values?.documentKind === 'order' ? (
|
|
1315
|
+
<SalesOrderDraftLines
|
|
1316
|
+
currencyCode={typeof values.currencyCode === 'string' ? values.currencyCode : defaultCurrency}
|
|
1317
|
+
organizationId={organizationId ?? null}
|
|
1318
|
+
tenantId={tenantId ?? null}
|
|
1319
|
+
lines={Array.isArray(value) ? value as SalesOrderLineDraft[] : []}
|
|
1320
|
+
error={error}
|
|
1321
|
+
onChange={(lines) => setValue(lines)}
|
|
1322
|
+
/>
|
|
1323
|
+
) : null,
|
|
1316
1324
|
},
|
|
1317
1325
|
], [
|
|
1318
1326
|
addressFormat,
|
|
@@ -1321,6 +1329,9 @@ export function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind
|
|
|
1321
1329
|
addressesLoading,
|
|
1322
1330
|
channelsEnabled,
|
|
1323
1331
|
currencyLabels,
|
|
1332
|
+
defaultCurrency,
|
|
1333
|
+
organizationId,
|
|
1334
|
+
tenantId,
|
|
1324
1335
|
fetchCurrencyOptions,
|
|
1325
1336
|
loadAddresses,
|
|
1326
1337
|
loadChannels,
|
|
@@ -1340,6 +1351,7 @@ export function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind
|
|
|
1340
1351
|
},
|
|
1341
1352
|
{ id: 'channels-comments', title: '', column: 1, fields: channelsEnabled ? ['channelId', 'comments'] : ['comments'] },
|
|
1342
1353
|
{ id: 'currency', title: '', column: 2, fields: ['currencyCode'] },
|
|
1354
|
+
{ id: 'lines', title: '', column: 1, fields: ['lines'] },
|
|
1343
1355
|
{ id: 'shipping', title: '', column: 2, fields: ['shippingAddressSection'] },
|
|
1344
1356
|
{ id: 'billing', title: '', column: 2, fields: ['billingAddressSection'] },
|
|
1345
1357
|
{ id: 'custom', title: t('sales.documents.form.customFields', 'Custom fields'), column: 2, kind: 'customFields' },
|
|
@@ -1361,6 +1373,21 @@ export function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind
|
|
|
1361
1373
|
channelId: inboxPreFill?.channelId || undefined,
|
|
1362
1374
|
customerEntityId: inboxPreFill?.customerEntityId || undefined,
|
|
1363
1375
|
comments: inboxPreFill?.comments || undefined,
|
|
1376
|
+
lines: (inboxPreFill?.lineItems ?? []).map((item, index) => {
|
|
1377
|
+
const unitPrice = Number(item.unitPrice ?? item.unitPriceGross ?? item.unitPriceNet ?? 0)
|
|
1378
|
+
const quantity = Number(item.quantity ?? 1)
|
|
1379
|
+
return createSalesOrderLineDraft({
|
|
1380
|
+
kind: item.kind || (item.productId ? 'product' : 'service'),
|
|
1381
|
+
productId: typeof item.productId === 'string' ? item.productId : undefined,
|
|
1382
|
+
productVariantId: typeof item.productVariantId === 'string' ? item.productVariantId : undefined,
|
|
1383
|
+
name: item.productName || item.name || `Line ${index + 1}`,
|
|
1384
|
+
quantity: Number.isFinite(quantity) ? quantity : 1,
|
|
1385
|
+
currencyCode: inboxPreFill?.currencyCode || defaultCurrency,
|
|
1386
|
+
unitPriceNet: Number.isFinite(unitPrice) ? unitPrice : 0,
|
|
1387
|
+
unitPriceGross: Number.isFinite(unitPrice) ? unitPrice : 0,
|
|
1388
|
+
metadata: item.productId ? undefined : { customLine: true, lineMode: 'custom' },
|
|
1389
|
+
})
|
|
1390
|
+
}),
|
|
1364
1391
|
useCustomShipping: false,
|
|
1365
1392
|
useCustomBilling: false,
|
|
1366
1393
|
sameAsShipping: true,
|
|
@@ -1398,6 +1425,21 @@ export function SalesDocumentForm({ onCreated, isSubmitting = false, initialKind
|
|
|
1398
1425
|
}
|
|
1399
1426
|
if (documentKind === 'order') {
|
|
1400
1427
|
payload.orderNumber = documentNumber
|
|
1428
|
+
const lines = Array.isArray(base.lines) ? base.lines : []
|
|
1429
|
+
if (lines.length === 0) {
|
|
1430
|
+
const message = t('sales.orders.linesRequired', 'Add at least one line item before creating the order.')
|
|
1431
|
+
throw createCrudFormError(message, { lines: message })
|
|
1432
|
+
}
|
|
1433
|
+
payload.lines = lines.map((line) => {
|
|
1434
|
+
const {
|
|
1435
|
+
orderId: _orderId,
|
|
1436
|
+
quoteId: _quoteId,
|
|
1437
|
+
organizationId: _organizationId,
|
|
1438
|
+
tenantId: _tenantId,
|
|
1439
|
+
...linePayload
|
|
1440
|
+
} = line.payload
|
|
1441
|
+
return { ...linePayload, currencyCode: linePayload.currencyCode || payload.currencyCode }
|
|
1442
|
+
})
|
|
1401
1443
|
} else {
|
|
1402
1444
|
payload.quoteNumber = documentNumber
|
|
1403
1445
|
}
|