Done Category, Product Catalogue, Customer, Vendor, Purchase Invoice Module

This commit is contained in:
2026-08-28 11:30:18 +05:30
parent 0d13833679
commit 189f49ed07
118 changed files with 12624 additions and 4004 deletions

View File

@@ -13,30 +13,30 @@ class UpcomingDuesWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final walletsState = ref.watch(walletProvider);
if (!walletsState.hasValue || walletsState.value == null) {
return const SizedBox.shrink();
}
final wallets = walletsState.value!;
// Filter for payables that have a due date/amount
final payables = wallets.where((w) => w.nature == 'PAYABLES').toList();
if (payables.isEmpty) {
return const SizedBox.shrink();
}
final now = DateTime.now();
// Calculate dues
final List<Map<String, dynamic>> dues = [];
for (var w in payables) {
double dueAmount = 0;
DateTime? dueDate;
String dueLabel = 'Upcoming Due';
if (w.subNature == 'CREDIT_CARD' || w.subNature == 'OD_LIMIT') {
// Balance is negative if we owe money
if (w.balance < 0) {
@@ -45,11 +45,11 @@ class UpcomingDuesWidget extends ConsumerWidget {
} else if (w.subNature == 'LOAN_EMI' || w.subNature == 'POLICY_PREMIUM') {
dueAmount = w.fixedAmount ?? 0;
}
if (dueAmount > 0 && w.cycleDate != null) {
int year = now.year;
int month = now.month;
// Find next due date based on cycle
if (w.paymentCycle == 'MONTHLY' || w.paymentCycle == null) {
if (now.day > w.cycleDate!) {
@@ -63,18 +63,20 @@ class UpcomingDuesWidget extends ConsumerWidget {
} else if (w.paymentCycle == 'YEARLY') {
// Assume cycleDate is day of current month, this is simplistic
if (now.day > w.cycleDate!) {
year++;
year++;
}
}
// Handle end of month issues (e.g. Feb 30th)
int maxDays = DateTime(year, month + 1, 0).day;
int day = w.cycleDate! > maxDays ? maxDays : w.cycleDate!;
dueDate = DateTime(year, month, day);
int daysLeft = dueDate.difference(DateTime(now.year, now.month, now.day)).inDays;
int daysLeft = dueDate
.difference(DateTime(now.year, now.month, now.day))
.inDays;
if (daysLeft == 0) {
dueLabel = 'Due Today';
} else if (daysLeft == 1) {
@@ -82,7 +84,7 @@ class UpcomingDuesWidget extends ConsumerWidget {
} else {
dueLabel = 'Due in $daysLeft days';
}
dues.add({
'wallet': w,
'amount': dueAmount,
@@ -92,25 +94,34 @@ class UpcomingDuesWidget extends ConsumerWidget {
});
}
}
if (dues.isEmpty) {
return const SizedBox.shrink();
}
// Sort by nearest due date
dues.sort((a, b) => (a['daysLeft'] as int).compareTo(b['daysLeft'] as int));
// Only show top 3 dues
final displayDues = dues.take(3).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(LucideIcons.calendarClock, color: Color(0xFF6C63FF), size: 20),
const Icon(
LucideIcons.calendarClock,
color: Color(0xFF6C63FF),
size: 20,
),
const SizedBox(width: 8),
Text('Upcoming Dues', style: Theme.of(context).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold)),
Text(
'Upcoming Dues',
style: Theme.of(
context,
).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 12),
@@ -120,18 +131,33 @@ class UpcomingDuesWidget extends ConsumerWidget {
final dueDate = due['dueDate'] as DateTime;
final label = due['label'] as String;
final daysLeft = due['daysLeft'] as int;
final isUrgent = daysLeft <= 3;
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isUrgent ? (isDark ? Colors.red.withOpacity(0.2) : Colors.red.shade50) : Theme.of(context).cardColor,
color: isUrgent
? (isDark ? Colors.red.withOpacity(0.2) : Colors.red.shade50)
: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: isUrgent ? (isDark ? Colors.red.withOpacity(0.5) : Colors.red.shade200) : Theme.of(context).dividerColor.withOpacity(isDark ? 0.1 : 0.2)),
border: Border.all(
color: isUrgent
? (isDark
? Colors.red.withOpacity(0.5)
: Colors.red.shade200)
: Theme.of(
context,
).dividerColor.withOpacity(isDark ? 0.1 : 0.2),
),
boxShadow: [
if (!isUrgent) BoxShadow(color: Colors.black.withOpacity(isDark ? 0.2 : 0.02), blurRadius: 8, offset: const Offset(0, 2))
if (!isUrgent)
BoxShadow(
color: Colors.black.withOpacity(isDark ? 0.2 : 0.02),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Row(
@@ -139,13 +165,22 @@ class UpcomingDuesWidget extends ConsumerWidget {
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: (isUrgent ? Colors.red : NatureColors.getColor('PAYABLES')).withOpacity(0.1),
color:
(isUrgent
? Colors.red
: NatureColors.getColor('PAYABLES'))
.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(
w.subNature == 'CREDIT_CARD' ? LucideIcons.creditCard :
(w.subNature == 'LOAN_EMI' ? LucideIcons.home : LucideIcons.fileText),
color: isUrgent ? Colors.red : NatureColors.getColor('PAYABLES'),
w.subNature == 'CREDIT_CARD'
? LucideIcons.creditCard
: (w.subNature == 'LOAN_EMI'
? LucideIcons.home
: LucideIcons.fileText),
color: isUrgent
? Colors.red
: NatureColors.getColor('PAYABLES'),
size: 20,
),
),
@@ -154,14 +189,25 @@ class UpcomingDuesWidget extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(w.name, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15, color: Theme.of(context).textTheme.bodyLarge?.color)),
Text(
w.name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Theme.of(context).textTheme.bodyLarge?.color,
),
),
const SizedBox(height: 4),
Text(
'$label${DateFormat('MMM dd').format(dueDate)}',
style: TextStyle(
color: isUrgent ? Colors.red.shade700 : Colors.grey.shade600,
color: isUrgent
? Colors.red.shade700
: Colors.grey.shade600,
fontSize: 12,
fontWeight: isUrgent ? FontWeight.bold : FontWeight.normal
fontWeight: isUrgent
? FontWeight.bold
: FontWeight.normal,
),
),
],
@@ -170,9 +216,11 @@ class UpcomingDuesWidget extends ConsumerWidget {
Text(
'Rs. ${amount.toStringAsFixed(0)}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold,
fontSize: 16,
color: isUrgent ? (isDark ? Colors.redAccent : Colors.red.shade700) : Theme.of(context).textTheme.bodyLarge?.color
color: isUrgent
? (isDark ? Colors.redAccent : Colors.red.shade700)
: Theme.of(context).textTheme.bodyLarge?.color,
),
),
],