Major refactor of the Sales Form and Bill controller to make code more readable and less error prone.
The functions now only do one thing.
This commit is contained in:
@ -13,8 +13,8 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
public class BillController
|
||||
{
|
||||
private readonly BillDict _bill;
|
||||
private Voucher _voucher;
|
||||
public readonly BillDict _bill;
|
||||
public Voucher _voucher;
|
||||
private Guid? _editVoucherID;
|
||||
private ISaleForm _saleForm;
|
||||
|
||||
@ -22,45 +22,13 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
this._editVoucherID = editVoucherID;
|
||||
_bill = new BillDict();
|
||||
_voucher = new Voucher(Session.User);
|
||||
using (var bi = new CustomerBI())
|
||||
_voucher.Customer = bi.Get(x => x.CustomerID == Constants.CASH_CUSTOMER);
|
||||
}
|
||||
public BillItemValue CurrentProduct
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_saleForm.BindingSource.Position == -1)
|
||||
return null;
|
||||
var item = _bill.ElementAt(_saleForm.BindingSource.Position);
|
||||
return item.Key.BillItemType != BillItemType.Kot ? item.Value : null;
|
||||
}
|
||||
}
|
||||
public BillItemKey CurrentKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_saleForm.BindingSource.Position == -1)
|
||||
return null;
|
||||
var item = _bill.ElementAt(_saleForm.BindingSource.Position);
|
||||
return item.Key;
|
||||
}
|
||||
}
|
||||
public BillItemKey CurrentKot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_saleForm.BindingSource.Position == -1)
|
||||
return null;
|
||||
var item = _bill.ElementAt(_saleForm.BindingSource.Position);
|
||||
return item.Key.BillItemType == BillItemType.Kot ? item.Key : null;
|
||||
}
|
||||
_voucher = new Voucher(Session.User, bi.Get(x => x.CustomerID == Constants.CASH_CUSTOMER));
|
||||
}
|
||||
|
||||
public void InitGui(ISaleForm saleForm)
|
||||
{
|
||||
this._saleForm = saleForm;
|
||||
this._saleForm.SetCustomerDisplay(_voucher.Customer.Name);
|
||||
this._saleForm.SetUserName(Session.User.Name);
|
||||
}
|
||||
public void AddProduct(Product product)
|
||||
@ -69,7 +37,6 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
|
||||
if (_bill.ContainsKey(newKey))
|
||||
{
|
||||
_saleForm.BindingSource.CurrencyManager.Position = _bill.IndexOfKey(newKey);
|
||||
_bill[newKey].inventory.Quantity += 1;
|
||||
}
|
||||
else
|
||||
@ -82,32 +49,20 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
billItemValue.inventory.Price = old.Value.inventory.Price;
|
||||
}
|
||||
_bill.Add(newKey, billItemValue);
|
||||
_saleForm.BindingSource.DataSource = _bill.ToList();
|
||||
_saleForm.BindingSource.CurrencyManager.Position = _saleForm.BindingSource.CurrencyManager.Count - 1;
|
||||
|
||||
using (var bi = new ProductGroupModifierBI())
|
||||
if (bi.HasCompulsoryModifier(product.ProductGroup.ProductGroupID))
|
||||
{
|
||||
ShowModifiers();
|
||||
}
|
||||
}
|
||||
ShowAmount();
|
||||
}
|
||||
public void ShowModifiers()
|
||||
|
||||
public void ShowModifiers(BillItemValue item)
|
||||
{
|
||||
var item = CurrentProduct;
|
||||
if (item == null || CurrentKey.KotID != Guid.Empty)
|
||||
return; // No Product or Old Product
|
||||
using (var frm = new ModifierForm(Cache.ProductGroupModifiers(item.inventory.Product.ProductGroup.ProductGroupID), item.inventory.InventoryModifier))
|
||||
{
|
||||
frm.ShowDialog();
|
||||
}
|
||||
ShowAmount();
|
||||
}
|
||||
public void SetDiscount()
|
||||
{
|
||||
if (!Session.IsAllowed("Discount"))
|
||||
return; // throw new PermissionException("Not Allowed to give Discount");
|
||||
return;
|
||||
|
||||
using (var bi = new ProductGroupBI())
|
||||
{
|
||||
@ -119,7 +74,7 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
var discount = frm.Selection(out outList);
|
||||
discount = discount / 100;
|
||||
if (discount > 1 || discount < 0)
|
||||
return; // throw new ValidationException("Invalid Discount Amount");
|
||||
return;
|
||||
|
||||
foreach (var item in _bill.Where(x => x.Key.BillItemType == BillItemType.Product && outList.Contains(x.Value.inventory.Product.ProductGroup.GroupType)))
|
||||
{
|
||||
@ -133,12 +88,9 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
}
|
||||
}
|
||||
}
|
||||
ShowAmount();
|
||||
}
|
||||
public void SetQuantity(decimal quantity, bool prompt)
|
||||
public void SetQuantity(BillItemKey key, BillItemValue item, decimal quantity, bool prompt)
|
||||
{
|
||||
var item = CurrentProduct;
|
||||
var key = CurrentKey;
|
||||
if (item == null || key.KotID != Guid.Empty)
|
||||
return; // No Product or Old Product
|
||||
if (prompt && !GetInput("Quantity", ref quantity))
|
||||
@ -147,21 +99,14 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
quantity += item.inventory.Quantity;
|
||||
if (quantity < 0 && !Session.IsAllowed("Edit Printed Product"))
|
||||
return;
|
||||
//TODO: check if he kotid of the item is not null
|
||||
//var total = quantity + _bill.Where(x => x.Key.ProductID == item.ProductID && x.Key.BillItemType == BillItemType.Product && x.Value.Printed).Sum(x => x.Value.Quantity);
|
||||
var total = quantity + _bill.Where(x => x.Key.ProductID == key.ProductID && x.Key.BillItemType == key.BillItemType).Sum(x => x.Value.inventory.Quantity);
|
||||
if (total < 0)
|
||||
quantity -= total;
|
||||
var total = quantity + _bill.Where(x => x.Key.ProductID == key.ProductID && x.Key.KotID != Guid.Empty && x.Key.BillItemType == key.BillItemType).Sum(x => x.Value.inventory.Quantity);
|
||||
quantity = Math.Max(quantity, 0);
|
||||
item.inventory.Quantity = quantity;
|
||||
ShowAmount();
|
||||
}
|
||||
public void SetPrice()
|
||||
public void SetPrice(BillItemValue item)
|
||||
{
|
||||
var item = CurrentProduct;
|
||||
if (item == null)
|
||||
throw new ValidationException("No Product Selected");
|
||||
if (!Session.IsAllowed("Change Rate"))
|
||||
throw new PermissionException("Price Change not Allowed");
|
||||
return;
|
||||
var price = item.inventory.Price;
|
||||
if (!GetInput("Price", ref price))
|
||||
return;
|
||||
@ -169,46 +114,14 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
throw new PermissionException("NC of Product is not Allowed");
|
||||
foreach (var sub in _bill.Where(x => x.Key.BillItemType == BillItemType.Product && x.Key.ProductID == item.inventory.Product.ProductID))
|
||||
sub.Value.inventory.Price = price;
|
||||
|
||||
ShowAmount();
|
||||
}
|
||||
public void ShowCustomers(bool reset)
|
||||
public void ShowCustomers()
|
||||
{
|
||||
if (!reset && ((_voucher.Customer == null) || _voucher.Customer.CustomerID == Constants.CASH_CUSTOMER))
|
||||
using (var frm = new CustomerListForm())
|
||||
{
|
||||
using (var selectCustomer = new SelectCustomer(CustomerBI.StaticList, true))
|
||||
{
|
||||
selectCustomer.CustomerEvent += selectCustomer_customerEvent;
|
||||
selectCustomer.ShowDialog();
|
||||
if (selectCustomer.SelectedItem != null)
|
||||
{
|
||||
_voucher.Customer = selectCustomer.SelectedItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var bi = new CustomerBI())
|
||||
_voucher.Customer = bi.Get(x => x.CustomerID == Constants.CASH_CUSTOMER);
|
||||
}
|
||||
}
|
||||
frm.ShowDialog();
|
||||
_voucher.Customer = frm.SelectedItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var bi = new CustomerBI())
|
||||
_voucher.Customer = bi.Get(x => x.CustomerID == Constants.CASH_CUSTOMER);
|
||||
}
|
||||
_saleForm.SetCustomerDisplay(_voucher.Customer.Name);
|
||||
}
|
||||
private Customer selectCustomer_customerEvent(object sender, CustomerEventArgs e)
|
||||
{
|
||||
using (var form = new CustomersForm(e.CustomerID, e.Phone))
|
||||
{
|
||||
form.ShowDialog();
|
||||
return form.Customer;
|
||||
}
|
||||
}
|
||||
private void ShowAmount()
|
||||
{
|
||||
_saleForm.ShowAmount(_bill.Discount, _bill.NetAmount, _bill.ServiceCharge, _bill.Tax, _bill.Amount, _bill);
|
||||
}
|
||||
private static bool IsPrintedOrVoid(Voucher voucher)
|
||||
{
|
||||
@ -236,13 +149,10 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
_voucher = bi.Get(x => x.VoucherID == voucherID);
|
||||
_bill.Clear();
|
||||
_saleForm.ShowInfo(_voucher);
|
||||
_bill.Load(_voucher);
|
||||
var newKotKey = new BillItemKey(Guid.Empty);
|
||||
var newKotItem = new BillItemValue();
|
||||
_bill.Add(newKotKey, newKotItem);
|
||||
ShowAmount();
|
||||
_saleForm.FormState = SaleFormState.Billing;
|
||||
}
|
||||
}
|
||||
public void LoadBill(string tableName)
|
||||
@ -261,37 +171,37 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
frm.ShowDialog();
|
||||
_voucher.Table = table;
|
||||
_voucher.Pax = frm.Pax;
|
||||
_saleForm.ShowInfo(_voucher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelBillChanges()
|
||||
public bool CancelBillChanges()
|
||||
{
|
||||
if (_bill.Any(x => x.Key.BillItemType != BillItemType.Kot) &&
|
||||
MessageBox.Show("Abandon Changes?", "Abandon Changes", MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.No)
|
||||
return;
|
||||
return false;
|
||||
ClearBill();
|
||||
return true;
|
||||
}
|
||||
public void ClearBill()
|
||||
{
|
||||
_voucher = new Voucher(Session.User);
|
||||
ShowCustomers(true);
|
||||
using (var bi = new CustomerBI())
|
||||
_voucher = new Voucher(Session.User, bi.Get(x => x.CustomerID == Constants.CASH_CUSTOMER));
|
||||
_bill.Clear();
|
||||
var newKotKey = new BillItemKey(Guid.Empty);
|
||||
var newKotItem = new BillItemValue();
|
||||
_bill.Add(newKotKey, newKotItem);
|
||||
_saleForm.ClearBill(_bill);
|
||||
}
|
||||
public SaleFormState FormLoad()
|
||||
{
|
||||
ClearBill();
|
||||
if (_editVoucherID.HasValue)
|
||||
{
|
||||
FoodTable ft = new FoodTableBI().Get(x => x.VoucherID == _editVoucherID.Value);
|
||||
if (ft == null)
|
||||
{
|
||||
LoadBill(_editVoucherID.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_editVoucherID = null;
|
||||
@ -301,10 +211,10 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
}
|
||||
return SaleFormState.Waiting;
|
||||
}
|
||||
internal void SettleBill()
|
||||
internal bool SettleBill()
|
||||
{
|
||||
if (_voucher.VoucherID == Guid.Empty || !_voucher.Printed || !Session.IsAllowed("Settle Bill"))
|
||||
return;
|
||||
return false;
|
||||
IDictionary<SettleOption, decimal> options;
|
||||
var amount = -1 * _voucher.Kots.Sum(x => x.Inventories.Sum(y => y.Amount));
|
||||
using (var frm = new SettleChoicesForm(Math.Round(amount) * -1, _voucher.VoucherType))
|
||||
@ -313,7 +223,7 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
options = frm.OptionsChosen;
|
||||
}
|
||||
if (options.Count == 0)
|
||||
return;
|
||||
return false;
|
||||
using (var bi = new VoucherBI())
|
||||
{
|
||||
bi.SettleVoucher(Session.User, _voucher.VoucherID, options);
|
||||
@ -322,6 +232,7 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
bi.SaveChanges();
|
||||
}
|
||||
ClearBill();
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Move Table(s) / Kot(s)
|
||||
@ -338,11 +249,11 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
return null;
|
||||
}
|
||||
}
|
||||
internal void MoveKot()
|
||||
internal void MoveKot(BillItemKey currentKot)
|
||||
{
|
||||
if (_voucher.VoucherID == Guid.Empty || IsPrintedOrVoid(_voucher))
|
||||
return;
|
||||
var kot = CurrentKot;
|
||||
var kot = currentKot;
|
||||
if (kot == null)
|
||||
return;
|
||||
var table = GetTableForMove(true);
|
||||
@ -368,7 +279,9 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
//Merge Table
|
||||
voucherID = MergeTable(table);
|
||||
if (voucherID != Guid.Empty)
|
||||
{
|
||||
LoadBill(voucherID);
|
||||
}
|
||||
}
|
||||
internal void MoveTable()
|
||||
{
|
||||
@ -439,15 +352,31 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
#endregion
|
||||
|
||||
#region Save
|
||||
public void SaveKot()
|
||||
public bool SaveAndPrintKot()
|
||||
{
|
||||
#region Check if Allowed
|
||||
if (!Session.IsAllowed("Print Kot"))
|
||||
return;
|
||||
if (!Session.IsAllowed("Print Kot") || _bill.Count(x => x.Key.BillItemType == BillItemType.Product) == 0)
|
||||
return false;
|
||||
bool isPrinted = false, isVoid = false;
|
||||
if (_voucher.VoucherID != Guid.Empty)
|
||||
using (var bi = new VoucherBI())
|
||||
{
|
||||
var dbVoucher = bi.Get(x => x.VoucherID == _voucher.VoucherID);
|
||||
isPrinted = dbVoucher.Printed;
|
||||
isVoid = dbVoucher.Void;
|
||||
}
|
||||
if (isVoid)
|
||||
{
|
||||
MessageBox.Show(string.Format("This Bill is already void.\nReason: {0}", _voucher.VoidReason), "Bill already Voided", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return false;
|
||||
}
|
||||
if (isPrinted)
|
||||
{
|
||||
MessageBox.Show(string.Format("This Bill is already printed and a kot cannot be added", _voucher.VoidReason), "Bill already Printed", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return false;
|
||||
}
|
||||
if (_voucher.VoucherID != Guid.Empty && IsPrintedOrVoid(_voucher))
|
||||
return;
|
||||
if (_bill.Count == 1) //new kot only
|
||||
return;
|
||||
return false;
|
||||
#endregion
|
||||
|
||||
//Save
|
||||
@ -461,15 +390,16 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
if (_editVoucherID.HasValue)
|
||||
_saleForm.CloseWindow();
|
||||
else
|
||||
{
|
||||
ClearBill();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void SaveBill()
|
||||
public bool SaveAndPrintBill()
|
||||
{
|
||||
#region Check if Allowed
|
||||
if (!Session.IsAllowed("Print Bill"))
|
||||
return;
|
||||
if (_bill.Count == 1) //new kot only
|
||||
return;
|
||||
if (!Session.IsAllowed("Print Bill") || _bill.Count(x => x.Key.BillItemType == BillItemType.Product) == 0)
|
||||
return false;
|
||||
bool isPrinted = false, isVoid = false;
|
||||
if (_voucher.VoucherID != Guid.Empty)
|
||||
using (var bi = new VoucherBI())
|
||||
@ -481,10 +411,10 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
if (isVoid)
|
||||
{
|
||||
MessageBox.Show(string.Format("This Bill is already void.\nReason: {0}", _voucher.VoidReason), "Bill already Voided", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (isPrinted && !Session.IsAllowed("Edit Printed Bill"))
|
||||
return;
|
||||
return false;
|
||||
#endregion
|
||||
|
||||
var amount = _bill.NetAmount;
|
||||
@ -501,15 +431,19 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
frm.ShowDialog();
|
||||
if (!frm.Selection.HasValue)
|
||||
return;
|
||||
return false;
|
||||
_voucher.VoucherType = frm.Selection.Value;
|
||||
}
|
||||
var saved = _voucher.VoucherID == Guid.Empty ? InsertVoucher(true, !_editVoucherID.HasValue) : UpdateVoucher(true, !_editVoucherID.HasValue);
|
||||
if (_voucher.VoucherID == Guid.Empty)
|
||||
InsertVoucher(true, !_editVoucherID.HasValue);
|
||||
else
|
||||
UpdateVoucher(true, !_editVoucherID.HasValue);
|
||||
}
|
||||
Thermal.PrintBill(_voucher.VoucherID);
|
||||
if (_editVoucherID.HasValue)
|
||||
_saleForm.CloseWindow();
|
||||
ClearBill();
|
||||
return true;
|
||||
}
|
||||
public void SplitBill()
|
||||
{
|
||||
@ -559,9 +493,8 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
return;
|
||||
|
||||
#region new voucherFirst
|
||||
var voucherFirst = new Voucher(Session.User)
|
||||
var voucherFirst = new Voucher(Session.User, _voucher.Customer)
|
||||
{
|
||||
Customer = _voucher.Customer,
|
||||
Table = table,
|
||||
Printed = isPrinted,
|
||||
Void = false,
|
||||
@ -575,9 +508,8 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
#endregion
|
||||
|
||||
#region new voucherSecond
|
||||
var voucherSecond = new Voucher(Session.User)
|
||||
var voucherSecond = new Voucher(Session.User, _voucher.Customer)
|
||||
{
|
||||
Customer = _voucher.Customer,
|
||||
Table = _voucher.Table,
|
||||
Printed = isPrinted,
|
||||
Void = false,
|
||||
@ -604,36 +536,31 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
}
|
||||
LoadBill(voucherFirst.VoucherID);
|
||||
}
|
||||
public void VoidBill()
|
||||
public bool VoidBill()
|
||||
{
|
||||
#region Check conditions and Permissions
|
||||
if (_voucher.VoucherID == Guid.Empty)
|
||||
return;
|
||||
if (_voucher.Void)
|
||||
return;
|
||||
if (!Session.IsAllowed("Void Bill"))
|
||||
return;
|
||||
if (_voucher.VoucherID == Guid.Empty || _voucher.Void || !Session.IsAllowed("Void Bill"))
|
||||
return false;
|
||||
if (MessageBox.Show("Are you sure you want to void this bill?", "Void Bill", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
|
||||
return;
|
||||
return false;
|
||||
#endregion
|
||||
|
||||
var voidReason = new SelectVoidReason(GetVoidReason, true);
|
||||
voidReason.ShowDialog();
|
||||
if (voidReason.SelectedItem != null)
|
||||
{
|
||||
using (var bi = new VoucherBI())
|
||||
{
|
||||
bi.VoidBill(_voucher.VoucherID, voidReason.SelectedItem.Description);
|
||||
if (!_editVoucherID.HasValue)
|
||||
bi.UpdateTable(x => x.FoodTableID == _voucher.Table.FoodTableID && x.VoucherID == _voucher.VoucherID, null, null);
|
||||
bi.SaveChanges();
|
||||
}
|
||||
ClearBill();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Please Select a reason if you want to void the bill", "Bill NOT Voided", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
|
||||
return false;
|
||||
}
|
||||
using (var bi = new VoucherBI())
|
||||
{
|
||||
bi.VoidBill(_voucher.VoucherID, voidReason.SelectedItem.Description);
|
||||
if (!_editVoucherID.HasValue)
|
||||
bi.UpdateTable(x => x.FoodTableID == _voucher.Table.FoodTableID && x.VoucherID == _voucher.VoucherID, null, null);
|
||||
bi.SaveChanges();
|
||||
}
|
||||
ClearBill();
|
||||
return true;
|
||||
}
|
||||
private static List<StringType> GetVoidReason(Dictionary<string, string> filter)
|
||||
{
|
||||
@ -658,9 +585,8 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
if (amountChanged || itemsChanged) // Discount or Products changed
|
||||
{
|
||||
#region new voucherFirst
|
||||
var newVoucher = new Voucher(Session.User)
|
||||
var newVoucher = new Voucher(Session.User, _voucher.Customer)
|
||||
{
|
||||
Customer = _voucher.Customer,
|
||||
Table = _voucher.Table,
|
||||
Printed = true,
|
||||
Void = false,
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
using Tanshu.Accounts.Contracts;
|
||||
using Tanshu.Common;
|
||||
using Tanshu.Accounts.Entities;
|
||||
using System;
|
||||
|
||||
namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
@ -55,7 +56,7 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Where(x => x.Key.BillItemType != BillItemType.Kot).Sum(i => i.Value.inventory.Amount);
|
||||
return Math.Round(this.Where(x => x.Key.BillItemType != BillItemType.Kot).Sum(i => i.Value.inventory.Amount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,13 +11,7 @@ namespace Tanshu.Accounts.PointOfSale
|
||||
{
|
||||
public interface ISaleForm
|
||||
{
|
||||
void ClearBill(BillDict bill);
|
||||
void SetCustomerDisplay(string name);
|
||||
void CloseWindow();
|
||||
void ShowAmount(decimal discountAmount, decimal grossAmount, decimal serviceChargeAmount, decimal taxAmount, decimal valueAmount, BillDict bill);
|
||||
void ShowInfo(Voucher voucher);
|
||||
void SetUserName(string name);
|
||||
BindingSource BindingSource { get; }
|
||||
SaleFormState FormState { set; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user