mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-18 02:55:20 +00:00
Merge branch 'rebrand-ui' of https://github.com/frappe/erpnext into desk-enhancements
This commit is contained in:
@@ -58,6 +58,7 @@ class Customer(TransactionBase):
|
||||
self.set_loyalty_program()
|
||||
self.check_customer_group_change()
|
||||
self.validate_default_bank_account()
|
||||
self.validate_internal_customer()
|
||||
|
||||
# set loyalty program tier
|
||||
if frappe.db.exists('Customer', self.name):
|
||||
@@ -82,6 +83,11 @@ class Customer(TransactionBase):
|
||||
if not is_company_account:
|
||||
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
|
||||
|
||||
def validate_internal_customer(self):
|
||||
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
|
||||
frappe.throw(_("Internal Customer for company {0} already exists").format(
|
||||
frappe.bold(self.represents_company)))
|
||||
|
||||
def on_update(self):
|
||||
self.validate_name_with_customer_group()
|
||||
self.create_primary_contact()
|
||||
@@ -398,7 +404,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
||||
# form a list of emails and names to show to the user
|
||||
credit_controller_users_formatted = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
|
||||
if not credit_controller_users_formatted:
|
||||
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
|
||||
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.").format(customer))
|
||||
|
||||
message = """Please contact any of the following users to extend the credit limits for {0}:
|
||||
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users_formatted))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:08",
|
||||
@@ -932,7 +933,7 @@
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"max_attachments": 1,
|
||||
"modified": "2020-07-26 17:46:19.951223",
|
||||
"modified": "2020-10-30 13:58:59.212060",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
|
||||
@@ -8,7 +8,7 @@ frappe.ui.form.on("Sales Order", {
|
||||
frm.custom_make_buttons = {
|
||||
'Delivery Note': 'Delivery Note',
|
||||
'Pick List': 'Pick List',
|
||||
'Sales Invoice': 'Invoice',
|
||||
'Sales Invoice': 'Sales Invoice',
|
||||
'Material Request': 'Material Request',
|
||||
'Purchase Order': 'Purchase Order',
|
||||
'Project': 'Project',
|
||||
@@ -326,9 +326,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frappe.msgprint({
|
||||
message: __('Work Orders Created: {0}',
|
||||
[r.message.map(function(d) {
|
||||
return repl('<a href="/desk/Form/Work Order/%(name)s">%(name)s</a>', {name:d})
|
||||
message: __('Work Orders Created: {0}', [r.message.map(function(d) {
|
||||
return repl('<a href="#Form/Work Order/%(name)s">%(name)s</a>', {name:d})
|
||||
}).join(', ')]),
|
||||
indicator: 'green'
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_auto_repeat": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-06-18 12:39:59",
|
||||
@@ -1460,7 +1461,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-07 14:30:01.782617",
|
||||
"modified": "2020-10-30 13:59:18.628077",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
||||
@@ -62,6 +62,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
|
||||
`tabItem` item {bin_join_selection}
|
||||
WHERE
|
||||
item.disabled = 0
|
||||
AND item.is_stock_item = 1
|
||||
AND item.has_variants = 0
|
||||
AND item.is_sales_item = 1
|
||||
AND item.is_fixed_asset = 0
|
||||
|
||||
@@ -98,48 +98,31 @@ erpnext.PointOfSale.Controller = class {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
prepare_app_defaults(data) {
|
||||
async prepare_app_defaults(data) {
|
||||
this.pos_opening = data.name;
|
||||
this.company = data.company;
|
||||
this.pos_profile = data.pos_profile;
|
||||
this.pos_opening_time = data.period_start_date;
|
||||
this.item_stock_map = {};
|
||||
this.settings = {};
|
||||
|
||||
frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => {
|
||||
this.allow_negative_stock = flt(message.allow_negative_stock) || false;
|
||||
});
|
||||
|
||||
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
|
||||
this.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
||||
this.cart.make_customer_selector();
|
||||
this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
||||
this.settings.hide_images = profile.hide_images;
|
||||
this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart;
|
||||
this.make_app();
|
||||
});
|
||||
|
||||
this.item_stock_map = {};
|
||||
|
||||
this.make_app();
|
||||
}
|
||||
|
||||
set_opening_entry_status() {
|
||||
this.page.set_title_sub(
|
||||
`<span class="indicator orange">
|
||||
<a class="text-muted" href="/desk/Form/POS%20Opening%20Entry/${this.pos_opening}">
|
||||
Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")}
|
||||
</a>
|
||||
</span>`);
|
||||
}
|
||||
|
||||
make_app() {
|
||||
return frappe.run_serially([
|
||||
() => frappe.dom.freeze(),
|
||||
() => {
|
||||
this.set_opening_entry_status();
|
||||
this.prepare_dom();
|
||||
this.prepare_components();
|
||||
this.prepare_menu();
|
||||
},
|
||||
() => this.make_new_invoice(),
|
||||
() => frappe.dom.unfreeze(),
|
||||
() => this.page.set_title(__('Point of Sale')),
|
||||
]);
|
||||
this.prepare_dom();
|
||||
this.prepare_components();
|
||||
this.prepare_menu();
|
||||
this.make_new_invoice();
|
||||
}
|
||||
|
||||
prepare_dom() {
|
||||
@@ -225,12 +208,11 @@ erpnext.PointOfSale.Controller = class {
|
||||
this.item_selector = new erpnext.PointOfSale.ItemSelector({
|
||||
wrapper: this.$components_wrapper,
|
||||
pos_profile: this.pos_profile,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
item_selected: args => this.on_cart_update(args),
|
||||
|
||||
get_frm: () => this.frm || {},
|
||||
|
||||
get_allowed_item_group: () => this.item_groups
|
||||
get_frm: () => this.frm || {}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -238,6 +220,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
init_item_cart() {
|
||||
this.cart = new erpnext.PointOfSale.ItemCart({
|
||||
wrapper: this.$components_wrapper,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
get_frm: () => this.frm,
|
||||
|
||||
@@ -260,9 +243,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
this.customer_details = details;
|
||||
// will add/remove LP payment method
|
||||
this.payment.render_loyalty_points_payment_mode();
|
||||
},
|
||||
|
||||
get_allowed_customer_group: () => this.customer_groups
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -416,8 +397,6 @@ erpnext.PointOfSale.Controller = class {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
toggle_recent_order_list(show) {
|
||||
this.toggle_components(!show);
|
||||
this.recent_order_list.toggle_component(show);
|
||||
@@ -434,10 +413,12 @@ erpnext.PointOfSale.Controller = class {
|
||||
|
||||
make_new_invoice() {
|
||||
return frappe.run_serially([
|
||||
() => frappe.dom.freeze(),
|
||||
() => this.make_sales_invoice_frm(),
|
||||
() => this.set_pos_profile_data(),
|
||||
() => this.set_pos_profile_status(),
|
||||
() => this.cart.load_invoice(),
|
||||
() => frappe.dom.unfreeze()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -494,16 +475,6 @@ erpnext.PointOfSale.Controller = class {
|
||||
return this.frm.trigger("set_pos_data");
|
||||
}
|
||||
|
||||
raise_exception_for_pos_profile() {
|
||||
setTimeout(() => frappe.set_route('List', 'POS Profile'), 2000);
|
||||
frappe.throw(__("POS Profile is required to use Point-of-Sale"));
|
||||
}
|
||||
|
||||
set_invoice_status() {
|
||||
const [status, indicator] = frappe.listview_settings["POS Invoice"].get_indicator(this.frm.doc);
|
||||
this.page.set_indicator(status, indicator);
|
||||
}
|
||||
|
||||
set_pos_profile_status() {
|
||||
this.page.set_indicator(this.pos_profile, "blue");
|
||||
}
|
||||
@@ -631,8 +602,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
})
|
||||
} else if (available_qty < qty_needed) {
|
||||
frappe.show_alert({
|
||||
message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.',
|
||||
[bold_item_code, bold_warehouse, bold_available_qty]),
|
||||
message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', [bold_item_code, bold_warehouse, bold_available_qty]),
|
||||
indicator: 'orange'
|
||||
});
|
||||
frappe.utils.play_sound("error");
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
erpnext.PointOfSale.ItemCart = class {
|
||||
constructor({ wrapper, events }) {
|
||||
constructor({ wrapper, events, settings }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.customer_info = undefined;
|
||||
|
||||
this.hide_images = settings.hide_images;
|
||||
this.allowed_customer_groups = settings.customer_groups;
|
||||
|
||||
this.init_component();
|
||||
}
|
||||
|
||||
|
||||
init_component() {
|
||||
this.prepare_dom();
|
||||
this.init_child_components();
|
||||
@@ -32,15 +34,16 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
`<div class="customer-section"></div>`
|
||||
)
|
||||
this.$customer_section = this.$component.find('.customer-section');
|
||||
this.make_customer_selector();
|
||||
}
|
||||
|
||||
|
||||
reset_customer_selector() {
|
||||
const frm = this.events.get_frm();
|
||||
frm.set_value('customer', '');
|
||||
this.make_customer_selector();
|
||||
this.customer_field.set_focus();
|
||||
}
|
||||
|
||||
|
||||
init_cart_components() {
|
||||
this.$component.append(
|
||||
`<div class="cart-container">
|
||||
@@ -54,7 +57,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
<div class="cart-items-section"></div>
|
||||
<div class="cart-totals-section"></div>
|
||||
<div class="numpad-section"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
this.$cart_container = this.$component.find('.cart-container');
|
||||
@@ -70,7 +73,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
this.make_no_items_placeholder();
|
||||
}
|
||||
|
||||
|
||||
make_no_items_placeholder() {
|
||||
this.$cart_header.css('display', 'none');
|
||||
this.$cart_items_wrapper.html(
|
||||
@@ -111,7 +114,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
this.$add_discount_elem = this.$component.find(".add-discount-wrapper");
|
||||
}
|
||||
|
||||
|
||||
make_cart_numpad() {
|
||||
this.$numpad_section = this.$component.find('.numpad-section');
|
||||
|
||||
@@ -147,7 +150,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
`<div class="numpad-btn checkout-btn" data-button-value="checkout">Checkout</div>`
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
bind_events() {
|
||||
const me = this;
|
||||
this.$customer_section.on('click', '.reset-customer-btn', function (e) {
|
||||
@@ -186,7 +189,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
this.$component.on('click', '.checkout-btn', function() {
|
||||
if ($(this).attr('style').indexOf('--blue-500') == -1) return;
|
||||
|
||||
|
||||
me.events.checkout();
|
||||
me.toggle_checkout_btn(false);
|
||||
});
|
||||
@@ -219,7 +222,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
if (btn === '.') shortcut_key = 'ctrl+>';
|
||||
|
||||
// to account for fieldname map
|
||||
const fieldname = this.number_pad.fieldnames[btn] ? this.number_pad.fieldnames[btn] :
|
||||
const fieldname = this.number_pad.fieldnames[btn] ? this.number_pad.fieldnames[btn] :
|
||||
typeof btn === 'string' ? frappe.scrub(btn) : btn;
|
||||
|
||||
let shortcut_label = shortcut_key.split('+').map(frappe.utils.to_title_case).join('+');
|
||||
@@ -230,7 +233,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const cart_is_visible = this.$component.is(":visible");
|
||||
if (cart_is_visible && this.item_is_selected && this.$numpad_section.is(":visible")) {
|
||||
this.$numpad_section.find(`.numpad-btn[data-button-value="${fieldname}"]`).click();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -268,7 +271,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
toggle_item_highlight(item) {
|
||||
const $cart_item = $(item);
|
||||
const item_is_highlighted = $cart_item.attr("style") == "background-color:var(--gray-50);";
|
||||
@@ -289,7 +292,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
`);
|
||||
const me = this;
|
||||
const query = { query: 'erpnext.controllers.queries.customer_query' };
|
||||
const allowed_customer_group = this.events.get_allowed_customer_group() || [];
|
||||
const allowed_customer_group = this.allowed_customer_groups || [];
|
||||
if (allowed_customer_group.length) {
|
||||
query.filters = {
|
||||
customer_group: ['in', allowed_customer_group]
|
||||
@@ -324,7 +327,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
});
|
||||
this.customer_field.toggle_label(false);
|
||||
}
|
||||
|
||||
|
||||
fetch_customer_details(customer) {
|
||||
if (customer) {
|
||||
return new Promise((resolve) => {
|
||||
@@ -411,9 +414,9 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_customer_section() {
|
||||
const { customer, email_id='', mobile_no='' } = this.customer_info || {};
|
||||
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
|
||||
|
||||
if (customer) {
|
||||
this.$customer_section.html(
|
||||
@@ -459,7 +462,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
return `<div class="customer-image customer-abbr">${frappe.get_abbr(customer)}</div>`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_totals_section(frm) {
|
||||
if (!frm) frm = this.events.get_frm();
|
||||
|
||||
@@ -469,7 +472,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
|
||||
this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
|
||||
}
|
||||
|
||||
|
||||
render_net_total(value) {
|
||||
const currency = this.events.get_frm().doc.currency;
|
||||
this.$totals_section.find('.net-total-container').html(
|
||||
@@ -480,7 +483,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
`<div>Net Total: <span>${format_currency(value, currency)}</span></div>`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
render_grand_total(value) {
|
||||
const currency = this.events.get_frm().doc.currency;
|
||||
this.$totals_section.find('.grand-total-container').html(
|
||||
@@ -518,12 +521,12 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
|
||||
const uom_attr = `[data-uom=${escape(uom)}]`;
|
||||
|
||||
const item_selector = batch_no ?
|
||||
const item_selector = batch_no ?
|
||||
`.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
|
||||
|
||||
|
||||
return this.$cart_items_wrapper.find(item_selector);
|
||||
}
|
||||
|
||||
|
||||
update_item_html(item, remove_item) {
|
||||
const $item = this.get_cart_item(item);
|
||||
|
||||
@@ -534,7 +537,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const search_field = batch_no ? 'batch_no' : 'item_code';
|
||||
const search_value = batch_no || item_code;
|
||||
const item_row = this.events.get_frm().doc.items.find(i => i[search_field] === search_value && i.uom === uom);
|
||||
|
||||
|
||||
this.render_cart_item(item_row, $item);
|
||||
}
|
||||
|
||||
@@ -543,14 +546,14 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
this.update_empty_cart_section(no_of_cart_items);
|
||||
}
|
||||
|
||||
|
||||
render_cart_item(item_data, $item_to_update) {
|
||||
const currency = this.events.get_frm().doc.currency;
|
||||
const me = this;
|
||||
|
||||
|
||||
if (!$item_to_update.length) {
|
||||
this.$cart_items_wrapper.append(
|
||||
`<div class="cart-item-wrapper"
|
||||
`<div class="cart-item-wrapper"
|
||||
data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
|
||||
data-batch-no="${escape(item_data.batch_no || '')}">
|
||||
</div>
|
||||
@@ -589,7 +592,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
me.$cart_header.find(".rate-amount-header").css("width", max_width);
|
||||
me.$cart_items_wrapper.find(".item-rate-amount").css("width", max_width);
|
||||
}
|
||||
|
||||
|
||||
function get_rate_discount_html() {
|
||||
if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) {
|
||||
return `
|
||||
@@ -641,7 +644,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop();
|
||||
this.$cart_items_wrapper.animate({ scrollTop });
|
||||
}
|
||||
|
||||
|
||||
update_selector_value_in_cart_item(selector, value, item) {
|
||||
const $item_to_update = this.get_cart_item(item);
|
||||
$item_to_update.attr(`data-${selector}`, value);
|
||||
@@ -670,7 +673,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update_empty_cart_section(no_of_cart_items) {
|
||||
const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
|
||||
|
||||
@@ -679,7 +682,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
|
||||
}
|
||||
|
||||
|
||||
on_numpad_event($btn) {
|
||||
const current_action = $btn.attr('data-button-value');
|
||||
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
|
||||
@@ -698,7 +701,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.prev_action = undefined;
|
||||
}
|
||||
this.numpad_value = '';
|
||||
|
||||
|
||||
} else if (current_action === 'checkout') {
|
||||
this.prev_action = undefined;
|
||||
this.toggle_item_highlight();
|
||||
@@ -724,7 +727,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
frappe.utils.play_sound("error");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (flt(this.numpad_value) > 100 && this.prev_action === 'discount_percentage') {
|
||||
frappe.show_alert({
|
||||
message: __('Discount cannot be greater than 100%'),
|
||||
@@ -736,7 +739,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
this.events.numpad_event(this.numpad_value, this.prev_action);
|
||||
}
|
||||
|
||||
|
||||
highlight_numpad_btn($btn, curr_action) {
|
||||
const curr_action_is_highlighted = $btn.hasClass('highlighted-numpad-btn');
|
||||
const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
|
||||
@@ -791,9 +794,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.$cart_container.css('display', 'none');
|
||||
this.$customer_section.css({
|
||||
'height': '100%',
|
||||
'padding-top': '0px',
|
||||
'overflow-x': 'hidden',
|
||||
'overflow-y': 'scroll'
|
||||
'padding-top': '0px'
|
||||
});
|
||||
this.$customer_section.find('.customer-details').html(
|
||||
`<div class="header">
|
||||
@@ -829,9 +830,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.$cart_container.css('display', 'flex');
|
||||
this.$customer_section.css({
|
||||
'height': '',
|
||||
'padding-top': '',
|
||||
'overflow-x': '',
|
||||
'overflow-y': ''
|
||||
'padding-top': ''
|
||||
});
|
||||
|
||||
this.update_customer_section();
|
||||
@@ -905,7 +904,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
}
|
||||
|
||||
fetch_customer_transactions() {
|
||||
frappe.db.get_list('POS Invoice', {
|
||||
frappe.db.get_list('POS Invoice', {
|
||||
filters: { customer: this.customer_info.customer, docstatus: 1 },
|
||||
fields: ['name', 'grand_total', 'status', 'posting_date', 'posting_time', 'currency'],
|
||||
limit: 20
|
||||
@@ -960,7 +959,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.events.customer_details_updated(this.customer_info);
|
||||
this.update_customer_section();
|
||||
})
|
||||
|
||||
|
||||
this.$cart_items_wrapper.html('');
|
||||
if (frm.doc.items.length) {
|
||||
frm.doc.items.forEach(item => {
|
||||
@@ -987,5 +986,5 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
toggle_component(show) {
|
||||
show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import onScan from 'onscan.js';
|
||||
|
||||
erpnext.PointOfSale.ItemSelector = class {
|
||||
constructor({ frm, wrapper, events, pos_profile }) {
|
||||
constructor({ frm, wrapper, events, pos_profile, settings }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.pos_profile = pos_profile;
|
||||
|
||||
this.hide_images = settings.hide_images;
|
||||
this.auto_add_item = settings.auto_add_item_to_cart;
|
||||
|
||||
this.inti_component();
|
||||
}
|
||||
|
||||
|
||||
inti_component() {
|
||||
this.prepare_dom();
|
||||
this.make_search_bar();
|
||||
@@ -28,8 +30,9 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
<div class="items-container"></div>
|
||||
</section>`
|
||||
);
|
||||
|
||||
|
||||
this.$component = this.wrapper.find('.items-selector');
|
||||
this.$items_container = this.$component.find('.items-container');
|
||||
}
|
||||
|
||||
async load_items_data() {
|
||||
@@ -53,7 +56,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
let { item_group, pos_profile } = this;
|
||||
|
||||
!item_group && (item_group = this.parent_item_group);
|
||||
|
||||
|
||||
return frappe.call({
|
||||
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
|
||||
freeze: true,
|
||||
@@ -63,7 +66,6 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
|
||||
|
||||
render_item_list(items) {
|
||||
this.$items_container = this.$component.find('.items-container');
|
||||
this.$items_container.html('');
|
||||
|
||||
items.forEach(item => {
|
||||
@@ -73,14 +75,15 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
}
|
||||
|
||||
get_item_html(item) {
|
||||
const me = this;
|
||||
const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
|
||||
const indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange";
|
||||
|
||||
function get_item_image_html() {
|
||||
if (item_image) {
|
||||
return `<div class="item-display">
|
||||
<img src="${item_image}" alt="${frappe.get_abbr(item.item_name)}">
|
||||
</div>`;
|
||||
if (!me.hide_images && item_image) {
|
||||
return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
|
||||
<img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
|
||||
</div>`
|
||||
} else {
|
||||
return `<div class="item-display abbr">${frappe.get_abbr(item.item_name)}</div>`;
|
||||
}
|
||||
@@ -166,7 +169,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
let batch_no = unescape($item.attr('data-batch-no'));
|
||||
let serial_no = unescape($item.attr('data-serial-no'));
|
||||
let uom = unescape($item.attr('data-uom'));
|
||||
|
||||
|
||||
// escape(undefined) returns "undefined" then unescape returns "undefined"
|
||||
batch_no = batch_no === "undefined" ? undefined : batch_no;
|
||||
serial_no = serial_no === "undefined" ? undefined : serial_no;
|
||||
@@ -204,6 +207,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
ignore_inputs: true,
|
||||
page: cur_page.page.page
|
||||
});
|
||||
|
||||
// for selecting the last filtered item on search
|
||||
frappe.ui.keys.on("enter", () => {
|
||||
const selector_is_visible = this.$component.is(':visible');
|
||||
@@ -225,7 +229,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
filter_items({ search_term='' }={}) {
|
||||
if (search_term) {
|
||||
search_term = search_term.toLowerCase();
|
||||
@@ -236,6 +240,7 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
const items = this.search_index[search_term];
|
||||
this.items = items;
|
||||
this.render_item_list(items);
|
||||
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -248,15 +253,20 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
}
|
||||
this.items = items;
|
||||
this.render_item_list(items);
|
||||
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
add_filtered_item_to_cart() {
|
||||
this.$items_container.find(".item-wrapper").click();
|
||||
}
|
||||
|
||||
resize_selector(minimize) {
|
||||
minimize ?
|
||||
minimize ?
|
||||
this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') :
|
||||
this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))');
|
||||
|
||||
minimize ?
|
||||
minimize ?
|
||||
this.$component.find('.search-field').css('margin', 'var(--margin-sm) 0px') :
|
||||
this.$component.find('.search-field').css('margin', '0px var(--margin-sm)');
|
||||
|
||||
|
||||
@@ -299,12 +299,12 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
}
|
||||
|
||||
load_summary_of(doc, after_submission=false) {
|
||||
this.toggle_summary_placeholder(false)
|
||||
|
||||
after_submission ?
|
||||
this.$summary_wrapper.css('grid-column', 'span 10 / span 10') :
|
||||
this.$summary_wrapper.css('grid-column', 'span 6 / span 6')
|
||||
|
||||
this.$component.css('grid-column', 'span 10 / span 10') :
|
||||
this.$component.css('grid-column', 'span 6 / span 6')
|
||||
|
||||
this.toggle_summary_placeholder(false)
|
||||
|
||||
this.doc = doc;
|
||||
|
||||
this.attach_document_info(doc);
|
||||
@@ -333,9 +333,25 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
doc.items.forEach((item, i) => {
|
||||
const item_dom = this.get_item_html(doc, item);
|
||||
this.$items_container.append(item_dom);
|
||||
this.set_dynamic_rate_header_width();
|
||||
});
|
||||
}
|
||||
|
||||
set_dynamic_rate_header_width() {
|
||||
const rate_cols = Array.from(this.$items_container.find(".item-rate-disc"));
|
||||
this.$items_container.find(".item-rate-disc").css("width", "");
|
||||
let max_width = rate_cols.reduce((max_width, elm) => {
|
||||
if ($(elm).width() > max_width)
|
||||
max_width = $(elm).width();
|
||||
return max_width;
|
||||
}, 0);
|
||||
|
||||
max_width += 1;
|
||||
if (max_width == 1) max_width = "";
|
||||
|
||||
this.$items_container.find(".item-rate-disc").css("width", max_width);
|
||||
}
|
||||
|
||||
attach_payments_info(doc) {
|
||||
this.$payment_container.html('');
|
||||
doc.payments.forEach(p => {
|
||||
|
||||
@@ -10,8 +10,8 @@ from frappe.utils.nestedset import get_descendants_of
|
||||
def execute(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
if filters.from_date > filters.to_date:
|
||||
frappe.throw(_('From Date cannot be greater than To Date'))
|
||||
|
||||
frappe.throw(_("From Date cannot be greater than To Date"))
|
||||
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
|
||||
@@ -148,14 +148,16 @@ def get_data(filters):
|
||||
company_list.append(filters.get("company"))
|
||||
|
||||
customer_details = get_customer_details()
|
||||
item_details = get_item_details()
|
||||
sales_order_records = get_sales_order_details(company_list, filters)
|
||||
|
||||
for record in sales_order_records:
|
||||
customer_record = customer_details.get(record.customer)
|
||||
item_record = item_details.get(record.item_code)
|
||||
row = {
|
||||
"item_code": record.item_code,
|
||||
"item_name": record.item_name,
|
||||
"item_group": record.item_group,
|
||||
"item_name": item_record.item_name,
|
||||
"item_group": item_record.item_group,
|
||||
"description": record.description,
|
||||
"quantity": record.qty,
|
||||
"uom": record.uom,
|
||||
@@ -196,8 +198,8 @@ def get_conditions(filters):
|
||||
return conditions
|
||||
|
||||
def get_customer_details():
|
||||
details = frappe.get_all('Customer',
|
||||
fields=['name', 'customer_name', "customer_group"])
|
||||
details = frappe.get_all("Customer",
|
||||
fields=["name", "customer_name", "customer_group"])
|
||||
customer_details = {}
|
||||
for d in details:
|
||||
customer_details.setdefault(d.name, frappe._dict({
|
||||
@@ -206,15 +208,25 @@ def get_customer_details():
|
||||
}))
|
||||
return customer_details
|
||||
|
||||
def get_item_details():
|
||||
details = frappe.db.get_all("Item",
|
||||
fields=["item_code", "item_name", "item_group"])
|
||||
item_details = {}
|
||||
for d in details:
|
||||
item_details.setdefault(d.item_code, frappe._dict({
|
||||
"item_name": d.item_name,
|
||||
"item_group": d.item_group
|
||||
}))
|
||||
return item_details
|
||||
|
||||
def get_sales_order_details(company_list, filters):
|
||||
conditions = get_conditions(filters)
|
||||
|
||||
return frappe.db.sql("""
|
||||
SELECT
|
||||
so_item.item_code, so_item.item_name, so_item.item_group,
|
||||
so_item.description, so_item.qty, so_item.uom,
|
||||
so_item.base_rate, so_item.base_amount, so.name,
|
||||
so.transaction_date, so.customer, so.territory,
|
||||
so_item.item_code, so_item.description, so_item.qty,
|
||||
so_item.uom, so_item.base_rate, so_item.base_amount,
|
||||
so.name, so.transaction_date, so.customer,so.territory,
|
||||
so.project, so_item.delivered_qty,
|
||||
so_item.billed_amt, so.company
|
||||
FROM
|
||||
|
||||
@@ -42,16 +42,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
me.frm.set_query('customer_address', erpnext.queries.address_query);
|
||||
me.frm.set_query('shipping_address_name', erpnext.queries.address_query);
|
||||
|
||||
if(this.frm.fields_dict.taxes_and_charges) {
|
||||
this.frm.set_query("taxes_and_charges", function() {
|
||||
return {
|
||||
filters: [
|
||||
['Sales Taxes and Charges Template', 'company', '=', me.frm.doc.company],
|
||||
['Sales Taxes and Charges Template', 'docstatus', '!=', 2]
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(this.frm.fields_dict.selling_price_list) {
|
||||
this.frm.set_query("selling_price_list", function() {
|
||||
@@ -479,7 +469,7 @@ frappe.ui.form.on(cur_frm.doctype,"project", function(frm) {
|
||||
$.each(frm.doc["items"] || [], function(i, row) {
|
||||
if(r.message) {
|
||||
frappe.model.set_value(row.doctype, row.name, "cost_center", r.message);
|
||||
frappe.msgprint(__("Cost Center For Item with Item Code '"+row.item_name+"' has been Changed to "+ r.message));
|
||||
frappe.msgprint(__("Cost Center For Item with Item Code {0} has been Changed to {1}", [row.item_name, r.message]));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user