refactor!: drop ecommerce in favor of webshop (#33265)

* refactor!: remove ecommerce item group field check

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove `e_commerce` directory

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove `get_context` from `item_group`

https://frappeframework.com/docs/v14/user/en/guides/portal-development/context

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove related `./templates`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(navbar): remove wishlist (ecommerce)

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(js): remove js from scripts

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove `www/all-products`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove pages and js

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove js/customer_reviews

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(portal utils): remove shopping cart debtor account

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove e_commerce events from hooks

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(web): remove e_commerce js from bundle

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(setup): remove shopping cart setup

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove pages

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor(item): remove website item button

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(payment request): remove `on_payment_authorized`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: @staticmethod `get_gateway_details`

to avoid monkey patching, in custom apps
https://discuss.erpnext.com/t/how-to-override-method-in-frappe/28786/36

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(pages): remove product page

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(homepage): do not setup website items

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor(workspace): remove link to ecommerce settings

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(www): remove shop-by-category

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(homepage): remove featured product

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor: remove products in homepage

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor(homepage): remove explore button

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor: remove products fields from homepage

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* Revert "refactor!: @staticmethod `get_gateway_details`"

This reverts commit 561bcd96680a930bb92627869502d9346b10611b.

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: remove payment gateway e_commerce import

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* chore: pre-commit

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!: pass `party` into `get_price`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor: move `get_item_codes_by_attributes` to `utilities/product`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* refactor!(quotation): input customer group

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* chore: pre-commit

* refactor: remove custom `navbar_items.html`

* refactor!(item): remove `published_in_website`

* refactor: move `validate_duplicate_website_item` before rename

* test: remove `test_shopping_cart_without_website_item`

* chore: add doctype drop patch

* refactor: removed website item related code

* refactor: removed shopping_cart code

* refactor: removed e-commerce related patches

* refactor: removed website related fields from item group

* fix: patch create_asset_depreciation_schedules_from_assets, KeyError: '0K BU64 AUY'

---------

Signed-off-by: Sabu Siyad <hello@ssiyad.com>
Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
This commit is contained in:
Sabu Siyad
2023-10-17 17:05:44 +05:30
committed by GitHub
parent 9d687ca6e9
commit f900a78995
160 changed files with 630 additions and 15222 deletions

View File

@@ -1,17 +0,0 @@
<div class="card address-card h-100">
<div class="btn btn-sm btn-default btn-change-address font-md" style="position: absolute; right: 0; top: 0;">
{{ _('Change') }}
</div>
<div class="card-body p-0">
<div class="card-title">{{ address.title }}</div>
<div class="card-text mb-2">
{{ address.display }}
</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>

View File

@@ -1,12 +0,0 @@
<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>

View File

@@ -1,189 +0,0 @@
{% from "erpnext/templates/includes/cart/cart_macros.html" import show_address %}
{% if addresses | length == 1%}
{% set select_address = True %}
{% endif %}
<div class="mb-3 frappe-card p-5" data-section="shipping-address">
<div class="d-flex">
<div class="col-6 address-header"><h6>{{ _("Shipping Address") }}</h6></div>
<div class="col-6" style="padding: 0;">
<a class="ml-4 btn-new-address" role="button">{{ _("Add a new address") }}</a>
</div>
</div>
<hr>
{% for address in shipping_addresses %}
{% if doc.shipping_address_name == address.name %}
<div class="row no-gutters" data-fieldname="shipping_address_name">
<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>
<!-- Billing Address -->
<div class="checkbox ml-1 mb-2">
<label for="input_same_billing">
<input type="checkbox" class="product-filter" id="input_same_billing" checked style="width: 14px !important">
<span class="label-area font-md">{{ _('Billing Address is same as Shipping Address') }}</span>
</label>
</div>
{% if billing_addresses %}
<div class="mb-3 frappe-card p-5" data-section="billing-address">
<div class="d-flex">
<div class="col-6 address-header"><h6>{{ _("Billing Address") }}</h6></div>
<div class="col-6" style="padding: 0;">
<a class="ml-4 btn-new-address" role="button">{{ _("Add a new address") }}</a>
</div>
</div>
<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>
{% endif %}
<script>
frappe.boot = {{ boot }}
frappe.ready(() => {
$(document).on('click', '.address-card', (e) => {
const $target = $(e.currentTarget);
const $section = $target.closest('[data-section]');
$section.find('.address-card').removeClass('active');
$target.addClass('active');
});
$('#input_same_billing').change((e) => {
const $check = $(e.target);
toggle_billing_address_section(!$check.is(':checked'));
});
$('.btn-new-address').click(() => {
const d = new frappe.ui.Dialog({
title: __('New Address'),
fields: [
{
label: __('Address Title'),
fieldname: 'address_title',
fieldtype: 'Data',
reqd: 1
},
{
label: __('Address Line 1'),
fieldname: 'address_line1',
fieldtype: 'Data',
reqd: 1
},
{
label: __('Address Line 2'),
fieldname: 'address_line2',
fieldtype: 'Data'
},
{
label: __('City/Town'),
fieldname: 'city',
fieldtype: 'Data',
reqd: 1
},
{
label: __('State'),
fieldname: 'state',
fieldtype: 'Data'
},
{
label: __('Country'),
fieldname: 'country',
fieldtype: 'Link',
options: 'Country',
only_select: true,
reqd: 1
},
{
fieldname: "column_break0",
fieldtype: "Column Break",
width: "50%"
},
{
label: __('Address Type'),
fieldname: 'address_type',
fieldtype: 'Select',
options: [
'Billing',
'Shipping'
],
reqd: 1
},
{
label: __('Postal Code'),
fieldname: 'pincode',
fieldtype: 'Data'
},
{
fieldname: "phone",
fieldtype: "Data",
label: "Phone",
reqd: 1
},
],
primary_action_label: __('Save'),
primary_action: (values) => {
frappe.call('erpnext.e_commerce.shopping_cart.cart.add_new_address', { doc: values })
.then(r => {
frappe.call({
method: "erpnext.e_commerce.shopping_cart.cart.update_cart_address",
args: {
address_type: r.message.address_type,
address_name: r.message.name
},
callback: function (r) {
d.hide();
window.location.reload();
}
});
});
}
})
d.show();
});
function setup_state() {
const shipping_address = $('[data-section="shipping-address"]')
.find('[data-address-name][data-active]').attr('data-address-name');
const billing_address = $('[data-section="billing-address"]')
.find('[data-address-name][data-active]').attr('data-address-name');
$('#input_same_billing').prop('checked', shipping_address === billing_address).trigger('change');
if (!shipping_address && !billing_address) {
$('#input_same_billing').prop('checked', true).trigger('change');
}
if (shipping_address) {
$(`[data-section="shipping-address"] [data-address-name="${shipping_address}"] .address-card`).addClass('active');
}
if (billing_address) {
$(`[data-section="billing-address"] [data-address-name="${billing_address}"] .address-card`).addClass('active');
}
}
setup_state();
function toggle_billing_address_section(flag) {
$('[data-section="billing-address"]').toggle(flag);
}
});
</script>

View File

@@ -1,3 +0,0 @@
<div class="mb-3 frappe-card p-5" data-section="shipping-address">
<h6>{{ _("Shipping Address") }}</h6>
</div>

View File

@@ -1,27 +0,0 @@
<div class="cart-dropdown-container">
<div id="cart-error" class="alert alert-danger"
style="display: none;"></div>
<div class="row checkout-btn">
<div class="col-sm-12 col-xs-12">
<a href="/cart" class="btn btn-block btn-primary">{{ _("Checkout") }}</a>
</div>
</div>
<div class="row cart-items-dropdown cart-item-header text-muted">
<div class="col-sm-6 col-xs-6 h6 text-uppercase">
{{ _("Item") }}
</div>
<div class="col-sm-6 col-xs-6 text-right h6 text-uppercase">
{{ _("Price") }}
</div>
</div>
{% if doc.items %}
<div class="row cart-items-dropdown">
<div class="col-sm-12 col-xs-12">
{% include "templates/includes/cart/cart_items_dropdown.html" %}
</div>
</div>
{% else %}
<p>{{ _("Cart is Empty") }}</p>
{% endif %}
</div>

View File

@@ -1,113 +0,0 @@
{% from "erpnext/templates/includes/macros.html" import product_image %}
{% macro item_subtotal(item) %}
<div>
{{ item.get_formatted('amount') }}
</div>
{% if item.is_free_item %}
<div class="text-success mt-4">
<span class="free-tag">
{{ _('FREE') }}
</span>
</div>
{% else %}
<span class="item-rate">
{{ _('Rate:') }} {{ item.get_formatted('rate') }}
</span>
{% endif %}
{% endmacro %}
{% for d in doc.items %}
<tr data-name="{{ d.name }}">
<td style="width: 60%;">
<div class="d-flex">
<div class="cart-item-image mr-4">
{% if d.thumbnail %}
{{ product_image(d.thumbnail, alt="d.web_item_name", no_border=True) }}
{% else %}
<div class = "no-image-cart-item">
{{ frappe.utils.get_abbr(d.web_item_name) or "NA" }}
</div>
{% endif %}
</div>
<div class="d-flex w-100" style="flex-direction: column;">
<div class="item-title mb-1 mr-3">
{{ d.get("web_item_name") or d.item_name }}
</div>
<div class="item-subtitle mr-2">
{{ d.item_code }}
</div>
{%- set variant_of = frappe.db.get_value('Item', d.item_code, 'variant_of') %}
{% if variant_of %}
<span class="item-subtitle mr-2">
{{ _('Variant of') }}
<a href="{{frappe.db.get_value('Website Item', {'item_code': variant_of}, 'route') or '#'}}">
{{ variant_of }}
</a>
</span>
{% endif %}
<div class="mt-2 notes">
<textarea data-item-code="{{d.item_code}}" class="form-control" rows="2" placeholder="{{ _('Add notes') }}">
{{d.additional_notes or ''}}
</textarea>
</div>
</div>
</div>
</td>
<!-- Qty column -->
<td class="text-right" style="width: 25%;">
<div class="d-flex">
{% set disabled = 'disabled' if d.is_free_item else '' %}
<div class="input-group number-spinner mt-1 mb-4">
<span class="input-group-prepend d-sm-inline-block">
<button class="btn cart-btn" data-dir="dwn" {{ disabled }}>
{{ '' if not d.is_free_item else ''}}
</button>
</span>
<input class="form-control text-center cart-qty" value="{{ d.get_formatted('qty') }}" data-item-code="{{ d.item_code }}"
style="max-width: 70px;" {{ disabled }}>
<span class="input-group-append d-sm-inline-block">
<button class="btn cart-btn" data-dir="up" {{ disabled }}>
{{ '+' if not d.is_free_item else ''}}
</button>
</span>
</div>
<div>
{% if not d.is_free_item %}
<div class="remove-cart-item column-sm-view d-flex" data-item-code="{{ d.item_code }}">
<span>
<svg class="icon sm remove-cart-item-logo"
width="18" height="18" viewBox="0 0 18 18"
xmlns="http://www.w3.org/2000/svg" id="icon-close">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.146 11.217a.5.5 0 1 0 .708.708l3.182-3.182 3.181 3.182a.5.5 0 1 0 .708-.708l-3.182-3.18 3.182-3.182a.5.5 0 1 0-.708-.708l-3.18 3.181-3.183-3.182a.5.5 0 0 0-.708.708l3.182 3.182-3.182 3.181z" stroke-width="0"></path>
</svg>
</span>
</div>
{% endif %}
</div>
</div>
<!-- Shown on mobile view, else hidden -->
{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
<div class="text-right sm-item-subtotal">
{{ item_subtotal(d) }}
</div>
{% endif %}
</td>
<!-- Subtotal column -->
{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
<td class="text-right item-subtotal column-sm-view w-100">
{{ item_subtotal(d) }}
</td>
{% endif %}
</tr>
{% endfor %}

View File

@@ -1,12 +0,0 @@
{% from "erpnext/templates/includes/order/order_macros.html" import item_name_and_description_cart %}
{% for d in doc.items %}
<div class="row cart-dropdown">
<div class="col-sm-8 col-xs-8 col-name-description">
{{ item_name_and_description_cart(d) }}
</div>
<div class="col-sm-4 col-xs-4 text-right col-amount">
{{ d.get_formatted("amount") }}
</div>
</div>
{% endfor %}

View File

@@ -1,10 +0,0 @@
<!-- Total at the end of the cart items -->
<tr>
<th></th>
<th class="text-left item-grand-total" colspan="1">
{{ _("Total") }}
</th>
<th class="text-left item-grand-total totals" colspan="3">
{{ doc.get_formatted("total") }}
</th>
</tr>

View File

@@ -1,22 +0,0 @@
{% macro show_address(address, doc, fieldname, select_address=False) %}
{% 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 %}

View File

@@ -1,84 +0,0 @@
<!-- Payment -->
{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
<h6>
{{ _("Payment Summary") }}
</h6>
{% endif %}
<div class="card h-100">
<div class="card-body p-0">
{% if cart_settings.enable_checkout or cart_settings.show_price_in_quotation %}
<table class="table w-100">
<tr>
{% set total_items = frappe.utils.cstr(frappe.utils.flt(doc.total_qty, 0)) %}
<td class="bill-label">{{ _("Net Total (") + total_items + _(" Items)") }}</td>
<td class="bill-content net-total text-right">{{ doc.get_formatted("net_total") }}</td>
</tr>
<!-- taxes -->
{% for d in doc.taxes %}
{% if d.base_tax_amount %}
<tr>
<td class="bill-label">
{{ d.description }}
</td>
<td class="bill-content text-right">
{{ d.get_formatted("base_tax_amount") }}
</td>
</tr>
{% endif %}
{% endfor %}
</table>
<!-- TODO: Apply Coupon Dialog-->
<!-- {% set show_coupon_code = cart_settings.show_apply_coupon_code_in_website and cart_settings.enable_checkout %}
{% if show_coupon_code %}
<button class="btn btn-coupon-code w-100 text-left">
<svg width="24" height="24" viewBox="0 0 24 24" stroke="var(--gray-600)" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 15.6213C19 15.2235 19.158 14.842 19.4393 14.5607L20.9393 13.0607C21.5251 12.4749 21.5251 11.5251 20.9393 10.9393L19.4393 9.43934C19.158 9.15804 19 8.7765 19 8.37868V6.5C19 5.67157 18.3284 5 17.5 5H15.6213C15.2235 5 14.842 4.84196 14.5607 4.56066L13.0607 3.06066C12.4749 2.47487 11.5251 2.47487 10.9393 3.06066L9.43934 4.56066C9.15804 4.84196 8.7765 5 8.37868 5H6.5C5.67157 5 5 5.67157 5 6.5V8.37868C5 8.7765 4.84196 9.15804 4.56066 9.43934L3.06066 10.9393C2.47487 11.5251 2.47487 12.4749 3.06066 13.0607L4.56066 14.5607C4.84196 14.842 5 15.2235 5 15.6213V17.5C5 18.3284 5.67157 19 6.5 19H8.37868C8.7765 19 9.15804 19.158 9.43934 19.4393L10.9393 20.9393C11.5251 21.5251 12.4749 21.5251 13.0607 20.9393L14.5607 19.4393C14.842 19.158 15.2235 19 15.6213 19H17.5C18.3284 19 19 18.3284 19 17.5V15.6213Z" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 9L9 15" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.5 9.5C10.5 10.0523 10.0523 10.5 9.5 10.5C8.94772 10.5 8.5 10.0523 8.5 9.5C8.5 8.94772 8.94772 8.5 9.5 8.5C10.0523 8.5 10.5 8.94772 10.5 9.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.5 14.5C15.5 15.0523 15.0523 15.5 14.5 15.5C13.9477 15.5 13.5 15.0523 13.5 14.5C13.5 13.9477 13.9477 13.5 14.5 13.5C15.0523 13.5 15.5 13.9477 15.5 14.5Z" fill="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="ml-2">Apply Coupon</span>
</button>
{% endif %} -->
<table class="table w-100 grand-total mt-6">
<tr>
<td class="bill-content net-total">{{ _("Grand Total") }}</td>
<td class="bill-content net-total text-right">{{ doc.get_formatted("grand_total") }}</td>
</tr>
</table>
{% endif %}
{% if cart_settings.enable_checkout %}
<button class="btn btn-primary btn-place-order font-md w-100" type="button">
{{ _('Place Order') }}
</button>
{% else %}
<button class="btn btn-primary btn-request-for-quotation font-md w-100" type="button">
{{ _('Request for Quote') }}
</button>
{% endif %}
</div>
</div>
<!-- TODO: Apply Coupon Dialog-->
<!-- <script>
frappe.ready(() => {
$('.btn-coupon-code').click((e) => {
const $btn = $(e.currentTarget);
const d = new frappe.ui.Dialog({
title: __('Coupons'),
fields: [
{
fieldname: 'coupons_area',
fieldtype: 'HTML'
}
]
});
d.show();
});
});
</script> -->

View File

@@ -1,22 +0,0 @@
{% extends 'frappe/templates/includes/navbar/navbar_items.html' %}
{% block navbar_right_extension %}
<li class="shopping-cart cart-icon hidden">
<a class="nav-link" href="/cart">
<svg class="icon icon-lg">
<use href="#icon-assets"></use>
</svg>
<span class="badge badge-primary shopping-badge" id="cart-count"></span>
</a>
</li>
{% if frappe.db.get_single_value("E Commerce Settings", "enable_wishlist") %}
<li class="wishlist wishlist-icon hidden">
<a class="nav-link" href="/wishlist">
<svg class="icon icon-lg">
<use href="#icon-heart"></use>
</svg>
<span class="badge badge-primary shopping-badge" id="wish-count"></span>
</a>
</li>
{% endif %}
{% endblock %}

View File

@@ -1,52 +0,0 @@
{% from "erpnext/templates/includes/macros.html" import product_image %}
{% macro item_name_and_description(d) %}
<div class="row item_name_and_description">
<div class="col-xs-4 col-sm-2 order-image-col">
<div class="order-image h-100">
{% if d.thumbnail or d.image %}
{{ product_image(d.thumbnail or d.image, no_border=True) }}
{% else %}
<div class="no-image-cart-item" style="min-height: 100px;">
{{ frappe.utils.get_abbr(d.item_name) or "NA" }}
</div>
{% endif %}
</div>
</div>
<div class="col-xs-8 col-sm-10">
{{ d.item_code }}
<div class="text-muted small item-description">
{{ html2text(d.description) | truncate(140) }}
</div>
<span class="text-muted mt-2 d-l-n order-qty">
{{ _("Qty ") }}({{ d.get_formatted("qty") }})
</span>
</div>
</div>
{% endmacro %}
{% macro item_name_and_description_cart(d) %}
<div class="row item_name_dropdown">
<div class="col-xs-4 col-sm-4 order-image-col">
<div class="order-image">
{{ product_image_square(d.thumbnail or d.image) }}
</div>
</div>
<div class="col-xs-8 col-sm-8">
{{ d.item_name|truncate(25) }}
<div class="input-group number-spinner">
<span class="input-group-btn">
<button class="btn btn-light cart-btn" data-dir="dwn">
</button>
</span>
<input class="form-control text-right cart-qty"
value = "{{ d.get_formatted('qty') }}"
data-item-code="{{ d.item_code }}">
<span class="input-group-btn">
<button class="btn btn-light cart-btn" data-dir="up">
+</button>
</span>
</div>
</div>
</div>
{% endmacro %}

View File

@@ -1,217 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ready(function() {
window.item_code = $('[itemscope] [itemprop="productID"]').text().trim();
var qty = 0;
frappe.call({
type: "POST",
method: "erpnext.e_commerce.shopping_cart.product_info.get_product_info_for_website",
args: {
item_code: get_item_code()
},
callback: function(r) {
if(r.message) {
if(r.message.cart_settings.enabled) {
let hide_add_to_cart = !r.message.product_info.price
|| (!r.message.product_info.in_stock && !r.message.cart_settings.allow_items_not_in_stock);
$(".item-cart, .item-price, .item-stock").toggleClass('hide', hide_add_to_cart);
}
if(r.message.cart_settings.show_price) {
$(".item-price").toggleClass("hide", false);
}
if(r.message.cart_settings.show_stock_availability) {
$(".item-stock").toggleClass("hide", false);
}
if(r.message.product_info.price) {
$(".item-price")
.html(r.message.product_info.price.formatted_price_sales_uom + "<div style='font-size: small'>\
(" + r.message.product_info.price.formatted_price + " / " + r.message.product_info.uom + ")</div>");
if(r.message.product_info.in_stock===0) {
$(".item-stock").html("<div style='color: red'> <i class='fa fa-close'></i> {{ _("Not in stock") }}</div>");
}
else if(r.message.product_info.in_stock===1 && r.message.cart_settings.show_stock_availability) {
var qty_display = "{{ _("In stock") }}";
if (r.message.product_info.show_stock_qty) {
qty_display += " ("+r.message.product_info.stock_qty+")";
}
$(".item-stock").html("<div style='color: green'>\
<i class='fa fa-check'></i> "+qty_display+"</div>");
}
if(r.message.product_info.qty) {
qty = r.message.product_info.qty;
toggle_update_cart(r.message.product_info.qty);
} else {
toggle_update_cart(0);
}
}
}
}
})
$("#item-add-to-cart button").on("click", function() {
frappe.provide('erpnext.shopping_cart');
erpnext.shopping_cart.update_cart({
item_code: get_item_code(),
qty: $("#item-spinner .cart-qty").val(),
callback: function(r) {
if(!r.exc) {
toggle_update_cart(1);
qty = 1;
}
},
btn: this,
});
});
$("#item-spinner").on('click', '.number-spinner button', function () {
var btn = $(this),
input = btn.closest('.number-spinner').find('input'),
oldValue = input.val().trim(),
newVal = 0;
if (btn.attr('data-dir') == 'up') {
newVal = Number.parseInt(oldValue) + 1;
} else if (btn.attr('data-dir') == 'dwn') {
if (Number.parseInt(oldValue) > 1) {
newVal = Number.parseInt(oldValue) - 1;
}
else {
newVal = Number.parseInt(oldValue);
}
}
input.val(newVal);
});
$("[itemscope] .item-view-attribute .form-control").on("change", function() {
try {
var item_code = encodeURIComponent(get_item_code());
} catch(e) {
// unable to find variant
// then chose the closest available one
var attribute = $(this).attr("data-attribute");
var attribute_value = $(this).val();
var item_code = find_closest_match(attribute, attribute_value);
if (!item_code) {
frappe.msgprint(__("Cannot find a matching Item. Please select some other value for {0}.", [attribute]))
throw e;
}
}
if (window.location.search == ("?variant=" + item_code) || window.location.search.includes(item_code)) {
return;
}
window.location.href = window.location.pathname + "?variant=" + item_code;
});
// change the item image src when alternate images are hovered
$(document.body).on('mouseover', '.item-alternative-image', (e) => {
const $alternative_image = $(e.currentTarget);
const src = $alternative_image.find('img').prop('src');
$('.item-image img').prop('src', src);
});
});
var toggle_update_cart = function(qty) {
$("#item-add-to-cart").toggle(qty ? false : true);
$("#item-update-cart")
.toggle(qty ? true : false)
.find("input").val(qty);
$("#item-spinner").toggle(qty ? false : true);
}
function get_item_code() {
var variant_info = window.variant_info;
if(variant_info) {
var attributes = get_selected_attributes();
var no_of_attributes = Object.keys(attributes).length;
for(var i in variant_info) {
var variant = variant_info[i];
if (variant.attributes.length < no_of_attributes) {
// the case when variant has less attributes than template
continue;
}
var match = true;
for(var j in variant.attributes) {
if(attributes[variant.attributes[j].attribute]
!= variant.attributes[j].attribute_value
) {
match = false;
break;
}
}
if(match) {
return variant.name;
}
}
throw "Unable to match variant";
} else {
return window.item_code;
}
}
function find_closest_match(selected_attribute, selected_attribute_value) {
// find the closest match keeping the selected attribute in focus and get the item code
var attributes = get_selected_attributes();
var previous_match_score = 0;
var previous_no_of_attributes = 0;
var matched;
var variant_info = window.variant_info;
for(var i in variant_info) {
var variant = variant_info[i];
var match_score = 0;
var has_selected_attribute = false;
for(var j in variant.attributes) {
if(attributes[variant.attributes[j].attribute]===variant.attributes[j].attribute_value) {
match_score = match_score + 1;
if (variant.attributes[j].attribute==selected_attribute && variant.attributes[j].attribute_value==selected_attribute_value) {
has_selected_attribute = true;
}
}
}
if (has_selected_attribute
&& ((match_score > previous_match_score) || (match_score==previous_match_score && previous_no_of_attributes < variant.attributes.length))) {
previous_match_score = match_score;
matched = variant;
previous_no_of_attributes = variant.attributes.length;
}
}
if (matched) {
for (var j in matched.attributes) {
var attr = matched.attributes[j];
$('[itemscope]')
.find(repl('.item-view-attribute .form-control[data-attribute="%(attribute)s"]', attr))
.val(attr.attribute_value);
}
return matched.name;
}
}
function get_selected_attributes() {
var attributes = {};
$('[itemscope]').find(".item-view-attribute .form-control").each(function() {
attributes[$(this).attr('data-attribute')] = $(this).val();
});
return attributes;
}