Słownie złotych:

Zgodnie z obowiązującym w Polsce prawem, faktura powinna zawierać przynajmniej:
(…)
– kwotę należności ogółem wraz z należnym podatkiem (brutto), wyrażoną cyframi i słownie.

Tak podaje wikipedia. Dziś zajmę się pogrubionym fragmentem cytatu: zamianą kwoty pieniędzy określonej waluty na jej słowną reprezentację. Zakładam, że wystawiając faktury na kwoty powyżej 999999,99 (czyli milion i więcej) jednostek monetarnych, użytkownik – z nieopisaną satysfakcją – wpisze kwotę słownie własnoręcznie.

Liczby => słowa
Na początek zajmę się zamianą liczb całkowitych na słowa. Zastosuję poniższy algorytm:
1. podział liczby na grupy cyfr (przykładowo liczbę 123456 podzielę na 123 i 456, a 12345 – na 12 i 345),
2. zamiana bardziej znaczącej grupy na słowa,
3. dodanie słowa “tysiące” w odpowiedniej formie,
4. zamiana mniej znaczącej grupy na słowa.

W implementacji algorytmu przyda mi się extension method zamieniająca liczbę na wektor jej cyfr:

private static int[] ToDigitArray(this int number)
{
	// przykładowo zamienia 123456 na { 6, 5, 4, 3, 2, 1 } 
	// (najmniej znacząca cyfra -> indeks 0)

	string str = number.ToString();
	int[] digitArray = new int[str.Length];

	for (int i = 0; i != digitArray.Length; i++)
		digitArray[i] = int.Parse(str[str.Length - 1 - i].ToString());

	return digitArray;
}

Metody zamieniające kolejne cyfry na słowa wyglądają, tu fajerwerków nie będzie, tak:
(zamienia jedności:)

private static string ConvertUnits(int number)
{
	switch (number)
	{
		case 1: return "jeden";
		case 2: return "dwa";
		case 3: return "trzy";
		case 4: return "cztery";
		case 5: return "pięć";
		case 6: return "sześć";
		case 7: return "siedem";
		case 8: return "osiem";
		case 9: return "dziewięć";
		default: return string.Empty;
	}
}

(zamienia “nastki”:)

private static string ConvertTeens(int number)
{
	switch (number)
	{
		case 11: return "jedenaście";
		case 12: return "dwanaście";
		case 13: return "trzynaście";
		case 14: return "czternaście";
		case 15: return "pięnaście";
		case 16: return "szesnaście";
		case 17: return "siedemnaście";
		case 18: return "osiemnaście";
		case 19: return "dziewiętnaście";
		default: return string.Empty;
	}
}

(zamienia dziesiątki:)

private static string ConvertTens(int number)
{
	switch (number)
	{
		case 1: return "dziesięć";
		case 2: return "dwadzieścia";
		case 3: return "trzydzieści";
		case 4: return "czterdzieści";
		case 5: return "pięćdziesiąt";
		case 6: return "sześćdziesiąt";
		case 7: return "siedemdziesiąt";
		case 8: return "osiemdziesiąt";
		case 9: return "dziewięćdziesiąt";
		default: return string.Empty;
	}
}

(zamienia setki:)

private static string ConvertHundreds(int number)
{
	switch (number)
	{
		case 1: return "sto";
		case 2: return "dwieście";
		case 3: return "trzysta";
		case 4: return "czterysta";
		case 5: return "pięćset";
		case 6: return "sześćset";
		case 7: return "siedemset";
		case 8: return "osiemset";
		case 9: return "dziewięćset";
		default: return string.Empty;
	}
}

Zamiana grup cyfr (o których mowa w pkt. 1 algorytmu) na słowa wygląda więc tak,

private static string GetHundreds(int[] numberArray)
{
	string result;

	switch (numberArray.Length)
	{
		case 1:	// np. 9
			result = ConvertUnits(numberArray[0]);
			break;
		case 2: // np. 99
			if (numberArray[1] == 1) // np. 91
				result = ConvertTeens(10 + numberArray[0]);
			else
				result = ConvertTens(numberArray[1]) + " " + ConvertUnits(numberArray[0]);
			break;
		case 3: // np. 999
			if (numberArray[1] == 1) // np. 919
				result = ConvertHundreds(numberArray[2]) + " " + ConvertTeens(10 + numberArray[0]);
			else
				result = ConvertHundreds(numberArray[2]) + " " + ConvertTens(numberArray[1]) + " " + ConvertUnits(numberArray[0]);
			break;
		default:
			result = string.Empty;
			break;
	}

	return result;
}

metoda używana przy trzecim kroku algorytmu – tak,

private static string GetGrammaticalThousands(int lastDigit)
{
	switch (lastDigit)
	{
		case 1:
			return "tysiąc";
		case 2:
		case 3:
		case 4:
			return "tysiące";
		default:
			return "tysięcy";
	}
}

a cały algorytm – tak (number to liczba pobrana w argumencie):

string result = string.Empty;
int[] digitArray = number.ToDigitArray();

if (digitArray.Length == 6) // e.g. 123456
{
	int[] moreSignificantDigits = { digitArray[3], digitArray[4], digitArray[5] }; // e.g. { 3, 2, 1 }
	int[] lessSignificantDigits = { digitArray[0], digitArray[1], digitArray[2] }; // e.g. { 6, 5, 4 }

	if (digitArray[4] == 1) // e.g. 919999
		result = GetHundreds(moreSignificantDigits) + " tysięcy " + GetHundreds(lessSignificantDigits);
	else
		result = GetHundreds(moreSignificantDigits) + " " + GetGrammaticalThousands(digitArray[3]) + " " + GetHundreds(lessSignificantDigits);
}
else if (digitArray.Length == 5) // e.g. 12345
{
	int[] moreSignificantDigits = { digitArray[3], digitArray[4] }; // e.g. { 2, 1 }
	int[] lessSignificantDigits = { digitArray[0], digitArray[1], digitArray[2] }; // e.g. { 5, 4, 3 }

	if (digitArray[4] == 1) // 91999
		result = GetHundreds(moreSignificantDigits) + " tysięcy " + GetHundreds(lessSignificantDigits);
	else
		result = GetHundreds(moreSignificantDigits) + " " + GetGrammaticalThousands(digitArray[3]) + " " + GetHundreds(lessSignificantDigits);
}
else if (digitArray.Length == 4) // e.g. 1234
{
	int[] moreSignificantDigits = { digitArray[3] }; // e.g. { 4 }
	int[] lessSignificantDigits = { digitArray[0], digitArray[1], digitArray[2] }; // e.g. { 3, 2, 1 }

	result = GetHundreds(moreSignificantDigits) + " " + GetGrammaticalThousands(digitArray[3]) + " " + GetHundreds(lessSignificantDigits);
}
else
{
	result = GetHundreds(digitArray);
}

Dla number == 0 metoda konwertująca (o niezbyt zaskakującej nazwie Convert) zwraca “zero”, a dla number >= 1,000,000 – “wpisz ręcznie”.

Kwota => słowa
Zamiana kwoty pieniężnej na słowa to już sprawa prosta:

public static string ConvertMoney(decimal amount, string currency)
{
	string result = Convert((int)amount); // konwersja części całkowitej
	result += " " + currency + " "; // dodanie waluty
	decimal decimals = (amount - decimal.Floor(amount)) * 100; // wyłuskanie części ułamkowej
	result += ((int)decimals).ToString(); // dodanie części ułamkowej
	result += "/100";

	return result;
}

W ten sposób kwota 123456,78 PLN jest zamieniana na: “sto dwadzieścia trzy tysiące czterysta pięćdziesiąt sześć PLN 78/100”.

Na koniec małe usprawiedliwienie. Nie można zaprzeczyć, że wpis jest rażąco lakoniczy i, co tu kryć, pisany na szybko. Jest to spowodowane tym, że aktualnie się przeprowadzam. Z jednego miejsca zamieszkania – w trzy (szczegóły tej sytuacji ciężko byłoby opisać w kilku słowach…). Dodatkowo, post ten napisałem na kilka dni przed jego opublikowaniem, ponieważ w chwili, gdy czytelnik beznamiętnie przewija kolejne kawały zaprezentowanego przeze mnie kodu, ja beztrosko bujam się na falach któregoś z jezior mazurskich. Taki natłok wydarzeń uniemożliwia mi regularne blogowanie, dlatego czeka mnie przynajmniej tydzień “urlopu” od konkursu.

1 thought on “Słownie złotych:”

Comments are closed.