Compare commits

...

21 Commits

Author SHA1 Message Date
Nabin Hait
edd4fd4692 Merge branch 'hotfix' 2017-09-28 12:30:48 +05:30
Nabin Hait
ca916a73de bumped to version 9.0.3 2017-09-28 13:00:48 +06:00
Makarand Bauskar
53e19075d1 [hotfix] fixes and refactored Quoted Item Comparison report (#10954) 2017-09-28 12:30:04 +05:30
Rushabh Mehta
a0ba5594f9 [fix] production_order.py via error report 2017-09-28 11:26:24 +05:30
rohitwaghchaure
16645803f9 [fix] Added billing address gstin number in the sales invoice and delivery note as well in the sales gst reports (#10872) 2017-09-28 11:05:03 +05:30
rohitwaghchaure
94799a8b93 [fix] ‘NoneType’ object is not iterable onload of POS (#10941) 2017-09-28 10:56:27 +05:30
Nabin Hait
7baacb7f74 Merge branch 'hotfix' 2017-09-27 17:19:55 +05:30
Nabin Hait
76615c8001 bumped to version 9.0.2 2017-09-27 17:49:55 +06:00
Prateeksha Singh
283922daa2 [fix] Check for stock_qty, else use qty (#10937) 2017-09-27 17:12:28 +05:30
rohitwaghchaure
da8de2f0c7 Re-order patch sequence (#10936) 2017-09-27 17:11:40 +05:30
rohitwaghchaure
0060993eab Merge pull request #10931 from rohitwaghchaure/customer_group_issue_for_lead
[fix] 'NoneType' object is not iterable while saving quotation
2017-09-27 16:08:35 +05:30
Rohit Waghchaure
566a0a05c8 [fix] 'NoneType' object is not iterable while saving quotation 2017-09-27 15:57:48 +05:30
Rushabh Mehta
f702d72c35 [minor] [performance] push hsn code creation after setup (#10928) 2017-09-27 15:31:30 +05:30
Nabin Hait
e0a845c356 Merge branch 'hotfix' 2017-09-27 13:05:20 +05:30
Nabin Hait
eb686f8b1a bumped to version 9.0.1 2017-09-27 13:35:20 +06:00
Brown-Harry Boma
d3fa19143d Properly redirect supplier after quotations submit (#10903) 2017-09-27 12:53:38 +05:30
Makarand Bauskar
70eaf2da95 [minor] removed the default precision for the margin_rate_or_amount field (#10923) 2017-09-27 12:49:16 +05:30
rohitwaghchaure
103b239a31 Merge pull request #10925 from rohitwaghchaure/pos_issues_v9
[fix] Discount amount not working for POS
2017-09-27 12:20:30 +05:30
Rohit Waghchaure
87e994e0a2 [fixed] Disocunt amount not working for POS 2017-09-27 12:16:58 +05:30
rohitwaghchaure
45d45f4247 [hotfix] Wrong calculation of total in taxes and totals (#10924) 2017-09-27 11:49:13 +05:30
Prateeksha Singh
df8fbd7d72 [fix] batch qty checked against stock_qty field (#10906) 2017-09-26 17:16:03 +05:30
22 changed files with 188 additions and 141 deletions

View File

@@ -4,7 +4,7 @@ import inspect
import frappe import frappe
from erpnext.hooks import regional_overrides from erpnext.hooks import regional_overrides
__version__ = '9.0.0' __version__ = '9.0.3'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@@ -1023,7 +1023,7 @@
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "2", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
@@ -1284,7 +1284,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-08-31 16:34:41.614743", "modified": "2017-09-27 08:31:38.432574",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Pricing Rule", "name": "Pricing Rule",

View File

@@ -699,7 +699,7 @@
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "2", "precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
@@ -2166,7 +2166,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-07-17 17:54:48.246507", "modified": "2017-09-27 08:31:37.827893",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@@ -8,6 +8,7 @@ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cstr, cint from frappe.utils import cstr, cint
from frappe.contacts.doctype.address.address import get_default_address from frappe.contacts.doctype.address.address import get_default_address
from frappe.utils.nestedset import get_root_of
from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
class IncorrectCustomerGroup(frappe.ValidationError): pass class IncorrectCustomerGroup(frappe.ValidationError): pass
@@ -136,7 +137,7 @@ def get_tax_template(posting_date, args):
if key=="use_for_shopping_cart": if key=="use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0)) conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
if key == 'customer_group': if key == 'customer_group':
if not value: value = _("All Customer Groups") if not value: value = get_root_of("Customer Group")
customer_group_condition = get_customer_group_condition(value) customer_group_condition = get_customer_group_condition(value)
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition)) conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
else: else:

View File

@@ -2,29 +2,29 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.query_reports["Quoted Item Comparison"] = { frappe.query_reports["Quoted Item Comparison"] = {
"filters": [ filters: [
{ {
"fieldname": "supplier_quotation", fieldtype: "Link",
"label": __("Supplier Quotation"), label: __("Supplier Quotation"),
"fieldtype": "Link", options: "Supplier Quotation",
"options": "Supplier Quotation", fieldname: "supplier_quotation",
"default": "", default: "",
"get_query": function () { get_query: () => {
return { filters: { "docstatus": ["<", 2] } } return { filters: { "docstatus": ["<", 2] } }
} }
}, },
{ {
"fieldname": "item", reqd: 1,
"label": __("Item"), default: "",
"fieldtype": "Link", options: "Item",
"options": "Item", label: __("Item"),
"default": "", fieldname: "item",
"reqd": 1, fieldtype: "Link",
"get_query": function () { get_query: () => {
var quote = frappe.query_report_filters_by_name.supplier_quotation.get_value(); let quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
if (quote != "") { if (quote != "") {
return { return {
query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query", query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
filters: { filters: {
"from": "Supplier Quotation Item", "from": "Supplier Quotation Item",
"parent": quote "parent": quote
@@ -39,47 +39,50 @@ frappe.query_reports["Quoted Item Comparison"] = {
} }
} }
], ],
onload: function (report) { onload: (report) => {
// Create a button for setting the default supplier // Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), function () { report.page.add_inner_button(__("Select Default Supplier"), () => {
let reporter = frappe.query_reports["Quoted Item Comparison"];
var reporter = frappe.query_reports["Quoted Item Comparison"];
//Always make a new one so that the latest values get updated //Always make a new one so that the latest values get updated
reporter.make_default_supplier_dialog(report); reporter.make_default_supplier_dialog(report);
report.dialog.show();
setTimeout(function () { report.dialog.input.focus(); }, 1000);
}, 'Tools'); }, 'Tools');
}, },
"make_default_supplier_dialog": function (report) { make_default_supplier_dialog: (report) => {
// Get the name of the item to change // Get the name of the item to change
var filters = report.get_values(); if(!report.data) return;
var item_code = filters.item;
let filters = report.get_values();
let item_code = filters.item;
// Get a list of the suppliers (with a blank as well) for the user to select // Get a list of the suppliers (with a blank as well) for the user to select
var select_options = ""; let suppliers = $.map(report.data, (row, idx)=>{ return row.supplier_name })
for (let supplier of report.data) {
select_options += supplier.supplier_name + '\n'
}
// Create a dialog window for the user to pick their supplier // Create a dialog window for the user to pick their supplier
var d = new frappe.ui.Dialog({ let dialog = new frappe.ui.Dialog({
title: __('Select Default Supplier'), title: __('Select Default Supplier'),
fields: [ fields: [
{ fieldname: 'supplier', fieldtype: 'Select', label: 'Supplier', reqd: 1, options: select_options }, {
{ fieldname: 'ok_button', fieldtype: 'Button', label: 'Set Default Supplier' }, reqd: 1,
label: 'Supplier',
fieldtype: 'Link',
options: 'Supplier',
fieldname: 'supplier',
get_query: () => {
return {
filters: {
'name': ['in', suppliers]
}
}
}
}
] ]
}); });
// On the user clicking the ok button dialog.set_primary_action("Set Default Supplier", () => {
d.fields_dict.ok_button.input.onclick = function () { let values = dialog.get_values();
var btn = d.fields_dict.ok_button.input; if(values) {
var v = report.dialog.get_values();
if (v) {
$(btn).set_working();
// Set the default_supplier field of the appropriate Item to the selected supplier // Set the default_supplier field of the appropriate Item to the selected supplier
frappe.call({ frappe.call({
method: "frappe.client.set_value", method: "frappe.client.set_value",
@@ -87,17 +90,17 @@ frappe.query_reports["Quoted Item Comparison"] = {
doctype: "Item", doctype: "Item",
name: item_code, name: item_code,
fieldname: "default_supplier", fieldname: "default_supplier",
value: v.supplier, value: values.supplier,
}, },
callback: function (r) { freeze: true,
$(btn).done_working(); callback: (r) => {
frappe.msgprint("Successfully Set Supplier"); frappe.msgprint("Successfully Set Supplier");
report.dialog.hide(); dialog.hide();
} }
}); });
} }
} });
report.dialog = d; dialog.show();
} }
} }

View File

@@ -8,53 +8,55 @@ import frappe
def execute(filters=None): def execute(filters=None):
qty_list = get_quantity_list(filters.item) qty_list = get_quantity_list(filters.item)
data = get_quote_list(filters.item, qty_list) data = get_quote_list(filters.item, qty_list)
columns = get_columns(qty_list) columns = get_columns(qty_list)
return columns, data return columns, data
def get_quote_list(item, qty_list): def get_quote_list(item, qty_list):
out = [] out = []
if item: if not item:
price_data = [] return []
suppliers = []
company_currency = frappe.db.get_default("currency") suppliers = []
float_precision = cint(frappe.db.get_default("float_precision")) or 2 price_data = []
# Get the list of suppliers company_currency = frappe.db.get_default("currency")
for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item` where item_code=%s and docstatus < 2""", item, as_dict=1): float_precision = cint(frappe.db.get_default("float_precision")) or 2
for splr in frappe.db.sql("""SELECT supplier from `tabSupplier Quotation` where name =%s and docstatus < 2""", root.parent, as_dict=1): # Get the list of suppliers
ip = frappe._dict({ for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
where item_code=%s and docstatus < 2""", item, as_dict=1):
for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
where name =%s and docstatus < 2""", root.parent, as_dict=1):
ip = frappe._dict({
"supplier": splr.supplier, "supplier": splr.supplier,
"qty": root.qty, "qty": root.qty,
"parent": root.parent, "parent": root.parent,
"rate": root.rate}) "rate": root.rate
price_data.append(ip)
suppliers.append(splr.supplier)
#Add a row for each supplier
for root in set(suppliers):
supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
if supplier_currency:
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
else:
exchange_rate = 1
row = frappe._dict({
"supplier_name": root
}) })
for col in qty_list: price_data.append(ip)
# Get the quantity for this row suppliers.append(splr.supplier)
for item_price in price_data:
if str(item_price.qty) == col.key and item_price.supplier == root: #Add a row for each supplier
row[col.key] = flt(item_price.rate * exchange_rate, float_precision) for root in set(suppliers):
row[col.key + "QUOTE"] = item_price.parent supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
break if supplier_currency:
else: exchange_rate = get_exchange_rate(supplier_currency, company_currency)
row[col.key] = "" else:
row[col.key + "QUOTE"] = "" exchange_rate = 1
out.append(row)
row = frappe._dict({
"supplier_name": root
})
for col in qty_list:
# Get the quantity for this row
for item_price in price_data:
if str(item_price.qty) == col.key and item_price.supplier == root:
row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
row[col.key + "QUOTE"] = item_price.parent
break
else:
row[col.key] = ""
row[col.key + "QUOTE"] = ""
out.append(row)
return out return out
@@ -62,7 +64,8 @@ def get_quantity_list(item):
out = [] out = []
if item: if item:
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item` where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1) qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1)
qty_list.sort(reverse=False) qty_list.sort(reverse=False)
for qt in qty_list: for qt in qty_list:
col = frappe._dict({ col = frappe._dict({
@@ -98,4 +101,4 @@ def get_columns(qty_list):
"width": 90 "width": 90
}) })
return columns return columns

View File

@@ -81,7 +81,7 @@ website_route_rules = [
{"from_route": "/supplier-quotations/<path:name>", "to_route": "order", {"from_route": "/supplier-quotations/<path:name>", "to_route": "order",
"defaults": { "defaults": {
"doctype": "Supplier Quotation", "doctype": "Supplier Quotation",
"parents": [{"label": _("Supplier Quotation"), "route": "quotations"}] "parents": [{"label": _("Supplier Quotation"), "route": "supplier-quotations"}]
} }
}, },
{"from_route": "/quotations", "to_route": "Quotation"}, {"from_route": "/quotations", "to_route": "Quotation"},

View File

@@ -51,9 +51,9 @@ class ProductionOrder(Document):
def validate_sales_order(self): def validate_sales_order(self):
if self.sales_order: if self.sales_order:
so = frappe.db.sql(""" so = frappe.db.sql("""
select so.name, so_item.delivery_date, so.project select so.name, so_item.delivery_date, so.project
from `tabSales Order` so, `tabSales Order Item` so_item from `tabSales Order` so, `tabSales Order Item` so_item
where so.name=%s and so.name=so_item.parent where so.name=%s and so.name=so_item.parent
and so.docstatus = 1 and so_item.item_code=%s and so.docstatus = 1 and so_item.item_code=%s
""", (self.sales_order, self.production_item), as_dict=1) """, (self.sales_order, self.production_item), as_dict=1)
@@ -112,7 +112,7 @@ class ProductionOrder(Document):
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"over_production_allowance_percentage")) "over_production_allowance_percentage"))
if total_qty > so_qty + (allowance_percentage/100 * so_qty): if total_qty > so_qty + (allowance_percentage/100 * so_qty):
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}") frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
.format(self.production_item, so_qty), OverProductionError) .format(self.production_item, so_qty), OverProductionError)
@@ -217,27 +217,27 @@ class ProductionOrder(Document):
def set_production_order_operations(self): def set_production_order_operations(self):
"""Fetch operations from BOM and set in 'Production Order'""" """Fetch operations from BOM and set in 'Production Order'"""
self.set('operations', []) self.set('operations', [])
if not self.bom_no \ if not self.bom_no \
or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")): or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
return return
if self.use_multi_level_bom: if self.use_multi_level_bom:
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree() bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
else: else:
bom_list = [self.bom_no] bom_list = [self.bom_no]
operations = frappe.db.sql(""" operations = frappe.db.sql("""
select select
operation, description, workstation, idx, operation, description, workstation, idx,
base_hour_rate as hour_rate, time_in_mins, base_hour_rate as hour_rate, time_in_mins,
"Pending" as status, parent as bom "Pending" as status, parent as bom
from from
`tabBOM Operation` `tabBOM Operation`
where where
parent in (%s) order by idx parent in (%s) order by idx
""" % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1) """ % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1)
self.set('operations', operations) self.set('operations', operations)
self.calculate_time() self.calculate_time()
@@ -277,7 +277,7 @@ class ProductionOrder(Document):
timesheet.set('time_logs', []) timesheet.set('time_logs', [])
for i, d in enumerate(self.operations): for i, d in enumerate(self.operations):
if d.status != 'Completed': if d.status != 'Completed':
self.set_start_end_time_for_workstation(d, i) self.set_start_end_time_for_workstation(d, i)
@@ -370,8 +370,8 @@ class ProductionOrder(Document):
self.actual_start_date = None self.actual_start_date = None
self.actual_end_date = None self.actual_end_date = None
if self.get("operations"): if self.get("operations"):
self.actual_start_date = min([d.actual_start_time for d in self.get("operations")]) self.actual_start_date = min([d.actual_start_time for d in self.get("operations") if d.actual_start_time])
self.actual_end_date = max([d.actual_end_time for d in self.get("operations")]) self.actual_end_date = max([d.actual_end_time for d in self.get("operations") if d.actual_end_time])
def delete_timesheet(self): def delete_timesheet(self):
for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}): for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}):
@@ -411,18 +411,18 @@ class ProductionOrder(Document):
if d.source_warehouse: if d.source_warehouse:
stock_bin = get_bin(d.item_code, d.source_warehouse) stock_bin = get_bin(d.item_code, d.source_warehouse)
stock_bin.update_reserved_qty_for_production() stock_bin.update_reserved_qty_for_production()
def get_items_and_operations_from_bom(self): def get_items_and_operations_from_bom(self):
self.set_required_items() self.set_required_items()
self.set_production_order_operations() self.set_production_order_operations()
return check_if_scrap_warehouse_mandatory(self.bom_no) return check_if_scrap_warehouse_mandatory(self.bom_no)
def set_available_qty(self): def set_available_qty(self):
for d in self.get("required_items"): for d in self.get("required_items"):
if d.source_warehouse: if d.source_warehouse:
d.available_qty_at_source_warehouse = get_latest_stock_qty(d.item_code, d.source_warehouse) d.available_qty_at_source_warehouse = get_latest_stock_qty(d.item_code, d.source_warehouse)
if self.wip_warehouse: if self.wip_warehouse:
d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse) d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse)
@@ -439,7 +439,7 @@ class ProductionOrder(Document):
'required_qty': item.qty, 'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse 'source_warehouse': item.source_warehouse or item.default_warehouse
}) })
self.set_available_qty() self.set_available_qty()
def update_transaferred_qty_for_required_items(self): def update_transaferred_qty_for_required_items(self):
@@ -463,12 +463,12 @@ class ProductionOrder(Document):
def get_item_details(item, project = None): def get_item_details(item, project = None):
res = frappe.db.sql(""" res = frappe.db.sql("""
select stock_uom, description select stock_uom, description
from `tabItem` from `tabItem`
where disabled=0 where disabled=0
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s) and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)
and name=%s and name=%s
""", (nowdate(), item), as_dict=1) """, (nowdate(), item), as_dict=1)
if not res: if not res:
return {} return {}
@@ -611,14 +611,14 @@ def make_new_timesheet(source_name, target_doc=None):
@frappe.whitelist() @frappe.whitelist()
def stop_unstop(production_order, status): def stop_unstop(production_order, status):
""" Called from client side on Stop/Unstop event""" """ Called from client side on Stop/Unstop event"""
if not frappe.has_permission("Production Order", "write"): if not frappe.has_permission("Production Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError) frappe.throw(_("Not permitted"), frappe.PermissionError)
pro_order = frappe.get_doc("Production Order", production_order) pro_order = frappe.get_doc("Production Order", production_order)
pro_order.update_status(status) pro_order.update_status(status)
pro_order.update_planned_qty() pro_order.update_planned_qty()
frappe.msgprint(_("Production Order has been {0}").format(status)) frappe.msgprint(_("Production Order has been {0}").format(status))
pro_order.notify_update() pro_order.notify_update()
return pro_order.status return pro_order.status

View File

@@ -408,6 +408,8 @@ erpnext.patches.v8_0.update_stock_qty_value_in_bom_item
erpnext.patches.v8_0.update_sales_cost_in_project erpnext.patches.v8_0.update_sales_cost_in_project
erpnext.patches.v8_0.save_system_settings erpnext.patches.v8_0.save_system_settings
erpnext.patches.v8_1.delete_deprecated_reports erpnext.patches.v8_1.delete_deprecated_reports
erpnext.patches.v9_0.remove_subscription_module
erpnext.patches.v8_7.make_subscription_from_recurring_data
erpnext.patches.v8_1.setup_gst_india #2017-06-27 erpnext.patches.v8_1.setup_gst_india #2017-06-27
execute:frappe.reload_doc('regional', 'doctype', 'gst_hsn_code') execute:frappe.reload_doc('regional', 'doctype', 'gst_hsn_code')
erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account
@@ -432,16 +434,15 @@ erpnext.patches.v8_5.update_customer_group_in_POS_profile
erpnext.patches.v8_6.update_timesheet_company_from_PO erpnext.patches.v8_6.update_timesheet_company_from_PO
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
erpnext.patches.v8_5.remove_project_type_property_setter erpnext.patches.v8_5.remove_project_type_property_setter
erpnext.patches.v8_7.add_more_gst_fields erpnext.patches.v8_7.add_more_gst_fields #21-09-2017
erpnext.patches.v8_7.fix_purchase_receipt_status erpnext.patches.v8_7.fix_purchase_receipt_status
erpnext.patches.v8_6.rename_bom_update_tool erpnext.patches.v8_6.rename_bom_update_tool
erpnext.patches.v8_7.set_offline_in_pos_settings #11-09-17 erpnext.patches.v8_7.set_offline_in_pos_settings #11-09-17
erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017 erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017
erpnext.patches.v8_9.rename_company_sales_target_field erpnext.patches.v8_9.rename_company_sales_target_field
erpnext.patches.v8_8.set_bom_rate_as_per_uom erpnext.patches.v8_8.set_bom_rate_as_per_uom
erpnext.patches.v9_0.remove_subscription_module
erpnext.patches.v8_7.make_subscription_from_recurring_data
erpnext.patches.v8_9.set_print_zero_amount_taxes erpnext.patches.v8_9.set_print_zero_amount_taxes
erpnext.patches.v8_9.set_default_customer_group erpnext.patches.v8_9.set_default_customer_group
erpnext.patches.v8_9.remove_employee_from_salary_structure_parent erpnext.patches.v8_9.remove_employee_from_salary_structure_parent
erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
erpnext.patches.v8_9.update_billing_gstin_for_indian_account

View File

@@ -8,9 +8,15 @@ from frappe.utils import today
def execute(): def execute():
frappe.reload_doc('accounts', 'doctype', 'subscription') frappe.reload_doc('accounts', 'doctype', 'subscription')
frappe.reload_doc('selling', 'doctype', 'sales_order') frappe.reload_doc('selling', 'doctype', 'sales_order')
frappe.reload_doc('selling', 'doctype', 'quotation')
frappe.reload_doc('buying', 'doctype', 'purchase_order') frappe.reload_doc('buying', 'doctype', 'purchase_order')
frappe.reload_doc('buying', 'doctype', 'supplier_quotation')
frappe.reload_doc('accounts', 'doctype', 'sales_invoice') frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
frappe.reload_doc('accounts', 'doctype', 'purchase_invoice') frappe.reload_doc('accounts', 'doctype', 'purchase_invoice')
frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
frappe.reload_doc('stock', 'doctype', 'delivery_note')
frappe.reload_doc('accounts', 'doctype', 'journal_entry')
frappe.reload_doc('accounts', 'doctype', 'payment_entry')
for doctype in ['Sales Order', 'Sales Invoice', for doctype in ['Sales Order', 'Sales Invoice',
'Purchase Invoice', 'Purchase Invoice']: 'Purchase Invoice', 'Purchase Invoice']:

View File

@@ -0,0 +1,15 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if company:
for doctype in ['Sales Invoice', 'Delivery Note']:
frappe.db.sql(""" update `tab{0}`
set billing_address_gstin = (select gstin from `tabAddress`
where name = customer_address)
where customer_address is not null and customer_address != ''""".format(doctype))

View File

@@ -288,8 +288,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}, },
set_cumulative_total: function(row_idx, tax) { set_cumulative_total: function(row_idx, tax) {
var tax_amount = (in_list(["Valuation and Total", "Total"], tax.category) ? var tax_amount = tax.tax_amount_after_discount_amount;
tax.tax_amount_after_discount_amount : 0); if (tax.category == 'Valuation') {
tax_amount = 0;
}
if (tax.add_deduct_tax == "Deduct") { tax_amount = -1*tax_amount; } if (tax.add_deduct_tax == "Deduct") { tax_amount = -1*tax_amount; }
if(row_idx==0) { if(row_idx==0) {

View File

@@ -12,7 +12,7 @@ def setup(company=None, patch=True):
make_custom_fields() make_custom_fields()
add_permissions() add_permissions()
add_custom_roles_for_reports() add_custom_roles_for_reports()
add_hsn_sac_codes() frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes')
add_print_formats() add_print_formats()
if not patch: if not patch:
update_address_template() update_address_template()
@@ -47,12 +47,14 @@ def add_hsn_sac_codes():
def create_hsn_codes(data, code_field): def create_hsn_codes(data, code_field):
for d in data: for d in data:
if not frappe.db.exists("GST HSN Code", d[code_field]): hsn_code = frappe.new_doc('GST HSN Code')
hsn_code = frappe.new_doc('GST HSN Code') hsn_code.description = d["description"]
hsn_code.description = d["description"] hsn_code.hsn_code = d[code_field]
hsn_code.hsn_code = d[code_field] hsn_code.name = d[code_field]
hsn_code.name = d[code_field] try:
hsn_code.db_insert() hsn_code.db_insert()
except frappe.DuplicateEntryError:
pass
def add_custom_roles_for_reports(): def add_custom_roles_for_reports():
for report_name in ('GST Sales Register', 'GST Purchase Register', for report_name in ('GST Sales Register', 'GST Purchase Register',
@@ -111,12 +113,15 @@ def make_custom_fields():
] ]
sales_invoice_gst_fields = [ sales_invoice_gst_fields = [
dict(fieldname='billing_address_gstin', label='Billing Address GSTIN',
fieldtype='Data', insert_after='customer_address',
options='customer_address.gstin', print_hide=1),
dict(fieldname='customer_gstin', label='Customer GSTIN', dict(fieldname='customer_gstin', label='Customer GSTIN',
fieldtype='Data', insert_after='shipping_address', fieldtype='Data', insert_after='shipping_address',
options='shipping_address_name.gstin', print_hide=1), options='shipping_address_name.gstin', print_hide=1),
dict(fieldname='place_of_supply', label='Place of Supply', dict(fieldname='place_of_supply', label='Place of Supply',
fieldtype='Data', insert_after='customer_gstin', print_hide=1, fieldtype='Data', insert_after='customer_gstin', print_hide=1,
options='shipping_address_name.gst_state_number', read_only=1), options='shipping_address_name.gst_state_number', read_only=0),
dict(fieldname='company_gstin', label='Company GSTIN', dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address', fieldtype='Data', insert_after='company_address',
options='company_address.gstin', print_hide=1) options='company_address.gstin', print_hide=1)

View File

@@ -8,6 +8,7 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i
def execute(filters=None): def execute(filters=None):
return _execute(filters, additional_table_columns=[ return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', width=120), dict(fieldtype='Data', label='Customer GSTIN', width=120),
dict(fieldtype='Data', label='Billing Address GSTIN', width=140),
dict(fieldtype='Data', label='Company GSTIN', width=120), dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Place of Supply', width=120), dict(fieldtype='Data', label='Place of Supply', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120), dict(fieldtype='Data', label='Reverse Charge', width=120),
@@ -17,6 +18,7 @@ def execute(filters=None):
dict(fieldtype='Data', label='HSN Code', width=120) dict(fieldtype='Data', label='HSN Code', width=120)
], additional_query_columns=[ ], additional_query_columns=[
'customer_gstin', 'customer_gstin',
'billing_address_gstin',
'company_gstin', 'company_gstin',
'place_of_supply', 'place_of_supply',
'reverse_charge', 'reverse_charge',

View File

@@ -8,6 +8,7 @@ from erpnext.accounts.report.sales_register.sales_register import _execute
def execute(filters=None): def execute(filters=None):
return _execute(filters, additional_table_columns=[ return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', width=120), dict(fieldtype='Data', label='Customer GSTIN', width=120),
dict(fieldtype='Data', label='Billing Address GSTIN', width=140),
dict(fieldtype='Data', label='Company GSTIN', width=120), dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Place of Supply', width=120), dict(fieldtype='Data', label='Place of Supply', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120), dict(fieldtype='Data', label='Reverse Charge', width=120),
@@ -16,6 +17,7 @@ def execute(filters=None):
dict(fieldtype='Data', label='E-Commerce GSTIN', width=130) dict(fieldtype='Data', label='E-Commerce GSTIN', width=130)
], additional_query_columns=[ ], additional_query_columns=[
'customer_gstin', 'customer_gstin',
'billing_address_gstin',
'company_gstin', 'company_gstin',
'place_of_supply', 'place_of_supply',
'reverse_charge', 'reverse_charge',

View File

@@ -684,7 +684,7 @@
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "2", "precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
@@ -1583,7 +1583,7 @@
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-05-10 17:14:45.736424", "modified": "2017-09-27 08:31:37.485134",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Quotation Item", "name": "Quotation Item",

View File

@@ -714,7 +714,7 @@
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "2", "precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
@@ -745,7 +745,7 @@
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "2", "precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
@@ -1963,7 +1963,7 @@
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-07-28 14:04:04.289428", "modified": "2017-09-27 08:31:37.129537",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@@ -21,7 +21,9 @@ frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
}; };
frappe.pages['point-of-sale'].refresh = function(wrapper) { frappe.pages['point-of-sale'].refresh = function(wrapper) {
cur_frm = wrapper.pos.frm; if (wrapper.pos) {
cur_frm = wrapper.pos.frm;
}
} }
erpnext.pos.PointOfSale = class PointOfSale { erpnext.pos.PointOfSale = class PointOfSale {
@@ -742,7 +744,7 @@ class POSCart {
this.wrapper.find('.discount_amount').on('change', (e) => { this.wrapper.find('.discount_amount').on('change', (e) => {
frappe.model.set_value(this.frm.doctype, this.frm.docname, frappe.model.set_value(this.frm.doctype, this.frm.docname,
'discount_amount', e.target.value); 'discount_amount', flt(e.target.value));
this.frm.trigger('discount_amount') this.frm.trigger('discount_amount')
.then(() => { .then(() => {
let discount_wrapper = this.wrapper.find('.additional_discount_percentage'); let discount_wrapper = this.wrapper.find('.additional_discount_percentage');

View File

@@ -3,12 +3,15 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe.utils.nestedset import get_root_of
@frappe.whitelist() @frappe.whitelist()
def get_items(start, page_length, price_list, item_group, search_value=""): def get_items(start, page_length, price_list, item_group, search_value=""):
serial_no = "" serial_no = ""
batch_no = "" batch_no = ""
item_code = search_value item_code = search_value
if not frappe.db.exists('Item Group', item_group):
item_group = get_root_of('Item Group')
if search_value: if search_value:
# search serial no # search serial no
@@ -31,7 +34,7 @@ def get_items(start, page_length, price_list, item_group, search_value=""):
ON ON
(item_det.item_code=i.name or item_det.item_code=i.variant_of) (item_det.item_code=i.name or item_det.item_code=i.variant_of)
where where
i.disabled = 0 and i.has_variants = 0 i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt}) and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt})
and (i.item_code like %(item_code)s and (i.item_code like %(item_code)s
or i.item_name like %(item_code)s or i.barcode like %(item_code)s) or i.item_name like %(item_code)s or i.barcode like %(item_code)s)

View File

@@ -103,14 +103,15 @@ def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None):
def set_batch_nos(doc, warehouse_field, throw = False): def set_batch_nos(doc, warehouse_field, throw = False):
'''Automatically select `batch_no` for outgoing items in item table''' '''Automatically select `batch_no` for outgoing items in item table'''
for d in doc.items: for d in doc.items:
qty = d.get('stock_qty') or d.get('qty') or 0
has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no') has_batch_no = frappe.db.get_value('Item', d.item_code, 'has_batch_no')
warehouse = d.get(warehouse_field, None) warehouse = d.get(warehouse_field, None)
if has_batch_no and warehouse and d.qty > 0: if has_batch_no and warehouse and qty > 0:
if not d.batch_no: if not d.batch_no:
d.batch_no = get_batch_no(d.item_code, warehouse, d.qty, throw) d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw)
else: else:
batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse) batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse)
if flt(batch_qty) < flt(d.qty): if flt(batch_qty) < flt(qty):
frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, d.qty)) frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, d.qty))
def get_batch_no(item_code, warehouse, qty, throw=False): def get_batch_no(item_code, warehouse, qty, throw=False):

View File

@@ -713,7 +713,7 @@
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "2", "precision": "",
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
@@ -1956,7 +1956,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-05-10 17:14:50.456930", "modified": "2017-09-27 08:31:38.768846",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note Item", "name": "Delivery Note Item",

View File

@@ -85,7 +85,7 @@ rfq = Class.extend({
frappe.unfreeze(); frappe.unfreeze();
if(r.message){ if(r.message){
$('.btn-sm').hide() $('.btn-sm').hide()
window.location.href = "/quotations/" + encodeURIComponent(r.message); window.location.href = "/supplier-quotations/" + encodeURIComponent(r.message);
} }
} }
}) })