/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * * or via info@compiere.org or http://www.compiere.org/license.html * *****************************************************************************/ package org.compiere.util; import java.math.BigDecimal; import java.math.RoundingMode; import static java.math.BigDecimal.valueOf; import java.util.HashMap; import java.util.Map; /** * Amount in Words for Croatian * * @author Mislav Kašner, algoritam: Domagoj Klepac * @version $Id: AmtInWords_HR.java,v 1.0 2009/03/28 */ public class AmtInWords_HR implements AmtInWords { /** * AmtInWords_HR */ public AmtInWords_HR () { super (); } // AmtInWords_HR /** * Konfiguracijski parametri. */ // odabrati prvu varijantu za veznik "i" prije jedinica - gramatika // dozvoljava obje varijante // primjer: "sto dvadeset i tri kune", "sto dvadeset tri kune" // private final static String I = " i "; private final static String I = ""; // ne preporučujem jer nije gramatički ispravno, ali razmak se //ukinuti za // // nepismenu verziju koju obično viđamo na raznim POS-ovima; ali u //tom // // slučaju I iznad treba također biti "". // primjer: "sto dvadeset tri kune i nula lipa", // "stodvadesettrikuneinulalipa" private final static String RAZMAK = " "; // private final static String RAZMAK = ""; /** * Vraća dani broj napisan slovima, u valuti (kune). Broj mora biti između * -10^100 i 10^100. Bit će automatski zaokružen na dvije decimale. * * @param broj * @return broj napisan slovima */ public static String slovimaUValuti(BigDecimal broj) { StringBuilder rezultat = new StringBuilder(); // zaokruži na dvije decimale broj = broj.setScale(2, RoundingMode.HALF_EVEN); // kune (dio bez decimala) BigDecimal kune = broj.setScale(0, RoundingMode.DOWN); rezultat.append(slovima(kune, "kuna", false, false)); rezultat.append(RAZMAK + "i" + RAZMAK); // lipe (samo decimale) BigDecimal lipe = broj.movePointRight(2).remainder(valueOf(100)); rezultat.append(slovima(lipe, "lipa", false, false)); return rezultat.toString(); } /** * Vraća dani broj napisan slovima. Broj mora biti između -10^100 i 10^100. * Decimale se ignoriraju (dakle režu, a ne zaokružuju). * * @param broj * @return broj napisan slovima */ public static StringBuilder slovima(BigDecimal broj) { return slovima(broj, null); } /** * Vraća dani broj napisan slovima. Broj mora biti između -10^100 i 10^100. * Decimale se ignoriraju (dakle režu, a ne zaokružuju). * * @param broj * @param jedinica u nominativu ("kuna", "lipa", "tisuća", "milijun"); može * biti null * @return broj napisan slovima */ public static StringBuilder slovima(BigDecimal broj, String jedinica) { return slovima(broj, jedinica, false, false); } /** * Privatne i cacheirane varijable - pune se samo kod prvog instanciranja * klase. */ private final static BigDecimal googol = valueOf(10).pow(100); private final static BigDecimal dvadeset = valueOf(20); private final static Map brojevi = new HashMap(39); private final static Map potencije = new HashMap(27); private final static String[][] nominativi = { { "da", "a", "a", "e", "i" }, { "a", "u", "a", "e", "a" }, { "n", "n", "n", "na", "na" }, }; // statički blok za napuniti hashmapove kod učitavanja klase static { brojevi.put(-1, "jedna"); brojevi.put(-2, "dvije"); // jedinice brojevi.put(0, "nula"); brojevi.put(1, "jedan"); brojevi.put(2, "dva"); brojevi.put(3, "tri"); brojevi.put(4, "četiri"); brojevi.put(5, "pet"); brojevi.put(6, "šest"); brojevi.put(7, "sedam"); brojevi.put(8, "osam"); brojevi.put(9, "devet"); // desetice brojevi.put(10, "deset"); brojevi.put(11, "jedanaest"); brojevi.put(12, "dvanaest"); brojevi.put(13, "trinaest"); brojevi.put(14, "četrnaest"); brojevi.put(15, "petnaest"); brojevi.put(16, "šesnaest"); brojevi.put(17, "sedamnaest"); brojevi.put(18, "osamnaest"); brojevi.put(19, "devetnaest"); // desetke brojevi.put(20, "dvadeset"); brojevi.put(30, "trideset"); brojevi.put(40, "četrdeset"); brojevi.put(50, "pedeset"); brojevi.put(60, "šezdeset"); brojevi.put(70, "sedamdeset"); brojevi.put(80, "osamdeset"); brojevi.put(90, "devedeset"); // stotke brojevi.put(100, "sto"); brojevi.put(200, "dvjesto"); brojevi.put(300, "tristo"); brojevi.put(400, "četristo"); brojevi.put(500, "petsto"); brojevi.put(600, "šesto"); brojevi.put(700, "sedamsto"); brojevi.put(800, "osamsto"); brojevi.put(900, "devetsto"); // potencije; 10^3 je tisuća, 10^6 milijun, itd. potencije.put(3, "tisuća"); potencije.put(6, "milijun"); potencije.put(9, "milijarda"); potencije.put(12, "bilijun"); potencije.put(15, "bilijarda"); potencije.put(18, "trilijun"); potencije.put(21, "trilijarda"); potencije.put(24, "kvatrilijun"); potencije.put(27, "kvatrilijarda"); potencije.put(30, "kvintilijun"); potencije.put(33, "kvintilijarda"); potencije.put(36, "sekstilijun"); potencije.put(39, "sekstilijarda"); potencije.put(42, "septilijun"); potencije.put(45, "septilijarda"); potencije.put(48, "oktilijun"); potencije.put(51, "oktilijarda"); potencije.put(54, "nonilijun"); potencije.put(57, "nonilijarda"); potencije.put(60, "decilijun"); potencije.put(63, "decilijarda"); potencije.put(66, "undecilijun"); potencije.put(69, "undecilijarda"); potencije.put(72, "duodecilijun"); potencije.put(75, "duodecilijarda"); potencije.put(78, "tridecilijun"); potencije.put(81, "tridecilijarda"); potencije.put(84, "kvatridecilijun"); potencije.put(87, "kvatridecilijarda"); potencije.put(90, "kvindecilijun"); potencije.put(93, "kvindecilijarda"); potencije.put(96, "seksdecilijun"); potencije.put(99, "seksdecilijarda"); potencije.put(100, "googol"); } /** * Vraća dani broj napisan slovima. Broj mora biti između -10^66 i 10^66. * Decimale se ignoriraju (dakle režu, a ne zaokružuju). * * @param broj * @param jedinica u nominativu ("kuna", "lipa", "tisuća", "milijun"); može * biti null * @param jediniceBezBroja false kada je jedinica valuta, a ne brojčana * jedinica * @param rekurzija true kad ova metoda poziva samu sebe * @return broj napisan slovima */ private static StringBuilder slovima(BigDecimal broj, String jedinica, boolean jediniceBezBroja, boolean rekurzija) { StringBuilder rezultat = new StringBuilder(); // za negativne iznose - stavi minus i radi dalje s pozitivnim //brojem if (broj.compareTo(valueOf(0)) == -1) { rezultat.append("minus" + RAZMAK); broj = broj.multiply(valueOf(-1)); } // provjera ulaznog parametra if (googol.compareTo(broj) < 1) { // googol je izuzetak od pravila jer potencija nije djeljiva sa //tri // pa ga i vrati kao izuzetak i nemoj dozvoliti veće brojeve if (googol.compareTo(broj) == 0) { rezultat.append(potencije.get(100)); return rezultat; } throw new IllegalArgumentException("nepoznat broj - nije između-googol i googol (-10^100 i 10^100)"); } int potencija = potencija(broj); // glavna petlja za logiku pisanja brojeva - poziva samu sebe i //sve // svodi na 1, <20, <100, <1000, 1 ? I.substring(1) : I); // jediniceSuValuta i ova logika služi tome da se "jedna kuna i // jedna lipa" ne napišu kao "kunu i lipu"; ali da bude npr. // "tisuću" a ne "jedna tisuća" rezultat.append(dekliniraj(1, jedinica, jediniceBezBroja)); } else if (broj.compareTo(dvadeset) < 1) { // brojevi manji od 20 su izuzeci i sve čupamo direktno iz mape // "i" ispred jedinica - ista logika kao i gore, s tim da je // još dodana provjera koja osigurava da se "i" dodaje samo za // jedinice a ne i veće brojeve if (!jediniceBezBroja && rekurzija && broj.compareTo(valueOf(10)) == -1) rezultat.append(I.length() > 1 ? I.substring(1) : I); rezultat.append(dekliniraj(broj.intValue(), jedinica, false)); } else if (potencija < 2) { // brojevi od 20 do 99 // .movePointRight je brži način potenciranja ako se radi // potenciranje broja 10; ostatak je u ovom slučaju zadnja // znamenka (npr. 4 za 34) int ostatak = broj.remainder(valueOf(1).movePointRight(potencija)).intValue(); // "dvadeset", "trideset"... rezultat.append(brojevi.get(broj.subtract(valueOf(ostatak)).intValue())); if (ostatak > 0) { // "i" ispred jedinica - "dvadeset i jedan" ili "dvadeset //jedan" rezultat.append(I.length() > 0 ? I : RAZMAK); // deklinirani ostatak i jedinica (kuna, lipa, tisuća...) rezultat.append(dekliniraj(ostatak, jedinica, false)); } else if (jedinica != null) { // ako nema ostatka, onda samo stavi "kuna", "lipa", //"tisuća"... rezultat.append(RAZMAK); rezultat.append(jedinica); } } else if (potencija < 3) { // brojevi od 100 do 999 BigDecimal ostatak = broj.remainder(valueOf(1).movePointRight(potencija)); if (ostatak.compareTo(valueOf(0)) == 1) { // "sto", "dvjesto"... rezultat.append(brojevi.get(broj.subtract(ostatak).intValue())); rezultat.append(RAZMAK); rezultat.append(slovima(ostatak, jedinica, jediniceBezBroja, true)); } else { rezultat.append(dekliniraj(broj.subtract(ostatak).intValue(), jedinica, false)); } } else { // svi ostali brojevi // prva niža potencija djeljiva sa tri (dakle ona za koju //postoji // ekvivalent u mapi s potencijama) int punaPotencija = potencija - potencija % 3; BigDecimal ostatak = broj.remainder(valueOf(1).movePointRight(punaPotencija)); rezultat.append(slovima(broj.subtract(ostatak).movePointLeft(punaPotencija), potencije.get(punaPotencija), true, true)); if (ostatak.compareTo(valueOf(0)) == 1) { rezultat.append(RAZMAK); rezultat.append(slovima(ostatak, jedinica, jediniceBezBroja, true)); } else if (jedinica != null) { rezultat.append(RAZMAK); rezultat.append(jedinica); } } return rezultat; } /** * Vraća broj za jedan manji od broja znamenki zadanog broja. * * @param broj * @return */ private static int potencija(BigDecimal broj) { int potencija = 0; BigDecimal deset = valueOf(10); // varijanta na: je li broj veći ili jednak deset - ako je, //povećaj // potenciju; je li broj veći ili jednak od sto... for (BigDecimal i = deset; i.compareTo(broj) < 1; i = i.multiply(deset)) potencija++; return potencija; } /** * Deklinira imenice koje završavaju na -a ili -n u nominativu. Ako je * nominativ null, onda vraća samo broj u muškom rodu. * * @param znamenka 0-20 * @param nominativ ("kuna", "lipa", "tisuća", "milijun"); može biti null * @param jedinceBezBroja ako je ovaj flag dignut, onda se preskače broj i * vraća se samo jedinica, npr. "milijarda" umjesto "jedna milijarda" * @return broj i imenicu dekliniranu s obzirom na broj ispred nje * @throws IllegalArgumentException ako nominativ ne završava na -n ili -a */ private static String dekliniraj(int znamenka, String nominativ, boolean jedinceBezBroja) { if (nominativ == null) // nema nominativa - vrati samo broj u muškom rodu return brojevi.get(znamenka); else { // ovisno o završetku nominativa pronađi podatke u arrayu //nominativi for (int i = 0; i < nominativi.length; i++) { if (nominativ.endsWith(nominativi[i][0])) { String korijen = nominativ.substring(0, nominativ.length() - 1); String broj = null; // je li nominativ u ženskom rodu? if (nominativ.endsWith("a")) broj = brojevi.get(znamenka * -1); // ako nema ženske varijante broja, onda je broj isti kao i // kod muškog roda if (broj == null) broj = brojevi.get(znamenka); // i sada ide deklinacija if (znamenka == 1) { if (jedinceBezBroja) { return korijen + nominativi[i][1]; } else { return broj + RAZMAK + korijen + nominativi[i][2]; } } else if (znamenka > 1 && znamenka < 5) { return broj + RAZMAK + korijen + nominativi[i][3]; } else { return broj + RAZMAK + korijen + nominativi[i][4]; } } } } throw new IllegalArgumentException("nominativ '" + nominativ + "'ne znam deklinirati!"); } /** * Get Amount in Words * @param amount numeric amount (352.80) * @return amount in words (three*five*two 80/100) * @throws Exception */ @Override public String getAmtInWords (String amount) throws Exception { if (amount == null) return amount; // StringBuilder sb = new StringBuilder (); amount = amount.replace(",", ""); Double iznos = Double.parseDouble(amount); sb.append (slovimaUValuti (BigDecimal.valueOf(Double.valueOf(iznos)))); return sb.toString (); } // getAmtInWords /** * Test Print * @param amt amount */ private void print (String amt) { try { System.out.println(amt + " = " + getAmtInWords(amt)); } catch (Exception e) { e.printStackTrace(); } } // print /** * Test * @param args ignored */ public static void main (String[] args) { AmtInWords_HR aiw = new AmtInWords_HR(); aiw.print("263.52"); aiw.print ("0.23"); aiw.print ("1.23"); aiw.print ("12.345"); aiw.print ("123.45"); aiw.print ("1234.56"); aiw.print ("12345.78"); aiw.print ("123457.89"); aiw.print ("1,234,578.90"); } // main } // AmtInWords_HR