Skip to content

Commit

Permalink
resolve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
luacmartins committed Aug 16, 2023
2 parents fa20e6b + 21aecbc commit 356ea9b
Show file tree
Hide file tree
Showing 23 changed files with 202 additions and 142 deletions.
5 changes: 4 additions & 1 deletion src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export default {
BANK_ACCOUNT_NEW: 'bank-account/new',
BANK_ACCOUNT_WITH_STEP_TO_OPEN: 'bank-account/:stepToOpen?',
BANK_ACCOUNT_PERSONAL: 'bank-account/personal',
getBankAccountRoute: (stepToOpen = '', policyID = '') => `bank-account/${stepToOpen}?policyID=${policyID}`,
getBankAccountRoute: (stepToOpen = '', policyID = '', backTo = '') => {
const backToParam = backTo ? `&backTo=${encodeURIComponent(backTo)}` : '';
return `bank-account/${stepToOpen}?policyID=${policyID}${backToParam}`;
},
HOME: '',
SETTINGS: 'settings',
SETTINGS_PROFILE: 'settings/profile',
Expand Down
4 changes: 3 additions & 1 deletion src/components/ConnectBankAccountButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import networkPropTypes from './networkPropTypes';
import Text from './Text';
import Navigation from '../libs/Navigation/Navigation';

const propTypes = {
...withLocalizePropTypes,
Expand All @@ -29,14 +30,15 @@ const defaultProps = {
};

function ConnectBankAccountButton(props) {
const activeRoute = Navigation.getActiveRoute().replace(/\?.*/, '');
return props.network.isOffline ? (
<View style={props.style}>
<Text>{`${props.translate('common.youAppearToBeOffline')} ${props.translate('common.thisFeatureRequiresInternet')}`}</Text>
</View>
) : (
<Button
text={props.translate('workspace.common.connectBankAccount')}
onPress={() => ReimbursementAccount.navigateToBankAccountRoute(props.policyID)}
onPress={() => ReimbursementAccount.navigateToBankAccountRoute(props.policyID, activeRoute)}
icon={Expensicons.Bank}
style={props.style}
iconStyles={[styles.buttonCTAIcon]}
Expand Down
4 changes: 2 additions & 2 deletions src/components/MoneyRequestConfirmationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function MoneyRequestConfirmationList(props) {
*/
const getParticipantsWithAmount = useCallback(
(participantsList) => {
const iouAmount = IOUUtils.calculateAmount(participantsList.length, props.iouAmount);
const iouAmount = IOUUtils.calculateAmount(participantsList.length, props.iouAmount, props.iouCurrencyCode);
return OptionsListUtils.getIOUConfirmationOptionsFromParticipants(participantsList, CurrencyUtils.convertToDisplayString(iouAmount, props.iouCurrencyCode));
},
[props.iouAmount, props.iouCurrencyCode],
Expand Down Expand Up @@ -176,7 +176,7 @@ function MoneyRequestConfirmationList(props) {
}));
}

const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, props.iouAmount, true);
const myIOUAmount = IOUUtils.calculateAmount(selectedParticipants.length, props.iouAmount, props.iouCurrencyCode, true);
const formattedPayeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(
payeePersonalDetails,
CurrencyUtils.convertToDisplayString(myIOUAmount, props.iouCurrencyCode),
Expand Down
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/IOUPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ function IOUPreview(props) {
{props.isBillSplit && !_.isEmpty(participantAccountIDs) && (
<Text style={[styles.textLabel, styles.colorMuted, styles.ml1]}>
{props.translate('iou.amountEach', {
amount: CurrencyUtils.convertToDisplayString(IOUUtils.calculateAmount(participantAccountIDs.length - 1, requestAmount), requestCurrency),
amount: CurrencyUtils.convertToDisplayString(IOUUtils.calculateAmount(participantAccountIDs.length - 1, requestAmount, requestCurrency), requestCurrency),
})}
</Text>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ReportActionItem/MoneyRequestAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function MoneyRequestAction(props) {
participantAccountIDs,
ReportUtils.getTransactionReportName(props.action),
'',
CONST.POLICY.OWNER_EMAIL_FAKE,
lodashGet(props.iouReport, 'policyID', CONST.POLICY.OWNER_EMAIL_FAKE),
CONST.POLICY.OWNER_ACCOUNT_ID_FAKE,
false,
'',
Expand Down
45 changes: 20 additions & 25 deletions src/libs/CurrencyUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Onyx.connect({
*/
function getCurrencyDecimals(currency = CONST.CURRENCY.USD) {
const decimals = lodashGet(currencyList, [currency, 'decimals']);
return _.isUndefined(decimals) ? 2 : Math.min(decimals, 2);
return _.isUndefined(decimals) ? 2 : decimals;
}

/**
Expand Down Expand Up @@ -73,54 +73,49 @@ function isCurrencySymbolLTR(currencyCode) {
}

/**
* Takes an amount as a floating point number and converts it to an integer amount.
* For example, given [25, USD], return 2500.
* Given [25.50, USD] return 2550.
* Given [2500, JPY], return 2500.
* Takes an amount as a floating point number and converts it to an integer equivalent to the amount in "cents".
* This is because the backend always stores amounts in "cents". The backend works in integer cents to avoid precision errors
* when doing math operations.
*
* @note we do not currently support any currencies with more than two decimal places. Sorry Tunisia :(
* @note we do not currently support any currencies with more than two decimal places. Decimal past the second place will be rounded. Sorry Tunisia :(
*
* @param {String} currency
* @param {Number} amountAsFloat
* @returns {Number}
*/
function convertToSmallestUnit(currency, amountAsFloat) {
const currencyUnit = getCurrencyUnit(currency);
// We round off the number to resolve floating-point precision issues.
return Math.round(amountAsFloat * currencyUnit);
function convertToBackendAmount(amountAsFloat) {
return Math.round(amountAsFloat * 100);
}

/**
* Takes an amount as an integer and converts it to a floating point amount.
* For example, give [25, USD], return 0.25
* Given [2550, USD], return 25.50
* Given [2500, JPY], return 2500
* Takes an amount in "cents" as an integer and converts it to a floating point amount used in the frontend.
*
* @note we do not support any currencies with more than two decimal places.
*
* @param {String} currency
* @param {Number} amountAsInt
* @returns {Number}
*/
function convertToWholeUnit(currency, amountAsInt) {
const currencyUnit = getCurrencyUnit(currency);
return Math.trunc(amountAsInt) / currencyUnit;
function convertToFrontendAmount(amountAsInt) {
return Math.trunc(amountAsInt) / 100.0;
}

/**
* Given an amount in the smallest units of a currency, convert it to a string for display in the UI.
* Given an amount in the "cents", convert it to a string for display in the UI.
* The backend always handle things in "cents" (subunit equal to 1/100)
*
* @param {Number} amountInSmallestUnit – should be an integer. Anything after a decimal place will be dropped.
* @param {Number} amountInCents – should be an integer. Anything after a decimal place will be dropped.
* @param {String} currency
* @returns {String}
*/
function convertToDisplayString(amountInSmallestUnit, currency = CONST.CURRENCY.USD) {
const currencyUnit = getCurrencyUnit(currency);
const convertedAmount = Math.trunc(amountInSmallestUnit) / currencyUnit;
function convertToDisplayString(amountInCents, currency = CONST.CURRENCY.USD) {
const convertedAmount = convertToFrontendAmount(amountInCents);
return NumberFormatUtils.format(BaseLocaleListener.getPreferredLocale(), convertedAmount, {
style: 'currency',
currency,

// We are forcing the number of decimals because we override the default number of decimals in the backend for RSD
// See: https://github.com/Expensify/PHP-Libs/pull/834
minimumFractionDigits: currency === 'RSD' ? getCurrencyDecimals(currency) : undefined,
});
}

export {getCurrencyDecimals, getCurrencyUnit, getLocalizedCurrencySymbol, isCurrencySymbolLTR, convertToSmallestUnit, convertToWholeUnit, convertToDisplayString};
export {getCurrencyDecimals, getCurrencyUnit, getLocalizedCurrencySymbol, isCurrencySymbolLTR, convertToBackendAmount, convertToFrontendAmount, convertToDisplayString};
18 changes: 12 additions & 6 deletions src/libs/IOUUtils.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import _ from 'underscore';
import CONST from '../CONST';
import * as TransactionUtils from './TransactionUtils';
import * as CurrencyUtils from './CurrencyUtils';

/**
* Calculates the amount per user given a list of participants
*
* @param {Number} numberOfParticipants - Number of participants in the chat. It should not include the current user.
* @param {Number} total - IOU total amount in the smallest units of the currency
* @param {Number} total - IOU total amount in backend format (cents, no matter the currency)
* @param {String} currency - This is used to know how many decimal places are valid to use when splitting the total
* @param {Boolean} isDefaultUser - Whether we are calculating the amount for the current user
* @returns {Number}
*/
function calculateAmount(numberOfParticipants, total, isDefaultUser = false) {
function calculateAmount(numberOfParticipants, total, currency, isDefaultUser = false) {
// Since the backend can maximum store 2 decimal places, any currency with more than 2 decimals
// has to be capped to 2 decimal places
const currencyUnit = Math.min(100, CurrencyUtils.getCurrencyUnit(currency));
const totalInCurrencySubunit = Math.round((total / 100) * currencyUnit);
const totalParticipants = numberOfParticipants + 1;
const amountPerPerson = Math.round(total / totalParticipants);
const amountPerPerson = Math.round(totalInCurrencySubunit / totalParticipants);
let finalAmount = amountPerPerson;
if (isDefaultUser) {
const sumAmount = amountPerPerson * totalParticipants;
const difference = total - sumAmount;
finalAmount = total !== sumAmount ? amountPerPerson + difference : amountPerPerson;
const difference = totalInCurrencySubunit - sumAmount;
finalAmount = totalInCurrencySubunit !== sumAmount ? amountPerPerson + difference : amountPerPerson;
}
return finalAmount;
return Math.round((finalAmount * 100) / currencyUnit);
}

/**
Expand Down
12 changes: 10 additions & 2 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ function getOptions(
includeTasks = false,
includeMoneyRequests = false,
excludeUnknownUsers = false,
includeP2P = true,
},
) {
if (!isPersonalDetailsReady(personalDetails)) {
Expand All @@ -631,7 +632,7 @@ function getOptions(
// This is a temporary fix for all the logic that's been breaking because of the new privacy changes
// See https://github.com/Expensify/Expensify/issues/293465 for more context
// eslint-disable-next-line no-param-reassign
personalDetails = _.pick(personalDetails, (detail) => Boolean(detail.login));
personalDetails = !includeP2P ? {} : _.pick(personalDetails, (detail) => Boolean(detail.login));

let recentReportOptions = [];
let personalDetailsOptions = [];
Expand Down Expand Up @@ -671,6 +672,11 @@ function getOptions(
return;
}

// When passing includeP2P false we are trying to hide features from users that are not ready for P2P and limited to workspace chats only.
if (!includeP2P && !isPolicyExpenseChat) {
return;
}

if (isThread && !includeThreads) {
return;
}
Expand Down Expand Up @@ -940,9 +946,10 @@ function getIOUConfirmationOptionsFromParticipants(participants, amountText) {
* @param {Array} [selectedOptions]
* @param {Array} [excludeLogins]
* @param {Boolean} [includeOwnedWorkspaceChats]
* @param {boolean} [includeP2P]
* @returns {Object}
*/
function getNewChatOptions(reports, personalDetails, betas = [], searchValue = '', selectedOptions = [], excludeLogins = [], includeOwnedWorkspaceChats = false) {
function getNewChatOptions(reports, personalDetails, betas = [], searchValue = '', selectedOptions = [], excludeLogins = [], includeOwnedWorkspaceChats = false, includeP2P = true) {
return getOptions(reports, personalDetails, {
betas,
searchInputValue: searchValue.trim(),
Expand All @@ -952,6 +959,7 @@ function getNewChatOptions(reports, personalDetails, betas = [], searchValue = '
maxRecentReportsToShow: 5,
excludeLogins,
includeOwnedWorkspaceChats,
includeP2P,
});
}

Expand Down
41 changes: 18 additions & 23 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import * as Expensicons from '../components/Icon/Expensicons';
import Navigation from './Navigation/Navigation';
import ROUTES from '../ROUTES';
import * as NumberUtils from './NumberUtils';
import * as NumberFormatUtils from './NumberFormatUtils';
import * as ReportActionsUtils from './ReportActionsUtils';
import * as TransactionUtils from './TransactionUtils';
import Permissions from './Permissions';
Expand Down Expand Up @@ -41,17 +40,6 @@ Onyx.connect({
},
});

let preferredLocale = CONST.LOCALES.DEFAULT;
Onyx.connect({
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
callback: (val) => {
if (!val) {
return;
}
preferredLocale = val;
},
});

let allPersonalDetails;
let currentUserPersonalDetails;
Onyx.connect({
Expand Down Expand Up @@ -1808,8 +1796,7 @@ function buildOptimisticExpenseReport(chatReportID, policyID, payeeAccountID, to
* @returns {Array}
*/
function getIOUReportActionMessage(type, total, comment, currency, paymentType = '', isSettlingUp = false) {
const currencyUnit = CurrencyUtils.getCurrencyUnit(currency);
const amount = NumberFormatUtils.format(preferredLocale, Math.abs(total) / currencyUnit, {style: 'currency', currency});
const amount = CurrencyUtils.convertToDisplayString(total, currency);
let paymentMethodMessage;
switch (paymentType) {
case CONST.IOU.PAYMENT_TYPE.ELSEWHERE:
Expand Down Expand Up @@ -1859,7 +1846,7 @@ function getIOUReportActionMessage(type, total, comment, currency, paymentType =
* @param {String} currency
* @param {String} comment - User comment for the IOU.
* @param {Array} participants - An array with participants details.
* @param {String} transactionID
* @param {String} [transactionID] - Not required if the IOUReportAction type is 'pay'
* @param {String} [paymentType] - Only required if the IOUReportAction type is 'pay'. Can be oneOf(elsewhere, payPal, Expensify).
* @param {String} [iouReportID] - Only required if the IOUReportActions type is oneOf(decline, cancel, pay). Generates a randomID as default.
* @param {Boolean} [isSettlingUp] - Whether we are settling up an IOU.
Expand All @@ -1873,7 +1860,7 @@ function buildOptimisticIOUReportAction(
currency,
comment,
participants,
transactionID,
transactionID = '',
paymentType = '',
iouReportID = '',
isSettlingUp = false,
Expand All @@ -1891,13 +1878,21 @@ function buildOptimisticIOUReportAction(
type,
};

// We store amount, comment, currency in IOUDetails when type = pay
if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY && isSendMoneyFlow) {
_.each(['amount', 'comment', 'currency'], (key) => {
delete originalMessage[key];
});
originalMessage.IOUDetails = {amount, comment, currency};
originalMessage.paymentType = paymentType;
if (type === CONST.IOU.REPORT_ACTION_TYPE.PAY) {
// In send money flow, we store amount, comment, currency in IOUDetails when type = pay
if (isSendMoneyFlow) {
_.each(['amount', 'comment', 'currency'], (key) => {
delete originalMessage[key];
});
originalMessage.IOUDetails = {amount, comment, currency};
originalMessage.paymentType = paymentType;
} else {
// In case of pay money request action, we dont store the comment
// and there is no single transctionID to link the action to.
delete originalMessage.IOUTransactionID;
delete originalMessage.comment;
originalMessage.paymentType = paymentType;
}
}

// IOUs of type split only exist in group DMs and those don't have an iouReport so we need to delete the IOUReportID key
Expand Down
Loading

0 comments on commit 356ea9b

Please sign in to comment.