feat: enhance ExpenseDetailModel and ExpenseDetailScreen with additional fields and improved UI structure
This commit is contained in:
parent
8edd189479
commit
5dd09869ad
@ -2,7 +2,7 @@
|
|||||||
class ExpenseDetailModel {
|
class ExpenseDetailModel {
|
||||||
final String id;
|
final String id;
|
||||||
final Project project;
|
final Project project;
|
||||||
final ExpensesType expensesType;
|
final ExpensesType expensesType;
|
||||||
final PaymentMode paymentMode;
|
final PaymentMode paymentMode;
|
||||||
final Person paidBy;
|
final Person paidBy;
|
||||||
final Person createdBy;
|
final Person createdBy;
|
||||||
@ -13,18 +13,25 @@ class ExpenseDetailModel {
|
|||||||
final String createdAt;
|
final String createdAt;
|
||||||
final String supplerName;
|
final String supplerName;
|
||||||
final double amount;
|
final double amount;
|
||||||
|
final double? baseAmount;
|
||||||
|
final double? taxAmount;
|
||||||
|
final double? tdsPercentage;
|
||||||
final ExpenseStatus status;
|
final ExpenseStatus status;
|
||||||
final List<ExpenseStatus> nextStatus;
|
final List nextStatus;
|
||||||
final bool preApproved;
|
final bool preApproved;
|
||||||
final String transactionId;
|
final String transactionId;
|
||||||
final String description;
|
final String description;
|
||||||
final String location;
|
final String location;
|
||||||
|
final Currency? currency;
|
||||||
final List<ExpenseDocument> documents;
|
final List<ExpenseDocument> documents;
|
||||||
final List<ExpenseLog> expenseLogs;
|
final List<ExpenseLog> expenseLogs;
|
||||||
|
|
||||||
final String? gstNumber;
|
final String? gstNumber;
|
||||||
final int noOfPersons;
|
final int? noOfPersons;
|
||||||
final bool isActive;
|
final bool isActive;
|
||||||
final dynamic expensesReimburse;
|
final dynamic expensesReimburse;
|
||||||
|
final String? expenseUId;
|
||||||
|
final String? paymentRequestUID;
|
||||||
|
|
||||||
ExpenseDetailModel({
|
ExpenseDetailModel({
|
||||||
required this.id,
|
required this.id,
|
||||||
@ -40,51 +47,87 @@ class ExpenseDetailModel {
|
|||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.supplerName,
|
required this.supplerName,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
|
this.baseAmount,
|
||||||
|
this.taxAmount,
|
||||||
|
this.tdsPercentage,
|
||||||
required this.status,
|
required this.status,
|
||||||
required this.nextStatus,
|
required this.nextStatus,
|
||||||
required this.preApproved,
|
required this.preApproved,
|
||||||
required this.transactionId,
|
required this.transactionId,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.location,
|
required this.location,
|
||||||
|
this.currency,
|
||||||
required this.documents,
|
required this.documents,
|
||||||
required this.expenseLogs,
|
required this.expenseLogs,
|
||||||
this.gstNumber,
|
this.gstNumber,
|
||||||
required this.noOfPersons,
|
this.noOfPersons,
|
||||||
required this.isActive,
|
required this.isActive,
|
||||||
this.expensesReimburse,
|
this.expensesReimburse,
|
||||||
|
this.expenseUId,
|
||||||
|
this.paymentRequestUID,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory ExpenseDetailModel.fromJson(Map<String, dynamic> json) {
|
factory ExpenseDetailModel.fromJson(Map<String, dynamic> json) {
|
||||||
return ExpenseDetailModel(
|
return ExpenseDetailModel(
|
||||||
id: json['id'] ?? '',
|
id: json['id'] ?? '',
|
||||||
project: json['project'] != null ? Project.fromJson(json['project']) : Project.empty(),
|
project: json['project'] != null
|
||||||
expensesType: json['expensesType'] != null
|
? Project.fromJson(json['project'])
|
||||||
? ExpensesType.fromJson(json['expensesType'])
|
: Project.empty(),
|
||||||
|
expensesType: json['expenseCategory'] != null
|
||||||
|
? ExpensesType.fromJson(json['expenseCategory'])
|
||||||
: ExpensesType.empty(),
|
: ExpensesType.empty(),
|
||||||
paymentMode: json['paymentMode'] != null
|
paymentMode: json['paymentMode'] != null
|
||||||
? PaymentMode.fromJson(json['paymentMode'])
|
? PaymentMode.fromJson(json['paymentMode'])
|
||||||
: PaymentMode.empty(),
|
: PaymentMode.empty(),
|
||||||
paidBy: json['paidBy'] != null ? Person.fromJson(json['paidBy']) : Person.empty(),
|
paidBy: json['paidBy'] != null
|
||||||
createdBy: json['createdBy'] != null ? Person.fromJson(json['createdBy']) : Person.empty(),
|
? Person.fromJson(json['paidBy'])
|
||||||
reviewedBy: json['reviewedBy'] != null ? Person.fromJson(json['reviewedBy']) : null,
|
: Person.empty(),
|
||||||
approvedBy: json['approvedBy'] != null ? Person.fromJson(json['approvedBy']) : null,
|
createdBy: json['createdBy'] != null
|
||||||
processedBy: json['processedBy'] != null ? Person.fromJson(json['processedBy']) : null,
|
? Person.fromJson(json['createdBy'])
|
||||||
|
: Person.empty(),
|
||||||
|
reviewedBy: json['reviewedBy'] != null
|
||||||
|
? Person.fromJson(json['reviewedBy'])
|
||||||
|
: null,
|
||||||
|
approvedBy: json['approvedBy'] != null
|
||||||
|
? Person.fromJson(json['approvedBy'])
|
||||||
|
: null,
|
||||||
|
processedBy: json['processedBy'] != null
|
||||||
|
? Person.fromJson(json['processedBy'])
|
||||||
|
: null,
|
||||||
transactionDate: json['transactionDate'] ?? '',
|
transactionDate: json['transactionDate'] ?? '',
|
||||||
createdAt: json['createdAt'] ?? '',
|
createdAt: json['createdAt'] ?? '',
|
||||||
supplerName: json['supplerName'] ?? '',
|
supplerName: json['supplerName'] ?? '',
|
||||||
amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
|
amount: (json['amount'] as num?)?.toDouble() ?? 0.0,
|
||||||
status: json['status'] != null ? ExpenseStatus.fromJson(json['status']) : ExpenseStatus.empty(),
|
baseAmount: (json['baseAmount'] as num?)?.toDouble(),
|
||||||
nextStatus: (json['nextStatus'] as List?)?.map((e) => ExpenseStatus.fromJson(e)).toList() ?? [],
|
taxAmount: (json['taxAmount'] as num?)?.toDouble(),
|
||||||
|
tdsPercentage: (json['tdsPercentage'] as num?)?.toDouble(),
|
||||||
|
status: json['status'] != null
|
||||||
|
? ExpenseStatus.fromJson(json['status'])
|
||||||
|
: ExpenseStatus.empty(),
|
||||||
|
nextStatus: (json['nextStatus'] as List?)
|
||||||
|
?.map((e) => ExpenseStatus.fromJson(e))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
preApproved: json['preApproved'] ?? false,
|
preApproved: json['preApproved'] ?? false,
|
||||||
transactionId: json['transactionId'] ?? '',
|
transactionId: json['transactionId'] ?? '',
|
||||||
description: json['description'] ?? '',
|
description: json['description'] ?? '',
|
||||||
location: json['location'] ?? '',
|
location: json['location'] ?? '',
|
||||||
documents: (json['documents'] as List?)?.map((e) => ExpenseDocument.fromJson(e)).toList() ?? [],
|
currency:
|
||||||
expenseLogs: (json['expenseLogs'] as List?)?.map((e) => ExpenseLog.fromJson(e)).toList() ?? [],
|
json['currency'] != null ? Currency.fromJson(json['currency']) : null,
|
||||||
|
documents: (json['documents'] as List?)
|
||||||
|
?.map((e) => ExpenseDocument.fromJson(e))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
expenseLogs: (json['expenseLogs'] as List?)
|
||||||
|
?.map((e) => ExpenseLog.fromJson(e))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
gstNumber: json['gstNumber']?.toString(),
|
gstNumber: json['gstNumber']?.toString(),
|
||||||
noOfPersons: json['noOfPersons'] ?? 0,
|
noOfPersons: json['noOfPersons'] != null ? json['noOfPersons'] : null,
|
||||||
isActive: json['isActive'] ?? true,
|
isActive: json['isActive'] ?? true,
|
||||||
expensesReimburse: json['expensesReimburse'],
|
expensesReimburse: json['expensesReimburse'],
|
||||||
|
expenseUId: json['expenseUId']?.toString(),
|
||||||
|
paymentRequestUID: json['paymentRequestUID']?.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,20 +137,20 @@ class Project {
|
|||||||
final String id;
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
final String shortName;
|
final String shortName;
|
||||||
final String projectAddress;
|
final String? projectAddress;
|
||||||
final String contactPerson;
|
final String? contactPerson;
|
||||||
final String startDate;
|
final String? startDate;
|
||||||
final String endDate;
|
final String? endDate;
|
||||||
final String projectStatusId;
|
final String projectStatusId;
|
||||||
|
|
||||||
Project({
|
Project({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.shortName,
|
required this.shortName,
|
||||||
required this.projectAddress,
|
this.projectAddress,
|
||||||
required this.contactPerson,
|
this.contactPerson,
|
||||||
required this.startDate,
|
this.startDate,
|
||||||
required this.endDate,
|
this.endDate,
|
||||||
required this.projectStatusId,
|
required this.projectStatusId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -116,10 +159,10 @@ class Project {
|
|||||||
id: json['id'] ?? '',
|
id: json['id'] ?? '',
|
||||||
name: json['name'] ?? '',
|
name: json['name'] ?? '',
|
||||||
shortName: json['shortName'] ?? '',
|
shortName: json['shortName'] ?? '',
|
||||||
projectAddress: json['projectAddress'] ?? '',
|
projectAddress: json['projectAddress'],
|
||||||
contactPerson: json['contactPerson'] ?? '',
|
contactPerson: json['contactPerson'],
|
||||||
startDate: json['startDate'] ?? '',
|
startDate: json['startDate'],
|
||||||
endDate: json['endDate'] ?? '',
|
endDate: json['endDate'],
|
||||||
projectStatusId: json['projectStatusId'] ?? '',
|
projectStatusId: json['projectStatusId'] ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -128,10 +171,6 @@ class Project {
|
|||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
shortName: '',
|
shortName: '',
|
||||||
projectAddress: '',
|
|
||||||
contactPerson: '',
|
|
||||||
startDate: '',
|
|
||||||
endDate: '',
|
|
||||||
projectStatusId: '',
|
projectStatusId: '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -141,12 +180,14 @@ class ExpensesType {
|
|||||||
final String id;
|
final String id;
|
||||||
final String name;
|
final String name;
|
||||||
final bool noOfPersonsRequired;
|
final bool noOfPersonsRequired;
|
||||||
|
final bool isAttachmentRequried;
|
||||||
final String description;
|
final String description;
|
||||||
|
|
||||||
ExpensesType({
|
ExpensesType({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.noOfPersonsRequired,
|
required this.noOfPersonsRequired,
|
||||||
|
required this.isAttachmentRequried,
|
||||||
required this.description,
|
required this.description,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -155,6 +196,7 @@ class ExpensesType {
|
|||||||
id: json['id'] ?? '',
|
id: json['id'] ?? '',
|
||||||
name: json['name'] ?? '',
|
name: json['name'] ?? '',
|
||||||
noOfPersonsRequired: json['noOfPersonsRequired'] ?? false,
|
noOfPersonsRequired: json['noOfPersonsRequired'] ?? false,
|
||||||
|
isAttachmentRequried: json['isAttachmentRequried'] ?? false,
|
||||||
description: json['description'] ?? '',
|
description: json['description'] ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -163,6 +205,7 @@ class ExpensesType {
|
|||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
noOfPersonsRequired: false,
|
noOfPersonsRequired: false,
|
||||||
|
isAttachmentRequried: false,
|
||||||
description: '',
|
description: '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -199,6 +242,7 @@ class Person {
|
|||||||
final String id;
|
final String id;
|
||||||
final String firstName;
|
final String firstName;
|
||||||
final String lastName;
|
final String lastName;
|
||||||
|
final String? email;
|
||||||
final String photo;
|
final String photo;
|
||||||
final String jobRoleId;
|
final String jobRoleId;
|
||||||
final String jobRoleName;
|
final String jobRoleName;
|
||||||
@ -207,6 +251,7 @@ class Person {
|
|||||||
required this.id,
|
required this.id,
|
||||||
required this.firstName,
|
required this.firstName,
|
||||||
required this.lastName,
|
required this.lastName,
|
||||||
|
this.email,
|
||||||
required this.photo,
|
required this.photo,
|
||||||
required this.jobRoleId,
|
required this.jobRoleId,
|
||||||
required this.jobRoleName,
|
required this.jobRoleName,
|
||||||
@ -217,7 +262,8 @@ class Person {
|
|||||||
id: json['id'] ?? '',
|
id: json['id'] ?? '',
|
||||||
firstName: json['firstName'] ?? '',
|
firstName: json['firstName'] ?? '',
|
||||||
lastName: json['lastName'] ?? '',
|
lastName: json['lastName'] ?? '',
|
||||||
photo: json['photo'] is String ? json['photo'] : '',
|
email: json['email'],
|
||||||
|
photo: json['photo'] ?? '',
|
||||||
jobRoleId: json['jobRoleId'] ?? '',
|
jobRoleId: json['jobRoleId'] ?? '',
|
||||||
jobRoleName: json['jobRoleName'] ?? '',
|
jobRoleName: json['jobRoleName'] ?? '',
|
||||||
);
|
);
|
||||||
@ -227,6 +273,7 @@ class Person {
|
|||||||
id: '',
|
id: '',
|
||||||
firstName: '',
|
firstName: '',
|
||||||
lastName: '',
|
lastName: '',
|
||||||
|
email: null,
|
||||||
photo: '',
|
photo: '',
|
||||||
jobRoleId: '',
|
jobRoleId: '',
|
||||||
jobRoleName: '',
|
jobRoleName: '',
|
||||||
@ -322,10 +369,39 @@ class ExpenseLog {
|
|||||||
factory ExpenseLog.fromJson(Map<String, dynamic> json) {
|
factory ExpenseLog.fromJson(Map<String, dynamic> json) {
|
||||||
return ExpenseLog(
|
return ExpenseLog(
|
||||||
id: json['id'] ?? '',
|
id: json['id'] ?? '',
|
||||||
updatedBy: json['updatedBy'] != null ? Person.fromJson(json['updatedBy']) : Person.empty(),
|
updatedBy: json['updatedBy'] != null
|
||||||
|
? Person.fromJson(json['updatedBy'])
|
||||||
|
: Person.empty(),
|
||||||
action: json['action'] ?? '',
|
action: json['action'] ?? '',
|
||||||
updateAt: json['updateAt'] ?? '',
|
updateAt: json['updateAt'] ?? '',
|
||||||
comment: json['comment'] ?? '',
|
comment: json['comment'] ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- Currency ----------------
|
||||||
|
class Currency {
|
||||||
|
final String id;
|
||||||
|
final String currencyCode;
|
||||||
|
final String currencyName;
|
||||||
|
final String symbol;
|
||||||
|
final bool isActive;
|
||||||
|
|
||||||
|
Currency({
|
||||||
|
required this.id,
|
||||||
|
required this.currencyCode,
|
||||||
|
required this.currencyName,
|
||||||
|
required this.symbol,
|
||||||
|
required this.isActive,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Currency.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Currency(
|
||||||
|
id: json['id'] ?? '',
|
||||||
|
currencyCode: json['currencyCode'] ?? '',
|
||||||
|
currencyName: json['currencyName'] ?? '',
|
||||||
|
symbol: json['symbol'] ?? '',
|
||||||
|
isActive: json['isActive'] ?? true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -97,49 +97,93 @@ class _ExpenseDetailScreenState extends State<ExpenseDetailScreen>
|
|||||||
final formattedAmount = formatExpenseAmount(expense.amount);
|
final formattedAmount = formatExpenseAmount(expense.amount);
|
||||||
|
|
||||||
return MyRefreshIndicator(
|
return MyRefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await controller.fetchExpenseDetails();
|
await controller.fetchExpenseDetails();
|
||||||
},
|
},
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
8, 8, 8, 30 + MediaQuery.of(context).padding.bottom),
|
12, 12, 12, 30 + MediaQuery.of(context).padding.bottom),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 520),
|
constraints: const BoxConstraints(maxWidth: 520),
|
||||||
child: Card(
|
child: Card(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(5)),
|
borderRadius: BorderRadius.circular(5)),
|
||||||
elevation: 3,
|
elevation: 3,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 14, horizontal: 14),
|
vertical: 14, horizontal: 14),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_InvoiceHeader(expense: expense),
|
// ---------------- Header & Status ----------------
|
||||||
const Divider(height: 30, thickness: 1.2),
|
_InvoiceHeader(expense: expense),
|
||||||
InvoiceLogs(logs: expense.expenseLogs),
|
const Divider(height: 30, thickness: 1.2),
|
||||||
const Divider(height: 30, thickness: 1.2),
|
|
||||||
_InvoiceParties(expense: expense),
|
// ---------------- Activity Logs ----------------
|
||||||
const Divider(height: 30, thickness: 1.2),
|
InvoiceLogs(logs: expense.expenseLogs),
|
||||||
_InvoiceDetailsTable(expense: expense),
|
const Divider(height: 30, thickness: 1.2),
|
||||||
const Divider(height: 30, thickness: 1.2),
|
// ---------------- Amount & Summary ----------------
|
||||||
_InvoiceDocuments(documents: expense.documents),
|
Row(
|
||||||
const Divider(height: 30, thickness: 1.2),
|
children: [
|
||||||
_InvoiceTotals(
|
Column(
|
||||||
expense: expense,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
formattedAmount: formattedAmount,
|
children: [
|
||||||
statusColor: statusColor,
|
MyText.bodyMedium('Amount',
|
||||||
),
|
fontWeight: 600),
|
||||||
const Divider(height: 30, thickness: 1.2),
|
const SizedBox(height: 4),
|
||||||
],
|
MyText.bodyLarge(
|
||||||
|
formattedAmount,
|
||||||
|
fontWeight: 700,
|
||||||
|
color: statusColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
// Optional: Pre-approved badge
|
||||||
|
if (expense.preApproved)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.green.withOpacity(0.15),
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
child: MyText.bodySmall(
|
||||||
|
'Pre-Approved',
|
||||||
|
fontWeight: 600,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(height: 30, thickness: 1.2),
|
||||||
|
|
||||||
|
// ---------------- Parties ----------------
|
||||||
|
_InvoicePartiesTable(expense: expense),
|
||||||
|
const Divider(height: 30, thickness: 1.2),
|
||||||
|
|
||||||
|
// ---------------- Expense Details ----------------
|
||||||
|
_InvoiceDetailsTable(expense: expense),
|
||||||
|
const Divider(height: 30, thickness: 1.2),
|
||||||
|
|
||||||
|
// ---------------- Documents ----------------
|
||||||
|
_InvoiceDocuments(documents: expense.documents),
|
||||||
|
const Divider(height: 30, thickness: 1.2),
|
||||||
|
|
||||||
|
// ---------------- Totals ----------------
|
||||||
|
_InvoiceTotals(
|
||||||
|
expense: expense,
|
||||||
|
formattedAmount: formattedAmount,
|
||||||
|
statusColor: statusColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
));
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
floatingActionButton: Obx(() {
|
floatingActionButton: Obx(() {
|
||||||
@ -498,24 +542,43 @@ class _InvoiceHeader extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InvoiceParties extends StatelessWidget {
|
class _InvoicePartiesTable extends StatelessWidget {
|
||||||
final ExpenseDetailModel expense;
|
final ExpenseDetailModel expense;
|
||||||
const _InvoiceParties({required this.expense});
|
const _InvoicePartiesTable({required this.expense});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// List of label-value pairs
|
||||||
|
final parties = [
|
||||||
|
{'label': 'Project', 'value': expense.project.name},
|
||||||
|
{
|
||||||
|
'label': 'Paid By',
|
||||||
|
'value': '${expense.paidBy.firstName} ${expense.paidBy.lastName}'
|
||||||
|
},
|
||||||
|
{'label': 'Supplier', 'value': expense.supplerName},
|
||||||
|
{
|
||||||
|
'label': 'Created By',
|
||||||
|
'value': '${expense.createdBy.firstName} ${expense.createdBy.lastName}'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: parties.map((item) {
|
||||||
labelValueBlock('Project', expense.project.name),
|
return Padding(
|
||||||
MySpacing.height(16),
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||||
labelValueBlock('Paid By:',
|
child: Row(
|
||||||
'${expense.paidBy.firstName} ${expense.paidBy.lastName}'),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
MySpacing.height(16),
|
children: [
|
||||||
labelValueBlock('Supplier', expense.supplerName),
|
MyText.bodySmall('${item['label']}:', fontWeight: 600),
|
||||||
MySpacing.height(16),
|
const SizedBox(width: 6),
|
||||||
labelValueBlock('Created By:',
|
Expanded(
|
||||||
'${expense.createdBy.firstName} ${expense.createdBy.lastName}'),
|
child: MyText.bodySmall(item['value']!, fontWeight: 500),
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,6 +586,7 @@ class _InvoiceParties extends StatelessWidget {
|
|||||||
class _InvoiceDetailsTable extends StatelessWidget {
|
class _InvoiceDetailsTable extends StatelessWidget {
|
||||||
final ExpenseDetailModel expense;
|
final ExpenseDetailModel expense;
|
||||||
const _InvoiceDetailsTable({required this.expense});
|
const _InvoiceDetailsTable({required this.expense});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final transactionDate = DateTimeUtils.convertUtcToLocal(
|
final transactionDate = DateTimeUtils.convertUtcToLocal(
|
||||||
@ -531,36 +595,45 @@ class _InvoiceDetailsTable extends StatelessWidget {
|
|||||||
final createdAt = DateTimeUtils.convertUtcToLocal(
|
final createdAt = DateTimeUtils.convertUtcToLocal(
|
||||||
expense.createdAt.toString(),
|
expense.createdAt.toString(),
|
||||||
format: 'dd MMM yyyy');
|
format: 'dd MMM yyyy');
|
||||||
|
|
||||||
|
// List of all label-value pairs
|
||||||
|
final details = [
|
||||||
|
{'label': 'Expense Type', 'value': expense.expensesType.name},
|
||||||
|
{'label': 'Payment Mode', 'value': expense.paymentMode.name},
|
||||||
|
{'label': 'Transaction Date', 'value': transactionDate},
|
||||||
|
{'label': 'Created At', 'value': createdAt},
|
||||||
|
{'label': 'Pre-Approved', 'value': expense.preApproved ? 'Yes' : 'No'},
|
||||||
|
{
|
||||||
|
'label': 'Description',
|
||||||
|
'value':
|
||||||
|
expense.description.trim().isNotEmpty ? expense.description : '-'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: details.map((item) {
|
||||||
_detailItem("Expense Type:", expense.expensesType.name),
|
final isDescription = item['label'] == 'Description';
|
||||||
_detailItem("Payment Mode:", expense.paymentMode.name),
|
return Padding(
|
||||||
_detailItem("Transaction Date:", transactionDate),
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||||
_detailItem("Created At:", createdAt),
|
child: Row(
|
||||||
_detailItem("Pre-Approved:", expense.preApproved ? 'Yes' : 'No'),
|
crossAxisAlignment: isDescription
|
||||||
_detailItem("Description:",
|
? CrossAxisAlignment.start
|
||||||
expense.description.trim().isNotEmpty ? expense.description : '-',
|
: CrossAxisAlignment.center,
|
||||||
isDescription: true),
|
children: [
|
||||||
],
|
MyText.bodySmall('${item['label']}:', fontWeight: 600),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: isDescription
|
||||||
|
? ExpandableDescription(description: item['value']!)
|
||||||
|
: MyText.bodySmall(item['value']!, fontWeight: 500),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _detailItem(String title, String value,
|
|
||||||
{bool isDescription = false}) =>
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
MyText.bodySmall(title, fontWeight: 600),
|
|
||||||
MySpacing.height(3),
|
|
||||||
isDescription
|
|
||||||
? ExpandableDescription(description: value)
|
|
||||||
: MyText.bodySmall(value, fontWeight: 500),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InvoiceDocuments extends StatelessWidget {
|
class _InvoiceDocuments extends StatelessWidget {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user