mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-19 04:59:18 +00:00
[fix] validate_valuation_rate for Repack
This commit is contained in:
committed by
Rushabh Mehta
parent
72fbf902d7
commit
52dfc32eca
@@ -147,7 +147,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) {
|
|||||||
cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) {
|
cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) {
|
||||||
return{
|
return{
|
||||||
query: "erpnext.controllers.queries.item_query",
|
query: "erpnext.controllers.queries.item_query",
|
||||||
filters: [["Item", "name", "!=", doc.item]]
|
filters: [["Item", "name", "!=", cur_frm.doc.item]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class StockEntry(StockController):
|
|||||||
self.validate_finished_goods()
|
self.validate_finished_goods()
|
||||||
self.validate_with_material_request()
|
self.validate_with_material_request()
|
||||||
self.validate_batch()
|
self.validate_batch()
|
||||||
|
|
||||||
self.set_actual_qty()
|
self.set_actual_qty()
|
||||||
self.calculate_rate_and_amount()
|
self.calculate_rate_and_amount()
|
||||||
|
|
||||||
@@ -212,10 +212,10 @@ class StockEntry(StockController):
|
|||||||
if fg_qty_already_entered >= qty:
|
if fg_qty_already_entered >= qty:
|
||||||
frappe.throw(_("Stock Entries already created for Production Order ")
|
frappe.throw(_("Stock Entries already created for Production Order ")
|
||||||
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
||||||
|
|
||||||
def set_actual_qty(self):
|
def set_actual_qty(self):
|
||||||
allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
|
allow_negative_stock = cint(frappe.db.get_value("Stock Settings", None, "allow_negative_stock"))
|
||||||
|
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
previous_sle = get_previous_sle({
|
previous_sle = get_previous_sle({
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
@@ -232,7 +232,7 @@ class StockEntry(StockController):
|
|||||||
frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
|
frappe.throw(_("""Row {0}: Qty not avalable in warehouse {1} on {2} {3}.
|
||||||
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
|
Available Qty: {4}, Transfer Qty: {5}""").format(d.idx, d.s_warehouse,
|
||||||
self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError)
|
self.posting_date, self.posting_time, d.actual_qty, d.transfer_qty), NegativeStockError)
|
||||||
|
|
||||||
def get_stock_and_rate(self):
|
def get_stock_and_rate(self):
|
||||||
self.set_actual_qty()
|
self.set_actual_qty()
|
||||||
self.calculate_rate_and_amount()
|
self.calculate_rate_and_amount()
|
||||||
@@ -244,7 +244,7 @@ class StockEntry(StockController):
|
|||||||
self.validate_valuation_rate()
|
self.validate_valuation_rate()
|
||||||
self.set_total_incoming_outgoing_value()
|
self.set_total_incoming_outgoing_value()
|
||||||
self.set_total_amount()
|
self.set_total_amount()
|
||||||
|
|
||||||
def set_basic_rate(self, force=False):
|
def set_basic_rate(self, force=False):
|
||||||
"""get stock and incoming rate on posting date"""
|
"""get stock and incoming rate on posting date"""
|
||||||
raw_material_cost = 0.0
|
raw_material_cost = 0.0
|
||||||
@@ -269,9 +269,9 @@ class StockEntry(StockController):
|
|||||||
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
|
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
|
||||||
if not d.t_warehouse:
|
if not d.t_warehouse:
|
||||||
raw_material_cost += flt(d.basic_amount)
|
raw_material_cost += flt(d.basic_amount)
|
||||||
|
|
||||||
self.set_basic_rate_for_finished_goods(raw_material_cost)
|
self.set_basic_rate_for_finished_goods(raw_material_cost)
|
||||||
|
|
||||||
def set_basic_rate_for_finished_goods(self, raw_material_cost):
|
def set_basic_rate_for_finished_goods(self, raw_material_cost):
|
||||||
if self.purpose in ["Manufacture", "Repack"]:
|
if self.purpose in ["Manufacture", "Repack"]:
|
||||||
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
|
||||||
@@ -279,11 +279,11 @@ class StockEntry(StockController):
|
|||||||
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
||||||
d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate"))
|
d.basic_rate = flt(raw_material_cost / flt(d.transfer_qty), d.precision("basic_rate"))
|
||||||
d.basic_amount = flt(raw_material_cost, d.precision("basic_amount"))
|
d.basic_amount = flt(raw_material_cost, d.precision("basic_amount"))
|
||||||
|
|
||||||
def distribute_additional_costs(self):
|
def distribute_additional_costs(self):
|
||||||
if self.purpose == "Material Issue":
|
if self.purpose == "Material Issue":
|
||||||
self.additional_costs = []
|
self.additional_costs = []
|
||||||
|
|
||||||
self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
|
self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
|
||||||
total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
|
total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
|
||||||
|
|
||||||
@@ -292,11 +292,11 @@ class StockEntry(StockController):
|
|||||||
d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs
|
d.additional_cost = (flt(d.basic_amount) / total_basic_amount) * self.total_additional_costs
|
||||||
else:
|
else:
|
||||||
d.additional_cost = 0
|
d.additional_cost = 0
|
||||||
|
|
||||||
def update_valuation_rate(self):
|
def update_valuation_rate(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount"))
|
d.amount = flt(d.basic_amount + flt(d.additional_cost), d.precision("amount"))
|
||||||
d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty),
|
d.valuation_rate = flt(flt(d.basic_rate) + flt(d.additional_cost) / flt(d.transfer_qty),
|
||||||
d.precision("valuation_rate"))
|
d.precision("valuation_rate"))
|
||||||
|
|
||||||
def validate_valuation_rate(self):
|
def validate_valuation_rate(self):
|
||||||
@@ -373,7 +373,7 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
def update_stock_ledger(self):
|
def update_stock_ledger(self):
|
||||||
sl_entries = []
|
sl_entries = []
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if cstr(d.s_warehouse) and self.docstatus == 1:
|
if cstr(d.s_warehouse) and self.docstatus == 1:
|
||||||
sl_entries.append(self.get_sl_entries(d, {
|
sl_entries.append(self.get_sl_entries(d, {
|
||||||
"warehouse": cstr(d.s_warehouse),
|
"warehouse": cstr(d.s_warehouse),
|
||||||
@@ -399,15 +399,15 @@ class StockEntry(StockController):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
|
self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
|
||||||
|
|
||||||
def get_gl_entries(self, warehouse_account):
|
def get_gl_entries(self, warehouse_account):
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||||
|
|
||||||
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
|
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
|
||||||
|
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
additional_cost = flt(d.additional_cost, d.precision("additional_cost"))
|
additional_cost = flt(d.additional_cost, d.precision("additional_cost"))
|
||||||
if additional_cost:
|
if additional_cost:
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": expenses_included_in_valuation,
|
"account": expenses_included_in_valuation,
|
||||||
"against": d.expense_account,
|
"against": d.expense_account,
|
||||||
@@ -415,7 +415,7 @@ class StockEntry(StockController):
|
|||||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||||
"credit": additional_cost
|
"credit": additional_cost
|
||||||
}))
|
}))
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": d.expense_account,
|
"account": d.expense_account,
|
||||||
"against": expenses_included_in_valuation,
|
"against": expenses_included_in_valuation,
|
||||||
@@ -521,7 +521,7 @@ class StockEntry(StockController):
|
|||||||
def get_items(self):
|
def get_items(self):
|
||||||
self.set('items', [])
|
self.set('items', [])
|
||||||
self.validate_production_order()
|
self.validate_production_order()
|
||||||
|
|
||||||
if not self.posting_date or not self.posting_time:
|
if not self.posting_date or not self.posting_time:
|
||||||
frappe.throw(_("Posting date and posting time is mandatory"))
|
frappe.throw(_("Posting date and posting time is mandatory"))
|
||||||
|
|
||||||
@@ -548,7 +548,7 @@ class StockEntry(StockController):
|
|||||||
for item in item_dict.values():
|
for item in item_dict.values():
|
||||||
item["to_warehouse"] = self.pro_doc.wip_warehouse
|
item["to_warehouse"] = self.pro_doc.wip_warehouse
|
||||||
self.add_to_stock_entry_detail(item_dict)
|
self.add_to_stock_entry_detail(item_dict)
|
||||||
|
|
||||||
elif self.production_order and self.purpose == "Manufacture" and \
|
elif self.production_order and self.purpose == "Manufacture" and \
|
||||||
frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture":
|
frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")== "Material Transferred for Manufacture":
|
||||||
self.get_transfered_raw_materials()
|
self.get_transfered_raw_materials()
|
||||||
@@ -578,10 +578,14 @@ class StockEntry(StockController):
|
|||||||
to_warehouse = self.pro_doc.fg_warehouse
|
to_warehouse = self.pro_doc.fg_warehouse
|
||||||
else:
|
else:
|
||||||
item_code = frappe.db.get_value("BOM", self.bom_no, "item")
|
item_code = frappe.db.get_value("BOM", self.bom_no, "item")
|
||||||
to_warehouse = ""
|
to_warehouse = self.to_warehouse
|
||||||
|
|
||||||
item = frappe.db.get_value("Item", item_code, ["item_name",
|
item = frappe.db.get_value("Item", item_code, ["item_name",
|
||||||
"description", "stock_uom", "expense_account", "buying_cost_center", "name"], as_dict=1)
|
"description", "stock_uom", "expense_account", "buying_cost_center", "name", "default_warehouse"], as_dict=1)
|
||||||
|
|
||||||
|
if not self.production_order and not to_warehouse:
|
||||||
|
# in case of BOM
|
||||||
|
to_warehouse = item.default_warehouse
|
||||||
|
|
||||||
self.add_to_stock_entry_detail({
|
self.add_to_stock_entry_detail({
|
||||||
item.name: {
|
item.name: {
|
||||||
@@ -600,57 +604,57 @@ class StockEntry(StockController):
|
|||||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
|
||||||
|
|
||||||
# item dict = { item_code: {qty, description, stock_uom} }
|
# item dict = { item_code: {qty, description, stock_uom} }
|
||||||
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
|
item_dict = get_bom_items_as_dict(self.bom_no, self.company, qty=qty,
|
||||||
fetch_exploded = self.use_multi_level_bom)
|
fetch_exploded = self.use_multi_level_bom)
|
||||||
|
|
||||||
for item in item_dict.values():
|
for item in item_dict.values():
|
||||||
item.from_warehouse = self.from_warehouse or item.default_warehouse
|
item.from_warehouse = self.from_warehouse or item.default_warehouse
|
||||||
return item_dict
|
return item_dict
|
||||||
|
|
||||||
def get_transfered_raw_materials(self):
|
def get_transfered_raw_materials(self):
|
||||||
transferred_materials = frappe.db.sql("""
|
transferred_materials = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
||||||
description, stock_uom, expense_account, cost_center
|
description, stock_uom, expense_account, cost_center
|
||||||
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
||||||
where
|
where
|
||||||
se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
|
se.name = sed.parent and se.docstatus=1 and se.purpose='Material Transfer for Manufacture'
|
||||||
and se.production_order= %s and ifnull(sed.t_warehouse, '') != ''
|
and se.production_order= %s and ifnull(sed.t_warehouse, '') != ''
|
||||||
group by sed.item_code, sed.t_warehouse
|
group by sed.item_code, sed.t_warehouse
|
||||||
""", self.production_order, as_dict=1)
|
""", self.production_order, as_dict=1)
|
||||||
|
|
||||||
materials_already_backflushed = frappe.db.sql("""
|
materials_already_backflushed = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
item_code, sed.s_warehouse as warehouse, sum(qty) as qty
|
item_code, sed.s_warehouse as warehouse, sum(qty) as qty
|
||||||
from
|
from
|
||||||
`tabStock Entry` se, `tabStock Entry Detail` sed
|
`tabStock Entry` se, `tabStock Entry Detail` sed
|
||||||
where
|
where
|
||||||
se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture'
|
se.name = sed.parent and se.docstatus=1 and se.purpose='Manufacture'
|
||||||
and se.production_order= %s and ifnull(sed.s_warehouse, '') != ''
|
and se.production_order= %s and ifnull(sed.s_warehouse, '') != ''
|
||||||
group by sed.item_code, sed.s_warehouse
|
group by sed.item_code, sed.s_warehouse
|
||||||
""", self.production_order, as_dict=1)
|
""", self.production_order, as_dict=1)
|
||||||
|
|
||||||
backflushed_materials= {}
|
backflushed_materials= {}
|
||||||
for d in materials_already_backflushed:
|
for d in materials_already_backflushed:
|
||||||
backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty})
|
backflushed_materials.setdefault(d.item_code,[]).append({d.warehouse: d.qty})
|
||||||
|
|
||||||
po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from
|
po_qty = frappe.db.sql("""select qty, produced_qty, material_transferred_for_manufacturing from
|
||||||
`tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0]
|
`tabProduction Order` where name=%s""", self.production_order, as_dict=1)[0]
|
||||||
manufacturing_qty = flt(po_qty.qty)
|
manufacturing_qty = flt(po_qty.qty)
|
||||||
produced_qty = flt(po_qty.produced_qty)
|
produced_qty = flt(po_qty.produced_qty)
|
||||||
trans_qty = flt(po_qty.material_transferred_for_manufacturing)
|
trans_qty = flt(po_qty.material_transferred_for_manufacturing)
|
||||||
|
|
||||||
for item in transferred_materials:
|
for item in transferred_materials:
|
||||||
qty= item.qty
|
qty= item.qty
|
||||||
|
|
||||||
if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
|
if manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
|
||||||
qty = (qty/trans_qty) * flt(self.fg_completed_qty)
|
qty = (qty/trans_qty) * flt(self.fg_completed_qty)
|
||||||
|
|
||||||
elif backflushed_materials.get(item.item_code):
|
elif backflushed_materials.get(item.item_code):
|
||||||
for d in backflushed_materials.get(item.item_code):
|
for d in backflushed_materials.get(item.item_code):
|
||||||
if d.get(item.warehouse):
|
if d.get(item.warehouse):
|
||||||
qty-= d.get(item.warehouse)
|
qty-= d.get(item.warehouse)
|
||||||
|
|
||||||
if qty > 0:
|
if qty > 0:
|
||||||
self.add_to_stock_entry_detail({
|
self.add_to_stock_entry_detail({
|
||||||
item.item_code: {
|
item.item_code: {
|
||||||
@@ -729,7 +733,7 @@ class StockEntry(StockController):
|
|||||||
se_child.s_warehouse = self.from_warehouse
|
se_child.s_warehouse = self.from_warehouse
|
||||||
if se_child.t_warehouse==None:
|
if se_child.t_warehouse==None:
|
||||||
se_child.t_warehouse = self.to_warehouse
|
se_child.t_warehouse = self.to_warehouse
|
||||||
|
|
||||||
# in stock uom
|
# in stock uom
|
||||||
se_child.transfer_qty = flt(item_dict[d]["qty"])
|
se_child.transfer_qty = flt(item_dict[d]["qty"])
|
||||||
se_child.conversion_factor = 1.00
|
se_child.conversion_factor = 1.00
|
||||||
@@ -761,7 +765,7 @@ class StockEntry(StockController):
|
|||||||
def get_production_order_details(production_order):
|
def get_production_order_details(production_order):
|
||||||
production_order = frappe.get_doc("Production Order", production_order)
|
production_order = frappe.get_doc("Production Order", production_order)
|
||||||
pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty)
|
pending_qty_to_produce = flt(production_order.qty) - flt(production_order.produced_qty)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"from_bom": 1,
|
"from_bom": 1,
|
||||||
"bom_no": production_order.bom_no,
|
"bom_no": production_order.bom_no,
|
||||||
@@ -771,7 +775,7 @@ def get_production_order_details(production_order):
|
|||||||
"fg_completed_qty": pending_qty_to_produce,
|
"fg_completed_qty": pending_qty_to_produce,
|
||||||
"additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce)
|
"additional_costs": get_additional_costs(production_order, fg_qty=pending_qty_to_produce)
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_additional_costs(production_order=None, bom_no=None, fg_qty=None):
|
def get_additional_costs(production_order=None, bom_no=None, fg_qty=None):
|
||||||
additional_costs = []
|
additional_costs = []
|
||||||
operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no)
|
operating_cost_per_unit = get_operating_cost_per_unit(production_order, bom_no)
|
||||||
@@ -780,33 +784,33 @@ def get_additional_costs(production_order=None, bom_no=None, fg_qty=None):
|
|||||||
"description": "Operating Cost as per Production Order / BOM",
|
"description": "Operating Cost as per Production Order / BOM",
|
||||||
"amount": operating_cost_per_unit * flt(fg_qty)
|
"amount": operating_cost_per_unit * flt(fg_qty)
|
||||||
})
|
})
|
||||||
|
|
||||||
if production_order and production_order.additional_operating_cost:
|
if production_order and production_order.additional_operating_cost:
|
||||||
additional_operating_cost_per_unit = \
|
additional_operating_cost_per_unit = \
|
||||||
flt(production_order.additional_operating_cost) / flt(production_order.qty)
|
flt(production_order.additional_operating_cost) / flt(production_order.qty)
|
||||||
|
|
||||||
additional_costs.append({
|
additional_costs.append({
|
||||||
"description": "Additional Operating Cost",
|
"description": "Additional Operating Cost",
|
||||||
"amount": additional_operating_cost_per_unit * flt(fg_qty)
|
"amount": additional_operating_cost_per_unit * flt(fg_qty)
|
||||||
})
|
})
|
||||||
|
|
||||||
return additional_costs
|
return additional_costs
|
||||||
|
|
||||||
def get_operating_cost_per_unit(production_order=None, bom_no=None):
|
def get_operating_cost_per_unit(production_order=None, bom_no=None):
|
||||||
operating_cost_per_unit = 0
|
operating_cost_per_unit = 0
|
||||||
if production_order:
|
if production_order:
|
||||||
if not bom_no:
|
if not bom_no:
|
||||||
bom_no = production_order.bom_no
|
bom_no = production_order.bom_no
|
||||||
|
|
||||||
for d in production_order.get("operations"):
|
for d in production_order.get("operations"):
|
||||||
if flt(d.completed_qty):
|
if flt(d.completed_qty):
|
||||||
operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
operating_cost_per_unit += flt(d.actual_operating_cost) / flt(d.completed_qty)
|
||||||
else:
|
else:
|
||||||
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty)
|
operating_cost_per_unit += flt(d.planned_operating_cost) / flt(production_order.qty)
|
||||||
|
|
||||||
# Get operating cost from BOM if not found in production_order.
|
# Get operating cost from BOM if not found in production_order.
|
||||||
if not operating_cost_per_unit and bom_no:
|
if not operating_cost_per_unit and bom_no:
|
||||||
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
bom = frappe.db.get_value("BOM", bom_no, ["operating_cost", "quantity"], as_dict=1)
|
||||||
operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
|
operating_cost_per_unit = flt(bom.operating_cost) / flt(bom.quantity)
|
||||||
|
|
||||||
return operating_cost_per_unit
|
return operating_cost_per_unit
|
||||||
|
|||||||
Reference in New Issue
Block a user