Compare commits

...

5 Commits

Author SHA1 Message Date
Paul Rastoin a771216f6e Fix field currency form settings (#11948)
# Introduction
Close #11947 

From my understanding this `singleQuote` manipulation is legacy due to
front preparing sql queries in some way.

We might wanna standardize this behavior for each `CURRENCIES` usage,
such as within the `ObjectFilterDropdownCurrencySelect` that does not
send nor expect singled quoted encapsulated currencies values.

Created core-team-issues tech issue
https://github.com/twentyhq/core-team-issues/issues/941


## Field creation variables
```json
{
  "input": {
    "field": {
      "defaultValue": {
        "amountMicros": null,
        "currencyCode": "'BYN'"
      },
      "description": "",
      "icon": "IconMoneybag",
      "label": "Maquito",
      "name": "maquito",
      "isLabelSyncedWithName": true,
      "objectMetadataId": "6ef5da20-cf03-49b5-81fe-73aa2c9856a9",
      "type": "CURRENCY"
    }
  }
}
```

## Filtering variables
```json
{
  "filter": {
    "test": {
      "currencyCode": {
        "in": [
          "ALL",
          "AED",
          "AFN",
          "ANG"
        ]
      }
    }
  },
  "orderBy": [
    {
      "position": "AscNullsFirst"
    }
  ]
}
```
2025-05-09 11:10:24 +02:00
Etienne fe371eb4c4 fix on field relation preview (#11926)
To reproduce : 
Modify object destination to an object with a 'simple' field as
labelIdentifier from data model settings, during creation of a relation
field from an object which has a composite field as labelIdentifier +
Relation type 'has many'

Fix : 
fieldValue and fieldDefinition does not update at the same time (when
object destination is modified) in RelationToManyFieldDisplay component.
Need to better define recordId to be sure fieldValue doesn't point on
previous record.

Tested : 
Relation field creation from Person with switch on relationType and
objectDestination (company/workspaceMember)
Relation field creation from Company with switch on relationType and
objectDestination (pet/person)
closes https://github.com/twentyhq/twenty/issues/11826
2025-05-07 17:10:41 +02:00
Guillim 1db92ce662 Critical email sync bug (#11885)
Removing the upsert to avoid the ON CONFLICT
2025-05-06 11:22:09 +02:00
prastoin 6bb0212a70 fix(server): upgrade command 2025-05-05 16:48:10 +02:00
Etienne 4a543a0e0a fix relation creation bug (#11848)
Context : In `RelationToOneFieldDisplay`, the `objectNameSingular` used
for the chip calculation was incorrect - it was using the value from the
opposite side of the relationship. Then, labelIdentifier based on
composite fields were causing errors (People and WorkspaceMembers with
name)


<img width="300" alt="Screenshot 2025-05-03 at 08 03 46"
src="https://github.com/user-attachments/assets/8d034700-5244-4b1b-978e-f77ae78b6ceb"
/>


Tested everywhere FieldDisplay is used. 
Tested for each both relation type. 

closes https://github.com/twentyhq/twenty/issues/11826
2025-05-03 08:57:53 +02:00
7 changed files with 57 additions and 31 deletions
@@ -13,7 +13,8 @@ import { isDefined } from 'twenty-shared/utils';
import { pascalCase } from '~/utils/string/pascalCase';
export const RelationFromManyFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useRelationFromManyFieldDisplay();
const { fieldValue, fieldDefinition, generateRecordChipData } =
useRelationFromManyFieldDisplay();
const { isFocused } = useFieldFocus();
const { disableChipClick } = useContext(FieldContext);
@@ -99,14 +100,17 @@ export const RelationFromManyFieldDisplay = () => {
} else {
return (
<ExpandableList isChipCountDisplayed={isFocused}>
{fieldValue.filter(isDefined).map((record) => (
<RecordChip
key={record.id}
objectNameSingular={objectNameSingular}
record={record}
forceDisableClick={disableChipClick}
/>
))}
{fieldValue.filter(isDefined).map((record) => {
const recordChipData = generateRecordChipData(record);
return (
<RecordChip
key={recordChipData.recordId}
objectNameSingular={recordChipData.objectNameSingular}
record={record}
forceDisableClick={disableChipClick}
/>
);
})}
</ExpandableList>
);
}
@@ -60,9 +60,8 @@ export const useRelationFromManyFieldDisplay = () => {
: (record: ObjectRecord) =>
generateDefaultRecordChipData({
record,
// @ts-expect-error Above assertions does not infer that fieldDefinition.metadata.objectMetadataNameSingular always defined
objectNameSingular:
fieldDefinition.metadata.objectMetadataNameSingular,
fieldDefinition.metadata.relationObjectMetadataNameSingular,
});
return {
@@ -60,9 +60,8 @@ export const useRelationToOneFieldDisplay = () => {
: (record: ObjectRecord) =>
generateDefaultRecordChipData({
record,
// @ts-expect-error Above assertions does not infer that fieldDefinition.metadata.objectMetadataNameSingular always defined
objectNameSingular:
fieldDefinition.metadata.objectMetadataNameSingular,
fieldDefinition.metadata.relationObjectMetadataNameSingular,
});
return {
@@ -9,6 +9,7 @@ import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fiel
import { Select } from '@/ui/input/components/Select';
import { useLingui } from '@lingui/react/macro';
import { IconCurrencyDollar } from 'twenty-ui/display';
import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString';
export const settingsDataModelFieldCurrencyFormSchema = z.object({
defaultValue: currencyFieldDefaultValueSchema,
@@ -58,7 +59,10 @@ export const SettingsDataModelFieldCurrencyForm = ({
onChange={onChange}
disabled={disabled}
dropdownId="object-field-default-value-select-currency"
options={CURRENCIES}
options={CURRENCIES.map(({ value, ...rest }) => ({
...rest,
value: applySimpleQuotesToString(value),
}))}
selectSizeVariant="small"
withSearchInput={true}
/>
@@ -102,7 +102,7 @@ export const SettingsDataModelFieldPreview = ({
fieldMetadataItem.name || `${fieldMetadataItem.type}-new-field`;
const recordId =
previewRecord?.id ??
`${objectMetadataItem.nameSingular}-${fieldName}-${fieldMetadataItem.relationDefinition?.direction}-preview`;
`${objectMetadataItem.nameSingular}-${fieldName}-${fieldMetadataItem.relationDefinition?.direction}-${relationObjectMetadataItem?.nameSingular}-preview`;
return (
<>
@@ -19,11 +19,11 @@ import { UpdateViewAggregateOperationsCommand } from 'src/database/commands/upgr
import { UpgradeCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/0-51/0-51-update-workflow-trigger-type-enum.command';
import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-migrate-relations-to-field-metadata.command';
import { UpgradeDateAndDateTimeFieldsSettingsJsonCommand } from 'src/database/commands/upgrade-version-command/0-52/0-52-upgrade-settings-field';
import { MigrateWorkflowEventListenersToAutomatedTriggersCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-migrate-workflow-event-listeners-to-automated-triggers.command';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
import { MigrateWorkflowEventListenersToAutomatedTriggersCommand } from 'src/database/commands/upgrade-version-command/0-53/0-53-migrate-workflow-event-listeners-to-automated-triggers.command';
type VersionCommands = {
beforeSyncMetadata: ActiveOrSuspendedWorkspacesMigrationCommandRunner[];
@@ -34,7 +34,7 @@ type VersionCommands = {
description: 'Upgrade workspaces to the latest version',
})
export class UpgradeCommand extends UpgradeCommandRunner {
fromWorkspaceVersion = new SemVer('0.50.0');
fromWorkspaceVersion = new SemVer('0.51.0');
private commands: VersionCommands;
constructor(
@@ -117,7 +117,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
],
};
this.commands = commands_053;
this.commands = _commands_052;
}
override async runBeforeSyncMetadata(args: RunOnWorkspaceArgs) {
@@ -62,19 +62,39 @@ export class MessagingMessageService {
});
if (existingMessage) {
await messageChannelMessageAssociationRepository.upsert(
{
messageChannelId,
messageId: existingMessage.id,
messageExternalId: message.externalId,
messageThreadExternalId: message.messageThreadExternalId,
},
{
conflictPaths: ['messageChannelId', 'messageId'],
indexPredicate: '"deletedAt" IS NULL',
},
transactionManager,
);
const existingAssociation =
await messageChannelMessageAssociationRepository.findOne(
{
where: {
messageChannelId,
messageId: existingMessage.id,
},
},
transactionManager,
);
if (existingAssociation) {
await messageChannelMessageAssociationRepository.update(
{
id: existingAssociation.id,
},
{
messageExternalId: message.externalId,
messageThreadExternalId: message.messageThreadExternalId,
},
transactionManager,
);
} else {
await messageChannelMessageAssociationRepository.insert(
{
messageChannelId,
messageId: existingMessage.id,
messageExternalId: message.externalId,
messageThreadExternalId: message.messageThreadExternalId,
},
transactionManager,
);
}
continue;
}