diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index f93c9679b76..7ce777213f7 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
-__version__ = '8.0.44'
+__version__ = '8.0.45'
def get_default_company(user=None):
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 94621513f3d..ad5ecc160de 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -78,7 +78,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
},
get_query_filters: {
docstatus: 1,
- status: ["!=", "Closed"],
+ status: ["not in", ["Closed", "Completed"]],
company: me.frm.doc.company,
is_return: 0
}
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index a899cde25d0..0f0569a104a 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -35,6 +35,7 @@ def get_pos_data():
'item_groups': get_item_groups(pos_profile),
'customers': customers,
'address': get_customers_address(customers),
+ 'contacts': get_contacts(customers),
'serial_no_data': get_serial_no_data(pos_profile, doc.company),
'batch_no_data': get_batch_no_data(),
'tax_data': get_item_tax_data(),
@@ -160,7 +161,7 @@ def get_item_groups(pos_profile):
item_group_dict[data.name] = [data.lft, data.rgt]
return item_group_dict
-def get_customers_list(pos_profile):
+def get_customers_list(pos_profile={}):
cond = "1=1"
customer_groups = []
if pos_profile.get('customer_groups'):
@@ -170,7 +171,7 @@ def get_customers_list(pos_profile):
cond = "customer_group in (%s)"%(', '.join(['%s']*len(customer_groups)))
return frappe.db.sql(""" select name, customer_name, customer_group,
- territory from tabCustomer where disabled = 0
+ territory, customer_pos_id from tabCustomer where disabled = 0
and {cond}""".format(cond=cond), tuple(customer_groups), as_dict=1) or {}
def get_customers_address(customers):
@@ -183,13 +184,29 @@ def get_customers_address(customers):
email_id, phone, fax, pincode from `tabAddress` where is_primary_address =1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Address')""", data.name, as_dict=1)
- if address:
- address_data = address[0]
- address_data.update({'full_name': data.customer_name})
- customer_address[data.name] = address_data
+ address_data = {}
+ if address: address_data = address[0]
+
+ address_data.update({'full_name': data.customer_name, 'customer_pos_id': data.customer_pos_id})
+ customer_address[data.name] = address_data
return customer_address
+def get_contacts(customers):
+ customer_contact = {}
+ if isinstance(customers, basestring):
+ customers = [frappe._dict({'name': customers})]
+
+ for data in customers:
+ contact = frappe.db.sql(""" select email_id, phone from `tabContact`
+ where is_primary_contact =1 and name in
+ (select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
+ and parenttype = 'Contact')""", data.name, as_dict=1)
+ if contact:
+ customer_contact[data.name] = contact[0]
+
+ return customer_contact
+
def get_child_nodes(group_type, root):
lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
@@ -294,7 +311,7 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
if isinstance(customers_list, basestring):
customers_list = json.loads(customers_list)
- customers = make_customer_and_address(customers_list)
+ customers_list = make_customer_and_address(customers_list)
name_list = []
for docs in doc_list:
for name, doc in docs.items():
@@ -303,6 +320,7 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
si_doc = frappe.new_doc('Sales Invoice')
si_doc.offline_pos_name = name
si_doc.update(doc)
+ si_doc.customer = get_customer_id(doc)
si_doc.due_date = doc.get('posting_date')
submit_invoice(si_doc, name, doc)
name_list.append(name)
@@ -310,30 +328,54 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
name_list.append(name)
email_queue = make_email_queue(email_queue_list)
+ customers = get_customers_list()
return {
'invoice': name_list,
'email_queue': email_queue,
- 'customers': customers
+ 'customers': customers_list,
+ 'synced_customers_list': customers,
+ 'synced_address': get_customers_address(customers),
+ 'synced_contacts': get_contacts(customers)
}
def validate_records(doc):
validate_item(doc)
-def make_customer_and_address(customers):
- customer_list = []
- for name, data in customers.items():
- if not frappe.db.exists('Customer', name):
- name = add_customer(name)
- data = json.loads(data)
- make_contact(data, name)
- make_address(data, name)
- customer_list.append(name)
- frappe.db.commit()
- return customer_list
+def get_customer_id(doc, customer=None):
+ cust_id = None
+ if doc.get('customer_pos_id'):
+ cust_id = frappe.db.get_value('Customer',
+ {'customer_pos_id': doc.get('customer_pos_id')}, 'name')
-def add_customer(name):
+ if not cust_id:
+ customer = customer or doc.get('customer')
+ if frappe.db.exists('Customer', customer):
+ cust_id = customer
+ else:
+ cust_id = add_customer(doc)
+
+ return cust_id
+
+def make_customer_and_address(customers):
+ customers_list = []
+ for customer, data in customers.items():
+ data = json.loads(data)
+ cust_id = get_customer_id(data, customer)
+ if not cust_id:
+ cust_id = add_customer(data)
+ else:
+ frappe.db.set_value("Customer", cust_id, "customer_name", data.get('full_name'))
+
+ make_contact(data, cust_id)
+ make_address(data, cust_id)
+ customers_list.append(customer)
+ frappe.db.commit()
+ return customers_list
+
+def add_customer(data):
customer_doc = frappe.new_doc('Customer')
- customer_doc.customer_name = name
+ customer_doc.customer_name = data.get('full_name') or data.get('customer')
+ customer_doc.customer_pos_id = data.get('customer_pos_id')
customer_doc.customer_type = 'Company'
customer_doc.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
customer_doc.territory = frappe.db.get_single_value('Selling Settings', 'territory')
@@ -348,6 +390,7 @@ def make_contact(args,customer):
{'link_doctype': 'Customer', 'link_name': customer, 'parenttype': 'Contact'}, 'parent')
args = {
+ 'first_name': args.get('full_name'),
'email_id': args.get('email_id'),
'phone': args.get('phone')
}
@@ -357,8 +400,8 @@ def make_contact(args,customer):
doc = frappe.get_doc('Contact', name)
doc.update(args)
+ doc.is_primary_contact = 1
if not name:
- doc.first_name = customer
doc.append('links',{
'link_doctype': 'Customer',
'link_name': customer
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 6d88326f1ae..17bfd576a5a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -247,7 +247,6 @@ class SalesInvoice(SellingController):
(not self.project and not data.sales_invoice) or \
(not sales_invoice and data.sales_invoice == self.name):
data.sales_invoice = sales_invoice
- if self.project: return
def on_update(self):
self.set_paid_amount()
@@ -487,13 +486,14 @@ class SalesInvoice(SellingController):
self.set('packed_items', [])
def set_billing_hours_and_amount(self):
- for timesheet in self.timesheets:
- ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
- if not timesheet.billing_hours and ts_doc.total_billable_hours:
- timesheet.billing_hours = ts_doc.total_billable_hours
+ if not self.project:
+ for timesheet in self.timesheets:
+ ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
+ if not timesheet.billing_hours and ts_doc.total_billable_hours:
+ timesheet.billing_hours = ts_doc.total_billable_hours
- if not timesheet.billing_amount and ts_doc.total_billable_amount:
- timesheet.billing_amount = ts_doc.total_billable_amount
+ if not timesheet.billing_amount and ts_doc.total_billable_amount:
+ timesheet.billing_amount = ts_doc.total_billable_amount
def update_timesheet_billing_for_project(self):
if not self.timesheets and self.project:
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index b80bf464cad..6af85e539d8 100644
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -20,6 +20,7 @@ frappe.pages['pos'].refresh = function (wrapper) {
erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
init: function (wrapper) {
this.page_len = 20;
+ this.freeze = false;
this.page = wrapper.page;
this.wrapper = $(wrapper).find('.page-content');
this.set_indicator();
@@ -72,6 +73,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
onload: function () {
var me = this;
this.get_data_from_server(function () {
+ me.make_control();
me.create_new();
});
},
@@ -96,7 +98,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.page.add_menu_item(__("Sync Master Data"), function () {
me.get_data_from_server(function () {
me.load_data(false);
- me.make_customer();
me.make_item_list();
me.set_missing_values();
})
@@ -311,6 +312,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.serial_no_data = r.message.serial_no_data;
this.batch_no_data = r.message.batch_no_data;
this.tax_data = r.message.tax_data;
+ this.contacts = r.message.contacts;
this.address = r.message.address || {};
this.price_list_data = r.message.price_list_data;
this.bin_data = r.message.bin_data;
@@ -320,7 +322,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.default_customer = r.message.default_customer || null;
this.print_settings = locals[":Print Settings"]["Print Settings"];
this.letter_head = (this.pos_profile_data.length > 0) ? frappe.boot.letter_heads[this.pos_profile_data[letter_head]] : {};
- this.make_control()
},
save_previous_entry: function () {
@@ -403,6 +404,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm = {}
this.frm.doc = this.doc
this.set_transaction_defaults("Customer");
+ this.frm.doc["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
this.wrapper.html(frappe.render_template("pos", this.frm.doc));
this.make_search();
this.make_customer();
@@ -707,15 +709,28 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
autoFirst: true,
list: [],
filter: function (item, input) {
- var value = item.value.toLowerCase();
- if (value.indexOf('is_action') !== -1 ||
- value.indexOf(input.toLowerCase()) !== -1) {
+ if (item.value.includes('is_action')) {
return true;
}
+
+ input = input.toLowerCase();
+ item = this.get_item(item.value);
+ var searchtext =
+ Object.keys(item)
+ .filter(key => ['customer_name', 'customer_group', 'value', 'label', 'email_id', 'phone'].includes(key))
+ .map(key => item[key])
+ .join(" ")
+ .toLowerCase();
+
+ return searchtext.includes(input)
},
item: function (item, input) {
- var d = item;
+ var d = this.get_item(item.value);
var html = "" + __(d.label || d.value) + "";
+ if(d.customer_name) {
+ html += '
' + __(d.customer_name) + '';
+ }
+
return $('
' + html + '
') @@ -723,28 +738,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ } }); - this.customers_mapper = this.customers.map(function (c) { - return { - label: c.name, - value: c.name, - customer_group: c.customer_group, - territory: c.territory - } - }); - - this.customers_mapper.push({ - label: "" - + " " - + __("Create a new Customer") - + "", - value: 'is_action', - action: me.add_customer - }); + this.prepare_customer_mapper() this.autocomplete_customers(); this.party_field.$input .on('input', function (e) { - me.party_field.awesomeplete.list = this.customers_mapper; + me.party_field.awesomeplete.list = me.customers_mapper; }) .on('awesomplete-select', function (e) { var customer = me.party_field.awesomeplete @@ -784,6 +783,32 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); }, + prepare_customer_mapper: function() { + var me = this; + + this.customers_mapper = this.customers.map(function (c) { + contact = me.contacts[c.name]; + return { + label: c.name, + value: c.name, + customer_name: c.customer_name, + customer_group: c.customer_group, + territory: c.territory, + phone: contact ? contact["phone"] : '', + email_id: contact ? contact["email_id"] : '' + } + }); + + this.customers_mapper.push({ + label: "" + + " " + + __("Create a new Customer") + + "", + value: 'is_action', + action: me.add_customer + }); + }, + autocomplete_customers: function() { this.party_field.awesomeplete.list = this.customers_mapper; }, @@ -871,10 +896,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ "label": __("ZIP Code"), "fieldname": "pincode", "fieldtype": "Data" + }, + { + "label": __("Customer POS Id"), + "fieldname": "customer_pos_id", + "fieldtype": "Data", + "hidden": 1 } ] }) - this.customer_doc.show() this.render_address_data() @@ -887,12 +917,19 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ render_address_data: function() { var me = this; - this.address_data = this.address[this.frm.doc.customer]; - this.customer_doc.set_values(this.address_data) + this.address_data = this.address[this.frm.doc.customer] || {}; + if(!this.address_data.email_id || !this.address_data.phone) { + this.address_data = this.contacts[this.frm.doc.customer]; + } + this.customer_doc.set_values(this.address_data) if(!this.customer_doc.fields_dict.full_name.$input.val()) { this.customer_doc.set_value("full_name", this.frm.doc.customer) } + + if(!this.customer_doc.fields_dict.customer_pos_id.value) { + this.customer_doc.set_value("customer_pos_id", $.now()) + } }, get_address_from_localstorage: function() { @@ -902,6 +939,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ make_offline_customer: function(new_customer) { this.frm.doc.customer = this.frm.doc.customer || this.customer_doc.get_values().full_name; + this.frm.doc.customer_pos_id = this.customer_doc.fields_dict.customer_pos_id.value; this.customer_details = this.get_customers_details(); this.customer_details[this.frm.doc.customer] = this.get_prompt_details(); this.party_field.$input.val(this.frm.doc.customer); @@ -923,12 +961,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }); } - this.address[this.frm.doc.customer] = this.customer_doc.get_values(); + this.address[this.frm.doc.customer] = JSON.parse(this.get_prompt_details()) }, get_prompt_details: function() { this.prompt_details = this.customer_doc.get_values(); this.prompt_details['country'] = this.pos_profile_data.country; + this.prompt_details['customer_pos_id'] = this.customer_doc.fields_dict.customer_pos_id.value; return JSON.stringify(this.prompt_details) }, @@ -942,26 +981,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.numeric_keypad.show(); }, - get_customers: function (key) { - var me = this; - key = key.toLowerCase().trim() - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '\\w*\\s*[a-zA-Z0-9]*')) - - if (key) { - return $.grep(this.customers, function (data) { - if (reg.test(data.name.toLowerCase()) - || reg.test(data.customer_name.toLowerCase()) - || (data.customer_group && reg.test(data.customer_group.toLowerCase()))) { - return data - } - }) - } else { - customers = this.customers.sort(function (a, b) { return a.idx < b.idx }) - return customers.slice(0, 20) - } - }, - make_item_list: function () { var me = this; if (!this.price_list) { @@ -1180,6 +1199,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.child_doc = this.get_child_item(this.item_code); $(this.wrapper).find('.selected-item').empty(); if(this.child_doc.length) { + this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0])) $(this.wrapper).find('.selected-item').html(this.selected_row) } @@ -1388,7 +1408,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ actual_qty: me.actual_qty_dict[d.item_code] || 0.0, projected_qty: d.projected_qty, rate: format_currency(d.rate, me.frm.doc.currency), - enabled: me.pos_profile_data["allow_user_to_edit_rate"] ? true : false, amount: format_currency(d.amount, me.frm.doc.currency), selected_class: (me.item_code == d.item_code) ? "active" : "" })).appendTo($items); @@ -1607,8 +1626,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.si_docs = this.get_submitted_invoice() || []; this.email_queue_list = this.get_email_queue() || {}; this.customers_list = this.get_customers_details() || {}; + if(this.customer_doc) { + this.freeze = this.customer_doc.display + } - if (this.si_docs.length || this.email_queue_list || this.customers_list) { + if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) { frappe.call({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", args: { @@ -1618,12 +1640,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ }, callback: function (r) { if (r.message) { + me.customers = r.message.synced_customers_list; + me.address = r.message.synced_address; + me.contacts = r.message.synced_contacts; me.removed_items = r.message.invoice; me.removed_email = r.message.email_queue me.removed_customers = r.message.customers me.remove_doc_from_localstorage(); me.remove_email_queue_from_localstorage(); me.remove_customer_from_localstorage(); + me.prepare_customer_mapper() + me.autocomplete_customers() } } }) diff --git a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json index b413321bfd4..28c853cc48d 100644 --- a/erpnext/accounts/print_format/point_of_sale/point_of_sale.json +++ b/erpnext/accounts/print_format/point_of_sale/point_of_sale.json @@ -7,10 +7,10 @@ "docstatus": 0, "doctype": "Print Format", "font": "Default", - "html": "\n\n\n\t{{ company }}
\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
\n
\n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
\n
| {{ __(\"Item\") }} | \n\t\t\t{{ __(\"Qty\") }} | \n\t\t\t{{ __(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_name }}\n\t\t\t | \n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }} @ {{ format_currency(item.rate, currency) }} | \n\t\t\t{{ format_currency(item.amount, currency) }} | \n\t\t
| \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ row.description }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t | \n\t\t
{{ terms }}
\n{{ __(\"Thank you, please visit again.\") }}
", + "html": "\n\n\n\t{{ company }}
\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}
\n
\n\t{{ __(\"Customer\") }}: {{ customer }}
\n
\n\t{{ __(\"Date\") }}: {{ dateutil.global_date_format(posting_date) }}
\n
| {{ __(\"Item\") }} | \n\t\t\t{{ __(\"Qty\") }} | \n\t\t\t{{ __(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_name }}\n\t\t\t | \n\t\t\t{{ format_number(item.qty, null,precision(\"difference\")) }} @ {{ format_currency(item.rate, currency) }} | \n\t\t\t{{ format_currency(item.amount, currency) }} | \n\t\t
| \n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ row.description }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ __(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t | \n\t\t
| \n\t\t\t\t{{ __(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t | \n\t\t
{{ terms }}
\n{{ __(\"Thank you, please visit again.\") }}
", "idx": 0, "line_breaks": 0, - "modified": "2017-04-19 13:28:05.129504", + "modified": "2017-05-19 14:36:04.740728", "modified_by": "Administrator", "module": "Accounts", "name": "Point of Sale", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 62d0d5a1672..9906893254f 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -33,7 +33,9 @@ class ReceivablePayableReport(object): if args.get("party_type") == "Supplier": columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"] - for label in ("Invoiced Amount", "Paid Amount", "Outstanding Amount"): + credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note" + + for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"): columns.append({ "label": label, "fieldtype": "Currency", @@ -95,13 +97,14 @@ class ReceivablePayableReport(object): self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company') company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency") + + return_entries = self.get_return_entries(args.get("party_type")) data = [] for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")): if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers): - outstanding_amount = flt(self.get_outstanding_amount(gle, - self.filters.report_date, dr_or_cr), currency_precision) - + outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle, + self.filters.report_date, dr_or_cr, return_entries, currency_precision) if abs(outstanding_amount) > 0.1/10**currency_precision: row = [gle.posting_date, gle.party] @@ -123,8 +126,8 @@ class ReceivablePayableReport(object): # invoiced and paid amounts invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0 - paid_amt = invoiced_amount - outstanding_amount - row += [invoiced_amount, paid_amt, outstanding_amount] + paid_amt = invoiced_amount - outstanding_amount - credit_note_amount + row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount] # ageing data entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date @@ -132,7 +135,8 @@ class ReceivablePayableReport(object): cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount) # issue 6371-Ageing buckets should not have amounts if due date is not reached - if self.filters.ageing_based_on == "Due Date" and getdate(due_date) > getdate(self.filters.report_date): + if self.filters.ageing_based_on == "Due Date" \ + and getdate(due_date) > getdate(self.filters.report_date): row[-1]=row[-2]=row[-3]=row[-4]=0 if self.filters.get(scrub(args.get("party_type"))): @@ -175,14 +179,28 @@ class ReceivablePayableReport(object): # entries adjusted with future vouchers ((gle.against_voucher_type, gle.against_voucher) in future_vouchers) ) + + def get_return_entries(self, party_type): + doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice" + return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})] - def get_outstanding_amount(self, gle, report_date, dr_or_cr): - payment_amount = 0.0 + def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision): + payment_amount, credit_note_amount = 0.0, 0.0 + reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit" + for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): if getdate(e.posting_date) <= report_date and e.name!=gle.name: - payment_amount += (flt(e.credit if gle.party_type == "Customer" else e.debit) - flt(e.get(dr_or_cr))) - - return flt(gle.get(dr_or_cr)) - flt(gle.credit if gle.party_type == "Customer" else gle.debit) - payment_amount + amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr)) + if e.voucher_no not in return_entries: + payment_amount += amount + else: + credit_note_amount += amount + + outstanding_amount = flt((flt(gle.get(dr_or_cr)) - flt(gle.get(reverse_dr_or_cr)) \ + - payment_amount - credit_note_amount), currency_precision) + credit_note_amount = flt(credit_note_amount, currency_precision) + + return outstanding_amount, credit_note_amount def get_party_name(self, party_type, party_name): return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or "" diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 42327b94aca..2f54fc01751 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -85,6 +85,16 @@ status_map = { ["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"], ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed'"], + ], + "Material Request": [ + ["Draft", None], + ["Stopped", "eval:self.status == 'Stopped'"], + ["Cancelled", "eval:self.docstatus == 2"], + ["Pending", "eval:self.status != 'Stopped' and self.per_ordered == 0 and self.docstatus == 1"], + ["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"], + ["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"], + ["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"], + ["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"] ] } @@ -127,7 +137,8 @@ class StatusUpdater(Document): self.status = s[0] break - if self.status != _status and self.status not in ("Submitted", "Cancelled"): + if self.status != _status and self.status not in ("Cancelled", "Partially Ordered", + "Ordered", "Issued", "Transferred"): self.add_comment("Label", _(self.status)) if update: diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index e18fc27e671..1cee02203d2 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -91,7 +91,7 @@ class SalarySlip(TransactionBase): frappe.throw(_("Name error: {0}".format(err))) except SyntaxError as err: frappe.throw(_("Syntax error in formula or condition: {0}".format(err))) - except Exception, e: + except Exception as e: frappe.throw(_("Error in formula or condition: {0}".format(e))) raise @@ -330,11 +330,18 @@ class SalarySlip(TransactionBase): frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))) for d in self.get(component_type): - if self.salary_structure and ((cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet) or\ - getdate(self.start_date) < joining_date or getdate(self.end_date) > relieving_date): + if (self.salary_structure and + cint(d.depends_on_lwp) and + (not + self.salary_slip_based_on_timesheet or + getdate(self.start_date) < joining_date or + getdate(self.end_date) > relieving_date + )): - d.amount = rounded((flt(d.default_amount) * flt(self.payment_days) - / cint(self.total_working_days)), self.precision("amount", component_type)) + d.amount = rounded( + (flt(d.default_amount) * flt(self.payment_days) + / cint(self.total_working_days)), self.precision("amount", component_type) + ) elif not self.payment_days and not self.salary_slip_based_on_timesheet: d.amount = 0 elif not d.amount: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index fef08721e13..3502d636325 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -398,3 +398,5 @@ erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice erpnext.patches.v8_0.set_sales_invoice_serial_number_from_delivery_note erpnext.patches.v8_0.delete_schools_depricated_doctypes +erpnext.patches.v8_0.update_customer_pos_id +erpnext.patches.v8_0.rename_items_in_status_field_of_material_request diff --git a/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py b/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py new file mode 100644 index 00000000000..5ad862a4362 --- /dev/null +++ b/erpnext/patches/v8_0/rename_items_in_status_field_of_material_request.py @@ -0,0 +1,25 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.db.sql( + """ + UPDATE `tabMaterial Request` + SET status = CASE + WHEN docstatus = 2 THEN 'Cancelled' + WHEN docstatus = 0 THEN 'Draft' + ELSE CASE + WHEN status = 'Stopped' THEN 'Stopped' + WHEN status != 'Stopped' AND per_ordered = 0 THEN 'Pending' + WHEN per_ordered < 100 AND per_ordered > 0 AND status != 'Stopped' + THEN 'Partially Ordered' + WHEN per_ordered = 100 AND material_request_type = 'Purchase' + AND status != 'Stopped' THEN 'Ordered' + WHEN per_ordered = 100 AND material_request_type = 'Material Transfer' + AND status != 'Stopped' THEN 'Transferred' + WHEN per_ordered = 100 AND material_request_type = 'Material Issue' + AND status != 'Stopped' THEN 'Issued' + END + END + """ + ) \ No newline at end of file diff --git a/erpnext/patches/v8_0/update_customer_pos_id.py b/erpnext/patches/v8_0/update_customer_pos_id.py new file mode 100644 index 00000000000..a772ae90c5d --- /dev/null +++ b/erpnext/patches/v8_0/update_customer_pos_id.py @@ -0,0 +1,9 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doctype("Customer") + frappe.db.sql(""" update `tabCustomer` set customer_pos_id = name """) \ No newline at end of file diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 9be19eb1fbf..c4d8155e799 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -609,7 +609,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.doc.change_amount = 0.0; this.frm.doc.base_change_amount = 0.0; if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) { - var payment_types = $.map(cur_frm.doc.payments, function(d) { return d.type }); + var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type }); if (in_list(payment_types, 'Cash')) { this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total + this.frm.doc.write_off_amount, precision("change_amount")); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index db94f3c3613..ca822b19535 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -98,6 +98,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frm.cscript.calculate_taxes_and_totals(); }); + + var me = this; + if(this.frm.fields_dict["items"].grid.get_field('batch_no')) { + this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) { + return me.set_query_for_batch(doc, cdt, cdn) + }); + } }, onload: function() { var me = this; @@ -1128,4 +1135,32 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ return method }, + + set_query_for_batch: function(doc, cdt, cdn) { + // Show item's batches in the dropdown of batch no + + var me = this; + var item = frappe.get_doc(cdt, cdn); + + if(!item.item_code) { + frappe.throw(__("Please enter Item Code to get batch no")); + } else if (doc.doctype == "Purchase Receipt" || + (doc.doctype == "Purchase Invoice" && doc.update_stock)) { + + return { + filters: {'item': item.item_code} + } + } else { + filters = { + 'item_code': item.item_code, + 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), + } + if(item.warehouse) filters["warehouse"] = item.warehouse + + return { + query : "erpnext.controllers.queries.get_batch_no", + filters: filters + } + } + }, }); diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html index 6065e603c79..485a94584e7 100644 --- a/erpnext/public/js/pos/pos.html +++ b/erpnext/public/js/pos/pos.html @@ -73,7 +73,7 @@ {% for(var j=i*3; j <(i+1)*3; j++) { %} {% } %} - + {% } %}