Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,7 @@ const CONST = {
BULK_EDIT: 'bulkEdit',
BULK_SUBMIT_APPROVE_PAY: 'bulkSubmitApprovePay',
VENDOR_MATCHING: 'vendorMatching',
INDIRECT_TAG_UPLOADS: 'indirectTagUploads',
BUSINESS_CENTRAL: 'businessCentral',
EXACT_ONLINE: 'exactOnline2026',
COMMUTER_EXCLUSIONS: 'commuterExclusions',
Expand Down Expand Up @@ -5040,6 +5041,7 @@ const CONST = {
ID_FAKE: '_FAKE_',
SECONDARY_ACTIONS: {
IMPORT_SPREADSHEET: 'importSpreadsheet',
APPEND_CUSTOM_TAG_LIST: 'appendCustomTagList',
DOWNLOAD_CSV: 'downloadCSV',
SETTINGS: 'settings',
EXPORT: 'export',
Expand Down
14 changes: 13 additions & 1 deletion src/components/ImportSpreadsheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,25 @@ type ImportSpreadsheetProps = {

isImportingMultiLevelTags?: boolean;

/** Whether the imported tag lists are appended to the existing (accounting connection) tag lists */
isAppendingToExistingLists?: boolean;

/** Whether an OFX/QFX bank statement can be picked alongside a spreadsheet */
shouldAllowBankStatements?: boolean;

/** Uploads a picked OFX/QFX bank statement */
onStatementPicked?: (file: FileObject) => Promise<void>;
};

function ImportSpreadsheet({backTo, goTo, shouldForceReplaceNavigation = false, isImportingMultiLevelTags, shouldAllowBankStatements, onStatementPicked}: ImportSpreadsheetProps) {
function ImportSpreadsheet({
backTo,
goTo,
shouldForceReplaceNavigation = false,
isImportingMultiLevelTags,
isAppendingToExistingLists,
shouldAllowBankStatements,
onStatementPicked,
}: ImportSpreadsheetProps) {
const [importedSpreadsheet] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET);
const icons = useMemoizedLazyExpensifyIcons(['SpreadsheetComputer']);
const styles = useThemeStyles();
Expand Down Expand Up @@ -189,6 +200,7 @@ function ImportSpreadsheet({backTo, goTo, shouldForceReplaceNavigation = false,
file.name,
isImportingMultiLevelTags ?? false,
importedSpreadsheet?.importTransactionSettings,
isAppendingToExistingLists,
);
})
.then(() => {
Expand Down
1 change: 1 addition & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7106,6 +7106,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU
tagRules: 'Tag-Regeln',
approverDescription: 'Genehmiger',
importTags: 'Tags importieren',
appendCustomTagList: 'Benutzerdefinierte Tag-Liste anhängen',
importTagsSupportingText: 'Kategorisiere deine Ausgaben mit einem oder mehreren Tags.',
configureMultiLevelTags: 'Konfiguriere deine Liste von Tags für mehrstufiges Tagging.',
importMultiLevelTagsSupportingText: `Hier ist eine Vorschau deiner Tags. Wenn alles gut aussieht, klicke unten, um sie zu importieren.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/el.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7230,6 +7230,7 @@ _Για πιο αναλυτικές οδηγίες, [επισκεφθείτε τ
tagRules: 'Κανόνες ετικετών',
approverDescription: 'Έγκριση',
importTags: 'Εισαγωγή ετικετών',
appendCustomTagList: 'Προσάρτηση προσαρμοσμένης λίστας ετικετών',
importTagsSupportingText: 'Κωδικοποιήστε τα έξοδά σας με έναν τύπο ετικέτας ή με πολλούς.',
configureMultiLevelTags: 'Ρυθμίστε τη λίστα με τις ετικέτες σας για πολυεπίπεδη επισήμανση.',
importMultiLevelTagsSupportingText: `Δείτε εδώ μια προεπισκόπηση των ετικετών σας. Αν όλα φαίνονται σωστά, κάντε κλικ παρακάτω για να τις εισαγάγετε.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7503,6 +7503,7 @@ const translations = {
tagRules: 'Tag rules',
approverDescription: 'Approver',
importTags: 'Import tags',
appendCustomTagList: 'Append custom tag list',
importTagsSupportingText: 'Code your expenses with one type of tag or many.',
configureMultiLevelTags: 'Configure your list of tags for multi-level tagging.',
importMultiLevelTagsSupportingText: `Here's a preview of your tags. If everything looks good, click below to import them.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6961,6 +6961,7 @@ ${amount} para ${merchant} - ${date}`,
tagRules: 'Reglas de etiquetas',
approverDescription: 'Aprobador',
importTags: 'Importar categorías',
appendCustomTagList: 'Añadir lista de etiquetas personalizada',
importTagsSupportingText: 'Clasifica tus gastos con un tipo de etiqueta o con varios.',
configureMultiLevelTags: 'Configura etiquetas multinivel',
importMultiLevelTagsSupportingText: `Aquí tienes una vista previa de tus etiquetas. Si todo se ve bien, haz clic abajo para importarlas.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7126,6 +7126,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST.
tagRules: 'Règles de tags',
approverDescription: 'Approbateur',
importTags: 'Importer des tags',
appendCustomTagList: 'Ajouter une liste de tags personnalisée',
importTagsSupportingText: 'Codez vos dépenses avec un seul type de tag ou plusieurs.',
configureMultiLevelTags: 'Configurez votre liste de tags pour le multi-niveau.',
importMultiLevelTagsSupportingText: `Voici un aperçu de vos tags. Si tout semble correct, cliquez ci-dessous pour les importer.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7065,6 +7065,7 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST.
tagRules: 'Regole dei tag',
approverDescription: 'Approvante',
importTags: 'Importa tag',
appendCustomTagList: 'Aggiungi elenco di tag personalizzato',
importTagsSupportingText: 'Codifica le tue spese con un solo tipo di tag o con molti.',
configureMultiLevelTags: 'Configura il tuo elenco di tag per la codifica multilivello.',
importMultiLevelTagsSupportingText: `Ecco un’anteprima dei tuoi tag. Se ti sembra tutto corretto, fai clic qui sotto per importarli.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6976,6 +6976,7 @@ _詳しい手順については、[ヘルプサイトをご覧ください](${CO
tagRules: 'タグルール',
approverDescription: '承認者',
importTags: 'タグをインポート',
appendCustomTagList: 'カスタムタグリストを追加',
importTagsSupportingText: '経費に 1 種類または複数のタグを付けて分類しましょう。',
configureMultiLevelTags: '複数レベルのタグ付けのために、タグの一覧を設定します。',
importMultiLevelTagsSupportingText: `こちらがタグのプレビューです。問題なければ、下をクリックしてインポートしてください。`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7049,6 +7049,7 @@ _Voor meer gedetailleerde instructies, [bezoek onze help-site](${CONST.NETSUITE_
tagRules: 'Tagregels',
approverDescription: 'Fiatteur',
importTags: 'Labels importeren',
appendCustomTagList: 'Aangepaste taglijst toevoegen',
importTagsSupportingText: 'Codeer je onkosten met één type tag of met meerdere.',
configureMultiLevelTags: 'Configureer je lijst met tags voor taggen op meerdere niveaus.',
importMultiLevelTagsSupportingText: `Hier is een voorbeeldweergave van je tags. Als alles er goed uitziet, klik hieronder om ze te importeren.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7075,6 +7075,7 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy
tagRules: 'Zasady tagów',
approverDescription: 'Osoba zatwierdzająca',
importTags: 'Importuj tagi',
appendCustomTagList: 'Dołącz niestandardową listę tagów',
importTagsSupportingText: 'Oznaczaj wydatki jednym typem taga lub wieloma.',
configureMultiLevelTags: 'Skonfiguruj swoją listę tagów do tagowania wielopoziomowego.',
importMultiLevelTagsSupportingText: `Oto podgląd Twoich tagów. Jeśli wszystko wygląda dobrze, kliknij poniżej, aby je zaimportować.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7051,6 +7051,7 @@ _Para instruções mais detalhadas, [visite nossa central de ajuda](${CONST.NETS
tagRules: 'Regras de tag',
approverDescription: 'Aprovador',
importTags: 'Importar tags',
appendCustomTagList: 'Anexar lista de tags personalizada',
importTagsSupportingText: 'Classifique suas despesas com um tipo de etiqueta ou várias.',
configureMultiLevelTags: 'Configure sua lista de tags para marcação em vários níveis.',
importMultiLevelTagsSupportingText: `Aqui está uma prévia de suas tags. Se tudo estiver certo, clique abaixo para importá-las.`,
Expand Down
1 change: 1 addition & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6799,6 +6799,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM
tagRules: '标签规则',
approverDescription: '审批人',
importTags: '导入标签',
appendCustomTagList: '追加自定义标签列表',
importTagsSupportingText: '为报销编码时可使用一种标签,也可使用多种标签。',
configureMultiLevelTags: '为多级标签配置您的标签列表。',
importMultiLevelTagsSupportingText: `这是您的标签预览。如果一切看起来都没问题,请点击下方以导入它们。`,
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/ImportMultiLevelTagsParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ type ImportMultiLevelTagsParams = {
isFirstLineHeader: boolean;
isIndependent: boolean;
isGLAdjacent: boolean;
appendToExistingLists?: boolean;
file: File;
};

Expand Down
2 changes: 2 additions & 0 deletions src/libs/actions/ImportSpreadsheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function setSpreadsheetData(
fileName: string,
isImportingMultiLevelTags: boolean,
importTransactionSettings?: ImportTransactionSettings,
isAppendingToExistingLists = false,
): Promise<void | void[]> {
// Validate that data is a non-empty array
if (!Array.isArray(data) || data.length === 0) {
Expand Down Expand Up @@ -55,6 +56,7 @@ function setSpreadsheetData(
fileType,
fileName,
isImportingMultiLevelTags,
isAppendingToExistingLists,
// Preserve transaction import settings that were configured before file upload
importTransactionSettings,
containsHeader: true,
Expand Down
30 changes: 20 additions & 10 deletions src/libs/actions/Policy/Tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,10 @@ function setImportedSpreadsheetIsImportingIndependentMultiLevelTags(isImportingI
Onyx.merge(ONYXKEYS.IMPORTED_SPREADSHEET, {isImportingIndependentMultiLevelTags});
}

function setImportedSpreadsheetIsAppendingToExistingLists(isAppendingToExistingLists: boolean) {
Onyx.merge(ONYXKEYS.IMPORTED_SPREADSHEET, {isAppendingToExistingLists});
}

function setImportedSpreadsheetIsFirstLineHeader(containsHeader: boolean) {
Onyx.merge(ONYXKEYS.IMPORTED_SPREADSHEET, {containsHeader});
}
Expand All @@ -916,6 +920,19 @@ function importMultiLevelTags(policyID: string, spreadsheet: ImportedSpreadsheet

const importFinalModalID = getImportFinalModalID();
const importFinalModalResult = waitForImportFinalModal(importFinalModalID);
const failureData: Array<OnyxUpdate<typeof ONYXKEYS.COLLECTION.POLICY | typeof ONYXKEYS.IMPORTED_SPREADSHEET>> = [
getImportFinalModalOnyxData(importFinalModalID, getImportFailedFinalModal()),
];
// When appending, the accounting connection tag lists are kept, so a failed append shouldn't change whether the policy has multiple tag lists
if (!spreadsheet.isAppendingToExistingLists) {
failureData.unshift({
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
hasMultipleTagLists: false,
},
});
}
const onyxData: OnyxData<typeof ONYXKEYS.COLLECTION.POLICY | typeof ONYXKEYS.IMPORTED_SPREADSHEET> = {
successData: [
{
Expand All @@ -927,16 +944,7 @@ function importMultiLevelTags(policyID: string, spreadsheet: ImportedSpreadsheet
},
getImportFinalModalOnyxData(importFinalModalID, getImportMultiLevelTagsFinalModal()),
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
hasMultipleTagLists: false,
},
},
getImportFinalModalOnyxData(importFinalModalID, getImportFailedFinalModal()),
],
failureData,
};

return new Promise((resolve) => {
Expand All @@ -949,6 +957,7 @@ function importMultiLevelTags(policyID: string, spreadsheet: ImportedSpreadsheet
isFirstLineHeader: spreadsheet.containsHeader,
isIndependent: spreadsheet.isImportingIndependentMultiLevelTags,
isGLAdjacent: spreadsheet.isGLAdjacent,
appendToExistingLists: !!spreadsheet.isAppendingToExistingLists,
file,
};

Expand Down Expand Up @@ -1542,6 +1551,7 @@ export {
cleanPolicyTags,
setImportedSpreadsheetIsImportingMultiLevelTags,
setImportedSpreadsheetIsImportingIndependentMultiLevelTags,
setImportedSpreadsheetIsAppendingToExistingLists,
setImportedSpreadsheetIsFirstLineHeader,
setImportedSpreadsheetIsGLAdjacent,
importMultiLevelTags,
Expand Down
46 changes: 26 additions & 20 deletions src/pages/workspace/tags/ImportMultiLevelTagsSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import useCloseImportPage from '@hooks/useCloseImportPage';
import useImportSpreadsheetConfirmModal from '@hooks/useImportSpreadsheetConfirmModal';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';
import useThemeStyles from '@hooks/useThemeStyles';

Expand Down Expand Up @@ -55,6 +56,8 @@ function ImportMultiLevelTagsSettingsPage({route}: ImportMultiLevelTagsSettingsP
const {setIsClosing} = useCloseImportPage();
const [spreadsheet, spreadsheetMetadata] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET);
const showImportSpreadsheetConfirmModal = useImportSpreadsheetConfirmModal();
const {isBetaEnabled} = usePermissions();
const isAppendingToExistingLists = hasAccountingConnections && isBetaEnabled(CONST.BETAS.INDIRECT_TAG_UPLOADS) && !!spreadsheet?.isAppendingToExistingLists;

useEffect(() => {
setImportedSpreadsheetIsFirstLineHeader(true);
Expand All @@ -79,13 +82,13 @@ function ImportMultiLevelTagsSettingsPage({route}: ImportMultiLevelTagsSettingsP
closeImportPageAndModal();
};

if (hasAccountingConnections) {
return <NotFoundPage />;
}

if (!spreadsheet && isLoadingOnyxValue(spreadsheetMetadata)) {
return;
}

if (hasAccountingConnections && !isAppendingToExistingLists) {
return <NotFoundPage />;
}
return (
<AccessOrNotFoundWrapper
policyID={policyID}
Expand Down Expand Up @@ -123,22 +126,25 @@ function ImportMultiLevelTagsSettingsPage({route}: ImportMultiLevelTagsSettingsP
/>
</View>

<View style={[styles.flexRow, styles.mh5, styles.mv4, styles.alignItemsCenter, styles.justifyContentBetween]}>
<Text
style={[styles.textNormal, styles.flex1, styles.mr2]}
accessible={false}
aria-hidden
>
{translate('workspace.tags.importMultiLevelTags.independentTags')}
</Text>
<Switch
isOn={spreadsheet?.isImportingIndependentMultiLevelTags ?? true}
accessibilityLabel={translate('workspace.tags.importMultiLevelTags.independentTags')}
onToggle={(value) => {
setImportedSpreadsheetIsImportingIndependentMultiLevelTags(value);
}}
/>
</View>
{/* Appended tag lists are always independent, so the dependent option isn't offered */}
{!isAppendingToExistingLists && (
<View style={[styles.flexRow, styles.mh5, styles.mv4, styles.alignItemsCenter, styles.justifyContentBetween]}>
<Text
style={[styles.textNormal, styles.flex1, styles.mr2]}
accessible={false}
aria-hidden
>
{translate('workspace.tags.importMultiLevelTags.independentTags')}
</Text>
<Switch
isOn={spreadsheet?.isImportingIndependentMultiLevelTags ?? true}
accessibilityLabel={translate('workspace.tags.importMultiLevelTags.independentTags')}
onToggle={(value) => {
setImportedSpreadsheetIsImportingIndependentMultiLevelTags(value);
}}
/>
</View>
)}

<View style={[styles.flexRow, styles.mh5, styles.mv4, styles.alignItemsCenter, styles.justifyContentBetween]}>
<Text
Expand Down
11 changes: 10 additions & 1 deletion src/pages/workspace/tags/ImportTagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ImportSpreadsheet from '@components/ImportSpreadsheet';

import useDynamicBackPath from '@hooks/useDynamicBackPath';
import useOnyx from '@hooks/useOnyx';
import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';

import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute';
Expand Down Expand Up @@ -32,14 +33,21 @@ function ImportTagsPage({route}: ImportTagsPageProps) {
const isQuickSettingsFlow = route.name === SCREENS.SETTINGS_TAGS.SETTINGS_TAGS_IMPORT;
const backTo = isQuickSettingsFlow && 'backTo' in route.params ? route.params.backTo : undefined;
const [spreadsheet, spreadsheetMetadata] = useOnyx(ONYXKEYS.IMPORTED_SPREADSHEET);
const {isBetaEnabled} = usePermissions();
const isAppendingToExistingLists =
!isQuickSettingsFlow &&
hasAccountingConnections &&
isBetaEnabled(CONST.BETAS.INDIRECT_TAG_UPLOADS) &&
!!spreadsheet?.isImportingMultiLevelTags &&
!!spreadsheet?.isAppendingToExistingLists;

const workspaceTagsListBackPath = useDynamicBackPath(DYNAMIC_ROUTES.WORKSPACE_TAGS_IMPORT.path);
const workspaceGoToImportedPath = createDynamicRoute(DYNAMIC_ROUTES.WORKSPACE_TAGS_IMPORTED.path, ROUTES.WORKSPACE_TAGS.getRoute(policyID));

if (!spreadsheet && isLoadingOnyxValue(spreadsheetMetadata)) {
return;
}
if (hasAccountingConnections) {
if (hasAccountingConnections && !isAppendingToExistingLists) {
return <NotFoundPage />;
}

Expand All @@ -65,6 +73,7 @@ function ImportTagsPage({route}: ImportTagsPageProps) {
return workspaceGoToImportedPath;
})()}
isImportingMultiLevelTags={spreadsheet?.isImportingMultiLevelTags}
isAppendingToExistingLists={isAppendingToExistingLists}
shouldForceReplaceNavigation={!isQuickSettingsFlow && !spreadsheet?.isImportingMultiLevelTags}
/>
</AccessOrNotFoundWrapper>
Expand Down
Loading
Loading