Skip to content

Commit

Permalink
Merge pull request #46567 from rezkiy37/feature/45168-enabling-invoic…
Browse files Browse the repository at this point in the history
…es-feature

Enabling Invoices as a feature
  • Loading branch information
madmax330 authored Aug 7, 2024
2 parents 03783a5 + 5bdf617 commit 2c60fc9
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 3 deletions.
8 changes: 8 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2789,6 +2789,10 @@ export default {
title: 'Spend',
subtitle: 'Enable optional functionality that helps you scale your team.',
},
earnSection: {
title: 'Earn',
subtitle: 'Enable optional functionality to streamline your revenue and get paid faster.',
},
organizeSection: {
title: 'Organize',
subtitle: 'Group and analyze spend, record every tax paid.',
Expand Down Expand Up @@ -2822,6 +2826,10 @@ export default {
title: 'Workflows',
subtitle: 'Configure how spend is approved and paid.',
},
invoices: {
title: 'Invoices',
subtitle: 'Send and receive invoices.',
},
categories: {
title: 'Categories',
subtitle: 'Track and organize spend.',
Expand Down
8 changes: 8 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2841,6 +2841,10 @@ export default {
title: 'Gasto',
subtitle: 'Habilita otras funcionalidades que ayudan a aumentar tu equipo.',
},
earnSection: {
title: 'Gane',
subtitle: 'Habilita funciones opcionales para agilizar tus ingresos y recibir pagos más rápido.',
},
organizeSection: {
title: 'Organizar',
subtitle: 'Agrupa y analiza el gasto, registra cada impuesto pagado.',
Expand Down Expand Up @@ -2874,6 +2878,10 @@ export default {
title: 'Flujos de trabajo',
subtitle: 'Configura cómo se aprueba y paga los gastos.',
},
invoices: {
title: 'Facturas',
subtitle: 'Enviar y recibir facturas.',
},
categories: {
title: 'Categorías',
subtitle: 'Monitoriza y organiza los gastos.',
Expand Down
6 changes: 6 additions & 0 deletions src/libs/API/parameters/EnablePolicyInvoicingParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type EnablePolicyInvoicingParams = {
policyID: string;
enabled: boolean;
};

export default EnablePolicyInvoicingParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export type {default as ConnectPolicyToNetSuiteParams} from './ConnectPolicyToNe
export type {default as CreateWorkspaceReportFieldParams} from './CreateWorkspaceReportFieldParams';
export type {default as UpdateWorkspaceReportFieldInitialValueParams} from './UpdateWorkspaceReportFieldInitialValueParams';
export type {default as EnableWorkspaceReportFieldListValueParams} from './EnableWorkspaceReportFieldListValueParams';
export type {default as EnablePolicyInvoicingParams} from './EnablePolicyInvoicingParams';
export type {default as CreateWorkspaceReportFieldListValueParams} from './CreateWorkspaceReportFieldListValueParams';
export type {default as RemoveWorkspaceReportFieldListValueParams} from './RemoveWorkspaceReportFieldListValueParams';
export type {default as OpenPolicyExpensifyCardsPageParams} from './OpenPolicyExpensifyCardsPageParams';
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ const WRITE_COMMANDS = {
ENABLE_POLICY_WORKFLOWS: 'EnablePolicyWorkflows',
ENABLE_POLICY_REPORT_FIELDS: 'EnablePolicyReportFields',
ENABLE_POLICY_EXPENSIFY_CARDS: 'EnablePolicyExpensifyCards',
ENABLE_POLICY_INVOICING: 'EnablePolicyInvoicing',
SET_POLICY_TAXES_CURRENCY_DEFAULT: 'SetPolicyCurrencyDefaultTax',
SET_POLICY_TAXES_FOREIGN_CURRENCY_DEFAULT: 'SetPolicyForeignCurrencyDefaultTax',
SET_POLICY_CUSTOM_TAX_NAME: 'SetPolicyCustomTaxName',
Expand Down Expand Up @@ -517,6 +518,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.ENABLE_POLICY_WORKFLOWS]: Parameters.EnablePolicyWorkflowsParams;
[WRITE_COMMANDS.ENABLE_POLICY_REPORT_FIELDS]: Parameters.EnablePolicyReportFieldsParams;
[WRITE_COMMANDS.ENABLE_POLICY_EXPENSIFY_CARDS]: Parameters.EnablePolicyExpensifyCardsParams;
[WRITE_COMMANDS.ENABLE_POLICY_INVOICING]: Parameters.EnablePolicyInvoicingParams;
[WRITE_COMMANDS.JOIN_POLICY_VIA_INVITE_LINK]: Parameters.JoinPolicyInviteLinkParams;
[WRITE_COMMANDS.ACCEPT_JOIN_REQUEST]: Parameters.AcceptJoinRequestParams;
[WRITE_COMMANDS.DECLINE_JOIN_REQUEST]: Parameters.DeclineJoinRequestParams;
Expand Down
2 changes: 2 additions & 0 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,8 @@ function getOptions(

const shouldShowInvoiceRoom =
includeInvoiceRooms && ReportUtils.isInvoiceRoom(reportOption.item) && ReportUtils.isPolicyAdmin(reportOption.policyID ?? '', policies) && !reportOption.isArchivedRoom;
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// && PolicyUtils.canSendInvoiceFromWorkspace(reportOption.policyID);

/**
Exclude the report option if it doesn't meet any of the following conditions:
Expand Down
9 changes: 9 additions & 0 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,17 @@ function getActiveAdminWorkspaces(policies: OnyxCollection<Policy> | null): Poli
return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline()) && isPolicyAdmin(policy));
}

/** Whether the user can send invoice from the workspace */
function canSendInvoiceFromWorkspace(policyID: string | undefined): boolean {
const policy = getPolicy(policyID);
return policy?.areInvoicesEnabled ?? false;
}

/** Whether the user can send invoice */
function canSendInvoice(policies: OnyxCollection<Policy> | null): boolean {
return getActiveAdminWorkspaces(policies).length > 0;
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// return getActiveAdminWorkspaces(policies).some((policy) => canSendInvoiceFromWorkspace(policy.id));
}

function hasDependentTags(policy: OnyxEntry<Policy>, policyTagList: OnyxEntry<PolicyTagList>) {
Expand Down Expand Up @@ -940,6 +948,7 @@ export {
isTaxTrackingEnabled,
shouldShowPolicy,
getActiveAdminWorkspaces,
canSendInvoiceFromWorkspace,
canSendInvoice,
hasDependentTags,
getXeroTenants,
Expand Down
2 changes: 2 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6122,6 +6122,8 @@ function getMoneyRequestOptions(report: OnyxEntry<Report>, policy: OnyxEntry<Pol
}

if (isInvoiceRoom(report)) {
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// if (PolicyUtils.canSendInvoiceFromWorkspace(policy?.id) && isPolicyAdmin(report?.policyID ?? '-1', allPolicies)) {
if (isPolicyAdmin(report?.policyID ?? '-1', allPolicies)) {
return [CONST.IOU.TYPE.INVOICE];
}
Expand Down
64 changes: 64 additions & 0 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
DeleteWorkspaceParams,
EnablePolicyConnectionsParams,
EnablePolicyExpensifyCardsParams,
EnablePolicyInvoicingParams,
EnablePolicyReportFieldsParams,
EnablePolicyTaxesParams,
EnablePolicyWorkflowsParams,
Expand Down Expand Up @@ -202,6 +203,7 @@ function getPolicy(policyID: string | undefined): OnyxEntry<Policy> {
/**
* Returns a primary policy for the user
*/
// TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
function getPrimaryPolicy(activePolicyID?: OnyxEntry<string>): Policy | undefined {
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
const primaryPolicy: Policy | null | undefined = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID ?? '-1'}`];
Expand All @@ -217,6 +219,17 @@ function hasInvoicingDetails(policy: OnyxEntry<Policy>): boolean {
return true;
}

/**
* Returns a primary invoice workspace for the user
*/
function getInvoicePrimaryWorkspace(activePolicyID?: OnyxEntry<string>): Policy | undefined {
if (PolicyUtils.canSendInvoiceFromWorkspace(activePolicyID)) {
return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID ?? '-1'}`];
}
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
return activeAdminWorkspaces.find((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id));
}

/**
* Check if the user has any active free policies (aka workspaces)
*/
Expand Down Expand Up @@ -2938,6 +2951,55 @@ function enableDistanceRequestTax(policyID: string, customUnitName: string, cust
API.write(WRITE_COMMANDS.ENABLE_DISTANCE_REQUEST_TAX, params, onyxData);
}

function enablePolicyInvoicing(policyID: string, enabled: boolean) {
const onyxData: OnyxData = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
areInvoicesEnabled: enabled,
pendingFields: {
areInvoicesEnabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
pendingFields: {
areInvoicesEnabled: null,
},
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: {
areInvoicesEnabled: !enabled,
pendingFields: {
areInvoicesEnabled: null,
},
},
},
],
};

const parameters: EnablePolicyInvoicingParams = {policyID, enabled};

API.write(WRITE_COMMANDS.ENABLE_POLICY_INVOICING, parameters, onyxData);

// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// if (enabled && getIsNarrowLayout()) {
// navigateWhenEnableFeature(policyID);
// }
}

function openPolicyMoreFeaturesPage(policyID: string) {
const params: OpenPolicyMoreFeaturesPageParams = {policyID};

Expand Down Expand Up @@ -3223,6 +3285,7 @@ export {
enablePolicyTaxes,
enablePolicyWorkflows,
enableDistanceRequestTax,
enablePolicyInvoicing,
openPolicyMoreFeaturesPage,
openPolicyProfilePage,
openPolicyInitialPage,
Expand All @@ -3239,6 +3302,7 @@ export {
clearPolicyErrorField,
isCurrencySupportedForDirectReimbursement,
getPrimaryPolicy,
getInvoicePrimaryWorkspace,
createDraftWorkspace,
savePreferredExportMethod,
buildPolicyData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const useIsFocused = () => {
return isFocused || (topmostCentralPane?.name === SCREENS.SEARCH.CENTRAL_PANE && shouldUseNarrowLayout);
};

type PolicySelector = Pick<OnyxTypes.Policy, 'type' | 'role' | 'isPolicyExpenseChatEnabled' | 'pendingAction' | 'avatarURL' | 'name' | 'id'>;
type PolicySelector = Pick<OnyxTypes.Policy, 'type' | 'role' | 'isPolicyExpenseChatEnabled' | 'pendingAction' | 'avatarURL' | 'name' | 'id' | 'areInvoicesEnabled'>;

type FloatingActionButtonAndPopoverOnyxProps = {
/** The list of policies the user has access to. */
Expand Down Expand Up @@ -97,6 +97,7 @@ const policySelector = (policy: OnyxEntry<OnyxTypes.Policy>): PolicySelector =>
pendingAction: policy.pendingAction,
avatarURL: policy.avatarURL,
name: policy.name,
areInvoicesEnabled: policy.areInvoicesEnabled,
}) as PolicySelector;

const getQuickActionIcon = (action: QuickActionName): React.FC<SvgProps> => {
Expand Down
1 change: 1 addition & 0 deletions src/pages/iou/request/MoneyRequestParticipantsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF
];

if (iouType === CONST.IOU.TYPE.INVOICE) {
// TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
const policyID = option.item && ReportUtils.isInvoiceRoom(option.item) ? option.policyID : Policy.getPrimaryPolicy(activePolicyID)?.id;
newParticipants.push({
policyID,
Expand Down
6 changes: 4 additions & 2 deletions src/pages/iou/request/step/IOURequestStepSendFrom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ function IOURequestStepSendFrom({route, transaction, allPolicies}: IOURequestSte
const selectedWorkspace = useMemo(() => transaction?.participants?.find((participant) => participant.isSender), [transaction]);

const workspaceOptions: WorkspaceListItem[] = useMemo(() => {
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
const availableWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// .filter((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id));

return activeAdminWorkspaces
return availableWorkspaces
.sort((policy1, policy2) => sortWorkspacesBySelected({policyID: policy1.id, name: policy1.name}, {policyID: policy2.id, name: policy2.name}, selectedWorkspace?.policyID))
.map((policy) => ({
text: policy.name,
Expand Down
18 changes: 18 additions & 0 deletions src/pages/workspace/WorkspaceMoreFeaturesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,19 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
});
}

const earnItems: Item[] = [
{
icon: Illustrations.InvoiceBlue,
titleTranslationKey: 'workspace.moreFeatures.invoices.title',
subtitleTranslationKey: 'workspace.moreFeatures.invoices.subtitle',
isActive: policy?.areInvoicesEnabled ?? false,
pendingAction: policy?.pendingFields?.areInvoicesEnabled,
action: (isEnabled: boolean) => {
Policy.enablePolicyInvoicing(policy?.id ?? '-1', isEnabled);
},
},
];

const organizeItems: Item[] = [
{
icon: Illustrations.FolderOpen,
Expand Down Expand Up @@ -247,6 +260,11 @@ function WorkspaceMoreFeaturesPage({policy, route}: WorkspaceMoreFeaturesPagePro
subtitleTranslationKey: 'workspace.moreFeatures.spendSection.subtitle',
items: spendItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.earnSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.earnSection.subtitle',
items: earnItems,
},
{
titleTranslationKey: 'workspace.moreFeatures.organizeSection.title',
subtitleTranslationKey: 'workspace.moreFeatures.organizeSection.subtitle',
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,9 @@ type Policy = OnyxCommon.OnyxValueWithOfflineFeedback<
/** Whether the Connections feature is enabled */
areConnectionsEnabled?: boolean;

/** Whether the Invoices feature is enabled */
areInvoicesEnabled?: boolean;

/** The verified bank account linked to the policy */
achAccount?: ACHAccount;

Expand Down

0 comments on commit 2c60fc9

Please sign in to comment.