Support adding tips in payments.

This commit is contained in:
John Preston 2021-03-30 18:49:41 +04:00
parent 00c915e58d
commit 8cac76931e
13 changed files with 355 additions and 33 deletions

View file

@ -1889,6 +1889,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_billing_zip_code" = "Zip Code";
"lng_payments_save_payment_about" = "You can save your payment information for future use.";
"lng_payments_save_information" = "Save Information";
"lng_payments_tips_label" = "Tips";
"lng_payments_tips_title" = "Tips";
"lng_payments_tips_enter" = "Enter tips amount";
"lng_call_status_incoming" = "is calling you...";
"lng_call_status_connecting" = "connecting...";

View file

@ -254,6 +254,8 @@ void CheckoutProcess::handleError(const Error &error) {
|| id == u"PAYMENT_CREDENTIALS_INVALID"_q
|| id == u"PAYMENT_CREDENTIALS_ID_INVALID"_q) {
showToast({ "Error: " + id + ". Your card has not been billed." });
} else {
showToast({ "Error: " + id });
}
break;
default: Unexpected("Error type in CheckoutProcess::handleError.");
@ -284,6 +286,7 @@ void CheckoutProcess::panelCloseSure() {
void CheckoutProcess::panelSubmit() {
if (_form->invoice().receipt.paid) {
panelCloseSure();
return;
} else if (_submitState == SubmitState::Validation
|| _submitState == SubmitState::Finishing) {
return;
@ -437,6 +440,10 @@ void CheckoutProcess::chooseShippingOption() {
_panel->chooseShippingOption(_form->shippingOptions());
}
void CheckoutProcess::chooseTips() {
_panel->chooseTips(_form->invoice());
}
void CheckoutProcess::editPaymentMethod() {
_panel->choosePaymentMethod(_form->paymentMethod().ui);
}
@ -453,6 +460,18 @@ void CheckoutProcess::panelChangeShippingOption(const QString &id) {
showForm();
}
void CheckoutProcess::panelChooseTips() {
if (_submitState != SubmitState::None) {
return;
}
chooseTips();
}
void CheckoutProcess::panelChangeTips(int64 value) {
_form->setTips(value);
showForm();
}
void CheckoutProcess::panelValidateInformation(
Ui::RequestedInformation data) {
_form->validateInformation(data);

View file

@ -67,6 +67,7 @@ private:
void showInformationError(Ui::InformationField field);
void showCardError(Ui::CardField field);
void chooseShippingOption();
void chooseTips();
void editPaymentMethod();
void performInitialSilentValidation();
@ -84,6 +85,8 @@ private:
void panelEditPhone() override;
void panelChooseShippingOption() override;
void panelChangeShippingOption(const QString &id) override;
void panelChooseTips() override;
void panelChangeTips(int64 value) override;
void panelValidateInformation(Ui::RequestedInformation data) override;
void panelValidateCard(Ui::UncheckedCardDetails data) override;

View file

@ -45,6 +45,10 @@ namespace {
});
}
[[nodiscard]] int64 ParsePriceAmount(uint64 value) {
return *reinterpret_cast<const int64*>(&value);
}
[[nodiscard]] std::vector<Ui::LabeledPrice> ParsePrices(
const MTPVector<MTPLabeledPrice> &data) {
return ranges::views::all(
@ -53,7 +57,7 @@ namespace {
return price.match([&](const MTPDlabeledPrice &data) {
return Ui::LabeledPrice{
.label = qs(data.vlabel()),
.price = *reinterpret_cast<const int64*>(&data.vamount().v),
.price = ParsePriceAmount(data.vamount().v),
};
});
}) | ranges::to_vector;
@ -290,6 +294,10 @@ void Form::processInvoice(const MTPDinvoice &data) {
.cover = std::move(_invoice.cover),
.prices = ParsePrices(data.vprices()),
.tipsMin = ParsePriceAmount(data.vmin_tip_amount().value_or_empty()),
.tipsMax = ParsePriceAmount(data.vmax_tip_amount().value_or_empty()),
.tipsSelected = ParsePriceAmount(
data.vdefault_tip_amount().value_or_empty()),
.currency = qs(data.vcurrency()),
.isNameRequested = data.is_name_requested(),
@ -330,8 +338,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
void Form::processDetails(const MTPDpayments_paymentReceipt &data) {
_invoice.receipt = Ui::Receipt{
.date = data.vdate().v,
.totalAmount = *reinterpret_cast<const int64*>(
&data.vtotal_amount().v),
.totalAmount = ParsePriceAmount(data.vtotal_amount().v),
.currency = qs(data.vcurrency()),
.paid = true,
};
@ -440,7 +447,8 @@ void Form::submit() {
: Flag::f_requested_info_id)
| (_shippingOptions.selectedId.isEmpty()
? Flag(0)
: Flag::f_shipping_option_id)),
: Flag::f_shipping_option_id)
| (_invoice.tipsSelected ? Flag::f_tip_amount : Flag(0))),
MTP_long(_details.formId),
_peer->input,
MTP_int(_msgId),
@ -449,7 +457,7 @@ void Form::submit() {
MTP_inputPaymentCredentials(
MTP_flags(0),
MTP_dataJSON(MTP_bytes(_paymentMethod.newCredentials.data))),
MTP_long(0) // #TODO payments tip_amount
MTP_long(_invoice.tipsSelected)
)).done([=](const MTPpayments_PaymentResult &result) {
result.match([&](const MTPDpayments_paymentResult &data) {
_updates.fire(PaymentFinished{ data.vupdates() });
@ -668,6 +676,13 @@ void Form::setShippingOption(const QString &id) {
_shippingOptions.selectedId = id;
}
void Form::setTips(int64 value) {
_invoice.tipsSelected = std::clamp(
value,
_invoice.tipsMin,
_invoice.tipsMax);
}
void Form::processShippingOptions(const QVector<MTPShippingOption> &data) {
_shippingOptions = Ui::ShippingOptions{ ranges::views::all(
data

View file

@ -173,6 +173,7 @@ public:
void validateCard(const Ui::UncheckedCardDetails &details);
void setPaymentCredentials(const NewCredentials &credentials);
void setShippingOption(const QString &id);
void setTips(int64 value);
void submit();
private:

View file

@ -46,6 +46,7 @@ namespace {
case FieldType::CardCVC:
case FieldType::Country:
case FieldType::Phone:
case FieldType::PriceAmount:
return true;
}
Unexpected("FieldType in Payments::Ui::UseMaskedField.");
@ -67,6 +68,7 @@ namespace {
case FieldType::CardCVC:
case FieldType::Country:
case FieldType::Phone:
case FieldType::PriceAmount:
return base::make_unique_q<RpWidget>(parent);
}
Unexpected("FieldType in Payments::Ui::CreateWrap.");
@ -94,6 +96,7 @@ namespace {
case FieldType::CardExpireDate:
case FieldType::CardCVC:
case FieldType::Country:
case FieldType::PriceAmount:
return CreateChild<MaskedInputField>(
wrap.get(),
st::paymentsField,

View file

@ -29,6 +29,7 @@ enum class FieldType {
Country,
Phone,
Email,
PriceAmount,
};
struct FieldValidateRequest {

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "data/data_countries.h"
#include "lang/lang_keys.h"
#include "base/unixtime.h"
@ -84,7 +85,7 @@ int64 FormSummary::computeTotalAmount() const {
std::plus<>(),
&LabeledPrice::price)
: int64(0);
return total + shipping;
return total + shipping + _invoice.tipsSelected;
}
void FormSummary::setupControls() {
@ -193,12 +194,15 @@ void FormSummary::setupCover(not_null<VerticalLayout*> layout) {
void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
const auto addRow = [&](
const QString &label,
const QString &value,
const TextWithEntities &value,
bool full = false) {
const auto &st = full
? st::paymentsFullPriceAmount
: st::paymentsPriceAmount;
const auto right = CreateChild<FlatLabel>(layout.get(), value, st);
const auto right = CreateChild<FlatLabel>(
layout.get(),
rpl::single(value),
st);
const auto &padding = st::paymentsPricePadding;
const auto left = layout->add(
object_ptr<FlatLabel>(
@ -220,13 +224,14 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
) | rpl::start_with_next([=](int top, int width) {
right->moveToRight(st::paymentsPricePadding.right(), top, width);
}, right->lifetime());
return right;
};
Settings::AddSkip(layout, st::paymentsPricesTopSkip);
if (_invoice.receipt) {
addRow(
tr::lng_payments_date_label(tr::now),
langDateTime(base::unixtime::parse(_invoice.receipt.date)),
{ langDateTime(base::unixtime::parse(_invoice.receipt.date)) },
true);
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
Settings::AddDivider(layout);
@ -237,7 +242,7 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
const QString &label,
int64 amount,
bool full = false) {
addRow(label, formatAmount(amount), full);
addRow(label, { formatAmount(amount) }, full);
};
for (const auto &price : _invoice.prices) {
add(price.label, price.price);
@ -251,7 +256,27 @@ void FormSummary::setupPrices(not_null<VerticalLayout*> layout) {
add(price.label, price.price);
}
}
add(tr::lng_payments_total_label(tr::now), computeTotalAmount(), true);
const auto computedTotal = computeTotalAmount();
const auto total = _invoice.receipt.paid
? _invoice.receipt.totalAmount
: computedTotal;
if (_invoice.receipt.paid) {
if (const auto tips = total - computedTotal) {
add(tr::lng_payments_tips_label(tr::now), tips);
}
} else if (_invoice.tipsMax > 0) {
const auto text = formatAmount(_invoice.tipsSelected);
const auto label = addRow(
tr::lng_payments_tips_label(tr::now),
Ui::Text::Link(text, "internal:edit_tips"));
label->setClickHandlerFilter([=](auto&&...) {
_delegate->panelChooseTips();
return false;
});
}
add(tr::lng_payments_total_label(tr::now), total, true);
Settings::AddSkip(layout, st::paymentsPricesBottomSkip);
}

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "payments/ui/payments_edit_information.h"
#include "payments/ui/payments_edit_card.h"
#include "payments/ui/payments_panel_delegate.h"
#include "payments/ui/payments_field.h"
#include "ui/widgets/separate_panel.h"
#include "ui/boxes/single_choice_box.h"
#include "lang/lang_keys.h"
@ -19,6 +20,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_passport.h"
namespace Payments::Ui {
namespace {
[[nodiscard]] auto PriceAmountValidator(int64 min, int64 max) {
return [=](FieldValidateRequest request) {
return FieldValidateResult{
.value = request.nowValue,
.position = request.nowPosition,
};
};
}
} // namespace
Panel::Panel(not_null<PanelDelegate*> delegate)
: _delegate(delegate)
@ -127,6 +140,40 @@ void Panel::chooseShippingOption(const ShippingOptions &options) {
}));
}
void Panel::chooseTips(const Invoice &invoice) {
const auto min = invoice.tipsMin;
const auto max = invoice.tipsMax;
const auto now = invoice.tipsSelected;
showBox(Box([=](not_null<Ui::GenericBox*> box) {
box->setTitle(tr::lng_payments_tips_title());
const auto row = box->lifetime().make_state<Field>(
box,
FieldConfig{
.type = FieldType::PriceAmount,
.placeholder = tr::lng_payments_tips_enter(),
.value = QString::number(now),
.validator = PriceAmountValidator(min, max),
});
box->setFocusCallback([=] {
row->setFocusFast();
});
box->addRow(row->ownedWidget());
box->addRow(object_ptr<FlatLabel>(box, "Min: " + QString::number(min), st::defaultFlatLabel));
box->addRow(object_ptr<FlatLabel>(box, "Max: " + QString::number(max), st::defaultFlatLabel));
box->addButton(tr::lng_settings_save(), [=] {
const auto value = row->value().toLongLong();
if (value < min || value > max) {
row->showError();
} else {
_delegate->panelChangeTips(value);
box->closeBox();
}
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}));
}
void Panel::showEditPaymentMethod(const PaymentMethodDetails &method) {
_widget->setTitle(tr::lng_payments_card_title());
if (method.native.supported) {

View file

@ -63,6 +63,7 @@ public:
const NativeMethodDetails &native,
CardField field);
void chooseShippingOption(const ShippingOptions &options);
void chooseTips(const Invoice &invoice);
void choosePaymentMethod(const PaymentMethodDetails &method);
bool showWebview(const QString &url, bool allowBack);

View file

@ -39,6 +39,9 @@ struct Invoice {
Cover cover;
std::vector<LabeledPrice> prices;
int64 tipsMin = 0;
int64 tipsMax = 0;
int64 tipsSelected = 0;
QString currency;
Receipt receipt;
@ -53,7 +56,7 @@ struct Invoice {
bool emailSentToProvider = false;
[[nodiscard]] bool valid() const {
return !currency.isEmpty() && !prices.empty();
return !currency.isEmpty() && (!prices.empty() || tipsMax);
}
[[nodiscard]] explicit operator bool() const {
return valid();

View file

@ -38,6 +38,8 @@ public:
virtual void panelEditPhone() = 0;
virtual void panelChooseShippingOption() = 0;
virtual void panelChangeShippingOption(const QString &id) = 0;
virtual void panelChooseTips() = 0;
virtual void panelChangeTips(int64 value) = 0;
virtual void panelValidateInformation(RequestedInformation data) = 0;
virtual void panelValidateCard(Ui::UncheckedCardDetails data) = 0;

View file

@ -10,6 +10,40 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include <QtCore/QLocale>
#include <locale>
#include <sstream>
#include <iostream>
[[nodiscard]] QString FormatWithSeparators(
double amount,
char decimal,
char thousands) {
Expects(decimal != 0);
// Thanks https://stackoverflow.com/a/5058949
struct FormattingHelper : std::numpunct<char> {
FormattingHelper(char decimal, char thousands)
: decimal(decimal)
, thousands(thousands) {
}
char do_decimal_point() const override { return decimal; }
char do_thousands_sep() const override { return thousands; }
char decimal = '.';
char thousands = ',';
};
auto stream = std::ostringstream();
auto helper = FormattingHelper(decimal, thousands ? thousands : '?');
stream.imbue(std::locale(stream.getloc(), &helper));
stream << std::fixed << amount;
auto result = QString::fromStdString(stream.str());
if (!thousands) {
result.replace('?', QString());
}
return result;
}
namespace Ui {
@ -126,13 +160,168 @@ QString FormatPlayedText(qint64 played, qint64 duration) {
}
QString FillAmountAndCurrency(int64 amount, const QString &currency) {
static const auto ShortCurrencyNames = QMap<QString, QString>{
{ u"USD"_q, QString::fromUtf8("\x24") },
{ u"GBP"_q, QString::fromUtf8("\xC2\xA3") },
{ u"EUR"_q, QString::fromUtf8("\xE2\x82\xAC") },
{ u"JPY"_q, QString::fromUtf8("\xC2\xA5") },
struct Rule {
//const char *name = "";
//const char *native = "";
const char *international = "";
char thousands = ',';
char decimal = '.';
bool left = true;
bool space = false;
};
static const auto Denominators = QMap<QString, int>{
static const auto kRules = std::vector<std::pair<QString, Rule>>{
{ u"AED"_q, { "", ',', '.', true, true } },
{ u"AFN"_q, {} },
{ u"ALL"_q, { "", '.', ',', false } },
{ u"AMD"_q, { "", ',', '.', false, true } },
{ u"ARS"_q, { "", '.', ',', true, true } },
{ u"AUD"_q, { "AU$" } },
{ u"AZN"_q, { "", ' ', ',', false, true } },
{ u"BAM"_q, { "", '.', ',', false, true } },
{ u"BDT"_q, { "", ',', '.', true, true } },
{ u"BGN"_q, { "", ' ', ',', false, true } },
{ u"BND"_q, { "", '.', ',', } },
{ u"BOB"_q, { "", '.', ',', true, true } },
{ u"BRL"_q, { "R$", '.', ',', true, true } },
{ u"BHD"_q, { "", ',', '.', true, true } },
{ u"BYR"_q, { "", ' ', ',', false, true } },
{ u"CAD"_q, { "CA$" } },
{ u"CHF"_q, { "", '\'', '.', false, true } },
{ u"CLP"_q, { "", '.', ',', true, true } },
{ u"CNY"_q, { "\x43\x4E\xC2\xA5" } },
{ u"COP"_q, { "", '.', ',', true, true } },
{ u"CRC"_q, { "", '.', ',', } },
{ u"CZK"_q, { "", ' ', ',', false, true } },
{ u"DKK"_q, { "", '\0', ',', false, true } },
{ u"DOP"_q, {} },
{ u"DZD"_q, { "", ',', '.', true, true } },
{ u"EGP"_q, { "", ',', '.', true, true } },
{ u"EUR"_q, { "\xE2\x82\xAC", ' ', ',', false, true } },
{ u"GBP"_q, { "\xC2\xA3" } },
{ u"GEL"_q, { "", ' ', ',', false, true } },
{ u"GTQ"_q, {} },
{ u"HKD"_q, { "HK$" } },
{ u"HNL"_q, { "", ',', '.', true, true } },
{ u"HRK"_q, { "", '.', ',', false, true } },
{ u"HUF"_q, { "", ' ', ',', false, true } },
{ u"IDR"_q, { "", '.', ',', } },
{ u"ILS"_q, { "\xE2\x82\xAA", ',', '.', true, true } },
{ u"INR"_q, { "\xE2\x82\xB9" } },
{ u"ISK"_q, { "", '.', ',', false, true } },
{ u"JMD"_q, {} },
{ u"JPY"_q, { "\xC2\xA5" } },
{ u"KES"_q, {} },
{ u"KGS"_q, { "", ' ', '-', false, true } },
{ u"KRW"_q, { "\xE2\x82\xA9" } },
{ u"KZT"_q, { "", ' ', '-', } },
{ u"LBP"_q, { "", ',', '.', true, true } },
{ u"LKR"_q, { "", ',', '.', true, true } },
{ u"MAD"_q, { "", ',', '.', true, true } },
{ u"MDL"_q, { "", ',', '.', false, true } },
{ u"MNT"_q, { "", ' ', ',', } },
{ u"MUR"_q, {} },
{ u"MVR"_q, { "", ',', '.', false, true } },
{ u"MXN"_q, { "MX$" } },
{ u"MYR"_q, {} },
{ u"MZN"_q, {} },
{ u"NGN"_q, {} },
{ u"NIO"_q, { "", ',', '.', true, true } },
{ u"NOK"_q, { "", ' ', ',', true, true } },
{ u"NPR"_q, {} },
{ u"NZD"_q, { "NZ$" } },
{ u"PAB"_q, { "", ',', '.', true, true } },
{ u"PEN"_q, { "", ',', '.', true, true } },
{ u"PHP"_q, {} },
{ u"PKR"_q, {} },
{ u"PLN"_q, { "", ' ', ',', false, true } },
{ u"PYG"_q, { "", '.', ',', true, true } },
{ u"QAR"_q, { "", ',', '.', true, true } },
{ u"RON"_q, { "", '.', ',', false, true } },
{ u"RSD"_q, { "", '.', ',', false, true } },
{ u"RUB"_q, { "", ' ', ',', false, true } },
{ u"SAR"_q, { "", ',', '.', true, true } },
{ u"SEK"_q, { "", '.', ',', false, true } },
{ u"SGD"_q, {} },
{ u"THB"_q, { "\xE0\xB8\xBF" } },
{ u"TJS"_q, { "", ' ', ';', false, true } },
{ u"TRY"_q, { "", '.', ',', false, true } },
{ u"TTD"_q, {} },
{ u"TWD"_q, { "NT$" } },
{ u"TZS"_q, {} },
{ u"UAH"_q, { "", ' ', ',', false } },
{ u"UGX"_q, {} },
{ u"USD"_q, { "$" } },
{ u"UYU"_q, { "", '.', ',', true, true } },
{ u"UZS"_q, { "", ' ', ',', false, true } },
{ u"VND"_q, { "\xE2\x82\xAB", '.', ',', false, true } },
{ u"YER"_q, { "", ',', '.', true, true } },
{ u"ZAR"_q, { "", ',', '.', true, true } },
{ u"IRR"_q, { "", ',', '/', false, true } },
{ u"IQD"_q, { "", ',', '.', true, true } },
{ u"VEF"_q, { "", '.', ',', true, true } },
{ u"SYP"_q, { "", ',', '.', true, true } },
//{ u"VUV"_q, { "", ',', '.', false } },
//{ u"WST"_q, {} },
//{ u"XAF"_q, { "FCFA", ',', '.', false } },
//{ u"XCD"_q, {} },
//{ u"XOF"_q, { "CFA", ' ', ',', false } },
//{ u"XPF"_q, { "", ',', '.', false } },
//{ u"ZMW"_q, {} },
//{ u"ANG"_q, {} },
//{ u"RWF"_q, { "", ' ', ',', true, true } },
//{ u"PGK"_q, {} },
//{ u"TOP"_q, {} },
//{ u"SBD"_q, {} },
//{ u"SCR"_q, {} },
//{ u"SHP"_q, {} },
//{ u"SLL"_q, {} },
//{ u"SOS"_q, {} },
//{ u"SRD"_q, {} },
//{ u"STD"_q, {} },
//{ u"SVC"_q, {} },
//{ u"SZL"_q, {} },
//{ u"AOA"_q, {} },
//{ u"AWG"_q, {} },
//{ u"BBD"_q, {} },
//{ u"BIF"_q, { "", ',', '.', false } },
//{ u"BMD"_q, {} },
//{ u"BSD"_q, {} },
//{ u"BWP"_q, {} },
//{ u"BZD"_q, {} },
//{ u"CDF"_q, { "", ',', '.', false } },
//{ u"CVE"_q, {} },
//{ u"DJF"_q, { "", ',', '.', false } },
//{ u"ETB"_q, {} },
//{ u"FJD"_q, {} },
//{ u"FKP"_q, {} },
//{ u"GIP"_q, {} },
//{ u"GMD"_q, { "", ',', '.', false } },
//{ u"GNF"_q, { "", ',', '.', false } },
//{ u"GYD"_q, {} },
//{ u"HTG"_q, {} },
//{ u"KHR"_q, { "", ',', '.', false } },
//{ u"KMF"_q, { "", ',', '.', false } },
//{ u"KYD"_q, {} },
//{ u"LAK"_q, { "", ',', '.', false } },
//{ u"LRD"_q, {} },
//{ u"LSL"_q, { "", ',', '.', false } },
//{ u"MGA"_q, {} },
//{ u"MKD"_q, { "", '.', ',', false, true } },
//{ u"MOP"_q, {} },
//{ u"MWK"_q, {} },
//{ u"NAD"_q, {} },
};
static const auto kRulesMap = [] {
// flat_multi_map_pair_type lacks some required constructors :(
auto &&pairs = kRules | ranges::views::transform([](auto &&pair) {
return base::flat_multi_map_pair_type<QString, Rule>(
pair.first,
pair.second);
});
return base::flat_map<QString, Rule>(begin(pairs), end(pairs));
}();
static const auto kDenominators = base::flat_map<QString, int>{
{ u"CLF"_q, 10000 },
{ u"BHD"_q, 1000 },
{ u"IQD"_q, 1000 },
@ -163,21 +352,31 @@ QString FillAmountAndCurrency(int64 amount, const QString &currency) {
{ u"XPF"_q, 1 },
{ u"MRO"_q, 10 },
};
const auto currencyText = ShortCurrencyNames.value(currency, currency);
const auto denominator = Denominators.value(currency, 100);
const auto currencyValue = amount / float64(denominator);
const auto digits = [&] {
auto result = 0;
for (auto test = 1; test < denominator; test *= 10) {
++result;
}
return result;
}();
return QLocale::system().toCurrencyString(currencyValue, currencyText);
//auto amountBucks = amount / 100;
//auto amountCents = amount % 100;
//auto amountText = u"%1,%2").arg(amountBucks).arg(amountCents, 2, 10, QChar('0'));
//return currencyText + amountText;
const auto denominatorIt = kDenominators.find(currency);
const auto denominator = (denominatorIt != end(kDenominators))
? denominatorIt->second
: 100;
const auto value = amount / float64(denominator);
const auto ruleIt = kRulesMap.find(currency);
if (ruleIt == end(kRulesMap)) {
return QLocale::system().toCurrencyString(value, currency);
}
const auto &rule = ruleIt->second;
const auto name = (*rule.international)
? QString::fromUtf8(rule.international)
: currency;
auto result = QString();
if (rule.left) {
result.append(name);
if (rule.space) result.append(' ');
}
result.append(
FormatWithSeparators(value, rule.decimal, rule.thousands));
if (!rule.left) {
if (rule.space) result.append(' ');
result.append(name);
}
return result;
}
QString ComposeNameString(