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:
parent
0d9a8ae4d2
commit
ac9e6ff704
@ -165,9 +165,9 @@ def toggle_disabling(doc):
|
||||
frappe.clear_cache(doctype=doctype)
|
||||
|
||||
def get_doctypes_with_dimensions():
|
||||
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||
doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
|
||||
"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||
"Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
||||
|
@ -57,7 +57,11 @@ class POSClosingEntry(Document):
|
||||
if not invalid_rows:
|
||||
return
|
||||
|
||||
error_list = [_("Row #{}: {}").format(row.get('idx'), row.get('msg')) for row in invalid_rows]
|
||||
error_list = []
|
||||
for row in invalid_rows:
|
||||
for msg in row.get('msg'):
|
||||
error_list.append(_("Row #{}: {}").format(row.get('idx'), msg))
|
||||
|
||||
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
||||
|
||||
def on_submit(self):
|
||||
|
@ -2,6 +2,7 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
{% include 'erpnext/selling/sales_common.js' %};
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend({
|
||||
setup(doc) {
|
||||
@ -9,12 +10,18 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
this._super(doc);
|
||||
},
|
||||
|
||||
company: function() {
|
||||
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
|
||||
},
|
||||
|
||||
onload(doc) {
|
||||
this._super();
|
||||
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
|
||||
this.frm.script_manager.trigger("is_pos");
|
||||
this.frm.refresh_fields();
|
||||
}
|
||||
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
|
||||
},
|
||||
|
||||
refresh(doc) {
|
||||
|
@ -78,7 +78,7 @@ class POSInvoice(SalesInvoice):
|
||||
mode_of_payment=pay.mode_of_payment, status="Paid"),
|
||||
fieldname="grand_total")
|
||||
|
||||
if pay.amount != paid_amt:
|
||||
if paid_amt and pay.amount != paid_amt:
|
||||
return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
|
||||
|
||||
def validate_stock_availablility(self):
|
||||
@ -297,7 +297,9 @@ class POSInvoice(SalesInvoice):
|
||||
self.set(fieldname, profile.get(fieldname))
|
||||
|
||||
if self.customer:
|
||||
customer_price_list, customer_group = frappe.db.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
|
||||
customer_price_list, customer_group, customer_currency = frappe.db.get_value(
|
||||
"Customer", self.customer, ['default_price_list', 'customer_group', 'default_currency']
|
||||
)
|
||||
customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list')
|
||||
selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list')
|
||||
else:
|
||||
@ -305,6 +307,8 @@ class POSInvoice(SalesInvoice):
|
||||
|
||||
if selling_price_list:
|
||||
self.set('selling_price_list', selling_price_list)
|
||||
if customer_currency != profile.get('currency'):
|
||||
self.set('currency', customer_currency)
|
||||
|
||||
# set pos values in items
|
||||
for item in self.get("items"):
|
||||
|
@ -5,10 +5,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_doc
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_doc, map_child_doc
|
||||
from frappe.utils import flt, getdate, nowdate
|
||||
|
||||
from six import iteritems
|
||||
|
||||
@ -83,7 +83,7 @@ class POSInvoiceMergeLog(Document):
|
||||
|
||||
credit_note.is_consolidated = 1
|
||||
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
||||
credit_note.return_against = self.consolidated_invoice
|
||||
# credit_note.return_against = self.consolidated_invoice
|
||||
credit_note.save()
|
||||
credit_note.submit()
|
||||
self.consolidated_credit_note = credit_note.name
|
||||
@ -111,7 +111,9 @@ class POSInvoiceMergeLog(Document):
|
||||
i.qty = i.qty + item.qty
|
||||
if not found:
|
||||
item.rate = item.net_rate
|
||||
items.append(item)
|
||||
item.price_list_rate = 0
|
||||
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
|
||||
items.append(si_item)
|
||||
|
||||
for tax in doc.get('taxes'):
|
||||
found = False
|
||||
@ -147,6 +149,8 @@ class POSInvoiceMergeLog(Document):
|
||||
invoice.set('taxes', taxes)
|
||||
invoice.additional_discount_percentage = 0
|
||||
invoice.discount_amount = 0.0
|
||||
invoice.taxes_and_charges = None
|
||||
invoice.ignore_pricing_rule = 1
|
||||
|
||||
return invoice
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
"company",
|
||||
"country",
|
||||
"column_break_9",
|
||||
"update_stock",
|
||||
"ignore_pricing_rule",
|
||||
"warehouse",
|
||||
"campaign",
|
||||
"company_address",
|
||||
@ -25,8 +23,14 @@
|
||||
"hide_images",
|
||||
"hide_unavailable_items",
|
||||
"auto_add_item_to_cart",
|
||||
"item_groups",
|
||||
"column_break_16",
|
||||
"update_stock",
|
||||
"ignore_pricing_rule",
|
||||
"allow_rate_change",
|
||||
"allow_discount_change",
|
||||
"section_break_23",
|
||||
"item_groups",
|
||||
"column_break_25",
|
||||
"customer_groups",
|
||||
"section_break_16",
|
||||
"print_format",
|
||||
@ -309,6 +313,7 @@
|
||||
"default": "1",
|
||||
"fieldname": "update_stock",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Update Stock",
|
||||
"read_only": 1
|
||||
},
|
||||
@ -329,13 +334,34 @@
|
||||
"fieldname": "auto_add_item_to_cart",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Add Filtered Item To Cart"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_rate_change",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow User to Edit Rate"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_discount_change",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow User to Edit Discount"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "section_break_23",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_25",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-10 13:59:28.877572",
|
||||
"modified": "2021-01-06 14:42:41.713864",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile",
|
||||
|
@ -435,7 +435,9 @@ class SalesInvoice(SellingController):
|
||||
if not for_validate and not self.customer:
|
||||
self.customer = pos.customer
|
||||
|
||||
self.ignore_pricing_rule = pos.ignore_pricing_rule
|
||||
if not for_validate:
|
||||
self.ignore_pricing_rule = pos.ignore_pricing_rule
|
||||
|
||||
if pos.get('account_for_change_amount'):
|
||||
self.account_for_change_amount = pos.get('account_for_change_amount')
|
||||
|
||||
|
@ -302,6 +302,7 @@ class AccountsController(TransactionBase):
|
||||
args["doctype"] = self.doctype
|
||||
args["name"] = self.name
|
||||
args["child_docname"] = item.name
|
||||
args["ignore_pricing_rule"] = self.ignore_pricing_rule if hasattr(self, 'ignore_pricing_rule') else 0
|
||||
|
||||
if not args.get("transaction_date"):
|
||||
args["transaction_date"] = args.get("posting_date")
|
||||
|
@ -262,6 +262,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
|
||||
if doc.get("is_return"):
|
||||
if doc.doctype == 'Sales Invoice' or doc.doctype == 'POS Invoice':
|
||||
doc.consolidated_invoice = ""
|
||||
doc.set('payments', [])
|
||||
for data in source.payments:
|
||||
paid_amount = 0.00
|
||||
|
@ -469,13 +469,19 @@ class SellingController(StockController):
|
||||
non_stock_items = [d.item_code, d.description]
|
||||
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
|
||||
duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code))
|
||||
duplicate_items_msg += "<br><br>"
|
||||
duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format(
|
||||
frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"),
|
||||
get_link_to_form("Selling Settings", "Selling Settings")
|
||||
)
|
||||
if stock_items in check_list:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
frappe.throw(duplicate_items_msg)
|
||||
else:
|
||||
check_list.append(stock_items)
|
||||
else:
|
||||
if non_stock_items in chk_dupl_itm:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
frappe.throw(duplicate_items_msg)
|
||||
else:
|
||||
chk_dupl_itm.append(non_stock_items)
|
||||
|
||||
|
@ -107,7 +107,7 @@ class calculate_taxes_and_totals(object):
|
||||
elif item.discount_amount and item.pricing_rules:
|
||||
item.rate = item.price_list_rate - item.discount_amount
|
||||
|
||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
|
||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item']:
|
||||
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
||||
if flt(item.rate_with_margin) > 0:
|
||||
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||
|
@ -44,6 +44,7 @@ class TestMpesaSettings(unittest.TestCase):
|
||||
create_mpesa_settings(payment_gateway_name="Payment")
|
||||
mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
|
||||
frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
|
||||
frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
|
||||
|
||||
pos_invoice = create_pos_invoice(do_not_submit=1)
|
||||
pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 500})
|
||||
@ -69,6 +70,8 @@ class TestMpesaSettings(unittest.TestCase):
|
||||
self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
|
||||
self.assertEquals(integration_request.status, "Completed")
|
||||
|
||||
frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
|
||||
|
||||
def create_mpesa_settings(payment_gateway_name="Express"):
|
||||
if frappe.db.exists("Mpesa Settings", payment_gateway_name):
|
||||
return frappe.get_doc("Mpesa Settings", payment_gateway_name)
|
||||
|
@ -677,7 +677,7 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
|
||||
erpnext.patches.v12_0.fix_quotation_expired_status
|
||||
erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
|
||||
erpnext.patches.v12_0.rename_pos_closing_doctype
|
||||
erpnext.patches.v13_0.replace_pos_payment_mode_table
|
||||
erpnext.patches.v13_0.replace_pos_payment_mode_table #2020-12-29
|
||||
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
|
||||
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
|
||||
execute:frappe.reload_doc("HR", "doctype", "Employee Advance")
|
||||
@ -743,6 +743,7 @@ erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
||||
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
||||
erpnext.patches.v13_0.add_po_to_global_search
|
||||
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||
erpnext.patches.v13_0.create_uae_pos_invoice_fields
|
||||
erpnext.patches.v13_0.update_project_template_tasks
|
||||
erpnext.patches.v13_0.set_company_in_leave_ledger_entry
|
||||
erpnext.patches.v13_0.convert_qi_parameter_to_link_field
|
||||
|
14
erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
Normal file
14
erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2019, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from erpnext.regional.united_arab_emirates.setup import make_custom_fields
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': ['in', ['Saudi Arabia', 'United Arab Emirates']]})
|
||||
if not company:
|
||||
return
|
||||
|
||||
make_custom_fields()
|
@ -6,12 +6,10 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "POS Payment Method")
|
||||
frappe.reload_doc("accounts", "doctype", "pos_payment_method")
|
||||
pos_profiles = frappe.get_all("POS Profile")
|
||||
|
||||
for pos_profile in pos_profiles:
|
||||
if not pos_profile.get("payments"): return
|
||||
|
||||
payments = frappe.db.sql("""
|
||||
select idx, parentfield, parenttype, parent, mode_of_payment, `default` from `tabSales Invoice Payment` where parent=%s
|
||||
""", pos_profile.name, as_dict=1)
|
||||
|
@ -110,9 +110,11 @@ def make_custom_fields():
|
||||
'Purchase Order': purchase_invoice_fields + invoice_fields,
|
||||
'Purchase Receipt': purchase_invoice_fields + invoice_fields,
|
||||
'Sales Invoice': sales_invoice_fields + invoice_fields,
|
||||
'POS Invoice': sales_invoice_fields + invoice_fields,
|
||||
'Sales Order': sales_invoice_fields + invoice_fields,
|
||||
'Delivery Note': sales_invoice_fields + invoice_fields,
|
||||
'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
|
||||
'POS Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
|
||||
'Purchase Invoice Item': invoice_item_fields,
|
||||
'Sales Order Item': invoice_item_fields,
|
||||
'Delivery Note Item': invoice_item_fields,
|
||||
|
@ -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']}
|
||||
];
|
||||
|
@ -672,13 +672,14 @@ class Item(WebsiteGenerator):
|
||||
if not records: return
|
||||
document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
|
||||
|
||||
msg = _("The items {0} and {1} are present in the following {2} : <br>"
|
||||
.format(frappe.bold(old_name), frappe.bold(new_name), document))
|
||||
msg = _("The items {0} and {1} are present in the following {2} : ").format(
|
||||
frappe.bold(old_name), frappe.bold(new_name), document)
|
||||
|
||||
msg += '<br>'
|
||||
msg += ', '.join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "<br><br>"
|
||||
|
||||
msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
|
||||
.format(frappe.bold(old_name)))
|
||||
msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}").format(
|
||||
frappe.bold(old_name))
|
||||
|
||||
frappe.throw(_(msg), title=_("Merge not allowed"))
|
||||
|
||||
@ -971,7 +972,7 @@ class Item(WebsiteGenerator):
|
||||
frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
|
||||
|
||||
def check_if_linked_document_exists(self, field):
|
||||
linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "Purchase Receipt Item",
|
||||
linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Receipt Item",
|
||||
"Purchase Invoice Item", "Stock Entry Detail", "Stock Reconciliation Item"]
|
||||
|
||||
# For "Is Stock Item", following doctypes is important
|
||||
|
@ -19,7 +19,7 @@ from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_m
|
||||
|
||||
from six import string_types, iteritems
|
||||
|
||||
sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
|
||||
sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'POS Invoice']
|
||||
purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
|
||||
|
||||
@frappe.whitelist()
|
||||
|
Loading…
x
Reference in New Issue
Block a user