mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-15 18:01:41 +00:00
fix(pos): multiple pos fixes and additions (#24227)
* fix: make custom fields in pos invoice similar to sales invoice * feat: allow/disallow rate & discount change * fix: any pos profile can be selected while creating pos opening * fix: cannot add item to cart * fix: validate phone payment only if payment request exists * fix: replace pos payment method patch * chore: rearrange item & customer group filter * fix: allow/disallow invoice level discount * fix: updating qty of item with uom having space char * fix: move configuration checbox to config section * fix: invalid item rate trigger * fix: cannot remove item from draft invoices * fix: customer currency not set in pos invoice * fix: duplicate item error message * fix: sales uom not fetched in pos invoice * fix: cannot add taxes to pos invoice for uae region * fix: cannot merge pos invoice into credit note * fix: tax calculation while merging pos invoices * feat: delete draft orders from order list * fix: merging of pos invoice with pricing rules
This commit is contained in:
@@ -69,6 +69,10 @@ erpnext.PointOfSale.Controller = class {
|
||||
dialog.fields_dict.balance_details.grid.refresh();
|
||||
});
|
||||
}
|
||||
const pos_profile_query = {
|
||||
query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
|
||||
filters: { company: frappe.defaults.get_default('company') }
|
||||
}
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __('Create POS Opening Entry'),
|
||||
static: true,
|
||||
@@ -80,6 +84,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
{
|
||||
fieldtype: 'Link', label: __('POS Profile'),
|
||||
options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
|
||||
get_query: () => pos_profile_query,
|
||||
onchange: () => fetch_pos_payment_methods()
|
||||
},
|
||||
{
|
||||
@@ -124,9 +129,8 @@ erpnext.PointOfSale.Controller = class {
|
||||
});
|
||||
|
||||
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
|
||||
Object.assign(this.settings, profile);
|
||||
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();
|
||||
});
|
||||
}
|
||||
@@ -255,11 +259,9 @@ erpnext.PointOfSale.Controller = class {
|
||||
get_frm: () => this.frm,
|
||||
|
||||
cart_item_clicked: (item_code, batch_no, uom) => {
|
||||
const item_row = this.frm.doc.items.find(
|
||||
i => i.item_code === item_code
|
||||
&& i.uom === uom
|
||||
&& (!batch_no || (batch_no && i.batch_no === batch_no))
|
||||
);
|
||||
const search_field = batch_no ? 'batch_no' : 'item_code';
|
||||
const search_value = batch_no || item_code;
|
||||
const item_row = this.frm.doc.items.find(i => i[search_field] === search_value && i.uom === uom);
|
||||
this.item_details.toggle_item_details_section(item_row);
|
||||
},
|
||||
|
||||
@@ -281,6 +283,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
init_item_details() {
|
||||
this.item_details = new erpnext.PointOfSale.ItemDetails({
|
||||
wrapper: this.$components_wrapper,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
get_frm: () => this.frm,
|
||||
|
||||
@@ -415,6 +418,11 @@ erpnext.PointOfSale.Controller = class {
|
||||
() => this.item_selector.toggle_component(true)
|
||||
]);
|
||||
},
|
||||
delete_order: (name) => {
|
||||
frappe.model.delete_doc(this.frm.doc.doctype, name, () => {
|
||||
this.recent_order_list.refresh_list();
|
||||
});
|
||||
},
|
||||
new_order: () => {
|
||||
frappe.run_serially([
|
||||
() => frappe.dom.freeze(),
|
||||
@@ -696,14 +704,14 @@ erpnext.PointOfSale.Controller = class {
|
||||
frappe.dom.freeze();
|
||||
const { doctype, name, current_item } = this.item_details;
|
||||
|
||||
frappe.model.set_value(doctype, name, 'qty', 0);
|
||||
|
||||
this.frm.script_manager.trigger('qty', doctype, name).then(() => {
|
||||
frappe.model.clear_doc(doctype, name);
|
||||
this.update_cart_html(current_item, true);
|
||||
this.item_details.toggle_item_details_section(undefined);
|
||||
frappe.dom.unfreeze();
|
||||
})
|
||||
frappe.model.set_value(doctype, name, 'qty', 0)
|
||||
.then(() => {
|
||||
frappe.model.clear_doc(doctype, name);
|
||||
this.update_cart_html(current_item, true);
|
||||
this.item_details.toggle_item_details_section(undefined);
|
||||
frappe.dom.unfreeze();
|
||||
})
|
||||
.catch(e => console.log(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.customer_info = undefined;
|
||||
this.hide_images = settings.hide_images;
|
||||
this.allowed_customer_groups = settings.customer_groups;
|
||||
this.allow_rate_change = settings.allow_rate_change;
|
||||
this.allow_discount_change = settings.allow_discount_change;
|
||||
|
||||
this.init_component();
|
||||
}
|
||||
@@ -201,7 +203,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
me.events.checkout();
|
||||
me.toggle_checkout_btn(false);
|
||||
|
||||
me.$add_discount_elem.removeClass("d-none");
|
||||
me.allow_discount_change && me.$add_discount_elem.removeClass("d-none");
|
||||
});
|
||||
|
||||
this.$totals_section.on('click', '.edit-cart-btn', () => {
|
||||
@@ -479,11 +481,15 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
update_totals_section(frm) {
|
||||
if (!frm) frm = this.events.get_frm();
|
||||
|
||||
this.render_net_total(frm.doc.base_net_total);
|
||||
this.render_grand_total(frm.doc.base_grand_total);
|
||||
this.render_net_total(frm.doc.net_total);
|
||||
this.render_grand_total(frm.doc.grand_total);
|
||||
|
||||
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);
|
||||
const taxes = frm.doc.taxes.map(t => {
|
||||
return {
|
||||
description: t.description, rate: t.rate
|
||||
}
|
||||
});
|
||||
this.render_taxes(frm.doc.total_taxes_and_charges, taxes);
|
||||
}
|
||||
|
||||
render_net_total(value) {
|
||||
@@ -545,7 +551,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
get_cart_item({ item_code, batch_no, uom }) {
|
||||
const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
|
||||
const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
|
||||
const uom_attr = `[data-uom=${escape(uom)}]`;
|
||||
const uom_attr = `[data-uom="${escape(uom)}"]`;
|
||||
|
||||
const item_selector = batch_no ?
|
||||
`.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
|
||||
@@ -667,7 +673,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
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);
|
||||
$item_to_update.attr(`data-${selector}`, escape(value));
|
||||
}
|
||||
|
||||
toggle_checkout_btn(show_checkout) {
|
||||
@@ -702,14 +708,26 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
on_numpad_event($btn) {
|
||||
const current_action = $btn.attr('data-button-value');
|
||||
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
|
||||
|
||||
this.highlight_numpad_btn($btn, current_action);
|
||||
const action_is_allowed = action_is_field_edit ? (
|
||||
(current_action == 'rate' && this.allow_rate_change) ||
|
||||
(current_action == 'discount_percentage' && this.allow_discount_change) ||
|
||||
(current_action == 'qty')) : true;
|
||||
|
||||
const action_is_pressed_twice = this.prev_action === current_action;
|
||||
const first_click_event = !this.prev_action;
|
||||
const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
|
||||
|
||||
if (action_is_field_edit) {
|
||||
if (!action_is_allowed) {
|
||||
const label = current_action == 'rate' ? 'Rate'.bold() : 'Discount'.bold();
|
||||
const message = __('Editing {0} is not allowed as per POS Profile settings', [label]);
|
||||
frappe.show_alert({
|
||||
indicator: 'red',
|
||||
message: message
|
||||
});
|
||||
frappe.utils.play_sound("error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (first_click_event || field_to_edit_changed) {
|
||||
this.prev_action = current_action;
|
||||
@@ -753,6 +771,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.numpad_value = current_action;
|
||||
}
|
||||
|
||||
this.highlight_numpad_btn($btn, current_action);
|
||||
this.events.numpad_event(this.numpad_value, this.prev_action);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
erpnext.PointOfSale.ItemDetails = class {
|
||||
constructor({ wrapper, events }) {
|
||||
constructor({ wrapper, events, settings }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.allow_rate_change = settings.allow_rate_change;
|
||||
this.allow_discount_change = settings.allow_discount_change;
|
||||
this.current_item = {};
|
||||
|
||||
this.init_component();
|
||||
@@ -207,17 +209,27 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
bind_custom_control_change_event() {
|
||||
const me = this;
|
||||
if (this.rate_control) {
|
||||
this.rate_control.df.onchange = function() {
|
||||
if (this.value || flt(this.value) === 0) {
|
||||
me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
|
||||
const item_row = frappe.get_doc(me.doctype, me.name);
|
||||
const doc = me.events.get_frm().doc;
|
||||
|
||||
me.$item_price.html(format_currency(item_row.rate, doc.currency));
|
||||
me.render_discount_dom(item_row);
|
||||
});
|
||||
}
|
||||
if (this.allow_rate_change) {
|
||||
this.rate_control.df.onchange = function() {
|
||||
if (this.value || flt(this.value) === 0) {
|
||||
me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
|
||||
const item_row = frappe.get_doc(me.doctype, me.name);
|
||||
const doc = me.events.get_frm().doc;
|
||||
|
||||
me.$item_price.html(format_currency(item_row.rate, doc.currency));
|
||||
me.render_discount_dom(item_row);
|
||||
});
|
||||
}
|
||||
};
|
||||
} else {
|
||||
this.rate_control.df.read_only = 1;
|
||||
}
|
||||
this.rate_control.refresh();
|
||||
}
|
||||
|
||||
if (this.discount_percentage_control && !this.allow_discount_change) {
|
||||
this.discount_percentage_control.df.read_only = 1;
|
||||
this.discount_percentage_control.refresh();
|
||||
}
|
||||
|
||||
if (this.warehouse_control) {
|
||||
@@ -294,8 +306,16 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
|
||||
frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
|
||||
const { item_code, batch_no, uom } = this.current_item;
|
||||
const item_code_is_same = item_code === item_row.item_code;
|
||||
const batch_is_same = batch_no == item_row.batch_no;
|
||||
const uom_is_same = uom === item_row.uom;
|
||||
// check if current_item is same as item_row
|
||||
const item_is_same = item_code_is_same && batch_is_same && uom_is_same ? true : false;
|
||||
|
||||
const field_control = me[`${fieldname}_control`];
|
||||
if (field_control) {
|
||||
|
||||
if (item_is_same && field_control && field_control.get_value() !== value) {
|
||||
field_control.set_value(value);
|
||||
cur_pos.update_cart_html(item_row);
|
||||
}
|
||||
|
||||
@@ -265,6 +265,14 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
this.$summary_wrapper.addClass('d-none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.delete-btn', () => {
|
||||
this.events.delete_order(this.doc.name);
|
||||
this.show_summary_placeholder();
|
||||
// this.toggle_component(false);
|
||||
// this.$component.find('.no-summary-placeholder').removeClass('d-none');
|
||||
// this.$summary_wrapper.addClass('d-none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.new-btn', () => {
|
||||
this.events.new_order();
|
||||
this.toggle_component(false);
|
||||
@@ -401,7 +409,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }];
|
||||
|
||||
return [
|
||||
{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order'] },
|
||||
{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order', 'Delete Order'] },
|
||||
{ condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return']},
|
||||
{ condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt']}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user