2
0
mirror of https://github.com/frappe/books.git synced 2024-11-12 16:36:27 +00:00

Merge pull request #974 from AbleKSaju/feat-grandtotal-in-words

feat: display grand total amount in words
This commit is contained in:
Akshay 2024-10-15 17:10:31 +05:30 committed by GitHub
commit cb48742d63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 123 additions and 0 deletions

View File

@ -11,5 +11,6 @@ export class PrintSettings extends Doc {
color?: string;
font?: string;
displayLogo?: boolean;
amountInWords?: boolean;
override hidden: HiddenMap = {};
}

View File

@ -114,6 +114,12 @@
"label": "Display Logo in Invoice",
"fieldtype": "Check",
"section": "Customizations"
},
{
"fieldname": "amountInWords",
"label": "Display Amount In Words",
"fieldtype": "Check",
"section": "Customizations"
}
]
}

View File

@ -12,6 +12,7 @@ import {
getSavePath,
showExportInFolder,
} from './ui';
import { Money } from 'pesa';
export type PrintTemplateHint = {
[key: string]: string | PrintTemplateHint | PrintTemplateHint[];
@ -28,6 +29,7 @@ const printSettingsFields = [
'phone',
'address',
'companyName',
'amountInWords',
];
const accountingSettingsFields = ['gstin', 'taxId'];
@ -63,6 +65,10 @@ export async function getPrintTemplatePropValues(
(values.doc as PrintTemplateData).showHSN = showHSN(doc);
}
(values.doc as PrintTemplateData).grandTotalInWords = getGrandTotalInWords(
(doc.grandTotal as Money).float
);
return values;
}
@ -95,6 +101,112 @@ export function getPrintTemplatePropHints(schemaName: string, fyo: Fyo) {
return hints;
}
function getGrandTotalInWords(total: number) {
const formattedTotal = total.toFixed(2);
const [integerPart, decimalPart] = formattedTotal.split('.');
const ones = [
'',
t`One`,
t`Two`,
t`Three`,
t`Four`,
t`Five`,
t`Six`,
t`Seven`,
t`Eight`,
t`Nine`,
];
const teens = [
t`Ten`,
t`Eleven`,
t`Twelve`,
t`Thirteen`,
t`Fourteen`,
t`Fifteen`,
t`Sixteen`,
t`Seventeen`,
t`Eighteen`,
t`Nineteen`,
];
const tens = [
'',
'',
t`Twenty`,
t`Thirty`,
t`Forty`,
t`Fifty`,
t`Sixty`,
t`Seventy`,
t`Eighty`,
t`Ninety`,
];
const scales = ['', t`Thousand`, t`Million`, t`Billion`];
function convertThreeDigitNumber(num: number) {
let result = '';
const hundredDigit = Math.floor(num / 100);
const remainder = num % 100;
if (hundredDigit > 0) {
result += ones[hundredDigit] + ` ${t`Hundred`}`;
}
if (remainder > 0) {
if (hundredDigit > 0) {
result += ` ${t`And`} `;
}
if (remainder < 10) {
result += ones[remainder];
} else if (remainder < 20) {
result += teens[remainder - 10];
} else {
const tensDigit = Math.floor(remainder / 10);
const onesDigit = remainder % 10;
result += tens[tensDigit];
if (onesDigit > 0) {
result += ' ' + ones[onesDigit];
}
}
}
return result;
}
let spelledOutInteger = '';
const integerGroups = integerPart.match(/(\d{1,3})(?=(\d{3})*$)/g) || [];
const groupCount = integerGroups.length;
integerGroups.forEach((group, index) => {
const groupValue = parseInt(group);
if (groupValue > 0) {
const groupText = convertThreeDigitNumber(groupValue);
const groupSuffix = scales[groupCount - index - 1];
spelledOutInteger +=
groupText + (groupSuffix ? ' ' + groupSuffix : '') + ' ';
}
});
spelledOutInteger = spelledOutInteger.trim() || t`Zero`;
let spelledOutDecimal = '';
const decimalCents = parseInt(decimalPart);
if (decimalCents !== 0) {
spelledOutDecimal =
` ${t`and`} ` + convertThreeDigitNumber(decimalCents) + ` ${t`Paisa`}`;
}
return `${spelledOutInteger}${spelledOutDecimal} ${t`only`}`;
}
function showHSN(doc: Doc): boolean {
const items = doc.items;
if (!Array.isArray(items)) {

View File

@ -126,5 +126,9 @@
<h3 class="text-lg font-semibold">Notes</h3>
<p class="mt-4 text-lg whitespace-pre-line">{{ doc.terms }}</p>
</section>
<div v-if="print.amountInWords" class="flex justify-end mt-10">
<h3 class="text-lg font-semibold mr-2">Grand Total In Words:</h3>
<p>{{doc.grandTotalInWords}}</p>
</div>
</footer>
</main>