Регистрация  |  Вход

Сумма прописью на Salesforce (русский вариант)

Привет всем. На днях наш коллега подкинул одну интересную тему - представление суммы в письменном виде. Идея популярная в бизнес кругах, но мне как программисту почему-то не приходилось с ней сталкиваться. Готового решения данной проблемы для русского сегмента на Salesforce я не нашел, поэтому, как многие в интернете, принялся за написани своего велосипеда.



Вернее, авторство принадлежит не мне, вот источник. Я всего лишь адаптировал код под Salesforce, я так же добавил тесты. Так что можете смело брать получившийся исходный код, модифицировать и использовать на свое усмотрение. Вот что можно сделать и получить с помощью данного класса:




MoneyConverterRus mc = new MoneyConverterRus('123.89');
String convertedMoney = mc.num2str();
String convertedMoneyWithoutKop = mc.num2str(true);


123.89 будет преобразовано в "сто двадцать три рубля 89 копеек" или "сто двадцать три рубля" если в качестве параметра методу num2str задать True На вход можно передавать сумму в форматах String, Decimal и Integer. Вот и сам класс:




public with sharing class MoneyConverterRus {

private Decimal amount;

/**
* Конструктор из Integer
*/
public MoneyConverterRus(Integer i) {
String s = String.valueOf(i);
if (!s.contains('.') )
s += '.0';
this.amount = Decimal.valueOf(s);
}

/**
* Конструктор из Decimal
*/
public MoneyConverterRus(Decimal d) {
String s = String.valueOf(d);
if (!s.contains('.'))
s += '.0';
this.amount = Decimal.valueOf(s);
}

/**
* Конструктор из String
*/
public MoneyConverterRus(String s) {
if (!s.contains('.') )
s += '.0';
this.amount = Decimal.valueOf(s);
}

/**
* Вернуть сумму как строку
*/
public String asString() {
return String.valueOf(amount);
}

/**
* Вернуть сумму прописью, с точностью до копеек
*/
public String num2str() {
return num2str(false);
}

/**
* Выводим сумму прописью
* @param stripkop boolean флаг - показывать копейки или нет
* @return String Сумма прописью
*/
public String num2str(boolean stripkop) {
List<String[]> str1 = new List<String[]> {
new String[] {'','один','два','три','четыре','пять','шесть','семь','восемь','девять'},
new String[] {'','одна','две','три','четыре','пять','шесть','семь','восемь','девять'}
};
String[] str100= new String[] {'','сто','двести','триста','четыреста','пятьсот','шестьсот','семьсот', 'восемьсот','девятьсот'};
String[] str11 = new String[] {'','десять','одиннадцать','двенадцать','тринадцать','четырнадцать', 'пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать','двадцать'};
String[] str10 = new String[] {'','десять','двадцать','тридцать','сорок','пятьдесят','шестьдесят', 'семьдесят','восемьдесят','девяносто'};
List<String[]> forms = new List<String[]> {
new String[] {'копейка', 'копейки', 'копеек', '1'},
new String[] {'рубль', 'рубля', 'рублей', '0'},
new String[] {'тысяча', 'тысячи', 'тысяч', '1'},
new String[] {'миллион', 'миллиона', 'миллионов', '0'},
new String[] {'миллиард','миллиарда','миллиардов','0'},
new String[] {'триллион','триллиона','триллионов','0'}
// можно добавлять дальше секстиллионы и т.д.
};
// получаем отдельно рубли и копейки
long rub = amount.intValue();
String[] moi = String.valueOf(amount).split('\\.');
long kop = Long.valueOf(moi[1]);
if (!moi[1].substring(0,1).equals('0') ){// начинается не с нуля
if (kop<10 )
kop *=10;
}
String kops = String.valueOf(kop);
if (kops.length()==1 )
kops = '0'+kops;
long rub_tmp = rub;
// Разбиватель суммы на сегменты по 3 цифры с конца
List<long> segments = new List<long>();
while(rub_tmp > 999) {
long seg = rub_tmp/1000;
segments.add(rub_tmp-(seg*1000));
rub_tmp=seg;
}
segments.add(rub_tmp);

//Collections.reverse(segments);
List<long> reversedSegments = new List<long>();
for (Integer i = segments.size()-1; i>=0; i--) {
reversedSegments.add(segments.get(i));
}

segments = reversedSegments;

// Анализируем сегменты
String o = '';
if (rub == 0) {// если Ноль
o = 'ноль '+morph( 0, forms[1][0],forms[1][1],forms[1][2]);
if (stripkop)
return o;
else
return o +' '+kop+' '+morph(kop,forms[0][0],forms[0][1],forms[0][2]);
}
// Больше нуля
Integer lev = segments.size();
for (Integer i= 0; i<segments.size(); i++ ) { // перебираем сегменты
Integer str1i = Integer.valueOf(String.valueOf(forms[lev][3])); // определяем род
Integer ri = Integer.valueOf(String.valueOf(segments.get(i))); // текущий сегмент
if (ri == 0 && lev>1) { // если сегмент ==0 И не последний уровень(там Units)
lev--;
continue;
}
String rs = String.valueOf(ri); // число в строку
// нормализация
if (rs.length() == 1) rs = '00' + rs; // два нулика в префикс?
if (rs.length() == 2) rs = '0' + rs; // или лучше один?
// получаем циферки для анализа
Integer r1 = Integer.valueOf( rs.substring(0,1) ); //первая цифра
Integer r2 = Integer.valueOf( rs.substring(1,2) ); //вторая
Integer r3 = Integer.valueOf( rs.substring(2,3) ); //третья
Integer r22= Integer.valueOf( rs.substring(1,3) ); //вторая и третья
// Супер-нано-анализатор циферок
if (ri>99) o = o + str100[r1] + ' '; // Сотни
if (r22>20) { // >20
o = o + str10[r2] + ' ';
o = o + str1[str1i][r3] + ' ';
}
else { // <=20
if (r22>9) o = o + str11[r22-9] + ' '; // 10-20
else o = o + str1[str1i][r3] + ' '; // 0-9
}
// Единицы измерения (рубли...)
o = o + morph(ri, forms[lev][0],forms[lev][1],forms[lev][2]) + ' ';
lev--;
}
// Копейки в цифровом виде
if (stripkop) {
o = o.replaceAll(' {2,}', ' ');
}
else {
o = o+''+kops+' '+morph(kop,forms[ 0][ 0],forms[ 0][1],forms[ 0][2]);
o = o.replaceAll(' {2,}', ' ');
}
return o.trim();
}

/**
* Склоняем словоформу
* @param n Long количество объектов
* @param f1 String вариант словоформы для одного объекта
* @param f2 String вариант словоформы для двух объектов
* @param f5 String вариант словоформы для пяти объектов
* @return String правильный вариант словоформы для указанного количества объектов
*/
public static String morph(long n, String f1, String f2, String f5) {
n = math.mod(math.abs(n), 100);
long n1 = math.mod(n, 10);
if (n > 10 && n < 20) return f5;
if (n1 > 1 && n1 < 5) return f2;
if (n1 == 1) return f1;
return f5;
}

// тесты
static testMethod void MoneyConverterRus_test() {

String m1 = '123.89';
MoneyConverterRus mc = new MoneyConverterRus(m1);
String convertedMoney = mc.num2str();
system.assertEquals('сто двадцать три рубля 89 копеек', convertedMoney);
convertedMoney = mc.num2str(true);
system.assertEquals('сто двадцать три рубля', convertedMoney);

Decimal m2 = 123.89;
mc = new MoneyConverterRus(m2);
convertedMoney = mc.num2str();
system.assertEquals('сто двадцать три рубля 89 копеек', convertedMoney);
convertedMoney = mc.num2str(true);
system.assertEquals('сто двадцать три рубля', convertedMoney);

Decimal m3 = 121;
mc = new MoneyConverterRus(m3);
convertedMoney = mc.num2str(true);
system.assertEquals('сто двадцать один рубль', convertedMoney);

Integer m4 = 129;
mc = new MoneyConverterRus(m4);
convertedMoney = mc.num2str(true);
system.assertEquals('сто двадцать девять рублей', convertedMoney);

}

}


Один нюанс! Так как с версии 28 API Salesforce тест методы необходимо выносить в отдельные тест классы, то непосредственная вставка данного куска кода во вновь созданный класс вызовет ошибку: "Save error: Test methods must be in test classes". Поэтому есть два выхода: либо создать отдельный тестовый класс и вынести MoneyConverterRus_test() или поставить в настройках данного класса версию API 27 или ниже. Для обсуждения данной темы приглашаю на форум в тему Перевод цифр в слова