-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New Feature: Add Split by date to the split expense flow for new splits #77920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Feature: Add Split by date to the split expense flow for new splits #77920
Conversation
|
Hey, I noticed you changed If you want to automatically generate translations for other locales, an Expensify employee will have to:
Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running: npx ts-node ./scripts/generateTranslations.ts --helpTypically, you'd want to translate only what you changed by running |
|
🚧 @lakchote has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, Desktop, and Web. Happy testing! 🧪🧪
|
|
I went through the testing steps and everything tested well. |
|
@ikevin127 Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
@parasharrajat |
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/de.ts b/src/languages/de.ts
index c45fe7ee..233ab57d 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -1380,6 +1380,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: 'Die Menge muss größer als Null sein',
invalidSubrateLength: 'Es muss mindestens einen Untertarif geben',
invalidRate: 'Satz für diesen Workspace ungültig. Bitte wählen Sie einen verfügbaren Satz aus dem Workspace aus.',
+ endDateBeforeStartDate: 'Das Enddatum darf nicht vor dem Startdatum liegen',
+ endDateSameAsStartDate: 'Das Enddatum darf nicht mit dem Startdatum identisch sein',
},
dismissReceiptError: 'Fehler ausblenden',
dismissReceiptErrorConfirmation: 'Achtung! Wenn du diesen Fehler verwirfst, wird dein hochgeladener Beleg vollständig entfernt. Bist du sicher?',
@@ -1525,6 +1527,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'Arbeitsbereich auswählen',
+ date: 'Datum',
+ splitDates: 'Datumsangaben aufteilen',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} bis ${endDate} (${count} Tage)`,
+ splitByDate: 'Nach Datum aufteilen',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 11ff89a8..e872baf9 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -1383,6 +1383,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: 'La quantité doit être supérieure à zéro',
invalidSubrateLength: 'Il doit y avoir au moins un sous-taux',
invalidRate: 'Taux non valide pour cet espace de travail. Veuillez sélectionner un taux disponible dans l’espace de travail.',
+ endDateBeforeStartDate: 'La date de fin ne peut pas être antérieure à la date de début',
+ endDateSameAsStartDate: 'La date de fin ne peut pas être identique à la date de début',
},
dismissReceiptError: 'Ignorer l’erreur',
dismissReceiptErrorConfirmation: 'Attention ! Ignorer cette erreur supprimera entièrement votre reçu téléchargé. Êtes-vous sûr ?',
@@ -1528,6 +1530,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'Choisir un espace de travail',
+ date: 'Date',
+ splitDates: 'Diviser les dates',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `Du ${startDate} au ${endDate} (${count} jours)`,
+ splitByDate: 'Scinder par date',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 691355df..f3082b9c 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -1376,6 +1376,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: 'La quantità deve essere maggiore di zero',
invalidSubrateLength: 'Deve esserci almeno un sottotariffa',
invalidRate: 'Tariffa non valida per questo workspace. Seleziona una tariffa disponibile dal workspace.',
+ endDateBeforeStartDate: 'La data di fine non può essere precedente alla data di inizio',
+ endDateSameAsStartDate: 'La data di fine non può essere uguale alla data di inizio',
},
dismissReceiptError: 'Ignora errore',
dismissReceiptErrorConfirmation: 'Attenzione! Se ignori questo errore, la ricevuta caricata verrà rimossa completamente. Sei sicuro?',
@@ -1521,6 +1523,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'Scegli uno spazio di lavoro',
+ date: 'Data',
+ splitDates: 'Dividi date',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} a ${endDate} (${count} giorni)`,
+ splitByDate: 'Dividi per data',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 64fe296d..1091889c 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -1377,6 +1377,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: '数量は0より大きくなければなりません',
invalidSubrateLength: '少なくとも 1 つのサブレートが必要です',
invalidRate: 'このワークスペースでは無効なレートです。ワークスペースから利用可能なレートを選択してください。',
+ endDateBeforeStartDate: '終了日は開始日より前にはできません',
+ endDateSameAsStartDate: '終了日は開始日と同じにはできません',
},
dismissReceiptError: 'エラーを閉じる',
dismissReceiptErrorConfirmation: '注意!このエラーを無視すると、アップロードした領収書が完全に削除されます。本当に実行しますか?',
@@ -1522,6 +1524,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'ワークスペースを選択',
+ date: '日付',
+ splitDates: '日付を分割',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} から ${endDate} まで(${count} 日間)`,
+ splitByDate: '日付で分割',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index e925f134..6fdc1aeb 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -1375,6 +1375,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: 'Hoeveelheid moet groter zijn dan nul',
invalidSubrateLength: 'Er moet ten minste één subtarief zijn',
invalidRate: 'Tarief is niet geldig voor deze workspace. Selecteer een beschikbaar tarief uit de workspace.',
+ endDateBeforeStartDate: 'De einddatum kan niet vóór de startdatum liggen',
+ endDateSameAsStartDate: 'De einddatum mag niet hetzelfde zijn als de startdatum',
},
dismissReceiptError: 'Foutmelding sluiten',
dismissReceiptErrorConfirmation: 'Let op! Als je deze foutmelding negeert, wordt je geüploade bon volledig verwijderd. Weet je het zeker?',
@@ -1520,6 +1522,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'Kies een workspace',
+ date: 'Datum',
+ splitDates: 'Datums splitsen',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} tot ${endDate} (${count} dagen)`,
+ splitByDate: 'Splitsen op datum',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 64311a57..1b41fef6 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -1373,6 +1373,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: 'Ilość musi być większa niż zero',
invalidSubrateLength: 'Musi istnieć co najmniej jedna podstawka',
invalidRate: 'Stawka nie jest prawidłowa dla tego przestrzeni roboczej. Wybierz dostępną stawkę z tej przestrzeni roboczej.',
+ endDateBeforeStartDate: 'Data zakończenia nie może być wcześniejsza niż data rozpoczęcia',
+ endDateSameAsStartDate: 'Data zakończenia nie może być taka sama jak data rozpoczęcia',
},
dismissReceiptError: 'Odrzuć błąd',
dismissReceiptErrorConfirmation: 'Uwaga! Odrzucenie tego błędu spowoduje całkowite usunięcie przesłanego paragonu. Czy na pewno chcesz kontynuować?',
@@ -1518,6 +1520,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'Wybierz przestrzeń roboczą',
+ date: 'Data',
+ splitDates: 'Podziel daty',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} do ${endDate} (${count} dni)`,
+ splitByDate: 'Podziel według daty',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index e50adb79..2c69c0cb 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -1373,6 +1373,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: 'A quantidade deve ser maior que zero',
invalidSubrateLength: 'Deve haver pelo menos uma subtarifa',
invalidRate: 'Taxa inválida para este workspace. Selecione uma taxa disponível do workspace.',
+ endDateBeforeStartDate: 'A data de término não pode ser anterior à data de início',
+ endDateSameAsStartDate: 'A data de término não pode ser igual à data de início',
},
dismissReceiptError: 'Dispensar erro',
dismissReceiptErrorConfirmation: 'Atenção! Ignorar este erro removerá completamente o seu recibo enviado. Tem certeza?',
@@ -1518,6 +1520,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: 'Escolha um workspace',
+ date: 'Data',
+ splitDates: 'Dividir datas',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} a ${endDate} (${count} dias)`,
+ splitByDate: 'Dividir por data',
},
transactionMerge: {
listPage: {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 2ec19d37..18ddb257 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -1353,6 +1353,8 @@ const translations: TranslationDeepObject<typeof en> = {
quantityGreaterThanZero: '数量必须大于零',
invalidSubrateLength: '必须至少有一个子费率',
invalidRate: '此汇率对该工作区无效。请选择此工作区中的可用汇率。',
+ endDateBeforeStartDate: '结束日期不能早于开始日期',
+ endDateSameAsStartDate: '结束日期不能与开始日期相同',
},
dismissReceiptError: '忽略错误',
dismissReceiptErrorConfirmation: '提醒!关闭此错误会完全删除你上传的收据。确定要继续吗?',
@@ -1495,6 +1497,10 @@ const translations: TranslationDeepObject<typeof en> = {
},
},
chooseWorkspace: '选择一个工作区',
+ date: '日期',
+ splitDates: '拆分日期',
+ splitDateRange: ({startDate, endDate, count}: SplitDateRangeParams) => `${startDate} 至 ${endDate}(${count} 天)`,
+ splitByDate: '按日期拆分',
},
transactionMerge: {
listPage: {
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
Codecov Report❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.
|
|
BUG: On reload, it shows not found page briefly. BUG: Header title is wrong. Expected; header title shows split by percentage or split by date based on tab 18.12.2025_22.52.38_REC.mp4 |
Can you please confirm this on slack or Gh? |
As I remember this issue was already in the previous PR |
I think this one should be fixed #77920 (comment). This impact offline behaviour as well. |
@ZhenjaHorbach I do not see this error on any other PR. |
But we don't use country key here |
|
May be try merging main first. |
I already merged |
|
@lakchote looks like this was merged without a test passing. Please add a note explaining why this was done and remove the |
|
Not an emergency, see #77920 (comment) |
|
🚀 Deployed to staging by https://github.com/lakchote in version: 9.2.82-0 🚀
|
|
🚀 Deployed to staging by https://github.com/lakchote in version: 9.2.82-0 🚀
|
|
🚀 Deployed to staging by https://github.com/lakchote in version: 9.2.82-0 🚀
|
|
🚀 Deployed to staging by https://github.com/lakchote in version: 9.2.84-0 🚀
|
|
🚀 Deployed to production by https://github.com/AndrewGable in version: 9.2.84-8 🚀
|




Explanation of Change
Implement a ‘Split by date’ flow that does the following:
Adds a third split options at the top of the split table: Amount, Percentage, (these should already exist) and Date (this is the new one)
When the user taps 'Date' in the split menu we'll change the view of the split table like so:
Add a date picker for Start Date in the UI
Add a date picker for End Date in the UI
When the user confirms the dates, use the selected dates to overrwrite the existing splits in the table, creating one split for each day of the date range selected.
Set the amounts of the individual splits to equal proportions of the original expense amount.
Important Notes:
We'll maintain the logic and copy for our current warnings/errors on saving splits where the sum of the splits differs from the original transaction amounts.
The 'Date' mode button will not be available when editing splits to avoid adding more logic to start
Fixed Issues
$ #72442
PROPOSAL:
Tests
Enabling Date Mode:
Changing Dates:
Offline tests
Enabling Date Mode:
Changing Dates:
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
Enabling Date Mode:
Changing Dates:
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
2025-12-18.15.16.03.mov