feat: shopping cart page
This commit is contained in:
parent
706eae7cac
commit
b00eb1b0cc
BIN
erpnext/public/images/ui-states/cart-empty-state.png
Normal file
BIN
erpnext/public/images/ui-states/cart-empty-state.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -5,6 +5,19 @@ body.product-page {
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
|
||||
.item-breadcrumbs {
|
||||
.breadcrumb-container {
|
||||
ol.breadcrumb {
|
||||
background-color: var(--gray-50) !important;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--gray-900);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.carousel-control {
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
@ -77,6 +90,18 @@ body.product-page {
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.no-image {
|
||||
@include flex(flex, center, center, null);
|
||||
height: 200px;
|
||||
margin: 0 auto;
|
||||
margin-top: var(--margin-xl);
|
||||
background: var(--gray-100);
|
||||
width: 80%;
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 2rem;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
|
||||
.product-title {
|
||||
font-size: 14px;
|
||||
color: var(--gray-800);
|
||||
@ -98,7 +123,7 @@ body.product-page {
|
||||
|
||||
.product-category {
|
||||
font-size: 13px;
|
||||
color: var(--gray-600);
|
||||
color: var(--text-muted);
|
||||
margin: var(--margin-sm) 0;
|
||||
}
|
||||
|
||||
@ -159,6 +184,15 @@ body.product-page {
|
||||
@include card($padding: var(--padding-md));
|
||||
min-height: 70vh;
|
||||
|
||||
.product-details {
|
||||
max-width: 40%;
|
||||
margin-left: -30px;
|
||||
|
||||
.btn-add-to-cart {
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
}
|
||||
|
||||
.product-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
@ -166,7 +200,7 @@ body.product-page {
|
||||
}
|
||||
|
||||
.product-code {
|
||||
color: var(--gray-600);
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@ -180,15 +214,15 @@ body.product-page {
|
||||
padding: 15px;
|
||||
|
||||
@include media-breakpoint-between(xs, md) {
|
||||
height: 320px;
|
||||
width: 320px;
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
height: 430px;
|
||||
width: 420px;
|
||||
height: 350px;
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
}
|
||||
@ -202,13 +236,13 @@ body.product-page {
|
||||
@include media-breakpoint-up(lg) {
|
||||
max-height: 430px;
|
||||
}
|
||||
|
||||
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.item-slideshow-image {
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
width: 6rem;
|
||||
object-fit: contain;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--table-border-color);
|
||||
@ -227,25 +261,57 @@ body.product-page {
|
||||
font-weight: 600;
|
||||
|
||||
.formatted-price {
|
||||
color: var(--gray-600);
|
||||
font-size: 14px;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
}
|
||||
|
||||
.no-stock {
|
||||
font-size: 14px;
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-breadcrumbs {
|
||||
.breadcrumb-container {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
.item-configurator-dialog {
|
||||
.modal-header {
|
||||
padding: var(--padding-md) var(--padding-xl);
|
||||
}
|
||||
|
||||
a {
|
||||
color: $text-muted;
|
||||
.modal-body {
|
||||
padding: 0 var(--padding-xl);
|
||||
padding-bottom: var(--padding-xl);
|
||||
|
||||
.status-area {
|
||||
.alert {
|
||||
padding: var(--padding-xs) var(--padding-sm);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
}
|
||||
|
||||
.form-layout {
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.section-body {
|
||||
.form-column {
|
||||
.form-group {
|
||||
.control-label {
|
||||
font-size: var(--text-md);
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.help-box {
|
||||
margin-top: 2px;
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,4 +323,168 @@ body.product-page {
|
||||
.carousel-inner.rounded-carousel {
|
||||
border-radius: $card-border-radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cart-icon {
|
||||
.cart-badge {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
left: -12px;
|
||||
background: var(--red-600);
|
||||
width: 16px;
|
||||
align-items: center;
|
||||
height: 16px;
|
||||
font-size: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#page-cart {
|
||||
.shopping-cart-header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cart-container {
|
||||
color: var(--text-color);
|
||||
|
||||
.frappe-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.cart-items-header {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cart-table {
|
||||
th, tr, td {
|
||||
border-color: var(--border-color);
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
padding: var(--padding-sm) 0;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: var(--padding-sm) 0;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.cart-items {
|
||||
.item-title {
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.item-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--text-md);
|
||||
}
|
||||
|
||||
.item-subtotal {
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-rate {
|
||||
font-size: var(--text-md);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.cart-tax-items {
|
||||
.item-grand-total {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cart-addresses {
|
||||
hr {
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.number-spinner {
|
||||
width: 75%;
|
||||
.cart-btn {
|
||||
border: none;
|
||||
background: var(--gray-100);
|
||||
box-shadow: none;
|
||||
height: 28px;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.cart-qty {
|
||||
height: 28px;
|
||||
font-size: var(--text-md);
|
||||
}
|
||||
}
|
||||
|
||||
.place-order-container {
|
||||
.btn-place-order {
|
||||
width: 62%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cart-empty.frappe-card {
|
||||
min-height: 76vh;
|
||||
@include flex(flex, center, center, column);
|
||||
|
||||
.cart-empty-message {
|
||||
font-size: 18px;
|
||||
color: var(--text-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.address-card {
|
||||
.card-title {
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card-text {
|
||||
font-size: var(--text-md);
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.card-link {
|
||||
font-size: var(--text-md);
|
||||
|
||||
svg use {
|
||||
stroke: var(--blue-500);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-change-address {
|
||||
color: var(--blue-500);
|
||||
box-shadow: none;
|
||||
border: 1px solid var(--blue-500);
|
||||
}
|
||||
}
|
||||
|
||||
.modal .address-card {
|
||||
.card-body {
|
||||
padding: var(--padding-sm);
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--dark-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,14 +42,30 @@ def get_cart_quotation(doc=None):
|
||||
|
||||
return {
|
||||
"doc": decorate_quotation_doc(doc),
|
||||
"shipping_addresses": [{"name": address.name, "title": address.address_title, "display": address.display}
|
||||
for address in addresses if address.address_type == "Shipping"],
|
||||
"billing_addresses": [{"name": address.name, "title": address.address_title, "display": address.display}
|
||||
for address in addresses if address.address_type == "Billing"],
|
||||
"shipping_addresses": get_shipping_addresses(party),
|
||||
"billing_addresses": get_billing_addresses(party),
|
||||
"shipping_rules": get_applicable_shipping_rules(party),
|
||||
"cart_settings": frappe.get_cached_doc("Shopping Cart Settings")
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_shipping_addresses(party=None):
|
||||
if not party:
|
||||
party = get_party()
|
||||
addresses = get_address_docs(party=party)
|
||||
return [{"name": address.name, "title": address.address_title, "display": address.display}
|
||||
for address in addresses if address.address_type == "Shipping"
|
||||
]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_billing_addresses(party=None):
|
||||
if not party:
|
||||
party = get_party()
|
||||
addresses = get_address_docs(party=party)
|
||||
return [{"name": address.name, "title": address.address_title, "display": address.display}
|
||||
for address in addresses if address.address_type == "Billing"
|
||||
]
|
||||
|
||||
@frappe.whitelist()
|
||||
def place_order():
|
||||
quotation = _get_cart_quotation()
|
||||
@ -203,27 +219,33 @@ def get_terms_and_conditions(terms_name):
|
||||
@frappe.whitelist()
|
||||
def update_cart_address(address_type, address_name):
|
||||
quotation = _get_cart_quotation()
|
||||
address_display = get_address_display(frappe.get_doc("Address", address_name).as_dict())
|
||||
address_doc = frappe.get_doc("Address", address_name).as_dict()
|
||||
address_display = get_address_display(address_doc)
|
||||
|
||||
if address_type.lower() == "billing":
|
||||
quotation.customer_address = address_name
|
||||
quotation.address_display = address_display
|
||||
quotation.shipping_address_name == quotation.shipping_address_name or address_name
|
||||
address_doc = next((doc for doc in get_billing_addresses() if doc["name"] == address_name), None)
|
||||
elif address_type.lower() == "shipping":
|
||||
quotation.shipping_address_name = address_name
|
||||
quotation.shipping_address = address_display
|
||||
quotation.customer_address == quotation.customer_address or address_name
|
||||
|
||||
address_doc = next((doc for doc in get_shipping_addresses() if doc["name"] == address_name), None)
|
||||
apply_cart_settings(quotation=quotation)
|
||||
|
||||
quotation.flags.ignore_permissions = True
|
||||
quotation.save()
|
||||
|
||||
context = get_cart_quotation(quotation)
|
||||
context['address'] = address_doc
|
||||
|
||||
return {
|
||||
"taxes": frappe.render_template("templates/includes/order/order_taxes.html",
|
||||
context),
|
||||
}
|
||||
"address": frappe.render_template("templates/includes/cart/address_card.html",
|
||||
context)
|
||||
}
|
||||
|
||||
def guess_territory():
|
||||
territory = None
|
||||
|
@ -14,7 +14,7 @@ $.extend(shopping_cart, {
|
||||
},
|
||||
|
||||
bind_events: function() {
|
||||
shopping_cart.bind_address_select();
|
||||
shopping_cart.bind_address_picker_dialog();
|
||||
shopping_cart.bind_place_order();
|
||||
shopping_cart.bind_request_quotation();
|
||||
shopping_cart.bind_change_qty();
|
||||
@ -23,28 +23,76 @@ $.extend(shopping_cart, {
|
||||
shopping_cart.bind_coupon_code();
|
||||
},
|
||||
|
||||
bind_address_select: function() {
|
||||
$(".cart-addresses").on('click', '.address-card', function(e) {
|
||||
const $card = $(e.currentTarget);
|
||||
const address_type = $card.closest('[data-address-type]').attr('data-address-type');
|
||||
const address_name = $card.closest('[data-address-name]').attr('data-address-name');
|
||||
return frappe.call({
|
||||
type: "POST",
|
||||
method: "erpnext.shopping_cart.cart.update_cart_address",
|
||||
freeze: true,
|
||||
args: {
|
||||
address_type,
|
||||
address_name
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$(".cart-tax-items").html(r.message.taxes);
|
||||
}
|
||||
}
|
||||
});
|
||||
bind_address_picker_dialog: function() {
|
||||
const d = this.get_update_address_dialog();
|
||||
this.parent.find('.btn-change-address').on('click', (e) => {
|
||||
const type = $(e.currentTarget).parents('.address-container').attr('data-address-type');
|
||||
$(d.get_field('address_picker').wrapper).html(
|
||||
this.get_address_template(type)
|
||||
);
|
||||
d.show();
|
||||
});
|
||||
},
|
||||
|
||||
get_update_address_dialog() {
|
||||
return new frappe.ui.Dialog({
|
||||
title: "Select Address",
|
||||
fields: [{
|
||||
'fieldtype': 'HTML',
|
||||
'fieldname': 'address_picker',
|
||||
}],
|
||||
primary_action_label: __('Set Address'),
|
||||
primary_action: () => {
|
||||
const $card = d.$wrapper.find('.address-card.active');
|
||||
const address_type = $card.closest('[data-address-type]').attr('data-address-type');
|
||||
const address_name = $card.closest('[data-address-name]').attr('data-address-name');
|
||||
frappe.call({
|
||||
type: "POST",
|
||||
method: "erpnext.shopping_cart.cart.update_cart_address",
|
||||
freeze: true,
|
||||
args: {
|
||||
address_type,
|
||||
address_name
|
||||
},
|
||||
callback: function(r) {
|
||||
d.hide();
|
||||
if(!r.exc) {
|
||||
$(".cart-tax-items").html(r.message.taxes);
|
||||
shopping_cart.parent.find(
|
||||
`.address-container[data-address-type="${address_type}"]`
|
||||
).html(r.message.address);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_address_template(type) {
|
||||
return {
|
||||
shipping: `<div class="mb-3" data-section="shipping-address">
|
||||
<div class="row no-gutters" data-fieldname="shipping_address_name">
|
||||
{% for address in shipping_addresses %}
|
||||
<div class="mr-3 mb-3 w-100" data-address-name="{{address.name}}" data-address-type="shipping"
|
||||
{% if doc.shipping_address_name == address.name %} data-active {% endif %}>
|
||||
{% include "templates/includes/cart/address_picker_card.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>`,
|
||||
billing: `<div class="mb-3" data-section="billing-address">
|
||||
<div class="row no-gutters" data-fieldname="customer_address">
|
||||
{% for address in billing_addresses %}
|
||||
<div class="mr-3 mb-3 w-100" data-address-name="{{address.name}}" data-address-type="billing"
|
||||
{% if doc.shipping_address_name == address.name %} data-active {% endif %}>
|
||||
{% include "templates/includes/cart/address_picker_card.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>`,
|
||||
}[type];
|
||||
},
|
||||
|
||||
bind_place_order: function() {
|
||||
$(".btn-place-order").on("click", function() {
|
||||
shopping_cart.place_order(this);
|
||||
@ -221,6 +269,7 @@ $.extend(shopping_cart, {
|
||||
|
||||
frappe.ready(function() {
|
||||
$(".cart-icon").hide();
|
||||
shopping_cart.parent = $(".cart-container");
|
||||
shopping_cart.bind_events();
|
||||
});
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
<div class="card address-card h-100">
|
||||
<div class="check" style="position: absolute; right: 15px; top: 15px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||
<div class="btn btn-sm btn-default btn-change-address" style="position: absolute; right: 0; top: 0;">
|
||||
{{ _('Change') }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ address.title }}</h5>
|
||||
<p class="card-text text-muted">
|
||||
<div class="card-body p-0">
|
||||
<div class="card-title">{{ address.title }}</div>
|
||||
<div class="card-text mb-2">
|
||||
{{ address.display }}
|
||||
</p>
|
||||
<a href="/addresses?name={{address.name}}" class="card-link">{{ _('Edit') }}</a>
|
||||
</div>
|
||||
<a href="/addresses?name={{address.name}}" class="card-link">
|
||||
<svg class="icon icon-sm">
|
||||
<use href="#icon-edit"></use>
|
||||
</svg>
|
||||
{{ _('Edit') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
12
erpnext/templates/includes/cart/address_picker_card.html
Normal file
12
erpnext/templates/includes/cart/address_picker_card.html
Normal file
@ -0,0 +1,12 @@
|
||||
<div class="card address-card h-100">
|
||||
<div class="check" style="position: absolute; right: 15px; top: 15px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ address.title }}</h5>
|
||||
<p class="card-text text-muted">
|
||||
{{ address.display }}
|
||||
</p>
|
||||
<a href="/addresses?name={{address.name}}" class="card-link">{{ _('Edit') }}</a>
|
||||
</div>
|
||||
</div>
|
@ -14,31 +14,39 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="mb-3" data-section="shipping-address">
|
||||
<h6 class="text-uppercase">{{ _("Shipping Address") }}</h6>
|
||||
<div class="mb-3 frappe-card p-5" data-section="shipping-address">
|
||||
<h6>{{ _("Shipping Address") }}</h6>
|
||||
<hr>
|
||||
{% for address in shipping_addresses %}
|
||||
{% if doc.shipping_address_name == address.name %}
|
||||
<div class="row no-gutters" data-fieldname="shipping_address_name">
|
||||
{% for address in shipping_addresses %}
|
||||
<div class="mr-3 mb-3 w-25" data-address-name="{{address.name}}" data-address-type="shipping" {% if doc.shipping_address_name == address.name %} data-active {% endif %}>
|
||||
{% include "templates/includes/cart/address_card.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="w-100 address-container" data-address-name="{{address.name}}" data-address-type="shipping" data-active>
|
||||
{% include "templates/includes/cart/address_card.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="mb-3" data-section="billing-address">
|
||||
<h6 class="text-uppercase">{{ _("Billing Address") }}</h6>
|
||||
<div class="row no-gutters" data-fieldname="customer_address">
|
||||
{% for address in billing_addresses %}
|
||||
<div class="mr-3 mb-3 w-25" data-address-name="{{address.name}}" data-address-type="billing" {% if doc.customer_address == address.name %} data-active {% endif %}>
|
||||
{% include "templates/includes/cart/address_card.html" %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="checkbox ml-1 mb-2">
|
||||
<label for="input_same_billing">
|
||||
<input type="checkbox" id="input_same_billing" checked>
|
||||
<span class="label-area">{{ _('Billing Address is same as Shipping Address') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="input_same_billing" checked>
|
||||
<label class="custom-control-label" for="input_same_billing">{{ _('Billing Address is same as Shipping Address') }}</label>
|
||||
<div class="mb-3 frappe-card p-5" data-section="billing-address">
|
||||
<h6>{{ _("Billing Address") }}</h6>
|
||||
<hr>
|
||||
{% for address in billing_addresses %}
|
||||
{% if doc.customer_address == address.name %}
|
||||
<div class="row no-gutters" data-fieldname="customer_address">
|
||||
<div class="w-100 address-container" data-address-name="{{address.name}}" data-address-type="billing" data-active>
|
||||
{% include "templates/includes/cart/address_card.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button class="btn btn-outline-primary btn-sm mt-3 btn-new-address">{{ _("Add a new address") }}</button>
|
||||
<button class="btn btn-outline-primary btn-sm mt-1 btn-new-address bg-white">{{ _("Add a new address") }}</button>
|
||||
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
@ -55,6 +63,7 @@ frappe.ready(() => {
|
||||
});
|
||||
|
||||
$('.btn-new-address').click(() => {
|
||||
console.log('clicked');
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('New Address'),
|
||||
fields: [
|
||||
@ -168,5 +177,12 @@ frappe.ready(() => {
|
||||
function toggle_billing_address_section(flag) {
|
||||
$('[data-section="billing-address"]').toggle(flag);
|
||||
}
|
||||
|
||||
$('.btn-change-address').click(() => {
|
||||
// const d = new frappe.ui.Dialog({
|
||||
// })
|
||||
|
||||
// d.show();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
4
erpnext/templates/includes/cart/cart_address_picker.html
Normal file
4
erpnext/templates/includes/cart/cart_address_picker.html
Normal file
@ -0,0 +1,4 @@
|
||||
<div class="mb-3 frappe-card p-5" data-section="shipping-address">
|
||||
<h6>{{ _("Shipping Address") }}</h6>
|
||||
</div>
|
||||
|
@ -1,15 +1,15 @@
|
||||
{% for d in doc.items %}
|
||||
<tr data-name="{{ d.name }}">
|
||||
<td>
|
||||
<div class="font-weight-bold">
|
||||
<div class="item-title mb-1">
|
||||
{{ d.item_name }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="item-subtitle">
|
||||
{{ d.item_code }}
|
||||
</div>
|
||||
{%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %}
|
||||
{% if variant_of %}
|
||||
<span class="text-muted">
|
||||
<span class="item-subtitle">
|
||||
{{ _('Variant of') }} <a href="{{frappe.db.get_value('Item', variant_of, 'route')}}">{{ variant_of }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
@ -20,20 +20,20 @@
|
||||
<td class="text-right">
|
||||
<div class="input-group number-spinner">
|
||||
<span class="input-group-prepend d-none d-sm-inline-block">
|
||||
<button class="btn btn-outline-secondary cart-btn" data-dir="dwn">–</button>
|
||||
<button class="btn cart-btn" data-dir="dwn">–</button>
|
||||
</span>
|
||||
<input class="form-control text-right cart-qty border-secondary" value="{{ d.get_formatted('qty') }}" data-item-code="{{ d.item_code }}">
|
||||
<input class="form-control text-center cart-qty" value="{{ d.get_formatted('qty') }}" data-item-code="{{ d.item_code }}">
|
||||
<span class="input-group-append d-none d-sm-inline-block">
|
||||
<button class="btn btn-outline-secondary cart-btn" data-dir="up">+</button>
|
||||
<button class="btn cart-btn" data-dir="up">+</button>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<td class="text-right">
|
||||
<td class="text-right item-subtotal">
|
||||
<div>
|
||||
{{ d.get_formatted('amount') }}
|
||||
</div>
|
||||
<span class="text-muted">
|
||||
<span class="item-rate">
|
||||
{{ _('Rate:') }} {{ d.get_formatted('rate') }}
|
||||
</span>
|
||||
</td>
|
||||
|
@ -90,6 +90,10 @@
|
||||
<div class="card {{ align_items_class }}">
|
||||
{% if image %}
|
||||
<img class="card-img" src="{{ image }}" alt="{{ title }}">
|
||||
{% else %}
|
||||
<div class="card-img-top no-image">
|
||||
{{ frappe.utils.get_abbr(title) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ item_card_body(title, description, url, rate, category, is_featured, align) }}
|
||||
</div>
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
{% block navbar_right_extension %}
|
||||
<li class="shopping-cart cart-icon hidden">
|
||||
<a href="/cart" class="nav-link">
|
||||
{{ _("Cart") }}
|
||||
<span class="badge badge-primary" id="cart-count"></span>
|
||||
<a class="nav-link" href="/cart">
|
||||
<svg class="icon icon-lg">
|
||||
<use href="#icon-assets"></use>
|
||||
</svg>
|
||||
<span class="badge badge-primary cart-badge" id="cart-count"></span>
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
@ -29,12 +29,12 @@
|
||||
{{ _("Discount") }}
|
||||
</th>
|
||||
<th class="text-right tot_quotation_discount">
|
||||
{% set tot_quotation_discount = [] %}
|
||||
{%- for item in doc.items -%}
|
||||
{% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
|
||||
* item.discount_percentage) / 100)) %}{% endif %}
|
||||
{% endfor %}
|
||||
{{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }}
|
||||
{% set tot_quotation_discount = [] %}
|
||||
{%- for item in doc.items -%}
|
||||
{% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
|
||||
* item.discount_percentage) / 100)) %}{% endif %}
|
||||
{% endfor %}
|
||||
{{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }}
|
||||
</th>
|
||||
</tr>
|
||||
{% endif %}
|
||||
@ -47,51 +47,52 @@
|
||||
{{ _("Total Amount") }}
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<span>
|
||||
{% set total_amount = [] %}
|
||||
{%- for item in doc.items -%}
|
||||
{% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %}
|
||||
{% endfor %}
|
||||
{{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }}
|
||||
</span>
|
||||
<span>
|
||||
{% set total_amount = [] %}
|
||||
{%- for item in doc.items -%}
|
||||
{% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %}
|
||||
{% endfor %}
|
||||
{{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }}
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right" colspan="2">
|
||||
{{ _("Applied Coupon Code") }}
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<span>
|
||||
{%- for row in frappe.get_all(doctype="Coupon Code",
|
||||
fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
|
||||
<span>{{ row.coupon_code }}</span>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</th>
|
||||
<th class="text-right" colspan="2">
|
||||
{{ _("Applied Coupon Code") }}
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<span>
|
||||
{%- for row in frappe.get_all(doctype="Coupon Code",
|
||||
fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
|
||||
<span>{{ row.coupon_code }}</span>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right" colspan="2">
|
||||
{{ _("Discount") }}
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<span>
|
||||
{% set tot_SO_discount = [] %}
|
||||
{%- for item in doc.items -%}
|
||||
{% if tot_SO_discount.append((((item.price_list_rate * item.qty)
|
||||
* item.discount_percentage) / 100)) %}{% endif %}
|
||||
{% endfor %}
|
||||
{{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
|
||||
</span>
|
||||
</th>
|
||||
<th class="text-right" colspan="2">
|
||||
{{ _("Discount") }}
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<span>
|
||||
{% set tot_SO_discount = [] %}
|
||||
{%- for item in doc.items -%}
|
||||
{% if tot_SO_discount.append((((item.price_list_rate * item.qty)
|
||||
* item.discount_percentage) / 100)) %}{% endif %}
|
||||
{% endfor %}
|
||||
{{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<th class="text-right" colspan="2">
|
||||
<th></th>
|
||||
<th class="item-grand-total">
|
||||
{{ _("Grand Total") }}
|
||||
</th>
|
||||
<th class="text-right">
|
||||
<th class="text-right item-grand-total">
|
||||
{{ doc.get_formatted("grand_total") }}
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{% block title %} {{ _("Shopping Cart") }} {% endblock %}
|
||||
|
||||
{% block header %}<h1>{{ _("Shopping Cart") }}</h1>{% endblock %}
|
||||
{% block header %}<h3 class="shopping-cart-header mt-2 mb-6">{{ _("Shopping Cart") }}</h1>{% endblock %}
|
||||
|
||||
<!--
|
||||
{% block script %}
|
||||
@ -18,94 +18,119 @@
|
||||
|
||||
{% from "templates/includes/macros.html" import item_name_and_description %}
|
||||
|
||||
{% if doc.items %}
|
||||
<div class="cart-container">
|
||||
<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
|
||||
<div class="row m-0">
|
||||
<div class="col-md-8 frappe-card p-5">
|
||||
<div>
|
||||
<div id="cart-error" class="alert alert-danger" style="display: none;"></div>
|
||||
<div class="cart-items-header">
|
||||
{{ _('Items') }}
|
||||
</div>
|
||||
<table class="table mt-3 cart-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="60%">{{ _('Item') }}</th>
|
||||
<th width="20%">{{ _('Quantity') }}</th>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<th width="20%" class="text-right">{{ _('Subtotal') }}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="cart-items">
|
||||
{% include "templates/includes/cart/cart_items.html" %}
|
||||
</tbody>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<tfoot class="cart-tax-items">
|
||||
{% include "templates/includes/order/order_taxes.html" %}
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<a class="btn btn-outline-primary" href="/orders">
|
||||
{{ _('See past orders') }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="btn btn-outline-primary" href="/quotations">
|
||||
{{ _('See past quotations') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-8">
|
||||
{% if doc.items %}
|
||||
<div class="place-order-container">
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<button class="btn btn-primary btn-place-order" type="button">
|
||||
{{ _("Place Order") }}
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="btn btn-primary btn-request-for-quotation" type="button">
|
||||
{{ _("Request for Quotation") }}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if doc.items %}
|
||||
<table class="table table-bordered mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="60%">{{ _('Item') }}</th>
|
||||
<th width="20%" class="text-right">{{ _('Quantity') }}</th>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<th width="20%" class="text-right">{{ _('Subtotal') }}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="cart-items">
|
||||
{% include "templates/includes/cart/cart_items.html" %}
|
||||
</tbody>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<tfoot class="cart-tax-items">
|
||||
{% include "templates/includes/order/order_taxes.html" %}
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted">{{ _('Your cart is Empty') }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.items %}
|
||||
<div class="place-order-container">
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<button class="btn btn-primary btn-place-order" type="button">
|
||||
{{ _("Place Order") }}
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="btn btn-primary btn-request-for-quotation" type="button">
|
||||
{{ _("Request for Quotation") }}
|
||||
</button>
|
||||
{% if doc.items %}
|
||||
{% if doc.tc_name %}
|
||||
<div class="terms-and-conditions-link">
|
||||
<a href class="link-terms-and-conditions" data-terms-name="{{ doc.tc_name }}">
|
||||
{{ _("Terms and Conditions") }}
|
||||
</a>
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
$('.link-terms-and-conditions').click((e) => {
|
||||
e.preventDefault();
|
||||
const $link = $(e.target);
|
||||
const terms_name = $link.attr('data-terms-name');
|
||||
show_terms_and_conditions(terms_name);
|
||||
})
|
||||
});
|
||||
function show_terms_and_conditions(terms_name) {
|
||||
frappe.call('erpnext.shopping_cart.cart.get_terms_and_conditions', { terms_name })
|
||||
.then(r => {
|
||||
frappe.msgprint({
|
||||
title: terms_name,
|
||||
message: r.message
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.items %}
|
||||
{% if doc.tc_name %}
|
||||
<div class="terms-and-conditions-link">
|
||||
<a href class="link-terms-and-conditions" data-terms-name="{{ doc.tc_name }}">
|
||||
{{ _("Terms and Conditions") }}
|
||||
</a>
|
||||
<script>
|
||||
frappe.ready(() => {
|
||||
$('.link-terms-and-conditions').click((e) => {
|
||||
e.preventDefault();
|
||||
const $link = $(e.target);
|
||||
const terms_name = $link.attr('data-terms-name');
|
||||
show_terms_and_conditions(terms_name);
|
||||
})
|
||||
});
|
||||
function show_terms_and_conditions(terms_name) {
|
||||
frappe.call('erpnext.shopping_cart.cart.get_terms_and_conditions', { terms_name })
|
||||
.then(r => {
|
||||
frappe.msgprint({
|
||||
title: terms_name,
|
||||
message: r.message
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<div class="col-md-4">
|
||||
<div class="cart-addresses">
|
||||
{% include "templates/includes/cart/cart_address.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="cart-addresses mt-5">
|
||||
{% include "templates/includes/cart/cart_address.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="col-12">
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<a href="/orders">
|
||||
{% else %}
|
||||
<div class="cart-empty frappe-card">
|
||||
<div class="cart-empty-state">
|
||||
<img src="/assets/erpnext/images/ui-states/cart-empty-state.png" alt="Empty State">
|
||||
</div>
|
||||
<div class="cart-empty-message mt-4">{{ _('Your cart is Empty') }}</p>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<a class="btn btn-outline-primary" href="/orders">
|
||||
{{ _('See past orders') }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="/quotations">
|
||||
<a class="btn btn-outline-primary" href="/quotations">
|
||||
{{ _('See past quotations') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
@ -137,13 +137,15 @@
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="row product-paging-area mt-5">
|
||||
<div class="col-3">
|
||||
</div>
|
||||
<div class="col-9 text-right">
|
||||
{% if frappe.form_dict.start|int > 0 %}
|
||||
<button class="btn btn-outline-secondary btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</button>
|
||||
<button class="btn btn-default btn-prev" data-start="{{ frappe.form_dict.start|int - page_length }}">{{ _("Prev") }}</button>
|
||||
{% endif %}
|
||||
{% if items|length >= page_length %}
|
||||
<button class="btn btn-outline-secondary btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
|
||||
<button class="btn btn-default btn-next" data-start="{{ frappe.form_dict.start|int + page_length }}">{{ _("Next") }}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user