feat(purchases): implement purchase receipt functionality with form and receipt template
- Added `ProductPurchaseComponent` to handle product purchases. - Created `PurchaseReceiptTemplateComponent` for displaying purchase receipt details. - Developed `PurchaseFormComponent` for managing purchase form inputs and submission. - Introduced `ProductChargeRowFormComponent` and `ProductChargeRowFormFieldsComponent` for handling product charge rows in the form. - Implemented `PurchaseReceiptProductRowComponent` for displaying individual product rows in the receipt. - Established API routes for purchase operations in `apiRoutes`. - Integrated suppliers and inventories selection components. - Added utility functions for converting prices to Persian alphabet. - Created a utility for generating full names from name parts. - Enhanced form validation and submission handling in the purchase form. - Updated styles and templates for improved UI/UX.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Persian number words for currency conversion
|
||||
*/
|
||||
const ONES = ['', 'یک', 'دو', 'سه', 'چهار', 'پنج', 'شش', 'هفت', 'هشت', 'نه'];
|
||||
|
||||
const TENS = ['', '', 'بیست', 'سی', 'چهل', 'پنجاه', 'شصت', 'هفتاد', 'هشتاد', 'نود'];
|
||||
|
||||
const HUNDREDS = ['', 'یکصد', 'دویست', 'سیصد', 'چهارصد', 'پانصد', 'ششصد', 'هفتصد', 'هشتصد', 'نهصد'];
|
||||
|
||||
const SCALE = ['', 'هزار', 'میلیون', 'میلیارد', 'تریلیون', 'کوادریلیون'];
|
||||
|
||||
const TEENS = [
|
||||
'ده',
|
||||
'یازده',
|
||||
'دوازده',
|
||||
'سیزده',
|
||||
'چهارده',
|
||||
'پانزده',
|
||||
'شانزده',
|
||||
'هفده',
|
||||
'هجده',
|
||||
'نوزده',
|
||||
];
|
||||
|
||||
/**
|
||||
* Convert a three-digit number to Persian words
|
||||
*/
|
||||
function convertThreeDigits(num: number): string {
|
||||
if (num === 0) return '';
|
||||
|
||||
const hundred = Math.floor(num / 100);
|
||||
const remainder = num % 100;
|
||||
const ten = Math.floor(remainder / 10);
|
||||
const one = remainder % 10;
|
||||
|
||||
let result = '';
|
||||
|
||||
if (hundred > 0) {
|
||||
result += HUNDREDS[hundred];
|
||||
}
|
||||
|
||||
if (remainder >= 10 && remainder <= 19) {
|
||||
if (result) result += ' و ';
|
||||
result += TEENS[remainder - 10];
|
||||
} else {
|
||||
if (ten > 0) {
|
||||
if (result) result += ' و ';
|
||||
result += TENS[ten];
|
||||
}
|
||||
if (one > 0) {
|
||||
if (result) result += ' و ';
|
||||
result += ONES[one];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a number to Persian words for Rial currency
|
||||
* @param price - The price as a number
|
||||
* @returns Persian text representation of the price in Rials
|
||||
* @example
|
||||
* getPriceInPersianAlphabet(1500) // "هزار و پانصد ریال"
|
||||
* getPriceInPersianAlphabet(1000000) // "یک میلیون ریال"
|
||||
*/
|
||||
export function getPriceInPersianAlphabet(price: number): string {
|
||||
if (price === 0) return 'صفر ریال';
|
||||
if (price < 0) return 'منفی ' + getPriceInPersianAlphabet(-price);
|
||||
|
||||
const parts: string[] = [];
|
||||
let scaleIndex = 0;
|
||||
|
||||
while (price > 0 && scaleIndex < SCALE.length) {
|
||||
const threeDigits = price % 1000;
|
||||
|
||||
if (threeDigits > 0) {
|
||||
const threeDigitWords = convertThreeDigits(threeDigits);
|
||||
if (SCALE[scaleIndex]) {
|
||||
parts.unshift(threeDigitWords + ' ' + SCALE[scaleIndex]);
|
||||
} else {
|
||||
parts.unshift(threeDigitWords);
|
||||
}
|
||||
}
|
||||
|
||||
price = Math.floor(price / 1000);
|
||||
scaleIndex++;
|
||||
}
|
||||
|
||||
return parts.join(' و ') + ' ریال';
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
export * from './currency';
|
||||
export * from './name';
|
||||
export * from './time';
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
export interface NameParts {
|
||||
gender?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full name from name parts
|
||||
* @param nameParts - Object containing optional gender, firstName, and lastName
|
||||
* @returns Full name string with parts separated by space
|
||||
*/
|
||||
export function getFullName(nameParts: NameParts): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (nameParts.gender) {
|
||||
parts.push(nameParts.gender);
|
||||
}
|
||||
|
||||
if (nameParts.firstName) {
|
||||
parts.push(nameParts.firstName);
|
||||
}
|
||||
|
||||
if (nameParts.lastName) {
|
||||
parts.push(nameParts.lastName);
|
||||
}
|
||||
|
||||
return parts.join(' ').trim();
|
||||
}
|
||||
Reference in New Issue
Block a user