mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-08 15:42:52 +00:00
[shopping-cart] cart via Jinja WIP
This commit is contained in:
@@ -35,6 +35,15 @@ class SalesInvoice(SellingController):
|
|||||||
'overflow_type': 'billing'
|
'overflow_type': 'billing'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
def set_indicator(self):
|
||||||
|
"""Set indicator for portal"""
|
||||||
|
if self.outstanding_amount > 0:
|
||||||
|
self.indicator_color = "orange"
|
||||||
|
self.indicator_title = _("Unpaid")
|
||||||
|
else:
|
||||||
|
self.indicator_color = "green"
|
||||||
|
self.indicator_title = _("Paid")
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(SalesInvoice, self).validate()
|
super(SalesInvoice, self).validate()
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ def post_process(doctype, data):
|
|||||||
doc.status_percent += flt(doc.per_delivered)
|
doc.status_percent += flt(doc.per_delivered)
|
||||||
doc.status_display.append(_("Delivered") if doc.per_delivered==100 else _("{0}% Delivered").format(doc.per_delivered))
|
doc.status_display.append(_("Delivered") if doc.per_delivered==100 else _("{0}% Delivered").format(doc.per_delivered))
|
||||||
|
|
||||||
|
if hasattr(doc, "set_indicator"):
|
||||||
|
doc.set_indicator()
|
||||||
|
|
||||||
doc.status_display = ", ".join(doc.status_display)
|
doc.status_display = ", ".join(doc.status_display)
|
||||||
doc.items_preview = ", ".join([d.item_name for d in doc.items])
|
doc.items_preview = ", ".join([d.item_name for d in doc.items])
|
||||||
result.append(doc)
|
result.append(doc)
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ def update_cart_address(address_fieldname, address_name):
|
|||||||
quotation.flags.ignore_permissions = True
|
quotation.flags.ignore_permissions = True
|
||||||
quotation.save()
|
quotation.save()
|
||||||
|
|
||||||
return get_cart_quotation(quotation)
|
return frappe.render_template("templates/includes/cart/cart_address.html",
|
||||||
|
get_cart_quotation(quotation))
|
||||||
|
|
||||||
def guess_territory():
|
def guess_territory():
|
||||||
territory = None
|
territory = None
|
||||||
|
|||||||
@@ -1,7 +1,25 @@
|
|||||||
.cart-content {
|
.cart-content {
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
|
margin-top: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cart-header, .cart-footer {
|
.cart-header, .cart-footer {
|
||||||
margin-bottom: 40px;
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item-header {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #d1d8dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tax-grand-total-row {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-addresses {
|
||||||
|
margin-top: 80px;
|
||||||
|
margin-bottom: 60px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ $.extend(shopping_cart, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
bind_events: function() {
|
bind_events: function() {
|
||||||
|
shopping_cart.bind_address_select();
|
||||||
|
|
||||||
// bind update button
|
// bind update button
|
||||||
$(document).on("click", ".item-update-cart button", function() {
|
$(document).on("click", ".item-update-cart button", function() {
|
||||||
var item_code = $(this).attr("data-item-code");
|
var item_code = $(this).attr("data-item-code");
|
||||||
@@ -31,19 +33,36 @@ $.extend(shopping_cart, {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#cart-add-shipping-address").on("click", function() {
|
|
||||||
window.location.href = "addresses";
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#cart-add-billing-address").on("click", function() {
|
|
||||||
window.location.href = "address";
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".btn-place-order").on("click", function() {
|
$(".btn-place-order").on("click", function() {
|
||||||
shopping_cart.place_order(this);
|
shopping_cart.place_order(this);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
bind_address_select: function() {
|
||||||
|
$(".cart-addresses").find('input[data-address-name]').on("click", function() {
|
||||||
|
if($(this).prop("checked")) {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
return frappe.call({
|
||||||
|
type: "POST",
|
||||||
|
method: "erpnext.shopping_cart.cart.update_cart_address",
|
||||||
|
args: {
|
||||||
|
address_fieldname: $(this).attr("data-fieldname"),
|
||||||
|
address_name: $(this).attr("data-address-name")
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(!r.exc) {
|
||||||
|
$('.cart-addresses').html(r.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
render: function(out) {
|
render: function(out) {
|
||||||
var doc = out.doc;
|
var doc = out.doc;
|
||||||
var addresses = out.addresses;
|
var addresses = out.addresses;
|
||||||
@@ -209,32 +228,6 @@ $.extend(shopping_cart, {
|
|||||||
+$(this).attr("data-address-name")+'"]').collapse("toggle");
|
+$(this).attr("data-address-name")+'"]').collapse("toggle");
|
||||||
});
|
});
|
||||||
|
|
||||||
$address_wrapper.find('input[type="checkbox"]').on("click", function() {
|
|
||||||
if($(this).prop("checked")) {
|
|
||||||
var me = this;
|
|
||||||
$address_wrapper.find('input[type="checkbox"]').each(function(i, chk) {
|
|
||||||
if($(chk).attr("data-address-name")!=$(me).attr("data-address-name")) {
|
|
||||||
$(chk).prop("checked", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return frappe.call({
|
|
||||||
type: "POST",
|
|
||||||
method: "erpnext.shopping_cart.cart.update_cart_address",
|
|
||||||
args: {
|
|
||||||
address_fieldname: $address_wrapper.attr("data-fieldname"),
|
|
||||||
address_name: $(this).attr("data-address-name")
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if(!r.exc) {
|
|
||||||
shopping_cart.render(r.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$address_wrapper.find('input[type="checkbox"][data-address-name="'+ address_name +'"]')
|
$address_wrapper.find('input[type="checkbox"][data-address-name="'+ address_name +'"]')
|
||||||
.prop("checked", true);
|
.prop("checked", true);
|
||||||
|
|||||||
24
erpnext/templates/includes/cart/cart_address.html
Normal file
24
erpnext/templates/includes/cart/cart_address.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{% from "erpnext/templates/includes/cart/cart_macros.html"
|
||||||
|
import show_address %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h4>{{ _("Shipping Address") }}</h4>
|
||||||
|
<div id="cart-shipping-address" class="panel-group"
|
||||||
|
data-fieldname="shipping_address_name">
|
||||||
|
{% for address in addresses %}
|
||||||
|
{{ show_address(address, doc, "shipping_address_name") }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-default btn-sm" href="/addresses">
|
||||||
|
{{ _("Manage Addresses") }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h4>Billing Address</h4>
|
||||||
|
<div id="cart-billing-address" class="panel-group"
|
||||||
|
data-fieldname="customer_address">
|
||||||
|
{% for address in addresses %}
|
||||||
|
{{ show_address(address, doc, "customer_address") }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
16
erpnext/templates/includes/cart/cart_item_line.html
Normal file
16
erpnext/templates/includes/cart/cart_item_line.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8 col-xs-6" style="margin-bottom: 10px;">
|
||||||
|
{{ item_name_and_description(d) }}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2 col-xs-3 text-right">
|
||||||
|
<div>{{ d.get_formatted('qty') }}</div>
|
||||||
|
<p class="text-muted small" style="margin-top: 10px;">
|
||||||
|
{{ _("Rate") + ': ' + d.get_formatted("rate") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2 col-xs-3 text-right">
|
||||||
|
{{ d.get_formatted("amount") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
21
erpnext/templates/includes/cart/cart_macros.html
Normal file
21
erpnext/templates/includes/cart/cart_macros.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{% macro show_address(address, doc, fieldname) %}
|
||||||
|
{% set selected=address.name==doc.get(fieldname) %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10 address-title"
|
||||||
|
data-address-name="{{ address.name }}">
|
||||||
|
<strong>{{ address.name }}</strong></div>
|
||||||
|
<div class="col-sm-2 text-right">
|
||||||
|
<input type="checkbox"
|
||||||
|
data-fieldname="{{ fieldname }}"
|
||||||
|
data-address-name="{{ address.name}}"
|
||||||
|
{{ "checked" if selected else "" }}></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-collapse"
|
||||||
|
data-address-name="{{ address.name }}">
|
||||||
|
<div class="panel-body text-muted small">{{ address.display }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
@@ -15,16 +15,3 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro item_name_and_description(d) %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-4 col-sm-2 order-image-col">
|
|
||||||
<div class="order-image">
|
|
||||||
{{ product_image_square(d.image) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-8 col-sm-10">
|
|
||||||
{{ d.item_code }}
|
|
||||||
<p class="text-muted small">{{ d.description }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|||||||
15
erpnext/templates/includes/order/order_macros.html
Normal file
15
erpnext/templates/includes/order/order_macros.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{% from "erpnext/templates/includes/macros.html" import product_image_square %}
|
||||||
|
|
||||||
|
{% macro item_name_and_description(d) %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-4 col-sm-2 order-image-col">
|
||||||
|
<div class="order-image">
|
||||||
|
{{ product_image_square(d.image) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-8 col-sm-10">
|
||||||
|
{{ d.item_code }}
|
||||||
|
<p class="text-muted small">{{ d.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
19
erpnext/templates/includes/order/order_taxes.html
Normal file
19
erpnext/templates/includes/order/order_taxes.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% if doc.taxes %}
|
||||||
|
<div class="row tax-net-total-row">
|
||||||
|
<div class="col-xs-6 text-right">{{ _("Net Total") }}</div>
|
||||||
|
<div class="col-xs-6 text-right">
|
||||||
|
{{ doc.get_formatted("net_total") }}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% for d in doc.taxes %}
|
||||||
|
<div class="row tax-row">
|
||||||
|
<div class="col-xs-6 text-right">{{ d.description }}</div>
|
||||||
|
<div class="col-xs-6 text-right">
|
||||||
|
{{ d.get_formatted("base_tax_amount") }}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="row tax-grand-total-row">
|
||||||
|
<div class="col-xs-6 text-right">{{ _("Grand Total") }}</div>
|
||||||
|
<div class="col-xs-6 text-right">
|
||||||
|
{{ doc.get_formatted("grand_total") }}</div>
|
||||||
|
</div>
|
||||||
@@ -5,81 +5,62 @@
|
|||||||
{% block script %}{% include "templates/includes/cart.js" %}{% endblock %}
|
{% block script %}{% include "templates/includes/cart.js" %}{% endblock %}
|
||||||
{% block style %}{% include "templates/includes/cart.css" %}{% endblock %}
|
{% block style %}{% include "templates/includes/cart.css" %}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block header_actions %}
|
||||||
|
{% if doc.items %}
|
||||||
|
<button class="btn btn-primary btn-place-order btn-sm"
|
||||||
|
type="button">
|
||||||
|
{{ _("Place Order") }}</button>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% from "erpnext/templates/includes/macros.html" import item_name_and_description %}
|
{% from "templates/includes/macros.html" import item_name_and_description %}
|
||||||
|
|
||||||
<div class="cart-content">
|
<div class="cart-content">
|
||||||
<p class="text-muted loading">{{ _("Loading") }}...</p>
|
|
||||||
<div id="cart-container">
|
<div id="cart-container">
|
||||||
<div class="cart-header">
|
<div id="cart-error" class="alert alert-danger"
|
||||||
<p class="pull-right"><button class="btn btn-primary btn-place-order btn-sm" type="button">
|
style="display: none;"></div>
|
||||||
{{ _("Place Order") }}</button></p>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
</div>
|
|
||||||
<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
|
|
||||||
<div id="cart-items">
|
<div id="cart-items">
|
||||||
|
<div class="row cart-item-header">
|
||||||
|
<div class="col-sm-8 col-xs-6">
|
||||||
|
Items
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2 col-xs-3 text-right">
|
||||||
|
Qty
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-2 col-xs-3 text-right">
|
||||||
|
Amount
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% if doc.items %}
|
{% if doc.items %}
|
||||||
{% for d in doc.items %}
|
{% for d in doc.items %}
|
||||||
<div class="cart-item">
|
<div class="cart-item">
|
||||||
<div class="row">
|
{% include "templates/includes/cart/cart_item_line.html" %}
|
||||||
<div class="col-sm-8 col-xs-6">
|
|
||||||
{{ item_name_and_description(d) }}
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4 col-xs-6 text-right">
|
|
||||||
<div class="item-update-cart">
|
|
||||||
<div class="clearfix">
|
|
||||||
<input type="text" placeholder="Qty"
|
|
||||||
style="max-width: 60px"
|
|
||||||
value="{{ d.qty }}"
|
|
||||||
data-item-code="{{ d.item_code }}"
|
|
||||||
class="text-right form-control pull-right">
|
|
||||||
</div>
|
|
||||||
<div class="text-right">
|
|
||||||
<button class="btn btn-default btn-sm"
|
|
||||||
data-item-code="{{ d.item_code }}">
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="text-muted small" style="margin-top: 10px;">
|
|
||||||
{{ _("Rate") + ': ' + d.get_formatted("rate") }}
|
|
||||||
</p>
|
|
||||||
<small style="margin-top: 10px;">
|
|
||||||
{{ d.get_formatted("amount") }}</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>{{ _("Cart is Empty") }}</p>
|
<p>{{ _("Cart is Empty") }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div id="cart-taxes">
|
{% if doc.items %}
|
||||||
|
<!-- taxes -->
|
||||||
|
<div class="cart-taxes row small">
|
||||||
|
<div class="col-sm-8"><!-- empty --></div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
{% include "templates/includes/order/order_taxes.html" %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="cart-totals">
|
<div id="cart-totals">
|
||||||
</div>
|
</div>
|
||||||
<div id="cart-addresses">
|
<div class="cart-addresses">
|
||||||
<div class="row">
|
{% include "templates/includes/cart/cart_address.html" %}
|
||||||
<div class="col-md-6">
|
|
||||||
<h4>{{ _("Shipping Address") }}</h4>
|
|
||||||
<div id="cart-shipping-address" class="panel-group"
|
|
||||||
data-fieldname="shipping_address_name"></div>
|
|
||||||
<button class="btn btn-default btn-sm" type="button" id="cart-add-shipping-address">
|
|
||||||
{{ _("Manage Addresses") }}</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h4>Billing Address</h4>
|
|
||||||
<div id="cart-billing-address" class="panel-group"
|
|
||||||
data-fieldname="customer_address"></div>
|
|
||||||
<button class="btn btn-default btn-sm" type="button" id="cart-add-billing-address">
|
|
||||||
{{ _("Manage Addresses") }}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="cart-footer text-right">
|
<p class="cart-footer text-right">
|
||||||
<button class="btn btn-primary btn-place-order btn-sm" type="button">
|
<button class="btn btn-primary btn-place-order btn-sm" type="button">
|
||||||
{{ _("Place Order") }}</button></p>
|
{{ _("Place Order") }}</button></p>
|
||||||
</div>
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -10,5 +10,3 @@ from erpnext.shopping_cart.cart import get_cart_quotation
|
|||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.update(get_cart_quotation())
|
context.update(get_cart_quotation())
|
||||||
|
|
||||||
print context
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<!-- <h6 class="text-muted">{{ doc._title or doc.doctype }}</h6> -->
|
<!-- <h6 class="text-muted">{{ doc._title or doc.doctype }}</h6> -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block style %}{% include "templates/includes/order.css" %}{% endblock %}
|
{% block style %}{% include "templates/includes/order/order.css" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% from "erpnext/templates/includes/macros.html" import item_name_and_description %}
|
{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description %}
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
@@ -64,25 +64,7 @@
|
|||||||
<div class="order-taxes row small">
|
<div class="order-taxes row small">
|
||||||
<div class="col-sm-8"><!-- empty --></div>
|
<div class="col-sm-8"><!-- empty --></div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
{% if doc.taxes %}
|
{% include "erpnext/templates/includes/order/order_taxes.html" %}
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-6 text-right">{{ _("Net Total") }}</div>
|
|
||||||
<div class="col-xs-6 text-right">
|
|
||||||
{{ doc.get_formatted("net_total") }}</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% for d in doc.taxes %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-6 text-right">{{ d.description }}</div>
|
|
||||||
<div class="col-xs-6 text-right">
|
|
||||||
{{ d.get_formatted("total") }}</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-6 text-right">{{ _("Grand Total") }}</div>
|
|
||||||
<div class="col-xs-6 text-right">
|
|
||||||
{{ doc.get_formatted("grand_total") }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ from frappe import _
|
|||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.no_cache = 1
|
context.no_cache = 1
|
||||||
context.doc = frappe.get_doc(frappe.form_dict.doctype, frappe.form_dict.name)
|
context.doc = frappe.get_doc(frappe.form_dict.doctype, frappe.form_dict.name)
|
||||||
|
if hasattr(context.doc, "set_indicator"):
|
||||||
|
context.doc.set_indicator()
|
||||||
|
|
||||||
context.parents = frappe.form_dict.parents
|
context.parents = frappe.form_dict.parents
|
||||||
|
|
||||||
if not context.doc.has_permission("read"):
|
if not context.doc.has_permission("read"):
|
||||||
|
|||||||
Reference in New Issue
Block a user