Credit the import-contacts onboarding reward on account connection (#22354)

Onboarding V2 shows a credit reward for connecting an email account, but
the reward was only ever a frontend localStorage counter, never granted
server-side. This applies it for real.

When the connect-account step is actually completed via a Google or
Microsoft connection, the workspace is credited
`ONBOARDING_IMPORT_CONTACTS_CREDITS_REWARD`. Eligibility is derived
server-side from the `ONBOARDING_CONNECT_ACCOUNT_PENDING` flag (set once
at workspace creation), so the reward is one-time and is not granted
when the step is skipped. Crediting is best-effort: it never blocks the
OAuth flow and no-ops when billing is disabled.

The invite-team reward is handled separately in #22309. The upgrade
reward needs no grant: it is applied structurally through the trial
resource-usage cap, so an explicit grant would double-count it.


<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22354?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Raphaël Bosi
2026-06-30 14:48:15 +02:00
committed by GitHub
parent 96e5d0f3ed
commit 82516d65e4
5 changed files with 206 additions and 12 deletions
@@ -132,12 +132,12 @@ export class KeyValuePairService<
key,
};
if (queryRunner) {
await queryRunner.manager
.getRepository(KeyValuePairEntity)
.delete(deleteConditions);
} else {
await this.keyValuePairRepository.delete(deleteConditions);
}
const { affected } = queryRunner
? await queryRunner.manager
.getRepository(KeyValuePairEntity)
.delete(deleteConditions)
: await this.keyValuePairRepository.delete(deleteConditions);
return affected;
}
}