first commit
This commit is contained in:
57
lib/pdf_report/ledger_report/generate_pdf_date_range.dart
Normal file
57
lib/pdf_report/ledger_report/generate_pdf_date_range.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
Map<String, String> getPdfDateRangeForSelectedTime(
|
||||
String selectedTime, {
|
||||
DateTime? fromDate,
|
||||
DateTime? toDate,
|
||||
}) {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime start;
|
||||
DateTime end;
|
||||
|
||||
switch (selectedTime.toLowerCase()) {
|
||||
case 'today':
|
||||
start = DateTime(now.year, now.month, now.day);
|
||||
end = start;
|
||||
break;
|
||||
case 'yesterday':
|
||||
start = DateTime(now.year, now.month, now.day).subtract(const Duration(days: 1));
|
||||
end = start;
|
||||
break;
|
||||
case 'last_seven_days':
|
||||
end = now;
|
||||
start = now.subtract(const Duration(days: 6));
|
||||
break;
|
||||
case 'last_thirty_days':
|
||||
end = now;
|
||||
start = now.subtract(const Duration(days: 29));
|
||||
break;
|
||||
case 'current_month':
|
||||
start = DateTime(now.year, now.month, 1);
|
||||
end = DateTime(now.year, now.month + 1, 0);
|
||||
break;
|
||||
case 'last_month':
|
||||
final lastMonth = DateTime(now.year, now.month - 1, 1);
|
||||
start = lastMonth;
|
||||
end = DateTime(lastMonth.year, lastMonth.month + 1, 0);
|
||||
break;
|
||||
case 'current_year':
|
||||
start = DateTime(now.year, 1, 1);
|
||||
end = DateTime(now.year, 12, 31);
|
||||
break;
|
||||
case 'custom_date':
|
||||
start = fromDate ?? now;
|
||||
end = toDate ?? now;
|
||||
break;
|
||||
default:
|
||||
start = now;
|
||||
end = now;
|
||||
}
|
||||
|
||||
final fromStr = "${start.day.toString().padLeft(2, '0')}-"
|
||||
"${start.month.toString().padLeft(2, '0')}-"
|
||||
"${start.year}";
|
||||
final toStr = "${end.day.toString().padLeft(2, '0')}-"
|
||||
"${end.month.toString().padLeft(2, '0')}-"
|
||||
"${end.year}";
|
||||
|
||||
return {'from': fromStr, 'to': toStr};
|
||||
}
|
||||
297
lib/pdf_report/ledger_report/ledger_report_excel.dart
Normal file
297
lib/pdf_report/ledger_report/ledger_report_excel.dart
Normal file
@@ -0,0 +1,297 @@
|
||||
import 'dart:io';
|
||||
import 'package:excel/excel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:open_file/open_file.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import '../../Screens/party ledger/model/party_ledger_model.dart';
|
||||
import '../../model/business_info_model.dart';
|
||||
import 'generate_pdf_date_range.dart';
|
||||
|
||||
Future<void> generateLedgerReportExcel(
|
||||
BuildContext context,
|
||||
List<PartyLedgerModel>? data,
|
||||
BusinessInformationModel business,
|
||||
DateTime? fromDate,
|
||||
DateTime? toDate,
|
||||
String selectedTime,
|
||||
) async {
|
||||
EasyLoading.show(status: 'Generating Excel');
|
||||
|
||||
try {
|
||||
if (data == null || data.isEmpty) {
|
||||
EasyLoading.showInfo('No transactions available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create Excel
|
||||
final excel = Excel.createExcel();
|
||||
final sheet = excel['Party Ledger'];
|
||||
|
||||
// ---------------------------
|
||||
// CALCULATE TOTALS
|
||||
// ---------------------------
|
||||
double creditBalance = 0;
|
||||
double debitBalance = 0;
|
||||
|
||||
// for (var item in data) {
|
||||
// if (item.type == 'credit') {
|
||||
// creditBalance += item.amount ?? 0;
|
||||
// } else {
|
||||
// debitBalance += item.amount ?? 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
for (var item in data) {
|
||||
creditBalance += item.creditAmount ?? 0;
|
||||
}
|
||||
for (var item in data) {
|
||||
debitBalance += item.debitAmount ?? 0;
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// DATE RANGE (same as PDF)
|
||||
// ---------------------------
|
||||
final pdfRange = getPdfDateRangeForSelectedTime(
|
||||
selectedTime,
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
);
|
||||
|
||||
final fromStr = pdfRange['from']!;
|
||||
final toStr = pdfRange['to']!;
|
||||
|
||||
// ---------------------------
|
||||
// HEADER SECTION
|
||||
// ---------------------------
|
||||
sheet.appendRow([
|
||||
// TextCellValue(business.data?.companyName ?? ''),
|
||||
TextCellValue(appsName),
|
||||
]);
|
||||
|
||||
sheet.appendRow([
|
||||
TextCellValue('Party Ledger'),
|
||||
]);
|
||||
|
||||
sheet.appendRow([
|
||||
TextCellValue('Duration: $fromStr to $toStr'),
|
||||
]);
|
||||
|
||||
sheet.appendRow([]); // empty space row
|
||||
|
||||
// ---------------------------
|
||||
// TABLE HEADER
|
||||
// ---------------------------
|
||||
final headerRow = sheet.maxRows;
|
||||
|
||||
sheet.appendRow([
|
||||
TextCellValue('Date'),
|
||||
TextCellValue('Reference No'),
|
||||
TextCellValue('Description'),
|
||||
TextCellValue('Credit'),
|
||||
TextCellValue('Debit'),
|
||||
TextCellValue('Balance'),
|
||||
]);
|
||||
|
||||
// Style header row (bold)
|
||||
for (int col = 0; col < 6; col++) {
|
||||
final cell = sheet.cell(
|
||||
CellIndex.indexByColumnRow(columnIndex: col, rowIndex: headerRow),
|
||||
);
|
||||
cell.cellStyle = CellStyle(bold: true);
|
||||
}
|
||||
|
||||
sheet.appendRow([]);
|
||||
|
||||
// ---------------------------
|
||||
// TABLE DATA
|
||||
// ---------------------------
|
||||
for (var item in data) {
|
||||
bool isOpening = item.platform == 'opening_balance';
|
||||
|
||||
sheet.appendRow([
|
||||
TextCellValue(DateFormat('dd-MM-yyyy').format(DateTime.parse(item.date.toString()))),
|
||||
TextCellValue(item.invoiceNumber ?? ''),
|
||||
TextCellValue(
|
||||
isOpening ? "Opening" : item.platform?.replaceAll('_', ' ') ?? 'Transaction',
|
||||
),
|
||||
TextCellValue((item.creditAmount ?? 0).toStringAsFixed(2)),
|
||||
TextCellValue((item.debitAmount ?? 0).toStringAsFixed(2)),
|
||||
TextCellValue((item.balance ?? 0).toStringAsFixed(2)),
|
||||
]);
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// TOTAL ROW
|
||||
// ---------------------------
|
||||
final totalRow = sheet.maxRows;
|
||||
|
||||
sheet.appendRow([
|
||||
TextCellValue(''),
|
||||
TextCellValue(''),
|
||||
TextCellValue('Total'),
|
||||
TextCellValue(creditBalance.toStringAsFixed(2)),
|
||||
TextCellValue(debitBalance.toStringAsFixed(2)),
|
||||
TextCellValue((data.last.balance ?? 0).toStringAsFixed(2)),
|
||||
]);
|
||||
|
||||
// Make TOTAL row bold
|
||||
for (int col = 0; col < 6; col++) {
|
||||
final cell = sheet.cell(
|
||||
CellIndex.indexByColumnRow(columnIndex: col, rowIndex: totalRow),
|
||||
);
|
||||
cell.cellStyle = CellStyle(bold: true);
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
// SAVE FILE
|
||||
// ---------------------------
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
final filePath = '${dir.path}/${business.data?.companyName ?? "Company"}_Ledger_Report_$timestamp.xlsx';
|
||||
|
||||
final file = File(filePath);
|
||||
await file.writeAsBytes(excel.encode()!);
|
||||
|
||||
EasyLoading.showSuccess('Excel Generated Successfully!');
|
||||
await OpenFile.open(filePath);
|
||||
} catch (e) {
|
||||
EasyLoading.showError("Error: $e");
|
||||
debugPrint("Excel Error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> generateLedgerReportExcel(
|
||||
// BuildContext context,
|
||||
// List<PartyLedgerModel>? data,
|
||||
// BusinessInformationModel? business,
|
||||
// DateTime? fromDate,
|
||||
// DateTime? toDate,
|
||||
// ) async {
|
||||
// EasyLoading.show(status: 'Generating Excel');
|
||||
//
|
||||
// try {
|
||||
// if (data == null) {
|
||||
// EasyLoading.showError('Invalid data provided');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Create Excel file & sheet
|
||||
// final excel = Excel.createExcel();
|
||||
// final sheet = excel['Party Ledger'];
|
||||
//
|
||||
// double creditBalance = 0;
|
||||
// double debitBalance = 0;
|
||||
//
|
||||
// for (var item in data) {
|
||||
// if (item.type == 'credit') {
|
||||
// creditBalance += item.amount ?? 0;
|
||||
// } else {
|
||||
// debitBalance += item.amount ?? 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // ---------------------------
|
||||
// // HEADER SECTION
|
||||
// // ---------------------------
|
||||
//
|
||||
// // Row 1: Company Name
|
||||
// sheet.appendRow([
|
||||
// TextCellValue(business?.data?.companyName ?? ''),
|
||||
// ]);
|
||||
//
|
||||
// // Row 2: Report Title
|
||||
// sheet.appendRow([
|
||||
// TextCellValue('Party Ledger'),
|
||||
// ]);
|
||||
//
|
||||
// // Row 3: Date Range
|
||||
// if (fromDate != null && toDate != null) {
|
||||
// final String formattedFrom = DateFormat('dd-MM-yyyy').format(fromDate);
|
||||
// final String formattedTo = DateFormat('dd-MM-yyyy').format(toDate);
|
||||
//
|
||||
// sheet.appendRow([
|
||||
// TextCellValue('Duration: $formattedFrom to $formattedTo'),
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// // Empty row
|
||||
// sheet.appendRow([]);
|
||||
//
|
||||
// // ---------------------------
|
||||
// // LEDGER TABLE HEADER
|
||||
// // ---------------------------
|
||||
// final headerRowIndex = sheet.maxRows;
|
||||
//
|
||||
// sheet.appendRow([
|
||||
// TextCellValue('Date'),
|
||||
// TextCellValue('Invoice No'),
|
||||
// TextCellValue('Details'),
|
||||
// TextCellValue('Credit'),
|
||||
// TextCellValue('Debit'),
|
||||
// TextCellValue('Balance'),
|
||||
// ]);
|
||||
//
|
||||
// // Add space row
|
||||
// sheet.appendRow([]);
|
||||
//
|
||||
// // Apply bold style on table header
|
||||
// for (var i = 0; i < 6; i++) {
|
||||
// sheet.cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: headerRowIndex)).cellStyle;
|
||||
// }
|
||||
//
|
||||
// // ---------------------------
|
||||
// // LEDGER TABLE BODY
|
||||
// // ---------------------------
|
||||
// for (int i = 0; i < data.length; i++) {
|
||||
// final item = data[i];
|
||||
// final isOpening = item.platform == 'opening_balance';
|
||||
//
|
||||
// sheet.appendRow([
|
||||
// TextCellValue(item.date ?? 'n/a'),
|
||||
// TextCellValue(item.invoiceNumber ?? ''),
|
||||
// TextCellValue(isOpening ? "Opening" : item.platform?.replaceAll('_', ' ') ?? 'Transaction'),
|
||||
// TextCellValue(item.type == 'credit' ? (item.amount ?? 0).toStringAsFixed(2) : '0.00'),
|
||||
// TextCellValue(item.type != 'credit' ? (item.amount ?? 0).toStringAsFixed(2) : '0.00'),
|
||||
// TextCellValue((item.balance ?? 0).toStringAsFixed(2)),
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// // ---------------------------
|
||||
// // TOTAL ROW
|
||||
// // ---------------------------
|
||||
// final totalRowIndex = sheet.maxRows;
|
||||
//
|
||||
// sheet.appendRow([
|
||||
// TextCellValue(''),
|
||||
// TextCellValue(''),
|
||||
// TextCellValue('Total'),
|
||||
// TextCellValue(creditBalance.toStringAsFixed(2)),
|
||||
// TextCellValue(debitBalance.toStringAsFixed(2)),
|
||||
// TextCellValue((data.last.balance ?? 0).toStringAsFixed(2)),
|
||||
// ]);
|
||||
//
|
||||
// // Make total row bold
|
||||
// for (var i = 0; i < 6; i++) {
|
||||
// sheet.cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: totalRowIndex)).cellStyle;
|
||||
// }
|
||||
//
|
||||
// // ---------------------------
|
||||
// // SAVE FILE
|
||||
// // ---------------------------
|
||||
// final dir = await getApplicationDocumentsDirectory();
|
||||
// final filePath = '${dir.path}/${business?.data?.companyName ?? "Company"}_Ledger_Report.xlsx';
|
||||
//
|
||||
// final file = File(filePath);
|
||||
// await file.writeAsBytes(excel.encode()!);
|
||||
//
|
||||
// EasyLoading.showSuccess('Ledger Excel Generated!');
|
||||
// await OpenFile.open(filePath);
|
||||
// } catch (e) {
|
||||
// EasyLoading.showError('Error: $e');
|
||||
// debugPrint('Ledger Excel Error: $e');
|
||||
// }
|
||||
// }
|
||||
216
lib/pdf_report/ledger_report/ledger_report_pdf.dart
Normal file
216
lib/pdf_report/ledger_report/ledger_report_pdf.dart
Normal file
@@ -0,0 +1,216 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobile_pos/Screens/Customers/Model/parties_model.dart';
|
||||
import 'package:mobile_pos/Screens/Products/Model/product_model.dart';
|
||||
import 'package:mobile_pos/Screens/party%20ledger/model/party_ledger_model.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:pdf/pdf.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
import 'package:printing/printing.dart';
|
||||
|
||||
import '../../Screens/PDF/pdf.dart';
|
||||
import '../../Screens/Products/Model/product_total_stock_model.dart';
|
||||
import '../../currency.dart';
|
||||
import '../../model/business_info_model.dart';
|
||||
import 'generate_pdf_date_range.dart';
|
||||
|
||||
Future<void> generateLedgerReportPdf(
|
||||
BuildContext context,
|
||||
List<PartyLedgerModel>? data,
|
||||
BusinessInformationModel business,
|
||||
DateTime? fromDate,
|
||||
DateTime? toDate,
|
||||
String selectedTime, // pass selected filter
|
||||
) async {
|
||||
if (data == null || data.isEmpty) {
|
||||
EasyLoading.showInfo('No transactions to generate PDF');
|
||||
return;
|
||||
}
|
||||
|
||||
final pdf = pw.Document();
|
||||
|
||||
try {
|
||||
EasyLoading.show(status: 'Generating PDF...');
|
||||
|
||||
double creditBalance = 0;
|
||||
double debitBalance = 0;
|
||||
// for (var item in data) {
|
||||
// if (item.type == 'credit') creditBalance += item.amount ?? 0;
|
||||
// if (item.type != 'credit') debitBalance += item.amount ?? 0;
|
||||
// }
|
||||
|
||||
for (var item in data) {
|
||||
creditBalance += item.creditAmount ?? 0;
|
||||
}
|
||||
|
||||
for (var item in data) {
|
||||
debitBalance += item.debitAmount ?? 0;
|
||||
}
|
||||
|
||||
// Calculate correct PDF date range
|
||||
final pdfDateRange = getPdfDateRangeForSelectedTime(
|
||||
selectedTime,
|
||||
fromDate: fromDate,
|
||||
toDate: toDate,
|
||||
);
|
||||
|
||||
final fromDateStr = pdfDateRange['from']!;
|
||||
final toDateStr = pdfDateRange['to']!;
|
||||
|
||||
pdf.addPage(
|
||||
pw.MultiPage(
|
||||
pageFormat: PdfPageFormat.letter.copyWith(marginBottom: 1.5 * PdfPageFormat.cm),
|
||||
margin: const pw.EdgeInsets.symmetric(horizontal: 16),
|
||||
header: (context) => pw.Center(
|
||||
child: pw.Column(
|
||||
children: [
|
||||
pw.Text(appsName, style: pw.TextStyle(fontSize: 20, fontWeight: pw.FontWeight.bold)),
|
||||
pw.Text('Party Ledger', style: pw.TextStyle(fontSize: 16, fontWeight: pw.FontWeight.bold)),
|
||||
pw.SizedBox(height: 2),
|
||||
pw.Text('$fromDateStr TO $toDateStr', style: const pw.TextStyle(fontSize: 14)),
|
||||
pw.SizedBox(height: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
footer: (pw.Context context) {
|
||||
return pw.Row(
|
||||
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
pw.Text('${business.data?.developByLevel ?? ''} ${business.data?.developBy ?? ''}'),
|
||||
pw.Text('Page-${context.pageNumber}'),
|
||||
],
|
||||
);
|
||||
},
|
||||
build: (pw.Context context) {
|
||||
final List<List<String>> tableData = [];
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
final isOpening = data[i].platform == 'opening_balance';
|
||||
// final stockPrice = (data[i].stocks != null && data[i].stocks!.isNotEmpty) ? data[i].stocks!.last.productPurchasePrice?.toString() ?? '0' : '0';
|
||||
tableData.add([
|
||||
DateFormat('dd-MM-yyyy').format(DateTime.parse(data[i].date.toString())),
|
||||
data[i].invoiceNumber ?? '--',
|
||||
isOpening ? "Opening" : (data[i].platform?.replaceAll('_', ' ') ?? 'Transaction'),
|
||||
'$currency${data[i].creditAmount ?? 0}',
|
||||
'$currency${data[i].debitAmount ?? 0}',
|
||||
'$currency${data[i].balance ?? 0}',
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
pw.SizedBox(height: 16),
|
||||
pw.Table.fromTextArray(
|
||||
headers: [
|
||||
'Date',
|
||||
'Reference No',
|
||||
'Description',
|
||||
'Credit',
|
||||
'Debit',
|
||||
'Balance',
|
||||
],
|
||||
data: tableData,
|
||||
headerDecoration: const pw.BoxDecoration(
|
||||
color: PdfColor.fromInt(0xffF7F7F7),
|
||||
),
|
||||
border: pw.TableBorder.all(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
headerStyle: pw.TextStyle(
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.black,
|
||||
),
|
||||
rowDecoration: const pw.BoxDecoration(
|
||||
color: PdfColors.white,
|
||||
),
|
||||
// oddRowDecoration: pw.BoxDecoration(
|
||||
// color: PdfColor.fromInt(0xffF7F7F7),
|
||||
// ),
|
||||
cellPadding: const pw.EdgeInsets.all(8),
|
||||
columnWidths: {
|
||||
0: const pw.FlexColumnWidth(3),
|
||||
1: const pw.FlexColumnWidth(4),
|
||||
2: const pw.FlexColumnWidth(4),
|
||||
3: const pw.FlexColumnWidth(3),
|
||||
4: const pw.FlexColumnWidth(3),
|
||||
5: const pw.FlexColumnWidth(3),
|
||||
},
|
||||
cellAlignments: {
|
||||
0: pw.Alignment.center,
|
||||
1: pw.Alignment.center,
|
||||
2: pw.Alignment.center,
|
||||
3: pw.Alignment.center,
|
||||
4: pw.Alignment.center,
|
||||
5: pw.Alignment.center,
|
||||
},
|
||||
),
|
||||
pw.Table.fromTextArray(
|
||||
border: pw.TableBorder.all(color: PdfColor.fromInt(0xffD9D9D9)),
|
||||
columnWidths: {
|
||||
0: const pw.FlexColumnWidth(3),
|
||||
1: const pw.FlexColumnWidth(4),
|
||||
2: const pw.FlexColumnWidth(4),
|
||||
3: const pw.FlexColumnWidth(3),
|
||||
4: const pw.FlexColumnWidth(3),
|
||||
5: const pw.FlexColumnWidth(3),
|
||||
},
|
||||
cellAlignments: {
|
||||
0: pw.Alignment.center,
|
||||
1: pw.Alignment.center,
|
||||
2: pw.Alignment.center,
|
||||
3: pw.Alignment.center,
|
||||
4: pw.Alignment.center,
|
||||
5: pw.Alignment.center,
|
||||
},
|
||||
data: [
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'Total',
|
||||
creditBalance,
|
||||
debitBalance,
|
||||
data.last.balance,
|
||||
]
|
||||
],
|
||||
// headerDecoration: const pw.BoxDecoration(
|
||||
// color: PdfColor.fromInt(0xffF7F7F7),
|
||||
// ),
|
||||
headerStyle: pw.TextStyle(
|
||||
fontWeight: pw.FontWeight.bold,
|
||||
color: PdfColors.black,
|
||||
),
|
||||
cellAlignment: pw.Alignment.center,
|
||||
cellPadding: const pw.EdgeInsets.all(8),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
final bytes = await pdf.save();
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||||
final file = File('${dir.path}/$appsName-leger-report-$timestamp.pdf');
|
||||
await file.writeAsBytes(bytes);
|
||||
await EasyLoading.dismiss();
|
||||
|
||||
if (context.mounted) {
|
||||
await Printing.layoutPdf(
|
||||
name: 'Leger Report',
|
||||
usePrinterSettings: true,
|
||||
dynamicLayout: true,
|
||||
forceCustomPrintPaper: true,
|
||||
onLayout: (PdfPageFormat format) async => pdf.save(),
|
||||
);
|
||||
}
|
||||
|
||||
// if (context.mounted) {
|
||||
// Navigator.push(context, MaterialPageRoute(builder: (_) => PDFViewerPage(path: file.path)));
|
||||
// }
|
||||
} catch (e) {
|
||||
await EasyLoading.dismiss();
|
||||
EasyLoading.showError('Failed to generate PDF: $e');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user