invoices history ready
This commit is contained in:
@@ -13,6 +13,8 @@ class FinancialRepositoryMock {
|
|||||||
totalAmount: 1500.0,
|
totalAmount: 1500.0,
|
||||||
workAmount: 1400.0,
|
workAmount: 1400.0,
|
||||||
addonsAmount: 100.0,
|
addonsAmount: 100.0,
|
||||||
|
invoiceNumber: 'INV-1',
|
||||||
|
issueDate: null,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ class Invoice extends Equatable {
|
|||||||
required this.totalAmount,
|
required this.totalAmount,
|
||||||
required this.workAmount,
|
required this.workAmount,
|
||||||
required this.addonsAmount,
|
required this.addonsAmount,
|
||||||
|
this.invoiceNumber,
|
||||||
|
this.issueDate,
|
||||||
});
|
});
|
||||||
/// Unique identifier.
|
/// Unique identifier.
|
||||||
final String id;
|
final String id;
|
||||||
@@ -57,6 +59,12 @@ class Invoice extends Equatable {
|
|||||||
/// Total amount for addons/extras.
|
/// Total amount for addons/extras.
|
||||||
final double addonsAmount;
|
final double addonsAmount;
|
||||||
|
|
||||||
|
/// Human-readable invoice number.
|
||||||
|
final String? invoiceNumber;
|
||||||
|
|
||||||
|
/// Date when the invoice was issued.
|
||||||
|
final DateTime? issueDate;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => <Object?>[
|
List<Object?> get props => <Object?>[
|
||||||
id,
|
id,
|
||||||
@@ -66,5 +74,7 @@ class Invoice extends Equatable {
|
|||||||
totalAmount,
|
totalAmount,
|
||||||
workAmount,
|
workAmount,
|
||||||
addonsAmount,
|
addonsAmount,
|
||||||
|
invoiceNumber,
|
||||||
|
issueDate,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -44,12 +44,22 @@ class BillingRepositoryImpl implements BillingRepository {
|
|||||||
/// Fetches the history of paid invoices.
|
/// Fetches the history of paid invoices.
|
||||||
@override
|
@override
|
||||||
Future<List<Invoice>> getInvoiceHistory() async {
|
Future<List<Invoice>> getInvoiceHistory() async {
|
||||||
final List<Invoice> invoices = await _financialRepository.getInvoices(
|
final String? businessId =
|
||||||
'current_business',
|
data_connect.ClientSessionStore.instance.session?.business?.id;
|
||||||
);
|
if (businessId == null || businessId.isEmpty) {
|
||||||
return invoices
|
return <Invoice>[];
|
||||||
.where((Invoice i) => i.status == InvoiceStatus.paid)
|
}
|
||||||
.toList();
|
|
||||||
|
final fdc.QueryResult<data_connect.ListInvoicesByBusinessIdData,
|
||||||
|
data_connect.ListInvoicesByBusinessIdVariables> result =
|
||||||
|
await _dataConnect
|
||||||
|
.listInvoicesByBusinessId(
|
||||||
|
businessId: businessId,
|
||||||
|
)
|
||||||
|
.limit(10)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return result.data.invoices.map(_mapInvoice).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches pending invoices (Open or Disputed).
|
/// Fetches pending invoices (Open or Disputed).
|
||||||
@@ -161,6 +171,42 @@ class BillingRepositoryImpl implements BillingRepository {
|
|||||||
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
|
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
|
||||||
return fdc.Timestamp(nanoseconds, seconds);
|
return fdc.Timestamp(nanoseconds, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Invoice _mapInvoice(data_connect.ListInvoicesByBusinessIdInvoices invoice) {
|
||||||
|
return Invoice(
|
||||||
|
id: invoice.id,
|
||||||
|
eventId: invoice.orderId,
|
||||||
|
businessId: invoice.businessId,
|
||||||
|
status: _mapInvoiceStatus(invoice.status),
|
||||||
|
totalAmount: invoice.amount,
|
||||||
|
workAmount: invoice.amount,
|
||||||
|
addonsAmount: invoice.otherCharges ?? 0,
|
||||||
|
invoiceNumber: invoice.invoiceNumber,
|
||||||
|
issueDate: invoice.issueDate.toDateTime(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InvoiceStatus _mapInvoiceStatus(
|
||||||
|
data_connect.EnumValue<data_connect.InvoiceStatus> status,
|
||||||
|
) {
|
||||||
|
if (status is data_connect.Known<data_connect.InvoiceStatus>) {
|
||||||
|
switch (status.value) {
|
||||||
|
case data_connect.InvoiceStatus.PAID:
|
||||||
|
return InvoiceStatus.paid;
|
||||||
|
case data_connect.InvoiceStatus.OVERDUE:
|
||||||
|
return InvoiceStatus.overdue;
|
||||||
|
case data_connect.InvoiceStatus.DISPUTED:
|
||||||
|
return InvoiceStatus.disputed;
|
||||||
|
case data_connect.InvoiceStatus.APPROVED:
|
||||||
|
return InvoiceStatus.verified;
|
||||||
|
case data_connect.InvoiceStatus.PENDING_REVIEW:
|
||||||
|
case data_connect.InvoiceStatus.PENDING:
|
||||||
|
case data_connect.InvoiceStatus.DRAFT:
|
||||||
|
return InvoiceStatus.open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InvoiceStatus.open;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RoleSummary {
|
class _RoleSummary {
|
||||||
|
|||||||
@@ -114,17 +114,21 @@ class BillingBloc extends Bloc<BillingEvent, BillingState> {
|
|||||||
// In a real app, fetches related Event/Business names via ID.
|
// In a real app, fetches related Event/Business names via ID.
|
||||||
// For now, mapping available fields and hardcoding missing UI placeholders.
|
// For now, mapping available fields and hardcoding missing UI placeholders.
|
||||||
// Preserving "Existing Behavior" means we show something.
|
// Preserving "Existing Behavior" means we show something.
|
||||||
|
final String dateLabel = invoice.issueDate == null
|
||||||
|
? '2024-01-24'
|
||||||
|
: invoice.issueDate!.toIso8601String().split('T').first;
|
||||||
|
final String titleLabel = invoice.invoiceNumber ?? invoice.id;
|
||||||
return BillingInvoice(
|
return BillingInvoice(
|
||||||
id: invoice.id,
|
id: titleLabel,
|
||||||
title: 'Invoice #${invoice.id}', // Placeholder as Invoice lacks title
|
title: 'Invoice #${invoice.id}', // Placeholder as Invoice lacks title
|
||||||
locationAddress:
|
locationAddress:
|
||||||
'Location for ${invoice.eventId}', // Placeholder for address
|
'Location for ${invoice.eventId}', // Placeholder for address
|
||||||
clientName: 'Client ${invoice.businessId}', // Placeholder for client name
|
clientName: 'Client ${invoice.businessId}', // Placeholder for client name
|
||||||
date: '2024-01-24', // Placeholder date
|
date: dateLabel,
|
||||||
totalAmount: invoice.totalAmount,
|
totalAmount: invoice.totalAmount,
|
||||||
workersCount: 5, // Placeholder count
|
workersCount: 5, // Placeholder count
|
||||||
totalHours: invoice.workAmount / 25.0, // Estimating hours from amount
|
totalHours: invoice.workAmount / 25.0, // Estimating hours from amount
|
||||||
status: invoice.status.name,
|
status: invoice.status.name.toUpperCase(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class _InvoiceItem extends StatelessWidget {
|
|||||||
'\$${invoice.totalAmount.toStringAsFixed(2)}',
|
'\$${invoice.totalAmount.toStringAsFixed(2)}',
|
||||||
style: UiTypography.body2b.textPrimary,
|
style: UiTypography.body2b.textPrimary,
|
||||||
),
|
),
|
||||||
const _PaidBadge(),
|
_StatusBadge(status: invoice.status),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space2),
|
const SizedBox(width: UiConstants.space2),
|
||||||
@@ -125,21 +125,24 @@ class _InvoiceItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PaidBadge extends StatelessWidget {
|
class _StatusBadge extends StatelessWidget {
|
||||||
const _PaidBadge();
|
const _StatusBadge({required this.status});
|
||||||
|
|
||||||
|
final String status;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final bool isPaid = status.toUpperCase() == 'PAID';
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.tagSuccess,
|
color: isPaid ? UiColors.tagSuccess : UiColors.tagPending,
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
t.client_billing.paid_badge,
|
isPaid ? t.client_billing.paid_badge : t.client_billing.pending_badge,
|
||||||
style: UiTypography.titleUppercase4b.copyWith(
|
style: UiTypography.titleUppercase4b.copyWith(
|
||||||
color: UiColors.iconSuccess,
|
color: isPaid ? UiColors.iconSuccess : UiColors.textWarning,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user