Merge branch 'show_address_in_online_pos' of https://github.com/mntechnique/erpnext into mntechnique-show_address_in_online_pos
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index 63db16c..20eb14b 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -2,23 +2,30 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe, json
-from frappe import _
-from frappe.utils import nowdate
-from erpnext.setup.utils import get_exchange_rate
-from frappe.core.doctype.communication.email import make
-from erpnext.stock.get_item_details import get_pos_profile
+
+import json
+
+import frappe
from erpnext.accounts.party import get_party_account_currency
from erpnext.controllers.accounts_controller import get_taxes_and_charges
+from erpnext.setup.utils import get_exchange_rate
+from erpnext.stock.get_item_details import get_pos_profile
+from frappe import _
+from frappe.core.doctype.communication.email import make
+from frappe.utils import nowdate
+
@frappe.whitelist()
def get_pos_data():
doc = frappe.new_doc('Sales Invoice')
- doc.is_pos = 1;
+ doc.is_pos = 1
pos_profile = get_pos_profile(doc.company) or {}
if not pos_profile:
frappe.throw(_("POS Profile is required to use Point-of-Sale"))
- if not doc.company: doc.company = pos_profile.get('company')
+
+ if not doc.company:
+ doc.company = pos_profile.get('company')
+
doc.update_stock = pos_profile.get('update_stock')
if pos_profile.get('name'):
@@ -30,18 +37,20 @@
update_multi_mode_option(doc, pos_profile)
default_print_format = pos_profile.get('print_format') or "Point of Sale"
print_template = frappe.db.get_value('Print Format', default_print_format, 'html')
+ items_list = get_items_list(pos_profile)
customers = get_customers_list(pos_profile)
return {
'doc': doc,
'default_customer': pos_profile.get('customer'),
- 'items': get_items_list(pos_profile),
+ 'items': items_list,
'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(),
+ 'barcode_data': get_barcode_data(items_list),
'tax_data': get_item_tax_data(),
'price_list_data': get_price_list_data(doc.selling_price_list),
'bin_data': get_bin_data(pos_profile),
@@ -51,20 +60,23 @@
'meta': get_meta()
}
+
def get_meta():
doctype_meta = {
'customer': frappe.get_meta('Customer'),
'invoice': frappe.get_meta('Sales Invoice')
}
- for row in frappe.get_all('DocField', fields = ['fieldname', 'options'],
- filters = {'parent': 'Sales Invoice', 'fieldtype': 'Table'}):
+ for row in frappe.get_all('DocField', fields=['fieldname', 'options'],
+ filters={'parent': 'Sales Invoice', 'fieldtype': 'Table'}):
doctype_meta[row.fieldname] = frappe.get_meta(row.options)
return doctype_meta
+
def get_company_data(company):
- return frappe.get_all('Company', fields = ["*"], filters= {'name': company})[0]
+ return frappe.get_all('Company', fields=["*"], filters={'name': company})[0]
+
def update_pos_profile_data(doc, pos_profile, company_data):
doc.campaign = pos_profile.get('campaign')
@@ -96,12 +108,14 @@
doc.terms = frappe.db.get_value('Terms and Conditions', pos_profile.get('tc_name'), 'terms') or doc.terms or ''
doc.offline_pos_name = ''
+
def get_root(table):
root = frappe.db.sql(""" select name from `tab%(table)s` having
- min(lft)"""%{'table': table}, as_dict=1)
+ min(lft)""" % {'table': table}, as_dict=1)
return root[0].name
+
def update_multi_mode_option(doc, pos_profile):
from frappe.model import default_fields
@@ -123,15 +137,18 @@
doc.append('payments', payment_mode)
+
def get_mode_of_payment(doc):
- return frappe.db.sql(""" select mpa.default_account, mpa.parent, mp.type as type from `tabMode of Payment Account` mpa,
- `tabMode of Payment` mp where mpa.parent = mp.name and mpa.company = %(company)s""", {'company': doc.company}, as_dict=1)
+ return frappe.db.sql(""" select mpa.default_account, mpa.parent, mp.type as type from `tabMode of Payment Account` mpa, \
+ `tabMode of Payment` mp where mpa.parent = mp.name and mpa.company = %(company)s""", {'company': doc.company}, as_dict=1)
+
def update_tax_table(doc):
taxes = get_taxes_and_charges('Sales Taxes and Charges Template', doc.taxes_and_charges)
for tax in taxes:
doc.append('taxes', tax)
+
def get_items_list(pos_profile):
cond = "1=1"
item_groups = []
@@ -139,19 +156,20 @@
# Get items based on the item groups defined in the POS profile
for d in pos_profile.get('item_groups'):
item_groups.extend([d.name for d in get_child_nodes('Item Group', d.item_group)])
- cond = "item_group in (%s)"%(', '.join(['%s']*len(item_groups)))
+ cond = "item_group in (%s)" % (', '.join(['%s'] * len(item_groups)))
- return frappe.db.sql("""
+ return frappe.db.sql("""
select
name, item_code, item_name, description, item_group, expense_account, has_batch_no,
- has_serial_no, expense_account, selling_cost_center, stock_uom, image,
- default_warehouse, is_stock_item, barcode, brand
+ has_serial_no, expense_account, selling_cost_center, stock_uom, image,
+ default_warehouse, is_stock_item, brand
from
tabItem
where
disabled = 0 and has_variants = 0 and is_sales_item = 1 and {cond}
""".format(cond=cond), tuple(item_groups), as_dict=1)
+
def get_item_groups(pos_profile):
item_group_dict = {}
item_groups = frappe.db.sql("""Select name,
@@ -161,6 +179,7 @@
item_group_dict[data.name] = [data.lft, data.rgt]
return item_group_dict
+
def get_customers_list(pos_profile={}):
cond = "1=1"
customer_groups = []
@@ -168,12 +187,13 @@
# Get customers based on the customer groups defined in the POS profile
for d in pos_profile.get('customer_groups'):
customer_groups.extend([d.name for d in get_child_nodes('Customer Group', d.customer_group)])
- cond = "customer_group in (%s)"%(', '.join(['%s']*len(customer_groups)))
+ cond = "customer_group in (%s)" % (', '.join(['%s'] * len(customer_groups)))
return frappe.db.sql(""" select name, customer_name, customer_group,
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):
customer_address = {}
if isinstance(customers, basestring):
@@ -185,33 +205,37 @@
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Address')""", data.name, as_dict=1)
address_data = {}
- if address: address_data = address[0]
+ 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, mobile_no from `tabContact`
+ contact = frappe.db.sql(""" select email_id, phone, mobile_no 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:
+ 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
lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1)
+
def get_serial_no_data(pos_profile, company):
# get itemwise serial no data
# example {'Nokia Lumia 1020': {'SN0001': 'Pune'}}
@@ -232,6 +256,7 @@
return itemwise_serial_no
+
def get_batch_no_data():
# get itemwise batch no data
# exmaple: {'LED-GRE': [Batch001, Batch002]}
@@ -248,6 +273,26 @@
return itemwise_batch
+
+def get_barcode_data(items_list):
+ # get itemwise batch no data
+ # exmaple: {'LED-GRE': [Batch001, Batch002]}
+ # where LED-GRE is item code, SN0001 is serial no and Pune is warehouse
+
+ itemwise_barcode = {}
+ for item in items_list:
+ barcodes = frappe.db.sql("""
+ select barcode from `tabItem Barcode` where parent = '{0}'
+ """.format(item.item_code), as_dict=1)
+
+ for barcode in barcodes:
+ if item.item_code not in itemwise_barcode:
+ itemwise_barcode.setdefault(item.item_code, [])
+ itemwise_barcode[item.item_code].append(barcode)
+
+ return itemwise_barcode
+
+
def get_item_tax_data():
# get default tax of an item
# example: {'Consulting Services': {'Excise 12 - TS': '12.000'}}
@@ -262,17 +307,19 @@
return itemwise_tax
+
def get_price_list_data(selling_price_list):
itemwise_price_list = {}
price_lists = frappe.db.sql("""Select ifnull(price_list_rate, 0) as price_list_rate,
item_code from `tabItem Price` ip where price_list = %(price_list)s""",
- {'price_list': selling_price_list}, as_dict=1)
+ {'price_list': selling_price_list}, as_dict=1)
for item in price_lists:
itemwise_price_list[item.item_code] = item.price_list_rate
return itemwise_price_list
+
def get_bin_data(pos_profile):
itemwise_bin_data = {}
cond = "1=1"
@@ -289,6 +336,7 @@
return itemwise_bin_data
+
def get_pricing_rule_data(doc):
pricing_rules = ""
if doc.ignore_pricing_rule == 0:
@@ -297,9 +345,10 @@
and ifnull(company, '') in (%(company)s, '') and disable = 0 and %(date)s
between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')
order by priority desc, name desc""",
- {'company': doc.company, 'price_list': doc.selling_price_list, 'date': nowdate()}, as_dict=1)
+ {'company': doc.company, 'price_list': doc.selling_price_list, 'date': nowdate()}, as_dict=1)
return pricing_rules
+
@frappe.whitelist()
def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
if isinstance(doc_list, basestring):
@@ -338,14 +387,15 @@
'synced_contacts': get_contacts(customers)
}
+
def validate_records(doc):
validate_item(doc)
+
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')
+ cust_id = frappe.db.get_value('Customer',{'customer_pos_id': doc.get('customer_pos_id')}, 'name')
if not cust_id:
customer = customer or doc.get('customer')
@@ -356,6 +406,7 @@
return cust_id
+
def make_customer_and_address(customers):
customers_list = []
for customer, data in customers.items():
@@ -372,6 +423,7 @@
frappe.db.commit()
return customers_list
+
def add_customer(data):
customer_doc = frappe.new_doc('Customer')
customer_doc.customer_name = data.get('full_name') or data.get('customer')
@@ -380,28 +432,29 @@
customer_doc.customer_group = get_customer_group(data)
customer_doc.territory = get_territory(data)
customer_doc.flags.ignore_mandatory = True
- customer_doc.save(ignore_permissions = True)
+ customer_doc.save(ignore_permissions=True)
frappe.db.commit()
return customer_doc.name
+
def get_territory(data):
if data.get('territory'):
return data.get('territory')
- return frappe.db.get_single_value('Selling Settings',
- 'territory') or _('All Territories')
+ return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
+
def get_customer_group(data):
if data.get('customer_group'):
return data.get('customer_group')
- return frappe.db.get_single_value('Selling Settings',
- 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
+ return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
-def make_contact(args,customer):
+
+def make_contact(args, customer):
if args.get('email_id') or args.get('phone'):
name = frappe.db.get_value('Dynamic Link',
- {'link_doctype': 'Customer', 'link_name': customer, 'parenttype': 'Contact'}, 'parent')
+ {'link_doctype': 'Customer', 'link_name': customer, 'parenttype': 'Contact'}, 'parent')
args = {
'first_name': args.get('full_name'),
@@ -416,16 +469,18 @@
doc.update(args)
doc.is_primary_contact = 1
if not name:
- doc.append('links',{
+ doc.append('links', {
'link_doctype': 'Customer',
'link_name': customer
})
doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True)
+
def make_address(args, customer):
- if not args.get('address_line1'): return
-
+ if not args.get('address_line1'):
+ return
+
name = args.get('name')
if not name:
@@ -437,7 +492,7 @@
else:
address = frappe.new_doc('Address')
address.country = frappe.db.get_value('Company', args.get('company'), 'country')
- address.append('links',{
+ address.append('links', {
'link_doctype': 'Customer',
'link_name': customer
})
@@ -446,7 +501,8 @@
address.is_shipping_address = 1
address.update(args)
address.flags.ignore_mandatory = True
- address.save(ignore_permissions = True)
+ address.save(ignore_permissions=True)
+
def make_email_queue(email_queue):
name_list = []
@@ -455,15 +511,16 @@
data = json.loads(data)
sender = frappe.session.user
print_format = "POS Invoice"
- attachments = [frappe.attach_print('Sales Invoice', name, print_format= print_format)]
+ attachments = [frappe.attach_print('Sales Invoice', name, print_format=print_format)]
- make(subject = data.get('subject'), content = data.get('content'), recipients = data.get('recipients'),
- sender=sender,attachments = attachments, send_email=True,
- doctype='Sales Invoice', name=name)
+ make(subject=data.get('subject'), content=data.get('content'), recipients=data.get('recipients'),
+ sender=sender, attachments=attachments, send_email=True,
+ doctype='Sales Invoice', name=name)
name_list.append(key)
return name_list
+
def validate_item(doc):
for item in doc.get('items'):
if not frappe.db.exists('Item', item.get('item_code')):
@@ -486,13 +543,15 @@
frappe.db.commit()
name_list.append(name)
except Exception as e:
- if frappe.message_log: frappe.message_log.pop()
+ if frappe.message_log:
+ frappe.message_log.pop()
frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
name_list = save_invoice(doc, name, name_list)
return name_list
+
def save_invoice(doc, name, name_list):
try:
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index ef39861..27ecbf7 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -4669,26 +4669,56 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "pos_total_qty",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Total Qty",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 1,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-file-text",
- "idx": 181,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2018-01-12 15:19:54.711885",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Sales Invoice",
- "name_case": "Title Case",
- "owner": "Administrator",
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-file-text",
+ "idx": 181,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "menu_index": 0,
+ "modified": "2018-02-08 05:16:44.331093",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice",
+ "name_case": "Title Case",
+ "owner": "Administrator",
"permissions": [
{
"amend": 1,
@@ -4782,4 +4812,4 @@
"title_field": "title",
"track_changes": 1,
"track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/share_balance/__init__.py b/erpnext/accounts/doctype/share_balance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/share_balance/__init__.py
diff --git a/erpnext/accounts/doctype/share_balance/share_balance.json b/erpnext/accounts/doctype/share_balance/share_balance.json
new file mode 100644
index 0000000..04d7bb7
--- /dev/null
+++ b/erpnext/accounts/doctype/share_balance/share_balance.json
@@ -0,0 +1,342 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2018-01-10 13:03:35.544736",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "share_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Share Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Share Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "from_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "no_of_shares",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "No of Shares",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "to_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amount",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_company",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Company",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "current_state",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Current State",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nIssued\nPurchased",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2018-01-10 18:32:36.201124",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Balance",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_balance/share_balance.py b/erpnext/accounts/doctype/share_balance/share_balance.py
new file mode 100644
index 0000000..bd165cd
--- /dev/null
+++ b/erpnext/accounts/doctype/share_balance/share_balance.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class ShareBalance(Document):
+ pass
diff --git a/erpnext/accounts/doctype/share_transfer/__init__.py b/erpnext/accounts/doctype/share_transfer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/__init__.py
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js
new file mode 100644
index 0000000..fbf12e5
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js
@@ -0,0 +1,35 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.provide("erpnext.share_transfer");
+
+frappe.ui.form.on('Share Transfer', {
+ refresh: function(frm) {
+ // Don't show Parties which are a Company
+ let shareholders = ['from_shareholder', 'to_shareholder'];
+ shareholders.forEach((shareholder) => {
+ frm.fields_dict[shareholder].get_query = function() {
+ return {
+ filters: [
+ ["Shareholder", "is_company", "=", 0]
+ ]
+ };
+ };
+ });
+ },
+ no_of_shares: (frm) => {
+ if (frm.doc.rate != undefined || frm.doc.rate != null){
+ erpnext.share_transfer.update_amount(frm);
+ }
+ },
+ rate: (frm) => {
+ if (frm.doc.no_of_shares != undefined || frm.doc.no_of_shares != null){
+ erpnext.share_transfer.update_amount(frm);
+ }
+ }
+});
+
+erpnext.share_transfer.update_amount = function(frm) {
+ frm.doc.amount = frm.doc.no_of_shares * frm.doc.rate;
+ frm.refresh_field("amount");
+};
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json
new file mode 100644
index 0000000..9e6f49d
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json
@@ -0,0 +1,699 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "ST.######",
+ "beta": 0,
+ "creation": "2017-12-25 17:18:03.143726",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "transfer_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Transfer Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nIssue\nPurchase\nTransfer",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_1",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_1",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Issue'",
+ "fieldname": "from_shareholder",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Shareholder",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Shareholder",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Issue'",
+ "fieldname": "from_folio_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Folio No",
+ "length": 0,
+ "no_copy": 0,
+ "options": "from_shareholder.folio_no",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Purchase'",
+ "fieldname": "to_shareholder",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To Shareholder",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Shareholder",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.transfer_type != 'Purchase'",
+ "fieldname": "to_folio_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To Folio No",
+ "length": 0,
+ "no_copy": 0,
+ "options": "to_shareholder.folio_no",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "share_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Share Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Share Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "(including)",
+ "fieldname": "from_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "no_of_shares",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "No of Shares",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "(including)",
+ "fieldname": "to_no",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To No",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "remarks",
+ "fieldtype": "Long Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Remarks",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-01-23 16:12:54.776896",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Transfer",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py
new file mode 100644
index 0000000..c2eaa03
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py
@@ -0,0 +1,271 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.model.naming import make_autoname
+
+class ShareTransfer(Document):
+ def before_save(self):
+ if self.transfer_type == 'Issue':
+ company_doc = self.get_shareholder_doc(self.company)
+ company_doc.append('share_balance', {
+ 'share_type': self.share_type,
+ 'from_no': self.from_no,
+ 'to_no': self.to_no,
+ 'rate': self.rate,
+ 'amount': self.amount,
+ 'no_of_shares': self.no_of_shares,
+ 'is_company': 1,
+ 'current_state': 'Issued'
+ })
+ company_doc.save()
+
+ doc = frappe.get_doc('Shareholder', self.to_shareholder)
+ doc.append('share_balance', {
+ 'share_type': self.share_type,
+ 'from_no': self.from_no,
+ 'to_no': self.to_no,
+ 'rate': self.rate,
+ 'amount': self.amount,
+ 'no_of_shares': self.no_of_shares
+ })
+ doc.save()
+
+ elif self.transfer_type == 'Purchase':
+ self.remove_shares(self.from_shareholder)
+ self.remove_shares(self.get_shareholder_doc(self.company).name)
+
+ elif self.transfer_type == 'Transfer':
+ self.remove_shares(self.from_shareholder)
+ doc = frappe.get_doc('Shareholder', self.to_shareholder)
+ doc.append('share_balance', {
+ 'share_type': self.share_type,
+ 'from_no': self.from_no,
+ 'to_no': self.to_no,
+ 'rate': self.rate,
+ 'amount': self.amount,
+ 'no_of_shares': self.no_of_shares
+ })
+ doc.save()
+
+ def validate(self):
+ self.basic_validations()
+ self.folio_no_validation()
+ if self.transfer_type == 'Issue':
+ if not self.get_shareholder_doc(self.company):
+ company_doc = frappe.get_doc({
+ 'doctype': 'Shareholder',
+ 'title': self.company,
+ 'company': self.company,
+ 'is_company': 1
+ })
+ company_doc.insert()
+ # validate share doesnt exist in company
+ ret_val = self.share_exists(self.get_shareholder_doc(self.company).name)
+ if ret_val != False:
+ frappe.throw('The shares already exist')
+ else:
+ # validate share exists with from_shareholder
+ ret_val = self.share_exists(self.from_shareholder)
+ if ret_val != True:
+ frappe.throw('The shares don\'t exist with the {0}'.format(self.from_shareholder))
+
+ def basic_validations(self):
+ if self.transfer_type == 'Purchase':
+ self.to_shareholder = ''
+ if self.from_shareholder is None or self.from_shareholder is '':
+ frappe.throw('The field \'From Shareholder\' cannot be blank')
+ if self.from_folio_no is None or self.from_folio_no is '':
+ self.to_folio_no = self.autoname_folio(self.to_shareholder)
+ elif (self.transfer_type == 'Issue'):
+ self.from_shareholder = ''
+ if self.to_shareholder is None or self.to_shareholder == '':
+ frappe.throw('The field \'To Shareholder\' cannot be blank')
+ if self.to_folio_no is None or self.to_folio_no is '':
+ self.to_folio_no = self.autoname_folio(self.to_shareholder)
+ else:
+ if self.from_shareholder is None or self.to_shareholder is None:
+ frappe.throw('The fields \'From Shareholder\' and \'To Shareholder\' cannot be blank')
+ if self.to_folio_no is None or self.to_folio_no is '':
+ self.to_folio_no = self.autoname_folio(self.to_shareholder)
+ if self.from_shareholder == self.to_shareholder:
+ frappe.throw('The seller and the buyer cannot be the same')
+ if self.no_of_shares != self.to_no - self.from_no + 1:
+ frappe.throw('The number of shares and the share numbers are inconsistent!')
+ if self.amount is None:
+ self.amount = self.rate * self.no_of_shares
+ if self.amount != self.rate * self.no_of_shares:
+ frappe.throw('There\'s inconsistency between the rate, no of shares and the amount calculated')
+
+ def share_exists(self, shareholder):
+ # return True if exits,
+ # False if completely doesn't exist,
+ # 'partially exists' if partailly doesn't exist
+ ret_val = self.recursive_share_check(shareholder, self.share_type,
+ query = {
+ 'from_no': self.from_no,
+ 'to_no': self.to_no
+ }
+ )
+ if all(boolean == True for boolean in ret_val):
+ return True
+ elif True in ret_val:
+ return 'partially exists'
+ else:
+ return False
+
+ def recursive_share_check(self, shareholder, share_type, query):
+ # query = {'from_no': share_starting_no, 'to_no': share_ending_no}
+ # Recursive check if a given part of shares is held by the shareholder
+ # return a list containing True and False
+ # Eg. [True, False, True]
+ # All True implies its completely inside
+ # All False implies its completely outside
+ # A mix implies its partially inside/outside
+ does_share_exist = []
+ doc = frappe.get_doc('Shareholder', shareholder)
+ for entry in doc.share_balance:
+ if entry.share_type != share_type or \
+ entry.from_no > query['to_no'] or \
+ entry.to_no < query['from_no']:
+ continue # since query lies outside bounds
+ elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']:
+ return [True] # absolute truth!
+ elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']:
+ # split and check
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': query['from_no'],
+ 'to_no': entry.from_no - 1
+ }
+ ))
+ does_share_exist.append(True)
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': entry.to_no + 1,
+ 'to_no': query['to_no']
+ }
+ ))
+ elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']:
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': query['from_no'],
+ 'to_no': entry.from_no - 1
+ }
+ ))
+ elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']:
+ does_share_exist.extend(self.recursive_share_check(shareholder,
+ share_type,
+ {
+ 'from_no': entry.to_no + 1,
+ 'to_no': query['to_no']
+ }
+ ))
+
+ does_share_exist.append(False)
+ return does_share_exist
+
+ def folio_no_validation(self):
+ shareholders = ['from_shareholder', 'to_shareholder']
+ shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not '']
+ for shareholder in shareholders:
+ doc = frappe.get_doc('Shareholder', self.get(shareholder))
+ if doc.company != self.company:
+ frappe.throw('The shareholder doesn\'t belong to this company')
+ if doc.folio_no is '' or doc.folio_no is None:
+ doc.folio_no = self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no;
+ doc.save()
+ else:
+ if doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no):
+ frappe.throw('The folio numbers are not matching')
+
+ def autoname_folio(self, shareholder, is_company=False):
+ if is_company:
+ doc = self.get_shareholder_doc(shareholder)
+ else:
+ doc = frappe.get_doc('Shareholder' , shareholder)
+ doc.folio_no = make_autoname('FN.#####')
+ doc.save()
+ return doc.folio_no
+
+ def remove_shares(self, shareholder):
+ self.iterative_share_removal(shareholder, self.share_type,
+ {
+ 'from_no': self.from_no,
+ 'to_no' : self.to_no
+ },
+ rate = self.rate,
+ amount = self.amount
+ )
+
+ def iterative_share_removal(self, shareholder, share_type, query, rate, amount):
+ # query = {'from_no': share_starting_no, 'to_no': share_ending_no}
+ # Shares exist for sure
+ # Iterate over all entries and modify entry if in entry
+ doc = frappe.get_doc('Shareholder', shareholder)
+ current_entries = doc.share_balance
+ new_entries = []
+
+ for entry in current_entries:
+ # use spaceage logic here
+ if entry.share_type != share_type or \
+ entry.from_no > query['to_no'] or \
+ entry.to_no < query['from_no']:
+ new_entries.append(entry)
+ continue # since query lies outside bounds
+ elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']:
+ #split
+ if entry.from_no == query['from_no']:
+ if entry.to_no == query['to_no']:
+ pass #nothing to append
+ else:
+ new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
+ else:
+ if entry.to_no == query['to_no']:
+ new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
+ else:
+ new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
+ new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
+ elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']:
+ # split and check
+ pass #nothing to append
+ elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']:
+ new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate))
+ elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']:
+ new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate))
+ else:
+ new_entries.append(entry)
+
+ doc.share_balance = []
+ for entry in new_entries:
+ doc.append('share_balance', entry)
+ doc.save()
+
+ def return_share_balance_entry(self, from_no, to_no, rate):
+ # return an entry as a dict
+ return {
+ 'share_type' : self.share_type,
+ 'from_no' : from_no,
+ 'to_no' : to_no,
+ 'rate' : rate,
+ 'amount' : self.rate * (to_no - from_no + 1),
+ 'no_of_shares' : to_no - from_no + 1
+ }
+
+ def get_shareholder_doc(self, shareholder):
+ # Get Shareholder doc based on the Shareholder title
+ doc = frappe.get_list('Shareholder',
+ filters = [
+ ('Shareholder', 'title', '=', shareholder)
+ ]
+ )
+ if len(doc) == 1:
+ return frappe.get_doc('Shareholder', doc[0]['name'])
+ else: #It will necessarily by 0 indicating it doesn't exist
+ return False
diff --git a/erpnext/accounts/doctype/share_transfer/test_records.json b/erpnext/accounts/doctype/share_transfer/test_records.json
new file mode 100644
index 0000000..13b37b4
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/test_records.json
@@ -0,0 +1,76 @@
+
+[
+ {
+ "doctype" : "Company",
+ "company_name" : "Stark Tower",
+ "abbr" : "ST",
+ "default_currency" : "INR"
+ },
+ {
+ "doctype" : "Share Type",
+ "title" : "Class A"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Issue",
+ "date" : "2018-01-01",
+ "to_shareholder" : "SH-00001",
+ "share_type" : "Equity",
+ "from_no" : 1,
+ "to_no" : 500,
+ "no_of_shares" : 500,
+ "rate" : 10,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-02",
+ "from_shareholder" : "SH-00001",
+ "to_shareholder" : "SH-00002",
+ "share_type" : "Equity",
+ "from_no" : 101,
+ "to_no" : 200,
+ "no_of_shares" : 100,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-03",
+ "from_shareholder" : "SH-00001",
+ "to_shareholder" : "SH-00003",
+ "share_type" : "Equity",
+ "from_no" : 201,
+ "to_no" : 500,
+ "no_of_shares" : 300,
+ "rate" : 20,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-04",
+ "from_shareholder" : "SH-00001",
+ "to_shareholder" : "SH-00002",
+ "share_type" : "Equity",
+ "from_no" : 201,
+ "to_no" : 400,
+ "no_of_shares" : 200,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ },
+ {
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Purchase",
+ "date" : "2018-01-05",
+ "from_shareholder" : "SH-00003",
+ "share_type" : "Equity",
+ "from_no" : 401,
+ "to_no" : 500,
+ "no_of_shares" : 100,
+ "rate" : 25,
+ "company" : "Stark Tower"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.js b/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
new file mode 100644
index 0000000..e5530fa
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Share Transfer", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Share Transfer
+ () => frappe.tests.make('Share Transfer', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
new file mode 100644
index 0000000..8dee358
--- /dev/null
+++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from frappe import ValidationError
+
+test_dependencies = ["Share Type", "Shareholder"]
+
+class TestShareTransfer(unittest.TestCase):
+ def test_invalid_share_transfer(self):
+ doc = frappe.get_doc({
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Transfer",
+ "date" : "2018-01-05",
+ "from_shareholder" : "SH-00003",
+ "to_shareholder" : "SH-00002",
+ "share_type" : "Equity",
+ "from_no" : 1,
+ "to_no" : 100,
+ "no_of_shares" : 100,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ })
+ self.assertRaises(ValidationError, doc.insert)
+
+ def test_invalid_share_purchase(self):
+ doc = frappe.get_doc({
+ "doctype" : "Share Transfer",
+ "transfer_type" : "Purchase",
+ "date" : "2018-01-02",
+ "from_shareholder" : "SH-00001",
+ "share_type" : "Equity",
+ "from_no" : 1,
+ "to_no" : 200,
+ "no_of_shares" : 200,
+ "rate" : 15,
+ "company" : "Stark Tower"
+ })
+ self.assertRaises(ValidationError, doc.insert)
diff --git a/erpnext/accounts/doctype/share_type/__init__.py b/erpnext/accounts/doctype/share_type/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/__init__.py
diff --git a/erpnext/accounts/doctype/share_type/share_type.js b/erpnext/accounts/doctype/share_type/share_type.js
new file mode 100644
index 0000000..1ae85e3
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/share_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Share Type', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/accounts/doctype/share_type/share_type.json b/erpnext/accounts/doctype/share_type/share_type.json
new file mode 100644
index 0000000..3373e03
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/share_type.json
@@ -0,0 +1,123 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:title",
+ "beta": 0,
+ "creation": "2017-12-28 14:55:20.950877",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "description",
+ "fieldtype": "Long Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Description",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-28 14:55:20.950877",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Type",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_type/share_type.py b/erpnext/accounts/doctype/share_type/share_type.py
new file mode 100644
index 0000000..ab4b8bc
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/share_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class ShareType(Document):
+ pass
diff --git a/erpnext/accounts/doctype/share_type/test_records.json b/erpnext/accounts/doctype/share_type/test_records.json
new file mode 100644
index 0000000..88cb8c0
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/test_records.json
@@ -0,0 +1,10 @@
+[
+ {
+ "doctype": "Share Type",
+ "title": "Class A"
+ },
+ {
+ "doctype": "Share Type",
+ "title": "Class B"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.js b/erpnext/accounts/doctype/share_type/test_share_type.js
new file mode 100644
index 0000000..620afa2
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/test_share_type.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Share Type", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Share Type
+ () => frappe.tests.make('Share Type', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/share_type/test_share_type.py b/erpnext/accounts/doctype/share_type/test_share_type.py
new file mode 100644
index 0000000..1c1f051
--- /dev/null
+++ b/erpnext/accounts/doctype/share_type/test_share_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestShareType(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/shareholder/__init__.py b/erpnext/accounts/doctype/shareholder/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/__init__.py
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.js b/erpnext/accounts/doctype/shareholder/shareholder.js
new file mode 100644
index 0000000..c6f101e
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/shareholder.js
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Shareholder', {
+ refresh: function(frm) {
+ frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' };
+
+ frm.toggle_display(['contact_html'], !frm.doc.__islocal);
+
+ if (frm.doc.__islocal) {
+ hide_field(['contact_html']);
+ frappe.contacts.clear_address_and_contact(frm);
+ }
+ else {
+ if (frm.doc.is_company){
+ hide_field(['company']);
+ } else {
+ unhide_field(['contact_html']);
+ frappe.contacts.render_address_and_contact(frm);
+ }
+ }
+
+ if (frm.doc.folio_no != undefined){
+ frm.add_custom_button(__("Share Balance"), function(){
+ frappe.route_options = {
+ "shareholder": frm.doc.name,
+ };
+ frappe.set_route("query-report", "Share Balance");
+ });
+ frm.add_custom_button(__("Share Ledger"), function(){
+ frappe.route_options = {
+ "shareholder": frm.doc.name,
+ };
+ frappe.set_route("query-report", "Share Ledger");
+ });
+ let fields = ['title', 'folio_no', 'company'];
+ fields.forEach((fieldname) => {
+ frm.fields_dict[fieldname].df.read_only = 1;
+ frm.refresh_fields(fieldname);
+ });
+ $(`.btn:contains("New Contact"):visible`).hide();
+ $(`.btn:contains("Edit"):visible`).hide();
+ }
+ },
+ validate: (frm) => {
+ let contact_list = {
+ contacts: []
+ };
+ $('div[data-fieldname=contact_html] > .address-box').each( (index, ele) => {
+ contact_list.contacts.push(ele.innerText.replace(' Edit', ''));
+ });
+ frm.doc.contact_list = JSON.stringify(contact_list);
+ }
+});
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.json b/erpnext/accounts/doctype/shareholder/shareholder.json
new file mode 100644
index 0000000..be35e7b
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/shareholder.json
@@ -0,0 +1,518 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "naming_series:",
+ "beta": 0,
+ "creation": "2017-12-25 16:50:53.878430",
+ "custom": 0,
+ "description": "",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Series",
+ "length": 0,
+ "no_copy": 0,
+ "options": "SH-",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_2",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "folio_no",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Folio no.",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Company",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Company",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "is_company",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Company",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_contacts",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address and Contacts",
+ "length": 0,
+ "no_copy": 0,
+ "options": "fa fa-map-marker",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "address_html",
+ "fieldtype": "HTML",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Address HTML",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "contact_html",
+ "fieldtype": "HTML",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Contact HTML",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Share Balance",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "share_balance",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Share Balance",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Share Balance",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "Hidden list maintaining the list of contacts linked to Shareholder",
+ "fieldname": "contact_list",
+ "fieldtype": "Code",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Contact List",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-01-23 17:49:02.941363",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Shareholder",
+ "name_case": "Title Case",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "folio_no",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.py b/erpnext/accounts/doctype/shareholder/shareholder.py
new file mode 100644
index 0000000..c507fcf
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/shareholder.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
+
+class Shareholder(Document):
+ def onload(self):
+ """Load address and contacts in `__onload`"""
+ load_address_and_contact(self)
+
+ def on_trash(self):
+ delete_contact_and_address('Shareholder', self.name)
+
+ def before_save(self):
+ for entry in self.share_balance:
+ entry.amount = entry.no_of_shares * entry.rate
diff --git a/erpnext/accounts/doctype/shareholder/test_records.json b/erpnext/accounts/doctype/shareholder/test_records.json
new file mode 100644
index 0000000..ca289cb
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/test_records.json
@@ -0,0 +1,17 @@
+[
+ {
+ "doctype": "Shareholder",
+ "series": "SH-",
+ "title": "Iron Man"
+ },
+ {
+ "doctype": "Shareholder",
+ "series": "SH-",
+ "title": "Thor"
+ },
+ {
+ "doctype": "Shareholder",
+ "series": "SH-",
+ "title": "Hulk"
+ }
+]
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.js b/erpnext/accounts/doctype/shareholder/test_shareholder.js
new file mode 100644
index 0000000..61c5312
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/test_shareholder.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Shareholder", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Shareholder
+ () => frappe.tests.make('Shareholder', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/shareholder/test_shareholder.py b/erpnext/accounts/doctype/shareholder/test_shareholder.py
new file mode 100644
index 0000000..9ce0093
--- /dev/null
+++ b/erpnext/accounts/doctype/shareholder/test_shareholder.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestShareholder(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index ab32d9f..ccb5553 100644
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -301,6 +301,7 @@
this.customers = r.message.customers;
this.serial_no_data = r.message.serial_no_data;
this.batch_no_data = r.message.batch_no_data;
+ this.barcode_data = r.message.barcode_data;
this.tax_data = r.message.tax_data;
this.contacts = r.message.contacts;
this.address = r.message.address || {};
@@ -415,7 +416,7 @@
});
this.serach_item.make_input();
-
+
this.serach_item.$input.on("keypress", function (event) {
clearTimeout(me.last_search_timeout);
@@ -423,7 +424,7 @@
if((me.serach_item.$input.val() != "") || (event.which == 13)) {
me.items = me.get_items();
me.make_item_list();
- }
+ }
}, 400);
});
@@ -1110,9 +1111,9 @@
search_status = false;
me.item_serial_no[item.item_code] = [me.serach_item.$input.val(), me.serial_no_data[item.item_code][me.serach_item.$input.val()]]
return true
- } else if (item.barcode == me.serach_item.$input.val()) {
+ } else if (in_list(me.barcode_data[item.item_code], me.serach_item.$input.val())) {
search_status = false;
- return item.barcode == me.serach_item.$input.val();
+ return true;
} else if (reg.test(item.item_code.toLowerCase()) || (item.description && reg.test(item.description.toLowerCase())) ||
reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase())) {
return true
@@ -1526,8 +1527,8 @@
me.print_document(html)
})
}
-
- if (this.frm.doc.docstatus == 1) {
+
+ if (this.frm.doc.docstatus == 1) {
this.page.add_menu_item(__("Email"), function () {
me.email_prompt()
})
diff --git a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json
index 432df7e..bef6a02 100644
--- a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json
@@ -6,10 +6,10 @@
"doc_type": "Sales Invoice",
"docstatus": 0,
"doctype": "Print Format",
- "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \" + (doc.select_print_heading or _(\"Invoice\")) }}<br>\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n\t{% endif %}\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
+ "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \" + (doc.select_print_heading or _(\"Invoice\")) }}<br>\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n\t{% endif %}\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- if doc.rounded_total -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Rounded Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- if doc.pos_total_qty -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Total Qty\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"pos_total_qty\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
"idx": 1,
"line_breaks": 0,
- "modified": "2018-02-07 12:40:06.259322",
+ "modified": "2018-02-08 05:39:47.280705",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js
index 760fa64..f22f3a1 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.js
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js
@@ -11,5 +11,3 @@
"default": 1
});
});
-
-
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js
index 455664f..391f57b 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.js
+++ b/erpnext/accounts/report/cash_flow/cash_flow.js
@@ -5,6 +5,11 @@
frappe.query_reports["Cash Flow"] = $.extend({},
erpnext.financial_statements);
+ // The last item in the array is the definition for Presentation Currency
+ // filter. It won't be used in cash flow for now so we pop it. Please take
+ // of this if you are working here.
+ frappe.query_reports["Cash Flow"]["filters"].pop();
+
frappe.query_reports["Cash Flow"]["filters"].push({
"fieldname": "accumulated_values",
"label": __("Accumulated Values"),
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index b0c49df..5d2a35b 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -2,12 +2,14 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+
import re
-from frappe import _
-from frappe.utils import (flt, getdate, get_first_day, get_last_day, date_diff,
- add_months, add_days, formatdate, cint)
+
+import frappe
+from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
from erpnext.accounts.utils import get_fiscal_year
+from frappe import _
+from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate)
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False,
@@ -84,6 +86,7 @@
return period_list
+
def get_fiscal_year_data(from_fiscal_year, to_fiscal_year):
fiscal_year = frappe.db.sql("""select min(year_start_date) as year_start_date,
max(year_end_date) as year_end_date from `tabFiscal Year` where
@@ -92,16 +95,19 @@
return fiscal_year[0] if fiscal_year else {}
+
def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year):
if not fiscal_year.get('year_start_date') and not fiscal_year.get('year_end_date'):
frappe.throw(_("End Year cannot be before Start Year"))
+
def get_months(start_date, end_date):
diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month)
return diff + 1
+
def get_label(periodicity, from_date, to_date):
- if periodicity=="Yearly":
+ if periodicity == "Yearly":
if formatdate(from_date, "YYYY") == formatdate(to_date, "YYYY"):
label = formatdate(from_date, "YYYY")
else:
@@ -111,28 +117,34 @@
return label
-def get_data(company, root_type, balance_must_be, period_list, filters=None,
+
+def get_data(
+ company, root_type, balance_must_be, period_list, filters=None,
accumulated_values=1, only_current_fiscal_year=True, ignore_closing_entries=False,
ignore_accumulated_values_for_fy=False):
+
accounts = get_accounts(company, root_type)
if not accounts:
return None
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
- company_currency = frappe.db.get_value("Company", company, "default_currency")
+ company_currency = get_appropriate_currency(company, filters)
gl_entries_by_account = {}
for root in frappe.db.sql("""select lft, rgt from tabAccount
where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
- set_gl_entries_by_account(company,
+ set_gl_entries_by_account(
+ company,
period_list[0]["year_start_date"] if only_current_fiscal_year else None,
period_list[-1]["to_date"],
root.lft, root.rgt, filters,
- gl_entries_by_account, ignore_closing_entries=ignore_closing_entries)
+ gl_entries_by_account, ignore_closing_entries=ignore_closing_entries
+ )
- calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy)
+ calculate_values(
+ accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy)
accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values)
out = prepare_data(accounts, balance_must_be, period_list, company_currency)
out = filter_out_zero_value_rows(out, parent_children_map)
@@ -143,7 +155,15 @@
return out
-def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy):
+def get_appropriate_currency(company, filters=None):
+ if filters and filters.get("presentation_currency"):
+ return filters["presentation_currency"]
+ else:
+ return frappe.db.get_value("Company", company, "default_currency")
+
+
+def calculate_values(
+ accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy):
for entries in gl_entries_by_account.values():
for entry in entries:
d = accounts_by_name.get(entry.account)
@@ -164,6 +184,7 @@
if entry.posting_date < period_list[0].year_start_date:
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
+
def accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values):
"""accumulate children's values in parent accounts"""
for d in reversed(accounts):
@@ -175,6 +196,7 @@
accounts_by_name[d.parent_account]["opening_balance"] = \
accounts_by_name[d.parent_account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
+
def prepare_data(accounts, balance_must_be, period_list, company_currency):
data = []
year_start_date = period_list[0]["year_start_date"].strftime("%Y-%m-%d")
@@ -192,10 +214,10 @@
"year_start_date": year_start_date,
"year_end_date": year_end_date,
"currency": company_currency,
- "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be=="Debit" else -1)
+ "opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
})
for period in period_list:
- if d.get(period.key) and balance_must_be=="Credit":
+ if d.get(period.key) and balance_must_be == "Credit":
# change sign based on Debit or Credit, since calculation is done using (debit - credit)
d[period.key] *= -1
@@ -212,6 +234,7 @@
return data
+
def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False):
data_with_value = []
for d in data:
@@ -228,6 +251,7 @@
return data_with_value
+
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
total_row = {
"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
@@ -246,16 +270,19 @@
total_row["total"] += flt(row["total"])
row["total"] = ""
- if total_row.has_key("total"):
+ if "total" in total_row:
out.append(total_row)
# blank row after Total
out.append({})
+
def get_accounts(company, root_type):
- return frappe.db.sql("""select name, parent_account, lft, rgt, root_type, report_type, account_name from `tabAccount`
+ return frappe.db.sql(
+ """select name, parent_account, lft, rgt, root_type, report_type, account_name from `tabAccount`
where company=%s and root_type=%s order by lft""", (company, root_type), as_dict=True)
+
def filter_accounts(accounts, depth=10):
parent_children_map = {}
accounts_by_name = {}
@@ -280,6 +307,7 @@
return filtered_accounts, accounts_by_name, parent_children_map
+
def sort_root_accounts(roots):
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
@@ -299,13 +327,14 @@
roots.sort(compare_roots)
-def set_gl_entries_by_account(company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
- ignore_closing_entries=False):
+
+def set_gl_entries_by_account(
+ company, from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, ignore_closing_entries=False):
"""Returns a dict like { "account": [gl entries], ... }"""
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
- gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year from `tabGL Entry`
+ gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
where company=%(company)s
{additional_conditions}
and posting_date <= %(to_date)s
@@ -321,11 +350,15 @@
},
as_dict=True)
+ if filters and filters.get('presentation_currency'):
+ convert_to_presentation_currency(gl_entries, get_currency(filters))
+
for entry in gl_entries:
gl_entries_by_account.setdefault(entry.account, []).append(entry)
return gl_entries_by_account
+
def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions = []
@@ -337,15 +370,17 @@
if filters:
if filters.get("project"):
- additional_conditions.append("project = '%s'"%(frappe.db.escape(filters.get("project"))))
+ additional_conditions.append("project = '%s'" % (frappe.db.escape(filters.get("project"))))
if filters.get("cost_center"):
additional_conditions.append(get_cost_center_cond(filters.get("cost_center")))
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
+
def get_cost_center_cond(cost_center):
lft, rgt = frappe.db.get_value("Cost Center", cost_center, ["lft", "rgt"])
- return (""" cost_center in (select name from `tabCost Center` where lft >=%s and rgt <=%s)"""%(lft, rgt))
+ return """ cost_center in (select name from `tabCost Center` where lft >=%s and rgt <=%s)""" % (lft, rgt)
+
def get_columns(periodicity, period_list, accumulated_values=1, company=None):
columns = [{
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html
index 9e1b884..5cfdc9f 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.html
+++ b/erpnext/accounts/report/general_ledger/general_ledger.html
@@ -44,44 +44,24 @@
<br>{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %}
{% } %}
</td>
- {% if(filters.print_in_account_currency) { %}
<td style="text-align: right">
- {%= format_currency(data[i].debit_in_account_currency, data[i].account_currency) %}
- </td>
+ {%= format_currency(data[i].debit, filters.presentation_currency) %}</td>
<td style="text-align: right">
- {%= format_currency(data[i].credit_in_account_currency, data[i].account_currency) %}
- </td>
- {% } else { %}
- <td style="text-align: right">
- {%= format_currency(data[i].debit) %}</td>
- <td style="text-align: right">
- {%= format_currency(data[i].credit) %}</td>
- {% } %}
+ {%= format_currency(data[i].credit, filters.presentation_currency) %}</td>
{% } else { %}
<td></td>
<td></td>
<td><b>{%= frappe.format(data[i].account, {fieldtype: "Link"}) || " " %}</b></td>
- {% if(filters.print_in_account_currency) { %}
- <td style="text-align: right">
- {%= data[i].account && format_currency(data[i].debit_in_account_currency, data[i].account_currency) %}</td>
- <td style="text-align: right">
- {%= data[i].account && format_currency(data[i].credit_in_account_currency, data[i].account_currency) %}</td>
- {% } else { %}
- <td style="text-align: right">
- {%= data[i].account && format_currency(data[i].debit) %}
- </td>
- <td style="text-align: right">
- {%= data[i].account && format_currency(data[i].credit) %}
- </td>
- {% } %}
- {% } %}
- {% if(filters.print_in_account_currency) { %}
<td style="text-align: right">
- {%= format_currency(data[i].balance_in_account_currency, data[i].account_currency) %}
+ {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency) %}
</td>
- {% } else { %}
- <td style="text-align: right">{%= format_currency(data[i].balance) %}</td>
- {% } %}
+ <td style="text-align: right">
+ {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency) %}
+ </td>
+ {% } %}
+ <td style="text-align: right">
+ {%= format_currency(data[i].balance, filters.presentation_currency) %}
+ </td>
</tr>
{% } %}
</tbody>
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index adefadd..d6a2aec 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -1,4 +1,4 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.query_reports["General Ledger"] = {
@@ -107,9 +107,10 @@
"fieldtype": "Check",
},
{
- "fieldname":"print_in_account_currency",
- "label": __("Print in Account Currency"),
- "fieldtype": "Check",
+ "fieldname": "presentation_currency",
+ "label": __("Currency"),
+ "fieldtype": "Select",
+ "options": erpnext.get_presentation_currency_list()
}
]
}
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 8e4259c..70ab67f 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -3,10 +3,13 @@
from __future__ import unicode_literals
import frappe
+from erpnext import get_company_currency, get_default_company
+from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
from frappe.utils import getdate, cstr, flt, fmt_money
from frappe import _, _dict
from erpnext.accounts.utils import get_account_currency
+
def execute(filters=None):
account_details = {}
@@ -29,6 +32,7 @@
return columns, res
+
def validate_filters(filters, account_details):
if not filters.get('company'):
frappe.throw(_('{0} is mandatory').format(_('Company')))
@@ -37,7 +41,7 @@
frappe.throw(_("Account {0} does not exists").format(filters.account))
if filters.get("account") and filters.get("group_by_account") \
- and account_details[filters.account].is_group == 0:
+ and account_details[filters.account].is_group == 0:
frappe.throw(_("Can not filter based on Account, if grouped by Account"))
if filters.get("voucher_no") and filters.get("group_by_voucher"):
@@ -56,6 +60,7 @@
elif not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
+
def set_account_currency(filters):
if not (filters.get("account") or filters.get("party")):
return filters
@@ -66,8 +71,13 @@
if filters.get("account"):
account_currency = get_account_currency(filters.account)
elif filters.get("party"):
- gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type,
- "party": filters.party, "company": filters.company}, "account_currency")
+ gle_currency = frappe.db.get_value(
+ "GL Entry", {
+ "party_type": filters.party_type, "party": filters.party, "company": filters.company
+ },
+ "account_currency"
+ )
+
if gle_currency:
account_currency = gle_currency
else:
@@ -90,29 +100,39 @@
return result
+
def get_gl_entries(filters):
+ currency_map = get_currency(filters)
select_fields = """, sum(debit_in_account_currency) as debit_in_account_currency,
sum(credit_in_account_currency) as credit_in_account_currency""" \
- if filters.get("show_in_account_currency") else ""
+
group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \
if filters.get("group_by_voucher") else "group by name"
- gl_entries = frappe.db.sql("""
+ gl_entries = frappe.db.sql(
+ """
select
posting_date, account, party_type, party,
sum(debit) as debit, sum(credit) as credit,
voucher_type, voucher_no, cost_center, project,
- against_voucher_type, against_voucher,
+ against_voucher_type, against_voucher, account_currency,
remarks, against, is_opening {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions}
{group_by_condition}
- order by posting_date, account"""\
- .format(select_fields=select_fields, conditions=get_conditions(filters),
- group_by_condition=group_by_condition), filters, as_dict=1)
+ order by posting_date, account
+ """.format(
+ select_fields=select_fields, conditions=get_conditions(filters),
+ group_by_condition=group_by_condition
+ ),
+ filters, as_dict=1)
- return gl_entries
+ if filters.get('presentation_currency'):
+ return convert_to_presentation_currency(gl_entries, currency_map)
+ else:
+ return gl_entries
+
def get_conditions(filters):
conditions = []
@@ -132,16 +152,20 @@
if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")):
conditions.append("posting_date >=%(from_date)s")
+ conditions.append("posting_date <=%(to_date)s")
if filters.get("project"):
conditions.append("project=%(project)s")
from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("GL Entry")
- if match_conditions: conditions.append(match_conditions)
+
+ if match_conditions:
+ conditions.append(match_conditions)
return "and {}".format(" and ".join(conditions)) if conditions else ""
+
def get_data_with_opening_closing(filters, account_details, gl_entries):
data = []
gle_map = initialize_gle_map(gl_entries)
@@ -178,14 +202,15 @@
return data
+
def get_totals_dict():
def _get_debit_credit_dict(label):
return _dict(
- account = "'{0}'".format(label),
- debit = 0.0,
- credit = 0.0,
- debit_in_account_currency = 0.0,
- credit_in_account_currency = 0.0
+ account="'{0}'".format(label),
+ debit=0.0,
+ credit=0.0,
+ debit_in_account_currency=0.0,
+ credit_in_account_currency=0.0
)
return _dict(
opening = _get_debit_credit_dict(_('Opening')),
@@ -193,12 +218,14 @@
closing = _get_debit_credit_dict(_('Closing (Opening + Total)'))
)
+
def initialize_gle_map(gl_entries):
gle_map = frappe._dict()
for gle in gl_entries:
- gle_map.setdefault(gle.account, _dict(totals = get_totals_dict(), entries = []))
+ gle_map.setdefault(gle.account, _dict(totals=get_totals_dict(), entries=[]))
return gle_map
+
def get_accountwise_gle(filters, gl_entries, gle_map):
totals = get_totals_dict()
entries = []
@@ -210,13 +237,12 @@
data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
-
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
for gle in gl_entries:
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
update_value_in_dict(gle_map[gle.account].totals, 'opening', gle)
update_value_in_dict(totals, 'opening', gle)
-
+
update_value_in_dict(gle_map[gle.account].totals, 'closing', gle)
update_value_in_dict(totals, 'closing', gle)
@@ -233,6 +259,7 @@
return totals, entries
+
def get_result_as_list(data, filters):
balance, balance_in_account_currency = 0, 0
inv_details = get_supplier_invoice_details()
@@ -272,6 +299,15 @@
return balance
def get_columns(filters):
+ if filters.get("presentation_currency"):
+ currency = filters["presentation_currency"]
+ else:
+ if filters.get("company"):
+ currency = get_company_currency(filters["company"])
+ else:
+ company = get_default_company()
+ currency = get_company_currency(company)
+
columns = [
{
"label": _("Posting Date"),
@@ -287,47 +323,25 @@
"width": 180
},
{
- "label": _("Debit"),
+ "label": _("Debit ({0})".format(currency)),
"fieldname": "debit",
"fieldtype": "Float",
"width": 100
},
{
- "label": _("Credit"),
+ "label": _("Credit ({0})".format(currency)),
"fieldname": "credit",
"fieldtype": "Float",
"width": 100
},
{
- "label": _("Balance (Dr - Cr)"),
+ "label": _("Balance ({0})".format(currency)),
"fieldname": "balance",
"fieldtype": "Float",
"width": 130
}
]
- if filters.get("show_in_account_currency"):
- columns.extend([
- {
- "label": _("Debit") + " (" + filters.account_currency + ")",
- "fieldname": "debit_in_account_currency",
- "fieldtype": "Float",
- "width": 100
- },
- {
- "label": _("Credit") + " (" + filters.account_currency + ")",
- "fieldname": "credit_in_account_currency",
- "fieldtype": "Float",
- "width": 100
- },
- {
- "label": _("Balance") + " (" + filters.account_currency + ")",
- "fieldname": "balance_in_account_currency",
- "fieldtype": "Data",
- "width": 100
- }
- ])
-
columns.extend([
{
"label": _("Voucher Type"),
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
index bcac2df..a02c592 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
@@ -1,6 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+
frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Profit and Loss Statement"] = $.extend({},
erpnext.financial_statements);
@@ -24,4 +25,4 @@
"fieldtype": "Check"
}
);
-});
\ No newline at end of file
+});
diff --git a/erpnext/accounts/report/share_balance/__init__.py b/erpnext/accounts/report/share_balance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/__init__.py
diff --git a/erpnext/accounts/report/share_balance/share_balance.js b/erpnext/accounts/report/share_balance/share_balance.js
new file mode 100644
index 0000000..6db5bdd
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/share_balance.js
@@ -0,0 +1,22 @@
+// -*- coding: utf-8 -*-
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Share Balance"] = {
+ "filters": [
+ {
+ "fieldname":"date",
+ "label": __("Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today(),
+ "reqd": 1
+ },
+ {
+ "fieldname":"shareholder",
+ "label": __("Shareholder"),
+ "fieldtype": "Link",
+ "options": "Shareholder"
+ }
+ ]
+}
diff --git a/erpnext/accounts/report/share_balance/share_balance.json b/erpnext/accounts/report/share_balance/share_balance.json
new file mode 100644
index 0000000..5477c09
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/share_balance.json
@@ -0,0 +1,23 @@
+{
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2017-12-28 16:12:34.154787",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2017-12-28 16:12:37.155649",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Balance",
+ "owner": "Administrator",
+ "ref_doctype": "Share Transfer",
+ "report_name": "Share Balance",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "System Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/share_balance/share_balance.py b/erpnext/accounts/report/share_balance/share_balance.py
new file mode 100644
index 0000000..9f22f81
--- /dev/null
+++ b/erpnext/accounts/report/share_balance/share_balance.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import msgprint, _
+
+def execute(filters=None):
+ if not filters: filters = {}
+
+ if not filters.get("date"):
+ frappe.throw(_("Please select date"))
+
+ columns = get_columns(filters)
+
+ date = filters.get("date")
+
+ data = []
+
+ if not filters.get("shareholder"):
+ pass
+ else:
+ share_type, no_of_shares, rate, amount = 1, 2, 3, 4
+
+ all_shares = get_all_shares(filters.get("shareholder"))
+ for share_entry in all_shares:
+ row = False
+ for datum in data:
+ if datum[share_type] == share_entry.share_type:
+ datum[no_of_shares] += share_entry.no_of_shares
+ datum[amount] += share_entry.amount
+ if datum[no_of_shares] == 0:
+ datum[rate] = 0
+ else:
+ datum[rate] = datum[amount] / datum[no_of_shares]
+ row = True
+ break
+ # new entry
+ if not row:
+ row = [filters.get("shareholder"),
+ share_entry.share_type, share_entry.no_of_shares, share_entry.rate, share_entry.amount]
+
+ data.append(row)
+
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ _("Shareholder") + ":Link/Shareholder:150",
+ _("Share Type") + "::90",
+ _("No of Shares") + "::90",
+ _("Average Rate") + ":Currency:90",
+ _("Amount") + ":Currency:90"
+ ]
+ return columns
+
+def get_all_shares(shareholder):
+ return frappe.get_doc('Shareholder', shareholder).share_balance
diff --git a/erpnext/accounts/report/share_ledger/__init__.py b/erpnext/accounts/report/share_ledger/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/__init__.py
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.js b/erpnext/accounts/report/share_ledger/share_ledger.js
new file mode 100644
index 0000000..6d1c44a
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/share_ledger.js
@@ -0,0 +1,22 @@
+// -*- coding: utf-8 -*-
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Share Ledger"] = {
+ "filters": [
+ {
+ "fieldname":"date",
+ "label": __("Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.get_today(),
+ "reqd": 1
+ },
+ {
+ "fieldname":"shareholder",
+ "label": __("Shareholder"),
+ "fieldtype": "Link",
+ "options": "Shareholder"
+ }
+ ]
+};
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.json b/erpnext/accounts/report/share_ledger/share_ledger.json
new file mode 100644
index 0000000..d374bb7
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/share_ledger.json
@@ -0,0 +1,23 @@
+{
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2017-12-27 16:15:52.615453",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2017-12-27 16:46:54.422356",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Ledger",
+ "owner": "Administrator",
+ "ref_doctype": "Share Transfer",
+ "report_name": "Share Ledger",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "Administrator"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/share_ledger/share_ledger.py b/erpnext/accounts/report/share_ledger/share_ledger.py
new file mode 100644
index 0000000..3ed3c91
--- /dev/null
+++ b/erpnext/accounts/report/share_ledger/share_ledger.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import cstr, cint, getdate
+from frappe import msgprint, _
+
+def execute(filters=None):
+ if not filters: filters = {}
+
+ if not filters.get("date"):
+ frappe.throw(_("Please select date"))
+
+ columns = get_columns(filters)
+
+ date = filters.get("date")
+
+ data = []
+
+ if not filters.get("shareholder"):
+ pass
+ else:
+ transfers = get_all_transfers(date, filters.get("shareholder"))
+ for transfer in transfers:
+ if transfer.transfer_type == 'Transfer':
+ if transfer.from_shareholder == filters.get("shareholder"):
+ transfer.transfer_type += ' to {}'.format(transfer.to_shareholder)
+ else:
+ transfer.transfer_type += ' from {}'.format(transfer.from_shareholder)
+ row = [filters.get("shareholder"), transfer.date, transfer.transfer_type,
+ transfer.share_type, transfer.no_of_shares, transfer.rate, transfer.amount,
+ transfer.company, transfer.name]
+
+ data.append(row)
+
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ _("Shareholder") + ":Link/Shareholder:150",
+ _("Date") + ":Date:100",
+ _("Transfer Type") + "::140",
+ _("Share Type") + "::90",
+ _("No of Shares") + "::90",
+ _("Rate") + ":Currency:90",
+ _("Amount") + ":Currency:90",
+ _("Company") + "::150",
+ _("Share Transfer") + ":Link/Share Transfer:90"
+ ]
+ return columns
+
+def get_all_transfers(date, shareholder):
+ condition = ' '
+ # if company:
+ # condition = 'AND company = %(company)s '
+ return frappe.db.sql("""SELECT * FROM `tabShare Transfer`
+ WHERE (DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition})
+ OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition})
+ ORDER BY date""".format(condition=condition),
+ {'date': date, 'shareholder': shareholder}, as_dict=1)
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
new file mode 100644
index 0000000..4490398
--- /dev/null
+++ b/erpnext/accounts/report/utils.py
@@ -0,0 +1,128 @@
+import frappe
+from erpnext import get_company_currency, get_default_company
+from erpnext.setup.utils import get_exchange_rate
+from frappe.utils import cint
+
+__exchange_rates = {}
+P_OR_L_ACCOUNTS = list(
+ sum(frappe.get_list('Account', fields=['name'], or_filters=[{'root_type': 'Income'}, {'root_type': 'Expense'}], as_list=True), ())
+)
+
+
+def get_currency(filters):
+ """
+ Returns a dictionary containing currency information. The keys of the dict are
+ - company: The company for which we are fetching currency information. if no
+ company is specified, it will fallback to the default company.
+ - company currency: The functional currency of the said company.
+ - presentation currency: The presentation currency to use. Only currencies that
+ have been used for transactions will be allowed.
+ - report date: The report date.
+ :param filters: Report filters
+ :type filters: dict
+
+ :return: str - Currency
+ """
+ company = get_appropriate_company(filters)
+ company_currency = get_company_currency(company)
+ presentation_currency = filters['presentation_currency'] if filters.get('presentation_currency') else company_currency
+ report_date = filters.get('to_date') or filters.get('to_fiscal_year')
+
+ currency_map = dict(company=company, company_currency=company_currency, presentation_currency=presentation_currency, report_date=report_date)
+
+ return currency_map
+
+
+def convert(value, from_, to, date):
+ """
+ convert `value` from `from_` to `to` on `date`
+ :param value: Amount to be converted
+ :param from_: Currency of `value`
+ :param to: Currency to convert to
+ :param date: exchange rate as at this date
+ :return: Result of converting `value`
+ """
+ rate = get_rate_as_at(date, from_, to)
+ converted_value = value / (rate or 1)
+ return converted_value
+
+
+def get_rate_as_at(date, from_currency, to_currency):
+ """
+ Gets exchange rate as at `date` for `from_currency` - `to_currency` exchange rate.
+ This calls `get_exchange_rate` so that we can get the correct exchange rate as per
+ the user's Accounts Settings.
+ It is made efficient by memoising results to `__exchange_rates`
+ :param date: exchange rate as at this date
+ :param from_currency: Base currency
+ :param to_currency: Quote currency
+ :return: Retrieved exchange rate
+ """
+ rate = __exchange_rates.get('{0}-{1}@{2}'.format(from_currency, to_currency, date))
+ if not rate:
+ rate = get_exchange_rate(from_currency, to_currency, date) or 1
+ __exchange_rates['{0}-{1}@{2}'.format(from_currency, to_currency, date)] = rate
+
+ return rate
+
+
+def is_p_or_l_account(account_name):
+ """
+ Check if the given `account name` is an `Account` with `root_type` of either 'Income'
+ or 'Expense'.
+ :param account_name:
+ :return: Boolean
+ """
+ return account_name in P_OR_L_ACCOUNTS
+
+
+def convert_to_presentation_currency(gl_entries, currency_info):
+ """
+ Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
+ in `currency_info`.
+ :param gl_entries:
+ :param currency_info:
+ :return:
+ """
+ converted_gl_list = []
+ presentation_currency = currency_info['presentation_currency']
+ company_currency = currency_info['company_currency']
+
+ for entry in gl_entries:
+ account = entry['account']
+ debit = cint(entry['debit'])
+ credit = cint(entry['credit'])
+ debit_in_account_currency = cint(entry['debit_in_account_currency'])
+ credit_in_account_currency = cint(entry['credit_in_account_currency'])
+ account_currency = entry['account_currency']
+
+ if account_currency != presentation_currency or (account_currency == presentation_currency and not is_p_or_l_account(account)):
+ value = debit or credit
+
+ date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date']
+
+ converted_value = convert(value, presentation_currency, company_currency, date)
+
+ if entry.get('debit'):
+ entry['debit'] = converted_value
+ else:
+ entry['credit'] = converted_value
+
+ elif account_currency == presentation_currency:
+ if entry.get('debit'):
+ entry['debit'] = debit_in_account_currency
+ else:
+ entry['credit'] = credit_in_account_currency
+
+ converted_gl_list.append(entry)
+
+ return converted_gl_list
+
+
+def get_appropriate_company(filters):
+ if filters.get('company'):
+ company = filters['company']
+ else:
+ company = get_default_company()
+
+ return company
diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py
index 9a71608..088a119 100644
--- a/erpnext/config/accounts.py
+++ b/erpnext/config/accounts.py
@@ -457,6 +457,34 @@
]
},
{
+ "label": _("Share Management"),
+ "icon": "fa fa-microchip ",
+ "items": [
+ {
+ "type": "doctype",
+ "name":"Shareholder",
+ "description": _("List of available Shareholders with folio numbers")
+ },
+ {
+ "type": "doctype",
+ "name":"Share Transfer",
+ "description": _("List of all share transactions"),
+ },
+ {
+ "type": "report",
+ "name": "Share Ledger",
+ "doctype": "Share Transfer",
+ "is_query_report": True
+ },
+ {
+ "type": "report",
+ "name": "Share Balance",
+ "doctype": "Share Transfer",
+ "is_query_report": True
+ }
+ ]
+ },
+ {
"label": _("Help"),
"icon": "fa fa-facetime-video",
"items": [
diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py
index 7556083..3c18844 100644
--- a/erpnext/config/desktop.py
+++ b/erpnext/config/desktop.py
@@ -360,6 +360,14 @@
"hidden": 1
},
{
+ "module_name": "Hotels",
+ "color": "#EA81E8",
+ "icon": "fa fa-bed",
+ "type": "module",
+ "label": _("Hotels"),
+ "hidden": 1
+ },
+ {
"module_name": "Agriculture",
"color": "#8BC34A",
"icon": "octicon octicon-globe",
diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py
index 37d95ad..34a9b70 100644
--- a/erpnext/config/healthcare.py
+++ b/erpnext/config/healthcare.py
@@ -131,27 +131,27 @@
{
"type": "doctype",
"name": "Lab Test Sample",
- "label": _("Test Sample."),
+ "label": _("Lab Test Sample"),
},
{
"type": "doctype",
"name": "Lab Test UOM",
- "label": _("Lab Test UOM.")
+ "label": _("Lab Test UOM")
},
{
"type": "doctype",
"name": "Antibiotic",
- "label": _("Antibiotic.")
+ "label": _("Antibiotic")
},
{
"type": "doctype",
"name": "Sensitivity",
- "label": _("Sensitivity Naming.")
+ "label": _("Sensitivity")
},
{
"type": "doctype",
"name": "Lab Test Template",
- "label": _("Lab Test Template.")
+ "label": _("Lab Test Template")
}
]
}
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index b3672cb..b7017c1 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -12,7 +12,7 @@
from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
from erpnext.exceptions import InvalidCurrency
-force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
+force_item_fields = ("item_group", "brand", "stock_uom")
class AccountsController(TransactionBase):
def __init__(self, *args, **kwargs):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 4b7b43c..b141455 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -218,7 +218,7 @@
if not exists:
rm = self.append(raw_material_table, {})
- required_qty = flt(flt(bom_item.qty_consumed_per_unit) * flt(item.qty) *
+ required_qty = flt(flt(bom_item.qty_consumed_per_unit) * (flt(item.qty) + getattr(item, 'rejected_qty', 0)) *
flt(item.conversion_factor), rm.precision("required_qty"))
rm.reference_name = item.name
rm.bom_detail_no = bom_item.name
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index fd7bdc7..9a93633 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -165,8 +165,8 @@
and (tabItem.`{key}` LIKE %(txt)s
or tabItem.item_group LIKE %(txt)s
or tabItem.item_name LIKE %(txt)s
- or tabItem.barcode LIKE %(txt)s
or tabItem.description LIKE %(txt)s)
+ or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
{fcond} {mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@@ -270,13 +270,14 @@
}
if args.get('warehouse'):
- batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom, batch.expiry_date
+ batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom, concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
from `tabStock Ledger Entry` sle
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
where
sle.item_code = %(item_code)s
and sle.warehouse = %(warehouse)s
- and sle.batch_no like %(txt)s
+ and (sle.batch_no like %(txt)s
+ or batch.manufacturing_date like %(txt)s)
and batch.docstatus < 2
{0}
{match_conditions}
@@ -287,9 +288,10 @@
if batch_nos:
return batch_nos
else:
- return frappe.db.sql("""select name, expiry_date from `tabBatch` batch
+ return frappe.db.sql("""select name, concat('MFG-', manufacturing_date), concat('EXP-',expiry_date) from `tabBatch` batch
where item = %(item_code)s
- and name like %(txt)s
+ and (name like %(txt)s
+ or manufacturing_date like %(txt)s)
and docstatus < 2
{0}
{match_conditions}
diff --git a/erpnext/demo/user/hr.py b/erpnext/demo/user/hr.py
index e59c3ee..d61aa4e 100644
--- a/erpnext/demo/user/hr.py
+++ b/erpnext/demo/user/hr.py
@@ -172,7 +172,6 @@
"from_date": frappe.flags.current_date,
"to_date": to_date,
"leave_type": allocated_leave.leave_type,
- "status": "Approved"
})
try:
leave_application.insert()
@@ -191,8 +190,9 @@
"employee": employee.name,
"attendance_date": attendance_date
})
+
leave = frappe.db.sql("""select name from `tabLeave Application`
- where employee = %s and %s between from_date and to_date and workflow_state = 'Approved'
+ where employee = %s and %s between from_date and to_date
and docstatus = 1""", (employee.name, attendance_date))
if leave:
diff --git a/erpnext/docs/assets/img/accounts/shareholder/sharebalance_1.png b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_1.png
new file mode 100644
index 0000000..3e22324
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/sharebalance_2.png b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_2.png
new file mode 100644
index 0000000..c33cae8
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/sharebalance_2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/shareholder_tonystark.png b/erpnext/docs/assets/img/accounts/shareholder/shareholder_tonystark.png
new file mode 100644
index 0000000..ee05b20
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/shareholder_tonystark.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/shareledger_1.png b/erpnext/docs/assets/img/accounts/shareholder/shareledger_1.png
new file mode 100644
index 0000000..a7cae31
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/shareledger_1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/shareledger_2.png b/erpnext/docs/assets/img/accounts/shareholder/shareledger_2.png
new file mode 100644
index 0000000..1d14ecc
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/shareledger_2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png b/erpnext/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png
new file mode 100644
index 0000000..535353f
--- /dev/null
+++ b/erpnext/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png
Binary files differ
diff --git a/erpnext/docs/assets/img/hotels/hotel-room.png b/erpnext/docs/assets/img/hotels/hotel-room.png
new file mode 100644
index 0000000..7aad227
--- /dev/null
+++ b/erpnext/docs/assets/img/hotels/hotel-room.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/accounts/share/share_balance.md b/erpnext/docs/user/manual/en/accounts/share/share_balance.md
new file mode 100644
index 0000000..2e66036
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/share_balance.md
@@ -0,0 +1,9 @@
+# Share Balance
+
+This is a report view which gives the list of all the shares held by a given Shareholder and its value
+
+> Accounts > Share Balance
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/sharebalance_1.png">
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/sharebalance_2.png">
diff --git a/erpnext/docs/user/manual/en/accounts/share/share_ledger.md b/erpnext/docs/user/manual/en/accounts/share/share_ledger.md
new file mode 100644
index 0000000..05b85b1
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/share_ledger.md
@@ -0,0 +1,9 @@
+# Share Ledger
+
+This is a report view which gives the list of all the transactions made by a given Shareholder
+
+> Accounts > Share Ledger
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/shareledger_1.png">
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/shareledger_2.png">
diff --git a/erpnext/docs/user/manual/en/accounts/share/share_transfer.md b/erpnext/docs/user/manual/en/accounts/share/share_transfer.md
new file mode 100644
index 0000000..fc1207b
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/share_transfer.md
@@ -0,0 +1,9 @@
+# Shareholder Transfer
+
+There may be times when you want to change the share structure of your company; either by adding a new shareholder or by changing the existing proportion of shares between shareholders. A share transfer is the process of transferring existing shares from one person to another; either by sale or gift.
+
+You can directly create your Shareholders via
+
+> Accounts > Share Transfer
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/sharetransfer_issue_tonystark.png">
diff --git a/erpnext/docs/user/manual/en/accounts/share/shareholder.md b/erpnext/docs/user/manual/en/accounts/share/shareholder.md
new file mode 100644
index 0000000..dfa065d
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/share/shareholder.md
@@ -0,0 +1,21 @@
+# Shareholder
+
+A shareholder is any person, company or other institution that owns at least one share of a company’s stock. Because shareholders are a company's owners, they reap the benefits of the company's successes in the form of increased stock valuation. If the company does poorly, however, shareholders can lose money if the price of its stock declines.
+
+A shareholder is uniquely identified by the Shareholder ID. Normally this ID is a naming series, starting with `SH-`. Also as soon as the Shareholder makes even a single transaction, a Folio number is allocated to him. This also is a unique to the Shareholder.
+
+You can directly create your Shareholders via
+
+> Accounts > Shareholder
+
+<img class="screenshot" alt="Create Shareholder" src="/docs/assets/img/accounts/shareholder/shareholder_tonystark.png">
+
+A Shareholder can avail the features (operations) in the [Share Transfer](/docs/user/manual/en/accounts/share/share_transfer.html) process.
+
+> Note: Shareholders are separate from Contacts and Addresses. A Shareholder can
+have multiple Contacts and Addresses.
+
+### Contacts and Addresses
+
+[Contacts and Addresses](/docs/user/manual/en/CRM/contact.html) in ERPNext are stored separately so that you can
+attach multiple Contacts or Addresses to Shareholders, Customers and Suppliers
\ No newline at end of file
diff --git a/erpnext/docs/user/manual/en/hospitality/hotel-room.md b/erpnext/docs/user/manual/en/hospitality/hotel-room.md
new file mode 100644
index 0000000..b788d1b
--- /dev/null
+++ b/erpnext/docs/user/manual/en/hospitality/hotel-room.md
@@ -0,0 +1,5 @@
+# Hotel Room
+
+Hotel Room is a master to create hotel rooms for reservation
+
+<img class="screenshot" alt="Hotel Room" src="/docs/assets/img/hotels/hotel-room.png">
diff --git a/erpnext/docs/user/manual/en/hospitality/index.md b/erpnext/docs/user/manual/en/hospitality/index.md
index efd7377..dc6c743 100644
--- a/erpnext/docs/user/manual/en/hospitality/index.md
+++ b/erpnext/docs/user/manual/en/hospitality/index.md
@@ -6,4 +6,8 @@
The Restaurant module in ERPNext will help you manage a chain of restaurants. You can create Restaurants, Menus, Tables, Reservations and a manage Order Entry and Billing.
+### Manage Hotels
+
+The Hotels module in ERPNext will help you manage creating Hotel Rooms, create Hotel Room Reservation. It will also help in creating Invoice from hotel room reservation
+
{index}
\ No newline at end of file
diff --git a/erpnext/docs/user/manual/en/hospitality/index.txt b/erpnext/docs/user/manual/en/hospitality/index.txt
index cbe6da0..0c909d8 100644
--- a/erpnext/docs/user/manual/en/hospitality/index.txt
+++ b/erpnext/docs/user/manual/en/hospitality/index.txt
@@ -1,4 +1,5 @@
restaurant
restaurant-menu
reservations
-order-entry
\ No newline at end of file
+order-entry
+hotel-room
diff --git a/erpnext/domains/hospitality.py b/erpnext/domains/hospitality.py
index bc55d9c..09b98c2 100644
--- a/erpnext/domains/hospitality.py
+++ b/erpnext/domains/hospitality.py
@@ -1,6 +1,7 @@
data = {
'desktop_icons': [
'Restaurant',
+ 'Hotels',
'Accounts',
'Buying',
'Stock',
@@ -9,7 +10,9 @@
'ToDo'
],
'restricted_roles': [
- 'Restaurant Manager'
+ 'Restaurant Manager',
+ 'Hotel Manager',
+ 'Hotel Reservation User'
],
'custom_fields': {
'Sales Invoice': [
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index 0a96820..90d4d0e 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -103,7 +103,7 @@
# Check if He/She on Leave
leave_record = frappe.db.sql("""select half_day from `tabLeave Application`
- where employee = %s and %s between from_date and to_date and status = 'Approved'
+ where employee = %s and %s between from_date and to_date
and docstatus = 1""", (employee, date), as_dict=True)
if leave_record:
if leave_record[0].half_day:
diff --git a/erpnext/healthcare/doctype/physician/physician.js b/erpnext/healthcare/doctype/physician/physician.js
index c607f23..c224b5d 100755
--- a/erpnext/healthcare/doctype/physician/physician.js
+++ b/erpnext/healthcare/doctype/physician/physician.js
@@ -31,21 +31,26 @@
name: frm.doc.user_id
},
callback: function (data) {
- if(!frm.doc.employee){
- frappe.model.get_value('Employee', {'user_id': frm.doc.user_id}, 'name',
- function(data) {
- if(data)
+
+ frappe.model.get_value('Employee', {'user_id': frm.doc.user_id}, 'name',
+ function(data) {
+ if(data){
+ if(!frm.doc.employee || frm.doc.employee != data.name)
frappe.model.set_value(frm.doctype,frm.docname, "employee", data.name);
- });
- }
- if(!frm.doc.first_name)
+ }else{
+ frappe.model.set_value(frm.doctype,frm.docname, "employee", "");
+ }
+ }
+ );
+
+ if(!frm.doc.first_name || frm.doc.first_name != data.message.first_name)
frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.first_name);
- if(!frm.doc.middle_name)
+ if(!frm.doc.middle_name || frm.doc.middle_name != data.message.middle_name)
frappe.model.set_value(frm.doctype,frm.docname, "middle_name", data.message.middle_name);
- if(!frm.doc.last_name)
+ if(!frm.doc.last_name || frm.doc.last_name != data.message.last_name)
frappe.model.set_value(frm.doctype,frm.docname, "last_name", data.message.last_name);
- if(!frm.doc.mobile_phone)
- frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.phone);
+ if(!frm.doc.mobile_phone || frm.doc.mobile_phone != data.message.mobile_no)
+ frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.mobile_no);
}
});
}
@@ -60,13 +65,18 @@
name: frm.doc.employee
},
callback: function (data) {
- if(!frm.doc.designation)
+ if(!frm.doc.user_id || frm.doc.user_id != data.message.user_id)
+ frm.set_value("user_id", data.message.user_id);
+ if(!frm.doc.designation || frm.doc.designation != data.message.designation)
frappe.model.set_value(frm.doctype,frm.docname, "designation", data.message.designation);
- if(!frm.doc.first_name)
+ if(!frm.doc.first_name || !frm.doc.user_id){
frappe.model.set_value(frm.doctype,frm.docname, "first_name", data.message.employee_name);
- if(!frm.doc.mobile_phone)
+ frappe.model.set_value(frm.doctype,frm.docname, "middle_name", "");
+ frappe.model.set_value(frm.doctype,frm.docname, "last_name", "");
+ }
+ if(!frm.doc.mobile_phone || !frm.doc.user_id)
frappe.model.set_value(frm.doctype,frm.docname, "mobile_phone", data.message.cell_number);
- if(!frm.doc.address)
+ if(!frm.doc.address || frm.doc.address != data.message.current_address)
frappe.model.set_value(frm.doctype,frm.docname, "address", data.message.current_address);
}
});
diff --git a/erpnext/healthcare/doctype/physician/physician.json b/erpnext/healthcare/doctype/physician/physician.json
index 3edad0b..1d9794e 100644
--- a/erpnext/healthcare/doctype/physician/physician.json
+++ b/erpnext/healthcare/doctype/physician/physician.json
@@ -155,7 +155,7 @@
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 1,
+ "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -810,8 +810,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-10-05 16:08:24.624644",
- "modified_by": "Administrator",
+ "modified": "2018-01-19 15:25:43.166877",
+ "modified_by": "jams@hcf.com",
"module": "Healthcare",
"name": "Physician",
"name_case": "",
diff --git a/erpnext/hotels/__init__.py b/erpnext/hotels/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/__init__.py
diff --git a/erpnext/hotels/doctype/__init__.py b/erpnext/hotels/doctype/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room/__init__.py b/erpnext/hotels/doctype/hotel_room/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room/hotel_room.js b/erpnext/hotels/doctype/hotel_room/hotel_room.js
new file mode 100644
index 0000000..76f22d5
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room/hotel_room.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Room', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_room/hotel_room.json b/erpnext/hotels/doctype/hotel_room/hotel_room.json
new file mode 100644
index 0000000..2567c07
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room/hotel_room.json
@@ -0,0 +1,175 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "prompt",
+ "beta": 1,
+ "creation": "2017-12-08 12:33:56.320420",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "hotel_room_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Hotel Room Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "capacity",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Capacity",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "extra_bed_capacity",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Extra Bed Capacity",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:10:50.670113",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Hotel Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room/hotel_room.py b/erpnext/hotels/doctype/hotel_room/hotel_room.py
new file mode 100644
index 0000000..8471aee
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room/hotel_room.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoom(Document):
+ def validate(self):
+ if not self.capacity:
+ self.capacity, self.extra_bed_capacity = frappe.db.get_value('Hotel Room Type',
+ self.hotel_room_type, ['capacity', 'extra_bed_capacity'])
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room/test_hotel_room.js b/erpnext/hotels/doctype/hotel_room/test_hotel_room.js
new file mode 100644
index 0000000..8b2b833
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room/test_hotel_room.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Room", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Room
+ () => frappe.tests.make('Hotel Room', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_room/test_hotel_room.py b/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
new file mode 100644
index 0000000..00d3aea
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room/test_hotel_room.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+test_records = [
+ dict(doctype="Hotel Room", name="1001",
+ hotel_room_type="Basic Room"),
+ dict(doctype="Hotel Room", name="1002",
+ hotel_room_type="Basic Room"),
+ dict(doctype="Hotel Room", name="1003",
+ hotel_room_type="Basic Room"),
+ dict(doctype="Hotel Room", name="1004",
+ hotel_room_type="Basic Room"),
+ dict(doctype="Hotel Room", name="1005",
+ hotel_room_type="Basic Room"),
+ dict(doctype="Hotel Room", name="1006",
+ hotel_room_type="Basic Room")
+]
+
+class TestHotelRoom(unittest.TestCase):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_amenity/__init__.py b/erpnext/hotels/doctype/hotel_room_amenity/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_amenity/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.json b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.json
new file mode 100644
index 0000000..29a0407
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.json
@@ -0,0 +1,103 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2017-12-08 12:35:36.572185",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "item",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Item",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "billable",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Billable",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:05:07.125687",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Amenity",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
new file mode 100644
index 0000000..69da007
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_amenity/hotel_room_amenity.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomAmenity(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_package/__init__.py b/erpnext/hotels/doctype/hotel_room_package/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_package/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.js b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.js
new file mode 100644
index 0000000..5b09ae5
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.js
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Room Package', {
+ hotel_room_type: function(frm) {
+ if (frm.doc.hotel_room_type) {
+ frappe.model.with_doc('Hotel Room Type', frm.doc.hotel_room_type, () => {
+ let hotel_room_type = frappe.get_doc('Hotel Room Type', frm.doc.hotel_room_type);
+
+ // reset the amenities
+ frm.doc.amenities = [];
+
+ for (let amenity of hotel_room_type.amenities) {
+ let d = frm.add_child('amenities');
+ d.item = amenity.item;
+ d.billable = amenity.billable;
+ }
+
+ frm.refresh();
+ });
+ }
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.json b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.json
new file mode 100644
index 0000000..57dad44
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.json
@@ -0,0 +1,215 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "prompt",
+ "beta": 1,
+ "creation": "2017-12-08 12:43:17.211064",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "hotel_room_type",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Hotel Room Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Type",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "item",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Item",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amenities",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amenities",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Amenity",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:10:31.111952",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Package",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
new file mode 100644
index 0000000..8a62eea
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_package/hotel_room_package.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomPackage(Document):
+ def validate(self):
+ if not self.item:
+ item = frappe.get_doc(dict(
+ doctype = 'Item',
+ item_code = self.name,
+ item_group = 'Products',
+ is_stock_item = 0,
+ stock_uom = 'Unit'
+ ))
+ item.insert()
+ self.item = item.name
diff --git a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js
new file mode 100644
index 0000000..f1ebad4
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Room Package", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Room Package
+ () => frappe.tests.make('Hotel Room Package', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py
new file mode 100644
index 0000000..ebf7f2b
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_package/test_hotel_room_package.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+test_records = [
+ dict(doctype='Item', item_code='Breakfast',
+ item_group='Products', is_stock_item=0),
+ dict(doctype='Item', item_code='Lunch',
+ item_group='Products', is_stock_item=0),
+ dict(doctype='Item', item_code='Dinner',
+ item_group='Products', is_stock_item=0),
+ dict(doctype='Item', item_code='WiFi',
+ item_group='Products', is_stock_item=0),
+ dict(doctype='Hotel Room Type', name="Delux Room",
+ capacity=4,
+ extra_bed_capacity=2,
+ amenities = [
+ dict(item='WiFi', billable=0)
+ ]),
+ dict(doctype='Hotel Room Type', name="Basic Room",
+ capacity=4,
+ extra_bed_capacity=2,
+ amenities = [
+ dict(item='Breakfast', billable=0)
+ ]),
+ dict(doctype="Hotel Room Package", name="Basic Room with Breakfast",
+ hotel_room_type="Basic Room",
+ amenities = [
+ dict(item="Breakfast", billable=0)
+ ]),
+ dict(doctype="Hotel Room Package", name="Basic Room with Lunch",
+ hotel_room_type="Basic Room",
+ amenities = [
+ dict(item="Breakfast", billable=0),
+ dict(item="Lunch", billable=0)
+ ]),
+ dict(doctype="Hotel Room Package", name="Basic Room with Dinner",
+ hotel_room_type="Basic Room",
+ amenities = [
+ dict(item="Breakfast", billable=0),
+ dict(item="Dinner", billable=0)
+ ])
+]
+
+class TestHotelRoomPackage(unittest.TestCase):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/__init__.py b/erpnext/hotels/doctype/hotel_room_pricing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.js b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.js
new file mode 100644
index 0000000..87bb192
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Room Pricing', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.json b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.json
new file mode 100644
index 0000000..0f5a776
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.json
@@ -0,0 +1,266 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "prompt",
+ "beta": 1,
+ "creation": "2017-12-08 12:51:47.088174",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "1",
+ "fieldname": "enabled",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Enabled",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Items",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Pricing Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:10:41.559559",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Pricing",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Hotel Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
new file mode 100644
index 0000000..8eee0f2
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing/hotel_room_pricing.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomPricing(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js
new file mode 100644
index 0000000..ba0d1fd
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Room Pricing", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Room Pricing
+ () => frappe.tests.make('Hotel Room Pricing', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py
new file mode 100644
index 0000000..2b7848b
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing/test_hotel_room_pricing.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+test_records = [
+ dict(doctype="Hotel Room Pricing", enabled=1,
+ name="Winter 2017",
+ from_date="2017-01-01", to_date="2017-01-10",
+ items = [
+ dict(item="Basic Room with Breakfast", rate=10000),
+ dict(item="Basic Room with Lunch", rate=11000),
+ dict(item="Basic Room with Dinner", rate=12000)
+ ])
+]
+
+class TestHotelRoomPricing(unittest.TestCase):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_item/__init__.py b/erpnext/hotels/doctype/hotel_room_pricing_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_item/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.json b/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.json
new file mode 100644
index 0000000..d6cd826
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.json
@@ -0,0 +1,103 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2017-12-08 12:50:13.486090",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "item",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Item",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:04:58.641703",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Pricing Item",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py b/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py
new file mode 100644
index 0000000..6bf01bf
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_item/hotel_room_pricing_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomPricingItem(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/__init__.py b/erpnext/hotels/doctype/hotel_room_pricing_package/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.js b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.js
new file mode 100644
index 0000000..f6decd9
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Room Pricing Package', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.json b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.json
new file mode 100644
index 0000000..92bd980
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.json
@@ -0,0 +1,162 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2017-12-08 12:50:13.486090",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "From Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "To Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "hotel_room_package",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Hotel Room Package",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Package",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-12-08 12:52:01.743866",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Pricing Package",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py
new file mode 100644
index 0000000..9ae9fcf
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/hotel_room_pricing_package.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomPricingPackage(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js
new file mode 100644
index 0000000..73a561c
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Room Pricing Package", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Room Pricing Package
+ () => frappe.tests.make('Hotel Room Pricing Package', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py
new file mode 100644
index 0000000..fec1c86
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_pricing_package/test_hotel_room_pricing_package.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestHotelRoomPricingPackage(unittest.TestCase):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/__init__.py b/erpnext/hotels/doctype/hotel_room_reservation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.js b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.js
new file mode 100644
index 0000000..2c9fd7b
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.js
@@ -0,0 +1,68 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Room Reservation', {
+ refresh: function(frm) {
+ if(frm.doc.docstatus == 1){
+ frm.add_custom_button(__("Make Invoice"), ()=> {
+ frm.trigger("make_invoice");
+ });
+ }
+ },
+ from_date: function(frm) {
+ frm.trigger("recalculate_rates");
+ },
+ to_date: function(frm) {
+ frm.trigger("recalculate_rates");
+ },
+ recalculate_rates: function(frm) {
+ if (!frm.doc.from_date || !frm.doc.to_date
+ || !frm.doc.items.length){
+ return;
+ }
+ frappe.call({
+ "method": "erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation.get_room_rate",
+ "args": {"hotel_room_reservation": frm.doc}
+ }).done((r)=> {
+ for (var i = 0; i < r.message.items.length; i++) {
+ frm.doc.items[i].rate = r.message.items[i].rate;
+ frm.doc.items[i].amount = r.message.items[i].amount;
+ }
+ frappe.run_serially([
+ ()=> frm.set_value("net_total", r.message.net_total),
+ ()=> frm.refresh_field("items")
+ ]);
+ });
+ },
+ make_invoice: function(frm) {
+ frappe.model.with_doc("Hotel Settings", "Hotel Settings", ()=>{
+ frappe.model.with_doctype("Sales Invoice", ()=>{
+ let hotel_settings = frappe.get_doc("Hotel Settings", "Hotel Settings");
+ let invoice = frappe.model.get_new_doc("Sales Invoice");
+ invoice.customer = frm.doc.customer || hotel_settings.default_customer;
+ if (hotel_settings.default_invoice_naming_series){
+ invoice.naming_series = hotel_settings.default_invoice_naming_series;
+ }
+ for (let d of frm.doc.items){
+ let invoice_item = frappe.model.add_child(invoice, "items")
+ invoice_item.item_code = d.item;
+ invoice_item.qty = d.qty;
+ invoice_item.rate = d.rate;
+ }
+ if (hotel_settings.default_taxes_and_charges){
+ invoice.taxes_and_charges = hotel_settings.default_taxes_and_charges;
+ }
+ frappe.set_route("Form", invoice.doctype, invoice.name);
+ });
+ });
+ }
+});
+
+frappe.ui.form.on('Hotel Room Reservation Item', {
+ item: function(frm, doctype, name) {
+ frm.trigger("recalculate_rates");
+ },
+ qty: function(frm) {
+ frm.trigger("recalculate_rates");
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.json b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.json
new file mode 100644
index 0000000..c65c4e1
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.json
@@ -0,0 +1,415 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "HRES.#######",
+ "beta": 1,
+ "creation": "2017-12-08 13:01:34.829175",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "guest_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Guest Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Customer",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Customer",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "From Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "To Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "late_checkin",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Late Checkin",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_6",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Booked\nAdvance Paid\nInvoiced\nPaid\nCompleted\nCancelled",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Items",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Reservation Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "net_total",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Net Total",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amended From",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Hotel Room Reservation",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 1,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:11:26.395419",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Reservation",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Hotel Reservation User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
new file mode 100644
index 0000000..f3f76a9
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe, json
+from frappe.model.document import Document
+from frappe import _
+from frappe.utils import date_diff, add_days, flt
+
+class HotelRoomUnavailableError(frappe.ValidationError): pass
+class HotelRoomPricingNotSetError(frappe.ValidationError): pass
+
+class HotelRoomReservation(Document):
+ def validate(self):
+ self.total_rooms = {}
+ self.set_rates()
+ self.validate_availability()
+
+ def validate_availability(self):
+ for i in xrange(date_diff(self.to_date, self.from_date)):
+ day = add_days(self.from_date, i)
+ self.rooms_booked = {}
+
+ for d in self.items:
+ if not d.item in self.rooms_booked:
+ self.rooms_booked[d.item] = 0
+
+ room_type = frappe.db.get_value("Hotel Room Package",
+ d.item, 'hotel_room_type')
+ rooms_booked = get_rooms_booked(room_type, day, exclude_reservation=self.name) \
+ + d.qty + self.rooms_booked.get(d.item)
+ total_rooms = self.get_total_rooms(d.item)
+ if total_rooms < rooms_booked:
+ frappe.throw(_("Hotel Rooms of type {0} are unavailable on {1}".format(d.item,
+ frappe.format(day, dict(fieldtype="Date")))), exc=HotelRoomUnavailableError)
+
+ self.rooms_booked[d.item] += rooms_booked
+
+ def get_total_rooms(self, item):
+ if not item in self.total_rooms:
+ self.total_rooms[item] = frappe.db.sql("""
+ select count(*)
+ from
+ `tabHotel Room Package` package
+ inner join
+ `tabHotel Room` room on package.hotel_room_type = room.hotel_room_type
+ where
+ package.item = %s""", item)[0][0] or 0
+
+ return self.total_rooms[item]
+
+ def set_rates(self):
+ self.net_total = 0
+ for d in self.items:
+ net_rate = 0.0
+ for i in xrange(date_diff(self.to_date, self.from_date)):
+ day = add_days(self.from_date, i)
+ if not d.item:
+ continue
+ day_rate = frappe.db.sql("""
+ select
+ item.rate
+ from
+ `tabHotel Room Pricing Item` item,
+ `tabHotel Room Pricing` pricing
+ where
+ item.parent = pricing.name
+ and item.item = %s
+ and %s between pricing.from_date
+ and pricing.to_date""", (d.item, day))
+
+ if day_rate:
+ net_rate += day_rate[0][0]
+ else:
+ frappe.throw(
+ _("Please set Hotel Room Rate on {}".format(
+ frappe.format(day, dict(fieldtype="Date")))), exc=HotelRoomPricingNotSetError)
+ d.rate = net_rate
+ d.amount = net_rate * flt(d.qty)
+ self.net_total += d.amount
+
+@frappe.whitelist()
+def get_room_rate(hotel_room_reservation):
+ """Calculate rate for each day as it may belong to different Hotel Room Pricing Item"""
+ doc = frappe.get_doc(json.loads(hotel_room_reservation))
+ doc.set_rates()
+ return doc.as_dict()
+
+def get_rooms_booked(room_type, day, exclude_reservation=None):
+ exclude_condition = ''
+ if exclude_reservation:
+ exclude_condition = 'and reservation.name != "{0}"'.format(frappe.db.escape(exclude_reservation))
+
+ return frappe.db.sql("""
+ select sum(item.qty)
+ from
+ `tabHotel Room Package` room_package,
+ `tabHotel Room Reservation Item` item,
+ `tabHotel Room Reservation` reservation
+ where
+ item.parent = reservation.name
+ and room_package.item = item.item
+ and room_package.hotel_room_type = %s
+ and reservation.docstatus = 1
+ {exclude_condition}
+ and %s between reservation.from_date
+ and reservation.to_date""".format(exclude_condition=exclude_condition),
+ (room_type, day))[0][0] or 0
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js
new file mode 100644
index 0000000..7f7322c
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js
@@ -0,0 +1,9 @@
+frappe.views.calendar["Hotel Room Reservation"] = {
+ field_map: {
+ "start": "from_date",
+ "end": "to_date",
+ "id": "name",
+ "title": "guest_name",
+ "status": "status"
+ }
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js
new file mode 100644
index 0000000..2897139
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Room Reservation", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Room Reservation
+ () => frappe.tests.make('Hotel Room Reservation', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py
new file mode 100644
index 0000000..55c6311
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation/test_hotel_room_reservation.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import HotelRoomPricingNotSetError, HotelRoomUnavailableError
+test_dependencies = ["Hotel Room Pricing", "Hotel Room"]
+
+class TestHotelRoomReservation(unittest.TestCase):
+ def setUp(self):
+ frappe.db.sql("delete from `tabHotel Room Reservation`")
+ frappe.db.sql("delete from `tabHotel Room Reservation Item`")
+
+ def test_reservation(self):
+ reservation = make_reservation(
+ from_date="2017-01-01",
+ to_date="2017-01-03",
+ items=[
+ dict(item="Basic Room with Dinner", qty=2)
+ ]
+ )
+ reservation.insert()
+ self.assertEqual(reservation.net_total, 48000)
+
+ def test_price_not_set(self):
+ reservation = make_reservation(
+ from_date="2016-01-01",
+ to_date="2016-01-03",
+ items=[
+ dict(item="Basic Room with Dinner", qty=2)
+ ]
+ )
+ self.assertRaises(HotelRoomPricingNotSetError, reservation.insert)
+
+ def test_room_unavailable(self):
+ reservation = make_reservation(
+ from_date="2017-01-01",
+ to_date="2017-01-03",
+ items=[
+ dict(item="Basic Room with Dinner", qty=2),
+ ]
+ )
+ reservation.insert()
+
+ reservation = make_reservation(
+ from_date="2017-01-01",
+ to_date="2017-01-03",
+ items=[
+ dict(item="Basic Room with Dinner", qty=20),
+ ]
+ )
+ self.assertRaises(HotelRoomUnavailableError, reservation.insert)
+
+def make_reservation(**kwargs):
+ kwargs["doctype"] = "Hotel Room Reservation"
+ if not "guest_name" in kwargs:
+ kwargs["guest_name"] = "Test Guest"
+ doc = frappe.get_doc(kwargs)
+ return doc
diff --git a/erpnext/hotels/doctype/hotel_room_reservation_item/__init__.py b/erpnext/hotels/doctype/hotel_room_reservation_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation_item/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.json b/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.json
new file mode 100644
index 0000000..2b7931e
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.json
@@ -0,0 +1,195 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "",
+ "beta": 0,
+ "creation": "2017-12-08 12:58:21.733330",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "item",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Item",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Item",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "qty",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Qty",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:04:34.562956",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Reservation Item",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py b/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py
new file mode 100644
index 0000000..3406fae
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_reservation_item/hotel_room_reservation_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomReservationItem(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_type/__init__.py b/erpnext/hotels/doctype/hotel_room_type/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_type/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.js b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.js
new file mode 100644
index 0000000..d73835d
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Room Type', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.json b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.json
new file mode 100644
index 0000000..3d26413
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.json
@@ -0,0 +1,204 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "prompt",
+ "beta": 1,
+ "creation": "2017-12-08 12:38:29.485175",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "capacity",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Capacity",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "extra_bed_capacity",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Extra Bed Capacity",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "amenities",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Amenities",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Hotel Room Amenity",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:10:23.355486",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Type",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Hotel Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
new file mode 100644
index 0000000..1fc1303
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_type/hotel_room_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelRoomType(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js
new file mode 100644
index 0000000..e2dd578
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Room Type", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Room Type
+ () => frappe.tests.make('Hotel Room Type', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py
new file mode 100644
index 0000000..3b243e9
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_room_type/test_hotel_room_type.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestHotelRoomType(unittest.TestCase):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_settings/__init__.py b/erpnext/hotels/doctype/hotel_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_settings/__init__.py
diff --git a/erpnext/hotels/doctype/hotel_settings/hotel_settings.js b/erpnext/hotels/doctype/hotel_settings/hotel_settings.js
new file mode 100644
index 0000000..0b4a2c3
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_settings/hotel_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Hotel Settings', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/erpnext/hotels/doctype/hotel_settings/hotel_settings.json b/erpnext/hotels/doctype/hotel_settings/hotel_settings.json
new file mode 100644
index 0000000..d9f5572
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_settings/hotel_settings.json
@@ -0,0 +1,175 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 1,
+ "creation": "2017-12-08 17:50:24.523107",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "default_customer",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Default Customer",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Customer",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "default_taxes_and_charges",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Default Taxes and Charges",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Sales Taxes and Charges Template",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "default_invoice_naming_series",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Default Invoice Naming Series",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 1,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2017-12-09 12:11:12.857308",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Settings",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 0,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 0,
+ "role": "Hotel Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "restrict_to_domain": "Hospitality",
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hotels/doctype/hotel_settings/hotel_settings.py b/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
new file mode 100644
index 0000000..d78bca1
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_settings/hotel_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class HotelSettings(Document):
+ pass
diff --git a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js
new file mode 100644
index 0000000..bc0b7f8
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Hotel Settings", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Hotel Settings
+ () => frappe.tests.make('Hotel Settings', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
new file mode 100644
index 0000000..a081acc
--- /dev/null
+++ b/erpnext/hotels/doctype/hotel_settings/test_hotel_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+class TestHotelSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/hotels/report/__init__.py b/erpnext/hotels/report/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/report/__init__.py
diff --git a/erpnext/hotels/report/hotel_room_occupancy/__init__.py b/erpnext/hotels/report/hotel_room_occupancy/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hotels/report/hotel_room_occupancy/__init__.py
diff --git a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.js b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.js
new file mode 100644
index 0000000..81efb2d
--- /dev/null
+++ b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.js
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Hotel Room Occupancy"] = {
+ "filters": [
+ {
+ "fieldname":"from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.now_date(),
+ "reqd":1
+ },
+ {
+ "fieldname":"to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "default": frappe.datetime.now_date(),
+ "reqd":1
+ }
+ ]
+}
diff --git a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.json b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.json
new file mode 100644
index 0000000..782a48b
--- /dev/null
+++ b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.json
@@ -0,0 +1,26 @@
+{
+ "add_total_row": 1,
+ "apply_user_permissions": 1,
+ "creation": "2017-12-09 14:31:26.306705",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2017-12-09 14:31:26.306705",
+ "modified_by": "Administrator",
+ "module": "Hotels",
+ "name": "Hotel Room Occupancy",
+ "owner": "Administrator",
+ "ref_doctype": "Hotel Room Reservation",
+ "report_name": "Hotel Room Occupancy",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "System Manager"
+ },
+ {
+ "role": "Hotel Reservation User"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
new file mode 100644
index 0000000..aebeb45
--- /dev/null
+++ b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import add_days, date_diff
+
+from erpnext.hotels.doctype.hotel_room_reservation.hotel_room_reservation import get_rooms_booked
+
+def execute(filters=None):
+ columns = get_columns(filters)
+ data = get_data(filters)
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ dict(label=_("Room Type"), fieldname="room_type"),
+ dict(label=_("Rooms Booked"), fieldtype="Int")
+ ]
+ return columns
+
+def get_data(filters):
+ out = []
+ for room_type in frappe.get_all('Hotel Room Type'):
+ total_booked = 0
+ for i in xrange(date_diff(filters.to_date, filters.from_date)):
+ day = add_days(filters.from_date, i)
+ total_booked += get_rooms_booked(room_type.name, day)
+
+ out.append([room_type.name, total_booked])
+
+ return out
\ No newline at end of file
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index fd7344a..7b04f7d 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -21,7 +21,7 @@
def check_leave_record(self):
leave_record = frappe.db.sql("""select leave_type, half_day from `tabLeave Application`
- where employee = %s and %s between from_date and to_date and workflow_state = 'Approved'
+ where employee = %s and %s between from_date and to_date
and docstatus = 1""", (self.employee, self.attendance_date), as_dict=True)
if leave_record:
if leave_record[0].half_day:
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index c2d8326..2e6e451 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -10,15 +10,6 @@
frm.set_value("posting_date", frappe.datetime.get_today());
}
- frm.set_query("leave_approver", function() {
- return {
- query: "erpnext.hr.doctype.leave_application.leave_application.get_approvers",
- filters: {
- employee: frm.doc.employee
- }
- };
- });
-
frm.set_query("employee", erpnext.queries.employee);
},
@@ -29,14 +20,11 @@
refresh: function(frm) {
if (frm.is_new()) {
- frm.set_value("workflow_state", "Open");
frm.trigger("calculate_total_days");
}
- },
-
- leave_approver: function(frm) {
- if(frm.doc.leave_approver){
- frm.set_value("leave_approver_name", frappe.user.full_name(frm.doc.leave_approver));
+ cur_frm.set_intro("");
+ if(frm.doc.__islocal && !in_list(frappe.user_roles, "Employee")) {
+ frm.set_intro(__("Fill the form and save it"));
}
},
diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json
index 88b5a55..2435d06 100644
--- a/erpnext/hr/doctype/leave_application/leave_application.json
+++ b/erpnext/hr/doctype/leave_application/leave_application.json
@@ -433,96 +433,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "column_break_15",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "",
- "fieldname": "leave_approver",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 1,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 1,
- "label": "Leave Approver",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "leave_approver_name",
- "fieldtype": "Read Only",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Leave Approver Name",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "sb10",
"fieldtype": "Section Break",
"hidden": 0,
@@ -736,7 +646,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 3,
- "modified": "2018-01-22 12:10:40.757274",
+ "modified": "2018-02-12 13:10:05.766762",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Application",
@@ -885,7 +795,7 @@
"submit": 0,
"write": 1
}
- ],
+ ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 5be44af..22fd0e5 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -21,17 +21,10 @@
from frappe.model.document import Document
class LeaveApplication(Document):
def get_feed(self):
- return _("{0}: From {0} of type {1}").format(self.workflow_state, self.employee_name, self.leave_type)
+ return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
def validate(self):
- if self.get("__islocal"): self.workflow_state = 'Open'
- if not getattr(self, "__islocal", None) and frappe.db.exists(self.doctype, self.name):
- self.previous_doc = frappe.get_value(self.doctype, self.name, "leave_approver", as_dict=True)
- else:
- self.previous_doc = None
-
set_employee_name(self)
-
self.validate_dates()
self.validate_balance_leaves()
self.validate_leave_overlap()
@@ -39,22 +32,12 @@
self.show_block_day_warning()
self.validate_block_days()
self.validate_salary_processed_days()
- self.validate_leave_approver()
self.validate_attendance()
- def on_update(self):
- if (not self.previous_doc and self.leave_approver) or (self.previous_doc and \
- self.workflow_state == "Open" and self.previous_doc.leave_approver != self.leave_approver):
- # notify leave approver about creation
- self.notify_leave_approver()
-
def on_submit(self):
self.validate_back_dated_application()
- # notify leave applier about approval
- self.notify_employee(self.workflow_state)
-
def on_cancel(self):
# notify leave applier about cancellation
self.notify_employee("cancelled")
@@ -128,7 +111,7 @@
block_dates = get_applicable_block_dates(self.from_date, self.to_date,
self.employee, self.company)
- if block_dates and self.workflow_state == "Approved":
+ if block_dates and self.docstatus == 1:
frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError)
def validate_balance_leaves(self):
@@ -143,7 +126,7 @@
self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date,
consider_all_leaves_in_the_allocation_period=True)
- if self.workflow_state != "Rejected" and self.leave_balance < self.total_leave_days:
+ if self.leave_balance < self.total_leave_days:
if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}")
.format(self.leave_type))
@@ -160,7 +143,7 @@
select
name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day_date
from `tabLeave Application`
- where employee = %(employee)s and docstatus < 2 and workflow_state in ("Open", "Approved")
+ where employee = %(employee)s and docstatus < 2
and to_date >= %(from_date)s and from_date <= %(to_date)s
and name != %(name)s""", {
"employee": self.employee,
@@ -190,7 +173,6 @@
leave_count_on_half_day_date = frappe.db.sql("""select count(name) from `tabLeave Application`
where employee = %(employee)s
and docstatus < 2
- and workflow_state in ("Open", "Approved")
and half_day = 1
and half_day_date = %(half_day_date)s
and name != %(name)s""", {
@@ -206,23 +188,6 @@
if max_days and self.total_leave_days > cint(max_days):
frappe.throw(_("Leave of type {0} cannot be longer than {1}").format(self.leave_type, max_days))
- def validate_leave_approver(self):
- employee = frappe.get_doc("Employee", self.employee)
- leave_approvers = [l.leave_approver for l in employee.get("leave_approvers")]
-
- if len(leave_approvers) and self.leave_approver not in leave_approvers:
- frappe.throw(_("Leave approver must be one of {0}")
- .format(comma_or(leave_approvers)), InvalidLeaveApproverError)
-
- elif self.leave_approver and not frappe.db.sql("""select name from `tabHas Role`
- where parent=%s and role='Leave Approver'""", self.leave_approver):
- frappe.throw(_("{0} ({1}) must have role 'Leave Approver'")\
- .format(get_fullname(self.leave_approver), self.leave_approver), InvalidLeaveApproverError)
-
- elif self.docstatus==1 and len(leave_approvers) and self.leave_approver != frappe.session.user:
- frappe.throw(_("Only the selected Leave Approver can submit this Leave Application"),
- LeaveApproverIdentityError)
-
def validate_attendance(self):
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s and (attendance_date between %s and %s)
and status = "Present" and docstatus = 1""",
@@ -231,7 +196,7 @@
frappe.throw(_("Attendance for employee {0} is already marked for this day").format(self.employee),
AttendanceAlreadyMarkedError)
- def notify_employee(self, workflow_state):
+ def notify_employee(self):
employee = frappe.get_doc("Employee", self.employee)
if not employee.user_id:
return
@@ -246,19 +211,15 @@
message += "Leave Type: {leave_type}".format(leave_type=self.leave_type)+"<br>"
message += "From Date: {from_date}".format(from_date=self.from_date)+"<br>"
message += "To Date: {to_date}".format(to_date=self.to_date)+"<br>"
- message += "Status: {workflow_state}".format(workflow_state=_(workflow_state))
return message
self.notify({
# for post in messages
"message": _get_message(url=True),
"message_to": employee.user_id,
- "subject": (_("Leave Application") + ": %s - %s") % (self.name, _(workflow_state))
+ "subject": (_("Leave Application") + ": %s - %s") % (self.name)
})
- def notify_leave_approver(self):
- employee = frappe.get_doc("Employee", self.employee)
-
def _get_message(url=False):
name = self.name
employee_name = cstr(employee.employee_name)
@@ -275,7 +236,6 @@
self.notify({
# for post in messages
"message": _get_message(url=True),
- "message_to": self.leave_approver,
# for email
"subject": (_("New Leave Application") + ": %s - " + _("Employee") + ": %s") % (self.name, cstr(employee.employee_name))
@@ -321,23 +281,6 @@
pass
@frappe.whitelist()
-def get_approvers(doctype, txt, searchfield, start, page_len, filters):
- if not filters.get("employee"):
- frappe.throw(_("Please select Employee Record first."))
-
- employee_user = frappe.get_value("Employee", filters.get("employee"), "user_id")
-
- approvers_list = frappe.db.sql("""select user.name, user.first_name, user.last_name from
- tabUser user, `tabEmployee Leave Approver` approver where
- approver.parent = %s
- and user.name like %s
- and approver.leave_approver=user.name""", (filters.get("employee"), "%" + txt + "%"))
-
- if not approvers_list:
- approvers_list = get_approver_list(employee_user)
- return approvers_list
-
-@frappe.whitelist()
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None):
number_of_days = 0
if half_day == 1:
@@ -371,7 +314,7 @@
select employee, leave_type, from_date, to_date, total_leave_days
from `tabLeave Application`
where employee=%(employee)s and leave_type=%(leave_type)s
- and workflow_state="Approved" and docstatus=1
+ and docstatus=1
and (from_date between %(from_date)s and %(to_date)s
or to_date between %(from_date)s and %(to_date)s
or (from_date < %(from_date)s and to_date > %(to_date)s))
@@ -471,11 +414,10 @@
def add_leaves(events, start, end, match_conditions=None):
query = """select name, from_date, to_date, employee_name, half_day,
- workflow_state, employee, docstatus
+ employee, docstatus
from `tabLeave Application` where
from_date <= %(end)s and to_date >= %(start)s <= to_date
- and docstatus < 2
- and workflow_state!="Rejected" """
+ and docstatus < 2"""
if match_conditions:
query += match_conditions
@@ -485,7 +427,6 @@
"doctype": "Leave Application",
"from_date": d.from_date,
"to_date": d.to_date,
- "workflow_state": d.workflow_state,
"title": cstr(d.employee_name) + \
(d.half_day and _(" (Half Day)") or ""),
"docstatus": d.docstatus
diff --git a/erpnext/hr/doctype/leave_application/leave_application_calendar.js b/erpnext/hr/doctype/leave_application/leave_application_calendar.js
index b06b40f..0286f30 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_calendar.js
+++ b/erpnext/hr/doctype/leave_application/leave_application_calendar.js
@@ -7,7 +7,7 @@
"end": "to_date",
"id": "name",
"title": "title",
- "workflow_state": "workflow_state",
+ "docstatus": 1
},
options: {
header: {
diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js
index 7798ae7..d7588da 100644
--- a/erpnext/hr/doctype/leave_application/leave_application_list.js
+++ b/erpnext/hr/doctype/leave_application/leave_application_list.js
@@ -1,7 +1,3 @@
frappe.listview_settings['Leave Application'] = {
- add_fields: ["workflow_state", "leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"],
- get_indicator: function(doc) {
- return [__(doc.workflow_state), frappe.utils.guess_colour(doc.workflow_state),
- "workflow_state,=," + doc.workflow_state];
- }
+ add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"]
};
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.js b/erpnext/hr/doctype/leave_application/test_leave_application.js
index 6d51b71..6d7b6a7 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.js
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.js
@@ -16,7 +16,6 @@
{to_date: leave_date},
{half_day: 1},
{employee: employee.message.name},
- {leave_approver: "Administrator"},
{follow_via_email: 0}
]);
},
@@ -36,8 +35,8 @@
() => frappe.set_route("List", "Leave Application", "List"),
() => frappe.timeout(1),
// // check approved application in list
- () => assert.deepEqual(["Test Employee 1", "Approved"], [cur_list.data[0].employee_name, cur_list.data[0].workflow_state]),
- // "leave for correct employee is approved"),
+ () => assert.deepEqual(["Test Employee 1", 1], [cur_list.data[0].employee_name, cur_list.data[0].docstatus]),
+ // "leave for correct employee is submitted"),
() => done()
]);
});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py
index b2f6054..520d7c9 100644
--- a/erpnext/hr/doctype/leave_application/test_leave_application.py
+++ b/erpnext/hr/doctype/leave_application/test_leave_application.py
@@ -103,7 +103,6 @@
application = self.get_application(_test_records[0])
application.insert()
- application.workflow_state = "Approved"
self.assertRaises(LeaveDayBlockedError, application.submit)
frappe.set_user("test1@example.com")
@@ -127,11 +126,9 @@
make_allocation_record()
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.insert()
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
self.assertRaises(OverlapError, application.insert)
def test_overlap_with_half_day_1(self):
@@ -148,14 +145,12 @@
# leave from 1-5, half day on 3rd
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.half_day = 1
application.half_day_date = "2013-01-03"
application.insert()
# Apply again for a half day leave on 3rd
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.from_date = "2013-01-03"
application.to_date = "2013-01-03"
application.half_day = 1
@@ -164,7 +159,6 @@
# Apply again for a half day leave on 3rd
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.from_date = "2013-01-03"
application.to_date = "2013-01-03"
application.half_day = 1
@@ -186,12 +180,10 @@
# leave from 1-5, no half day
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.insert()
# Apply again for a half day leave on 1st
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.half_day = 1
application.half_day_date = application.from_date
@@ -211,14 +203,12 @@
# leave from 1-5, half day on 5th
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.half_day = 1
application.half_day_date = "2013-01-05"
application.insert()
# Apply leave from 4-7, half day on 5th
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.from_date = "2013-01-04"
application.to_date = "2013-01-07"
application.half_day = 1
@@ -228,7 +218,6 @@
# Apply leave from 5-7, half day on 5th
application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
application.from_date = "2013-01-05"
application.to_date = "2013-01-07"
application.half_day = 1
@@ -246,7 +235,6 @@
make_allocation_record(employee="_T-Employee-0002")
application = self.get_application(_test_records[1])
- application.leave_approver = "test@example.com"
frappe.db.set_value("Leave Block List", "_Test Leave Block List",
"applies_to_all_departments", 1)
@@ -257,7 +245,6 @@
application.insert()
frappe.set_user("test@example.com")
- application.workflow_state = "Approved"
# clear permlevel access cache on change user
del application._has_access_to
@@ -267,114 +254,6 @@
frappe.db.set_value("Leave Block List", "_Test Leave Block List",
"applies_to_all_departments", 0)
- def test_leave_approval(self):
- self._clear_roles()
-
- from frappe.utils.user import add_role
- add_role("test@example.com", "Employee")
- add_role("test1@example.com", "HR User")
- add_role("test1@example.com", "Leave Approver")
- add_role("test2@example.com", "Leave Approver")
-
- self._test_leave_approval_basic_case()
- self._test_leave_approval_invalid_leave_approver_insert()
- self._test_leave_approval_invalid_leave_approver_submit()
- self._test_leave_approval_valid_leave_approver_insert()
-
- def _test_leave_approval_basic_case(self):
- self._clear_applications()
-
- self._add_employee_leave_approver("_T-Employee-0001", "test1@example.com")
-
- # create leave application as Employee
- frappe.set_user("test@example.com")
-
- make_allocation_record()
-
- application = self.get_application(_test_records[0])
- application.leave_approver = "test1@example.com"
- application.insert()
-
- # submit leave application by Leave Approver
- frappe.set_user("test1@example.com")
- application.workflow_state = "Approved"
- del application._has_access_to
- application.submit()
- self.assertEqual(frappe.db.get_value("Leave Application", application.name,
- "docstatus"), 1)
-
- def _test_leave_approval_invalid_leave_approver_insert(self):
- from erpnext.hr.doctype.leave_application.leave_application import InvalidLeaveApproverError
-
- self._clear_applications()
-
- # add a different leave approver in the employee's list
- # should raise exception if not a valid leave approver
- self._add_employee_leave_approver("_T-Employee-0001", "test2@example.com")
- self._remove_employee_leave_approver("_T-Employee-0001", "test1@example.com")
-
- make_allocation_record()
-
- application = self.get_application(_test_records[0])
- frappe.set_user("test@example.com")
-
- application.leave_approver = "test1@example.com"
- self.assertRaises(InvalidLeaveApproverError, application.insert)
-
- frappe.db.sql("""delete from `tabEmployee Leave Approver` where parent=%s""",
- "_T-Employee-0001")
-
- def _test_leave_approval_invalid_leave_approver_submit(self):
- self._clear_applications()
- self._add_employee_leave_approver("_T-Employee-0001", "test2@example.com")
-
- # create leave application as employee
- # but submit as invalid leave approver - should raise exception
- frappe.set_user("test@example.com")
-
- make_allocation_record()
-
- application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
- application.insert()
- frappe.set_user("test1@example.com")
- del application._has_access_to
- application.workflow_state = "Approved"
-
- from erpnext.hr.doctype.leave_application.leave_application import LeaveApproverIdentityError
- self.assertRaises(LeaveApproverIdentityError, application.submit)
-
- frappe.db.sql("""delete from `tabEmployee Leave Approver` where parent=%s""",
- "_T-Employee-0001")
-
- def _test_leave_approval_valid_leave_approver_insert(self):
- self._clear_applications()
- self._add_employee_leave_approver("_T-Employee-0001", "test2@example.com")
-
- original_department = frappe.db.get_value("Employee", "_T-Employee-0001", "department")
- frappe.db.set_value("Employee", "_T-Employee-0001", "department", None)
-
- frappe.set_user("test@example.com")
-
- make_allocation_record()
-
- application = self.get_application(_test_records[0])
- application.leave_approver = "test2@example.com"
- application.insert()
-
- # change to valid leave approver and try to submit leave application
- frappe.set_user("test2@example.com")
- application.workflow_state = "Approved"
- del application._has_access_to
- application.submit()
- self.assertEqual(frappe.db.get_value("Leave Application", application.name,
- "docstatus"), 1)
-
- frappe.db.sql("""delete from `tabEmployee Leave Approver` where parent=%s""",
- "_T-Employee-0001")
-
- frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department)
-
def make_allocation_record(employee=None, leave_type=None):
frappe.db.sql("delete from `tabLeave Allocation`")
@@ -388,4 +267,4 @@
})
allocation.insert(ignore_permissions=True)
- allocation.submit()
+ allocation.submit()
\ No newline at end of file
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index 878c6b4..da2598f 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -300,7 +300,6 @@
where t2.name = t1.leave_type
and t2.is_lwp = 1
and t1.docstatus = 1
- and t1.workflow_state = 'Approved'
and t1.employee = %(employee)s
and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
WHEN t2.include_holiday THEN %(dt)s between from_date and to_date
diff --git a/erpnext/modules.txt b/erpnext/modules.txt
index 42f0f0b..d469145 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -20,4 +20,5 @@
Restaurant
Agriculture
ERPNext Integrations
-Non Profit
\ No newline at end of file
+Non Profit
+Hotels
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6f4c63e..69d8a47 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -489,7 +489,8 @@
erpnext.patches.v10_0.update_assessment_plan
erpnext.patches.v10_0.update_assessment_result
erpnext.patches.v10_0.added_extra_gst_custom_field
-erpnext.patches.v10_0.workflow_leave_application #2018-01-24
+erpnext.patches.v10_0.workflow_leave_application #2018-01-24 #2018-02-02 #2018-02-08
erpnext.patches.v10_0.set_default_payment_terms_based_on_company
erpnext.patches.v10_0.update_sales_order_link_to_purchase_order
erpnext.patches.v10_0.added_extra_gst_custom_field_in_gstr2
+erpnext.patches.v10_0.item_barcode_childtable_migrate
diff --git a/erpnext/patches/v10_0/item_barcode_childtable_migrate.py b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
new file mode 100644
index 0000000..d985bbf
--- /dev/null
+++ b/erpnext/patches/v10_0/item_barcode_childtable_migrate.py
@@ -0,0 +1,22 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+
+
+def execute():
+ items_barcode = frappe.db.sql("""SELECT name, barcode FROM tabItem
+ WHERE barcode IS NOT NULL and barcode != ''""", as_dict=1)
+
+ frappe.reload_doc("stock", "doctype", "item")
+ frappe.reload_doc("stock", "doctype", "item_barcode")
+
+ for item in items_barcode:
+ doc = frappe.get_doc("Item", item.get("name"))
+ if item.get("barcode"):
+ doc.append("barcodes", {"barcode": item.get("barcode")})
+ doc.flags.ignore_validate = True
+ doc.flags.ignore_mandatory = True
+ doc.save()
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/workflow_leave_application.py b/erpnext/patches/v10_0/workflow_leave_application.py
index 8a68f89..0d33402 100644
--- a/erpnext/patches/v10_0/workflow_leave_application.py
+++ b/erpnext/patches/v10_0/workflow_leave_application.py
@@ -3,11 +3,66 @@
from __future__ import unicode_literals
import frappe
-from erpnext.setup.install import leave_application_workflow
def execute():
- frappe.reload_doc("hr", "doctype", "leave_application")
- frappe.reload_doc("workflow", "doctype", "workflow")
- leave_application_workflow()
- if frappe.db.has_column("Leave Application", "status"):
- frappe.db.sql("""update `tabLeave Application` set workflow_state = status""")
+ if frappe.db.a_row_exists("Leave Application"):
+ frappe.reload_doc("hr", "doctype", "leave_application")
+ frappe.reload_doc("workflow", "doctype", "workflow")
+ states = {'Approved': 'Success', 'Rejected': 'Danger', 'Open': 'Warning'}
+
+ for state, style in states.items():
+ if not frappe.db.exists("Workflow State", state):
+ frappe.get_doc({
+ 'doctype': 'Workflow State',
+ 'workflow_state_name': state,
+ 'style': style
+ }).insert(ignore_permissions=True)
+
+ for action in ['Approve', 'Reject']:
+ if not frappe.db.exists("Workflow Action", action):
+ frappe.get_doc({
+ 'doctype': 'Workflow Action',
+ 'workflow_action_name': action
+ }).insert(ignore_permissions=True)
+
+ if not frappe.db.exists("Workflow", "Leave Approval"):
+ frappe.get_doc({
+ 'doctype': 'Workflow',
+ 'workflow_name': 'Leave Approval',
+ 'document_type': 'Leave Application',
+ 'is_active': 1,
+ 'workflow_state_field': 'workflow_state',
+ 'states': [{
+ "state": 'Open',
+ "doc_status": 0,
+ "allow_edit": 'Employee'
+ }, {
+ "state": 'Approved',
+ "doc_status": 1,
+ "allow_edit": 'Leave Approver'
+ }, {
+ "state": 'Rejected',
+ "doc_status": 0,
+ "allow_edit": 'Leave Approver'
+ }],
+ 'transitions': [{
+ "state": 'Open',
+ "action": 'Approve',
+ "next_state": 'Approved',
+ "allowed": 'Leave Approver'
+ },
+ {
+ "state": 'Open',
+ "action": 'Reject',
+ "next_state": 'Rejected',
+ "allowed": 'Leave Approver'
+ }]
+ }).insert(ignore_permissions=True)
+
+ if frappe.db.has_column("Leave Application", "status"):
+ frappe.db.sql("""update `tabLeave Application` set workflow_state = status""")
+
+ if frappe.db.has_column("Leave Application", "workflow_state"):
+ frappe.db.sql("""update `tabWorkflow Document State` set doc_status = 0 where parent = "Leave Approval" \
+ and state = "Rejected" and doc_status = 1""")
+ frappe.db.sql("""update `tabLeave Application` set docstatus = 0 where workflow_state = "Rejected" and docstatus = 1""")
diff --git a/erpnext/public/css/pos.css b/erpnext/public/css/pos.css
index e2d301d..613a5ff 100644
--- a/erpnext/public/css/pos.css
+++ b/erpnext/public/css/pos.css
@@ -174,3 +174,6 @@
.rounded-total-value {
font-size: 18px;
}
+.quantity-total {
+ font-size: 18px;
+}
diff --git a/erpnext/public/css/website.css b/erpnext/public/css/website.css
index 733194a..9ac9aac 100644
--- a/erpnext/public/css/website.css
+++ b/erpnext/public/css/website.css
@@ -182,7 +182,7 @@
display: none;
}
.cart-dropdown-container .checkout-btn {
- padding-top: 25px;
+ padding-bottom: 25px;
}
.cart-dropdown-container .col-name-description {
margin-bottom: 8px;
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index 1eeb896..29e3999 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -98,6 +98,16 @@
],
"default": "Monthly",
"reqd": 1
+ },
+ // Note:
+ // If you are modifying this array such that the presentation_currency object
+ // is no longer the last object, please make adjustments in cash_flow.js
+ // accordingly.
+ {
+ "fieldname": "presentation_currency",
+ "label": __("Currency"),
+ "fieldtype": "Select",
+ "options": erpnext.get_presentation_currency_list()
}
]
}
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 6841354..d50fa19 100644
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -13,6 +13,12 @@
return frappe.boot.sysdefaults.currency;
},
+ get_presentation_currency_list: () => {
+ const docs = frappe.boot.docs;
+ const currency_list = docs.filter(d => d.doctype === ":Currency").map(d => d.name);
+ return currency_list;
+ },
+
toggle_naming_series: function() {
if(cur_frm.fields_dict.naming_series) {
cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal?true:false);
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index a4d7265..0fb8730 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -215,7 +215,7 @@
in_list_view:1,
get_query: function() {
return {
- filters: {item_code: me.item_code },
+ filters: {item: me.item_code },
query: 'erpnext.controllers.queries.get_batch_numbers'
};
},
diff --git a/erpnext/public/less/pos.less b/erpnext/public/less/pos.less
index af3008e..b081ed4 100644
--- a/erpnext/public/less/pos.less
+++ b/erpnext/public/less/pos.less
@@ -222,4 +222,8 @@
.rounded-total-value {
font-size: 18px;
+}
+
+.quantity-total {
+ font-size: 18px;
}
\ No newline at end of file
diff --git a/erpnext/public/less/website.less b/erpnext/public/less/website.less
index 25172a1..14d033c 100644
--- a/erpnext/public/less/website.less
+++ b/erpnext/public/less/website.less
@@ -235,7 +235,7 @@
}
.checkout-btn {
- padding-top:25px;
+ padding-bottom:25px;
}
.col-name-description {
margin-bottom:8px;
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index fa0a448..c11337a 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -107,7 +107,7 @@
dict(fieldname='reason_for_issuing_document', label='Reason For Issuing document',
fieldtype='Select', insert_after='gst_col_break', print_hide=1,
depends_on='eval:doc.is_return==1', reqd=1,
- options='\n01-Sales Return\n02-Post Sale Discount\n03-Deficiency in services\n04-Correction in Invoice\n05-Change in POS\n06-Finalization of Provisional assessment\n07-Others', default='01-Sales Return')
+ options='\n01-Sales Return\n02-Post Sale Discount\n03-Deficiency in services\n04-Correction in Invoice\n05-Change in POS\n06-Finalization of Provisional assessment\n07-Others')
]
purchase_invoice_gst_fields = [
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 1ad673f..4dd1300 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -12,6 +12,8 @@
class Gstr1Report(object):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
+ self.columns = []
+ self.data = []
self.doctype = "Sales Invoice"
self.tax_doctype = "Sales Taxes and Charges"
self.select_columns = """
@@ -41,16 +43,15 @@
self.get_gst_accounts()
self.get_invoice_data()
- if not self.invoices: return
+ if self.invoices:
+ self.get_invoice_items()
+ self.get_items_based_on_tax_rate()
+ self.invoice_fields = [d["fieldname"] for d in self.invoice_columns]
+ self.get_data()
- self.get_invoice_items()
- self.get_items_based_on_tax_rate()
- self.invoice_fields = [d["fieldname"] for d in self.invoice_columns]
- self.get_data()
return self.columns, self.data
def get_data(self):
- self.data = []
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv)
for rate, items in items_based_on_rate.items():
diff --git a/erpnext/regional/report/gstr_2/gstr_2.py b/erpnext/regional/report/gstr_2/gstr_2.py
index a69a67f..679e567 100644
--- a/erpnext/regional/report/gstr_2/gstr_2.py
+++ b/erpnext/regional/report/gstr_2/gstr_2.py
@@ -12,6 +12,8 @@
class Gstr2Report(Gstr1Report):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
+ self.columns = []
+ self.data = []
self.doctype = "Purchase Invoice"
self.tax_doctype = "Purchase Taxes and Charges"
self.select_columns = """
@@ -39,7 +41,6 @@
def get_data(self):
self.get_igst_invoices()
- self.data = []
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv)
for rate, items in items_based_on_rate.items():
diff --git a/erpnext/restaurant/doctype/restaurant/restaurant.json b/erpnext/restaurant/doctype/restaurant/restaurant.json
index f4ecba7..8572687 100644
--- a/erpnext/restaurant/doctype/restaurant/restaurant.json
+++ b/erpnext/restaurant/doctype/restaurant/restaurant.json
@@ -4,7 +4,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "prompt",
- "beta": 0,
+ "beta": 1,
"creation": "2017-09-15 12:40:41.546933",
"custom": 0,
"docstatus": 0,
@@ -269,7 +269,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-10-05 17:41:14.422242",
+ "modified": "2017-12-09 12:13:10.185496",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant",
diff --git a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.json b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.json
index 264634b..1b1610d 100644
--- a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.json
+++ b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.json
@@ -4,7 +4,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "prompt",
- "beta": 0,
+ "beta": 1,
"creation": "2017-09-15 12:48:29.818715",
"custom": 0,
"docstatus": 0,
@@ -207,7 +207,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-09-21 11:04:20.671542",
+ "modified": "2017-12-09 12:13:13.684500",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Menu",
diff --git a/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.json b/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.json
index 6a2ffa1..0698758 100644
--- a/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.json
+++ b/erpnext/restaurant/doctype/restaurant_reservation/restaurant_reservation.json
@@ -4,7 +4,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "REST.######",
- "beta": 0,
+ "beta": 1,
"creation": "2017-09-15 13:05:51.063661",
"custom": 0,
"docstatus": 0,
@@ -297,7 +297,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-09-15 14:40:56.759315",
+ "modified": "2017-12-09 12:13:20.027942",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Reservation",
diff --git a/erpnext/restaurant/doctype/restaurant_table/restaurant_table.json b/erpnext/restaurant/doctype/restaurant_table/restaurant_table.json
index da1bcde..5fc6e62 100644
--- a/erpnext/restaurant/doctype/restaurant_table/restaurant_table.json
+++ b/erpnext/restaurant/doctype/restaurant_table/restaurant_table.json
@@ -4,7 +4,7 @@
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
- "beta": 0,
+ "beta": 1,
"creation": "2017-09-15 12:45:24.717355",
"custom": 0,
"docstatus": 0,
@@ -116,7 +116,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-09-15 13:18:05.254106",
+ "modified": "2017-12-09 12:13:24.382345",
"modified_by": "Administrator",
"module": "Restaurant",
"name": "Restaurant Table",
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index da187f7..72250c2 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -250,6 +250,7 @@
this.cart.add_item(item);
this.cart.update_taxes_and_totals();
this.cart.update_grand_total();
+ this.cart.update_qty_total();
frappe.dom.unfreeze();
}
@@ -577,6 +578,9 @@
<div class="grand-total">
${this.get_grand_total()}
</div>
+ <div class="quantity-total">
+ ${this.get_item_qty_total()}
+ </div>
</div>
</div>
<div class="number-pad-container">
@@ -588,6 +592,7 @@
this.$taxes_and_totals = this.wrapper.find('.taxes-and-totals');
this.$discount_amount = this.wrapper.find('.discount-amount');
this.$grand_total = this.wrapper.find('.grand-total');
+ this.$qty_total = this.wrapper.find('.quantity-total');
this.toggle_taxes_and_totals(false);
this.$grand_total.on('click', () => {
@@ -622,6 +627,11 @@
return total;
}
+ get_item_qty_total() {
+ let total = this.get_total_template('Total Qty', 'quantity-total');
+ return total;
+ }
+
get_total_template(label, class_name) {
return `
<div class="list-item">
@@ -709,6 +719,17 @@
);
}
+ update_qty_total() {
+ var total_item_qty = 0;
+ $.each(this.frm.doc["items"] || [], function (i, d) {
+ if (d.qty > 0) {
+ total_item_qty += d.qty;
+ }
+ });
+ this.$qty_total.find('.quantity-total').text(total_item_qty)
+ this.frm.set_value("pos_total_qty",total_item_qty);
+ }
+
make_customer_field() {
this.customer_field = frappe.ui.form.make_control({
df: {
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index 4f0d250..666284f 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -29,7 +29,7 @@
batch_no, item_code = batch_no_data
if not serial_no and not batch_no:
- barcode_data = frappe.db.get_value('Item', {'barcode': search_value}, ['name', 'barcode'])
+ barcode_data = frappe.db.get_value('Item Barcode', {'barcode': search_value}, ['parent', 'barcode'])
if barcode_data:
item_code, barcode = barcode_data
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 2cc280f..81f909a 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -12,7 +12,6 @@
<a style="color: #888" href="http://erpnext.org">ERPNext</a></div>"""
def after_install():
- leave_application_workflow()
frappe.get_doc({'doctype': "Role", "role_name": "Analytics"}).insert()
set_single_defaults()
create_compact_item_print_custom_field()
@@ -20,58 +19,6 @@
add_all_roles_to("Administrator")
frappe.db.commit()
-def leave_application_workflow():
- states = {'Approved': 'Success', 'Rejected': 'Danger', 'Open': 'Warning'}
-
- for state, style in states.items():
- if not frappe.db.exists("Workflow State", state):
- frappe.get_doc({
- 'doctype': 'Workflow State',
- 'workflow_state_name': state,
- 'style': style
- }).insert(ignore_permissions=True)
-
- for action in ['Approve', 'Reject']:
- if not frappe.db.exists("Workflow Action", action):
- frappe.get_doc({
- 'doctype': 'Workflow Action',
- 'workflow_action_name': action
- }).insert(ignore_permissions=True)
-
- if not frappe.db.exists("Workflow", "Leave Approval"):
- frappe.get_doc({
- 'doctype': 'Workflow',
- 'workflow_name': 'Leave Approval',
- 'document_type': 'Leave Application',
- 'is_active': 1,
- 'workflow_state_field': 'workflow_state',
- 'states': [{
- "state": 'Open',
- "doc_status": 0,
- "allow_edit": 'Employee'
- }, {
- "state": 'Approved',
- "doc_status": 1,
- "allow_edit": 'Leave Approver'
- }, {
- "state": 'Rejected',
- "doc_status": 1,
- "allow_edit": 'Leave Approver'
- }],
- 'transitions': [{
- "state": 'Open',
- "action": 'Approve',
- "next_state": 'Approved',
- "allowed": 'Leave Approver'
- },
- {
- "state": 'Open',
- "action": 'Reject',
- "next_state": 'Rejected',
- "allowed": 'Leave Approver'
- }]
- }).insert(ignore_permissions=True)
-
def check_setup_wizard_not_completed():
if frappe.db.get_default('desktop:home_page') == 'desktop':
print()
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index dce3c28..2aa4d5e 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -216,6 +216,7 @@
{'doctype': "Party Type", "party_type": "Supplier"},
{'doctype': "Party Type", "party_type": "Employee"},
{'doctype': "Party Type", "party_type": "Member"},
+ {'doctype': "Party Type", "party_type": "Shareholder"},
{'doctype': "Opportunity Type", "name": "Hub"},
{'doctype': "Opportunity Type", "name": _("Sales")},
@@ -246,6 +247,11 @@
{'doctype': 'Assessment Group', 'assessment_group_name': _('All Assessment Groups'),
'is_group': 1, 'parent_assessment_group': ''},
+ # Share Management
+ {"doctype": "Share Type", "title": _("Equity")},
+ {"doctype": "Share Type", "title": _("Preference")},
+
+
]
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js
index 12aa0bd..e2ea7f9 100644
--- a/erpnext/stock/doctype/batch/batch.js
+++ b/erpnext/stock/doctype/batch/batch.js
@@ -25,9 +25,17 @@
}
},
item: (frm) => {
- frappe.db.get_value('Item', {name: frm.doc.item}, 'has_expiry_date', (r) => {
- frm.toggle_reqd('expiry_date', r.has_expiry_date);
- });
+ // frappe.db.get_value('Item', {name: frm.doc.item}, 'has_expiry_date', (r) => {
+ // frm.toggle_reqd('expiry_date', r.has_expiry_date);
+ // });
+ frappe.db.get_value('Item', {name: frm.doc.item}, ['shelf_life_in_days', 'has_expiry_date'], (r) => {
+ if (r.has_expiry_date && r.shelf_life_in_days) {
+ // Calculate expiry date based on shelf_life_in_days
+ frm.set_value('expiry_date', frappe.datetime.add_days(frm.doc.manufacturing_date, r.shelf_life_in_days));
+ }else if(r.has_expiry_date){
+ frm.toggle_reqd('expiry_date', r.has_expiry_date);
+ }
+ })
},
make_dashboard: (frm) => {
if(!frm.is_new()) {
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index f60798b..cc1aac5 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -1,493 +1,494 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 0,
- "autoname": "",
- "beta": 0,
- "creation": "2013-03-05 14:50:38",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 1,
+ "allow_rename": 0,
+ "autoname": "",
+ "beta": 0,
+ "creation": "2013-03-05 14:50:38",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.__islocal",
- "fieldname": "batch_id",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Batch ID",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "batch_id",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.__islocal",
+ "fieldname": "batch_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Batch ID",
+ "length": 0,
+ "no_copy": 1,
+ "oldfieldname": "batch_id",
+ "oldfieldtype": "Data",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "item",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Item",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "item",
- "oldfieldtype": "Link",
- "options": "Item",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "item",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Item",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "item",
+ "oldfieldtype": "Link",
+ "options": "Item",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "image",
- "fieldtype": "Attach Image",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "image",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "image",
+ "fieldtype": "Attach Image",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "image",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.parent_batch",
- "fieldname": "parent_batch",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Parent Batch",
- "length": 0,
- "no_copy": 0,
- "options": "Batch",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "eval:doc.parent_batch",
+ "fieldname": "parent_batch",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Parent Batch",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Batch",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "manufacturing_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Manufacturing Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Today",
+ "fieldname": "manufacturing_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Manufacturing Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "expiry_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Expiry Date",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "expiry_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "expiry_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Expiry Date",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "expiry_date",
+ "oldfieldtype": "Date",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "source",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Source",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "source",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Source",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "supplier",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Supplier",
- "length": 0,
- "no_copy": 0,
- "options": "Supplier",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_9",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reference_doctype",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Source Document Type",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Source Document Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "reference_name",
- "fieldtype": "Dynamic Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Source Document Name",
- "length": 0,
- "no_copy": 0,
- "options": "reference_doctype",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Source Document Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_7",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Batch Description",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "description",
- "oldfieldtype": "Small Text",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Batch Description",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "description",
+ "oldfieldtype": "Small Text",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0,
"width": "300px"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-archive",
- "idx": 1,
- "image_field": "image",
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 5,
- "modified": "2018-01-08 21:55:54.306693",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Batch",
- "owner": "harshada@webnotestech.com",
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-archive",
+ "idx": 1,
+ "image_field": "image",
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 5,
+ "modified": "2018-01-23 17:41:06.862477",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Batch",
+ "owner": "harshada@webnotestech.com",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Item Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Item Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
"write": 1
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_order": "DESC",
- "title_field": "item",
- "track_changes": 0,
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_order": "DESC",
+ "title_field": "item",
+ "track_changes": 0,
"track_seen": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index b5674d3..645ab1b 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -7,24 +7,13 @@
from frappe.model.document import Document
from frappe.model.naming import make_autoname, revert_series_if_last
from frappe.utils import flt, cint
-
+from frappe.utils.jinja import render_template
+from frappe.utils.data import add_days
class UnableToSelectBatchError(frappe.ValidationError):
pass
-def get_name_from_naming_series():
- """
- Get a name generated for a Batch from the Batch's naming series.
- :return: The string that was generated.
- """
- naming_series_prefix = _get_batch_prefix()
- key = _make_naming_series_key(naming_series_prefix)
- name = make_autoname(key)
-
- return name
-
-
def get_name_from_hash():
"""
Get a name for a Batch by generating a unique hash.
@@ -99,7 +88,7 @@
if not self.batch_id:
if frappe.db.get_value('Item', self.item, 'create_new_batch'):
if batch_uses_naming_series():
- self.batch_id = get_name_from_naming_series()
+ self.batch_id = self.get_name_from_naming_series()
else:
self.batch_id = get_name_from_hash()
else:
@@ -120,6 +109,28 @@
if frappe.db.get_value("Item", self.item, "has_batch_no") == 0:
frappe.throw(_("The selected item cannot have Batch"))
+ def before_save(self):
+ has_expiry_date, shelf_life_in_days = frappe.db.get_value('Item', self.item, ['has_expiry_date', 'shelf_life_in_days'])
+ if not self.expiry_date and has_expiry_date and shelf_life_in_days:
+ self.expiry_date = add_days(self.manufacturing_date, shelf_life_in_days)
+
+ if has_expiry_date and not self.expiry_date:
+ frappe.throw('Expiry date is mandatory for selected item')
+ frappe.msgprint('Set items shelf life in days, to set expiry based on manufacturing_date plus self life ')
+
+ def get_name_from_naming_series(self):
+ """
+ Get a name generated for a Batch from the Batch's naming series.
+ :return: The string that was generated.
+ """
+ naming_series_prefix = _get_batch_prefix()
+ # validate_template(naming_series_prefix)
+ naming_series_prefix = render_template(str(naming_series_prefix), self.__dict__)
+ key = _make_naming_series_key(naming_series_prefix)
+ name = make_autoname(key)
+
+ return name
+
@frappe.whitelist()
def get_batch_qty(batch_no=None, warehouse=None, item_code=None):
@@ -206,6 +217,7 @@
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, qty))
+
@frappe.whitelist()
def get_batch_no(item_code, warehouse, qty=1, throw=False):
"""
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 2b87069..f334973 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -178,35 +178,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "barcode",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Barcode",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "",
"fieldname": "item_group",
"fieldtype": "Link",
@@ -702,6 +673,67 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sb_barcodes",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Barcodes",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "barcodes",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Barcodes",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Item Barcode",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
@@ -3484,7 +3516,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
- "modified": "2018-01-24 20:42:23.303090",
+ "modified": "2018-02-12 15:42:23.303090",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index df90ad9..27f2c7b 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -2,30 +2,38 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
-import erpnext
-import json
-import itertools
-from frappe import msgprint, _
-from frappe.utils import (cstr, flt, cint, getdate, now_datetime, formatdate,
- strip, get_timestamp, random_string)
-from frappe.utils.html_utils import clean_html
-from frappe.website.website_generator import WebsiteGenerator
-from erpnext.setup.doctype.item_group.item_group import invalidate_cache_for, get_parent_item_groups
-from frappe.website.render import clear_cache
-from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
-from erpnext.controllers.item_variant import (get_variant, copy_attributes_to_variant,
- make_variant_item_code, validate_item_variant_attributes, ItemVariantExistsError)
-class DuplicateReorderRows(frappe.ValidationError): pass
-class StockExistsForTemplate(frappe.ValidationError): pass
+import itertools
+import json
+import erpnext
+import frappe
+from erpnext.controllers.item_variant import (ItemVariantExistsError,
+ copy_attributes_to_variant, get_variant, make_variant_item_code, validate_item_variant_attributes)
+from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for)
+from frappe import _, msgprint
+from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate,
+ now_datetime, random_string, strip)
+from frappe.utils.html_utils import clean_html
+from frappe.website.doctype.website_slideshow.website_slideshow import \
+ get_slideshow
+from frappe.website.render import clear_cache
+from frappe.website.website_generator import WebsiteGenerator
+
+
+class DuplicateReorderRows(frappe.ValidationError):
+ pass
+
+
+class StockExistsForTemplate(frappe.ValidationError):
+ pass
+
class Item(WebsiteGenerator):
website = frappe._dict(
- page_title_field = "item_name",
- condition_field = "show_in_website",
- template = "templates/generators/item.html",
- no_cache = 1
+ page_title_field="item_name",
+ condition_field="show_in_website",
+ template="templates/generators/item.html",
+ no_cache=1
)
def onload(self):
@@ -37,14 +45,14 @@
self.set_onload("asset_exists", True if asset else False)
def autoname(self):
- if frappe.db.get_default("item_naming_by")=="Naming Series":
+ if frappe.db.get_default("item_naming_by") == "Naming Series":
if self.variant_of:
if not self.item_code:
template_item_name = frappe.db.get_value("Item", self.variant_of, "item_name")
self.item_code = make_variant_item_code(self.variant_of, template_item_name, self)
else:
from frappe.model.naming import make_autoname
- self.item_code = make_autoname(self.naming_series+'.#####')
+ self.item_code = make_autoname(self.naming_series + '.#####')
elif not self.item_code:
msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
@@ -102,8 +110,8 @@
if not self.get("__islocal"):
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
self.old_website_item_groups = frappe.db.sql_list("""select item_group
- from `tabWebsite Item Group`
- where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name)
+ from `tabWebsite Item Group`
+ where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name)
def on_update(self):
invalidate_cache_for_item(self)
@@ -121,7 +129,7 @@
'''Add a new price'''
if not price_list:
price_list = (frappe.db.get_single_value('Selling Settings', 'selling_price_list')
- or frappe.db.get_value('Price List', _('Standard Selling')))
+ or frappe.db.get_value('Price List', _('Standard Selling')))
if price_list:
item_price = frappe.get_doc({
"doctype": "Item Price",
@@ -147,19 +155,19 @@
# default warehouse, or Stores
default_warehouse = (self.default_warehouse
- or frappe.db.get_single_value('Stock Settings', 'default_warehouse')
- or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')}))
+ or frappe.db.get_single_value('Stock Settings', 'default_warehouse')
+ or frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')}))
if default_warehouse:
stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse,
- qty=self.opening_stock, rate=self.valuation_rate)
+ qty=self.opening_stock, rate=self.valuation_rate)
stock_entry.add_comment("Comment", _("Opening Stock"))
def make_route(self):
if not self.route:
return cstr(frappe.db.get_value('Item Group', self.item_group,
- 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
+ 'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
def validate_website_image(self):
"""Validate if the website image is a public file"""
@@ -176,14 +184,13 @@
"file_url": self.website_image
}, fields=["name", "is_private"], order_by="is_private asc", limit_page_length=1)
-
if file_doc:
file_doc = file_doc[0]
if not file_doc:
if not auto_set_website_image:
frappe.msgprint(_("Website Image {0} attached to Item {1} cannot be found")
- .format(self.website_image, self.name))
+ .format(self.website_image, self.name))
self.website_image = None
@@ -219,7 +226,8 @@
self.website_image = None
except requests.exceptions.SSLError:
- frappe.msgprint(_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
+ frappe.msgprint(
+ _("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
self.website_image = None
# for CSV import
@@ -259,12 +267,13 @@
def validate_retain_sample(self):
if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'):
- frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"));
+ frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"))
if self.retain_sample and not self.has_batch_no:
- frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(self.item_code))
+ frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(
+ self.item_code))
def get_context(self, context):
- context.show_search=True
+ context.show_search = True
context.search_link = '/product_search'
context.parents = get_parent_item_groups(self.item_group)
@@ -282,8 +291,8 @@
# load variants
# also used in set_attribute_context
context.variants = frappe.get_all("Item",
- filters={"variant_of": self.name, "show_variant_in_website": 1},
- order_by="name asc")
+ filters={"variant_of": self.name, "show_variant_in_website": 1},
+ order_by="name asc")
variant = frappe.form_dict.variant
if not variant and context.variants:
@@ -294,7 +303,7 @@
context.variant = frappe.get_doc("Item", variant)
for fieldname in ("website_image", "web_long_description", "description",
- "website_specifications"):
+ "website_specifications"):
if context.variant.get(fieldname):
value = context.variant.get(fieldname)
if isinstance(value, list):
@@ -317,14 +326,14 @@
# load attributes
for v in context.variants:
v.attributes = frappe.get_all("Item Variant Attribute",
- fields=["attribute", "attribute_value"], filters={"parent": v.name})
+ fields=["attribute", "attribute_value"], filters={"parent": v.name})
for attr in v.attributes:
values = attribute_values_available.setdefault(attr.attribute, [])
if attr.attribute_value not in values:
values.append(attr.attribute_value)
- if v.name==context.variant.name:
+ if v.name == context.variant.name:
context.selected_attributes[attr.attribute] = attr.attribute_value
# filter attributes, order based on attribute table
@@ -338,7 +347,7 @@
else:
# get list of values defined (for sequence)
for attr_value in frappe.db.get_all("Item Attribute Value",
- fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
+ fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
values.append(attr_value.attribute_value)
@@ -373,7 +382,7 @@
return True
for i, attr in enumerate(self.attributes):
- if i==0:
+ if i == 0:
continue
combination_source = []
@@ -414,7 +423,7 @@
for d in template.get("reorder_levels"):
n = {}
for k in ("warehouse", "warehouse_reorder_level",
- "warehouse_reorder_qty", "material_request_type"):
+ "warehouse_reorder_qty", "material_request_type"):
n[k] = d.get(k)
self.append("reorder_levels", n)
@@ -422,12 +431,14 @@
check_list = []
for d in self.get('uoms'):
if cstr(d.uom) in check_list:
- frappe.throw(_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
+ frappe.throw(
+ _("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
else:
check_list.append(cstr(d.uom))
if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
- frappe.throw(_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
+ frappe.throw(
+ _("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
def validate_item_type(self):
if self.has_serial_no == 1 and self.is_stock_item == 0:
@@ -436,29 +447,30 @@
if self.has_serial_no == 0 and self.serial_no_series:
self.serial_no_series = None
-
def check_for_active_boms(self):
if self.default_bom:
bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
if bom_item not in (self.name, self.variant_of):
- frappe.throw(_("Default BOM ({0}) must be active for this item or its template").format(bom_item))
+ frappe.throw(
+ _("Default BOM ({0}) must be active for this item or its template").format(bom_item))
def fill_customer_code(self):
""" Append all the customer codes and insert into "customer_code" field of item table """
- cust_code=[]
+ cust_code = []
for d in self.get('customer_items'):
cust_code.append(d.ref_code)
- self.customer_code=','.join(cust_code)
+ self.customer_code = ','.join(cust_code)
def check_item_tax(self):
"""Check whether Tax Rate is not entered twice for same Tax Type"""
- check_list=[]
+ check_list = []
for d in self.get('taxes'):
if d.tax_type:
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
- frappe.throw(_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
+ frappe.throw(
+ _("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
else:
if d.tax_type in check_list:
frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
@@ -466,12 +478,20 @@
check_list.append(d.tax_type)
def validate_barcode(self):
- if self.barcode:
- duplicate = frappe.db.sql("""select name from tabItem where barcode = %s
- and name != %s""", (self.barcode, self.name))
- if duplicate:
- frappe.throw(_("Barcode {0} already used in Item {1}").format(self.barcode, duplicate[0][0]))
+ from stdnum import ean
+ if len(self.barcodes) > 0:
+ for item_barcode in self.barcodes:
+ if item_barcode.barcode:
+ duplicate = frappe.db.sql(
+ """select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
+ if duplicate:
+ frappe.throw(_("Barcode {0} already used in Item {1}").format(
+ item_barcode.barcode, duplicate[0][0]))
+ if item_barcode.barcode_type:
+ if not ean.is_valid(item_barcode.barcode):
+ frappe.throw(_("Barcode {0} is not a valid {1} code").format(
+ item_barcode.barcode, item_barcode.barcode_type))
def validate_warehouse_for_reorder(self):
'''Validate Reorder level table for duplicate and conditional mandatory'''
@@ -483,7 +503,7 @@
warehouse += [d.get("warehouse")]
else:
frappe.throw(_("Row {0}: An Reorder entry already exists for this warehouse {1}")
- .format(d.idx, d.warehouse), DuplicateReorderRows)
+ .format(d.idx, d.warehouse), DuplicateReorderRows)
if d.warehouse_reorder_level and not d.warehouse_reorder_qty:
frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx))
@@ -497,12 +517,13 @@
def validate_name_with_item_group(self):
# causes problem with tree build
if frappe.db.exists("Item Group", self.name):
- frappe.throw(_("An Item Group exists with same name, please change the item name or rename the item group"))
+ frappe.throw(
+ _("An Item Group exists with same name, please change the item name or rename the item group"))
def update_item_price(self):
frappe.db.sql("""update `tabItem Price` set item_name=%s,
item_description=%s, modified=NOW() where item_code=%s""",
- (self.item_name, self.description, self.name))
+ (self.item_name, self.description, self.name))
def on_trash(self):
super(Item, self).on_trash()
@@ -512,7 +533,7 @@
frappe.delete_doc("Item", variant_of.name)
def before_rename(self, old_name, new_name, merge=False):
- if self.item_name==old_name:
+ if self.item_name == old_name:
frappe.db.set_value("Item", old_name, "item_name", new_name)
if merge:
@@ -524,7 +545,7 @@
new_properties = [cstr(d) for d in frappe.db.get_value("Item", new_name, field_list)]
if new_properties != [cstr(self.get(fld)) for fld in field_list]:
frappe.throw(_("To merge, following properties must be same for both items")
- + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list]))
+ + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list]))
def after_rename(self, old_name, new_name, merge):
if self.route:
@@ -547,7 +568,7 @@
item_wise_tax_detail.pop(old_name)
frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
- json.dumps(item_wise_tax_detail), update_modified=False)
+ json.dumps(item_wise_tax_detail), update_modified=False)
def set_last_purchase_rate(self, new_name):
last_purchase_rate = get_last_purchase_details(new_name).get("base_rate", 0)
@@ -575,13 +596,14 @@
self.set("website_specifications", [])
if self.item_group:
for label, desc in frappe.db.get_values("Item Website Specification",
- {"parent": self.item_group}, ["label", "description"]):
- row = self.append("website_specifications")
- row.label = label
- row.description = desc
+ {"parent": self.item_group}, ["label", "description"]):
+ row = self.append("website_specifications")
+ row.label = label
+ row.description = desc
def update_bom_item_desc(self):
- if self.is_new(): return
+ if self.is_new():
+ return
if self.db_get('description') != self.description:
frappe.db.sql("""
@@ -620,11 +642,11 @@
def update_variants(self):
if self.flags.dont_update_variants or \
- frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
+ frappe.db.get_single_value('Item Variant Settings', 'do_not_update_variants'):
return
if self.has_variants:
updated = []
- variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name })
+ variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
for d in variants:
variant = frappe.get_doc("Item", d)
copy_attributes_to_variant(self, variant)
@@ -641,39 +663,41 @@
def validate_stock_exists_for_template_item(self):
if self.stock_ledger_created() and self._doc_before_save:
if (self._doc_before_save.has_variants != self.has_variants
- or self._doc_before_save.variant_of != self.variant_of):
+ or self._doc_before_save.variant_of != self.variant_of):
frappe.throw(_("Cannot change Variant properties after stock transction. You will have to make a new Item to do this.").format(self.name),
- StockExistsForTemplate)
+ StockExistsForTemplate)
if self.has_variants or self.variant_of:
if not self.is_child_table_same('attributes'):
- frappe.throw(_('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
+ frappe.throw(
+ _('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
def validate_uom(self):
if not self.get("__islocal"):
check_stock_uom_with_bin(self.name, self.stock_uom)
if self.has_variants:
- for d in frappe.db.get_all("Item", filters= {"variant_of": self.name}):
+ for d in frappe.db.get_all("Item", filters={"variant_of": self.name}):
check_stock_uom_with_bin(d.name, self.stock_uom)
if self.variant_of:
template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
if template_uom != self.stock_uom:
frappe.throw(_("Default Unit of Measure for Variant '{0}' must be same as in Template '{1}'")
- .format(self.stock_uom, template_uom))
+ .format(self.stock_uom, template_uom))
def validate_attributes(self):
- if (self.has_variants or self.variant_of) and self.variant_based_on=='Item Attribute':
+ if (self.has_variants or self.variant_of) and self.variant_based_on == 'Item Attribute':
attributes = []
if not self.attributes:
frappe.throw(_("Attribute table is mandatory"))
for d in self.attributes:
if d.attribute in attributes:
- frappe.throw(_("Attribute {0} selected multiple times in Attributes Table".format(d.attribute)))
+ frappe.throw(
+ _("Attribute {0} selected multiple times in Attributes Table".format(d.attribute)))
else:
attributes.append(d.attribute)
def validate_variant_attributes(self):
- if self.variant_of and self.variant_based_on=='Item Attribute':
+ if self.variant_of and self.variant_based_on == 'Item Attribute':
args = {}
for d in self.attributes:
if not d.attribute_value:
@@ -683,10 +707,11 @@
variant = get_variant(self.variant_of, args, self.name)
if variant:
frappe.throw(_("Item variant {0} exists with same attributes")
- .format(variant), ItemVariantExistsError)
+ .format(variant), ItemVariantExistsError)
validate_item_variant_attributes(self, args)
+
def get_timeline_data(doctype, name):
'''returns timeline data based on stock ledger entry'''
out = {}
@@ -697,21 +722,23 @@
for date, count in items.iteritems():
timestamp = get_timestamp(date)
- out.update({ timestamp: count })
+ out.update({timestamp: count})
return out
+
def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
if (not end_of_life) or (disabled is None):
end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
- if end_of_life and end_of_life!="0000-00-00" and getdate(end_of_life) <= now_datetime().date():
+ if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date():
msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
_msgprint(msg, verbose)
if disabled:
_msgprint(_("Item {0} is disabled").format(item_code), verbose)
+
def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
if not is_stock_item:
is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
@@ -721,6 +748,7 @@
_msgprint(msg, verbose)
+
def validate_cancelled_item(item_code, docstatus=None, verbose=1):
if docstatus is None:
docstatus = frappe.db.get_value("Item", item_code, "docstatus")
@@ -729,6 +757,7 @@
msg = _("Item {0} is cancelled").format(item_code)
_msgprint(msg, verbose)
+
def _msgprint(msg, verbose):
if verbose:
msgprint(msg, raise_exception=True)
@@ -760,19 +789,19 @@
order by pr.posting_date desc, pr.posting_time desc, pr.name desc
limit 1""", (item_code, cstr(doc_name)), as_dict=1)
- purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date \
- or "1900-01-01")
- purchase_receipt_date = getdate(last_purchase_receipt and \
- last_purchase_receipt[0].posting_date or "1900-01-01")
+ purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
+ or "1900-01-01")
+ purchase_receipt_date = getdate(last_purchase_receipt and
+ last_purchase_receipt[0].posting_date or "1900-01-01")
if (purchase_order_date > purchase_receipt_date) or \
- (last_purchase_order and not last_purchase_receipt):
+ (last_purchase_order and not last_purchase_receipt):
# use purchase order
last_purchase = last_purchase_order[0]
purchase_date = purchase_order_date
elif (purchase_receipt_date > purchase_order_date) or \
- (last_purchase_receipt and not last_purchase_order):
+ (last_purchase_receipt and not last_purchase_order):
# use purchase receipt
last_purchase = last_purchase_receipt[0]
purchase_date = purchase_receipt_date
@@ -797,11 +826,12 @@
return out
+
def invalidate_cache_for_item(doc):
invalidate_cache_for(doc, doc.item_group)
website_item_groups = list(set((doc.get("old_website_item_groups") or [])
- + [d.item_group for d in doc.get({"doctype":"Website Item Group"}) if d.item_group]))
+ + [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]))
for item_group in website_item_groups:
invalidate_cache_for(doc, item_group)
@@ -809,13 +839,14 @@
if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
invalidate_cache_for(doc, doc.old_item_group)
+
def check_stock_uom_with_bin(item, stock_uom):
if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
return
- matched=True
+ matched = True
ref_uom = frappe.db.get_value("Stock Ledger Entry",
- {"item_code": item}, "stock_uom")
+ {"item_code": item}, "stock_uom")
if ref_uom:
if cstr(ref_uom) != cstr(stock_uom):
@@ -823,13 +854,14 @@
else:
bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
for bin in bin_list:
- if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
- or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
- matched = False
- break
+ if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0
+ or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
+ matched = False
+ break
if matched and bin_list:
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
if not matched:
- frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
+ frappe.throw(
+ _("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
diff --git a/erpnext/stock/doctype/item_barcode/__init__.py b/erpnext/stock/doctype/item_barcode/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/item_barcode/__init__.py
diff --git a/erpnext/stock/doctype/item_barcode/item_barcode.json b/erpnext/stock/doctype/item_barcode/item_barcode.json
new file mode 100644
index 0000000..c8a3a89
--- /dev/null
+++ b/erpnext/stock/doctype/item_barcode/item_barcode.json
@@ -0,0 +1,103 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:barcode",
+ "beta": 0,
+ "creation": "2017-12-09 18:54:50.562438",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "barcode",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 1,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Barcode",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "barcode_type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Barcode Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nEAN\nUPC-A",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2017-12-10 20:55:23.814039",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item Barcode",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item_barcode/item_barcode.py b/erpnext/stock/doctype/item_barcode/item_barcode.py
new file mode 100644
index 0000000..e85f93b
--- /dev/null
+++ b/erpnext/stock/doctype/item_barcode/item_barcode.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+from frappe.model.document import Document
+
+
+class ItemBarcode(Document):
+ pass
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index 89ece33..23a18f4 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -25,7 +25,7 @@
frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
# show/hide barcode field
- frappe.make_property_setter({'fieldname': 'barcode', 'property': 'hidden',
+ frappe.make_property_setter({'fieldname': 'barcodes', 'property': 'hidden',
'value': 0 if self.show_barcode_field else 1})
self.cant_change_valuation_method()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 6b67233..01f5206 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -120,7 +120,7 @@
@frappe.whitelist()
def get_item_code(barcode=None, serial_no=None):
if barcode:
- item_code = frappe.db.get_value("Item", {"barcode": barcode})
+ item_code = frappe.db.get_value("Item Barcode", {"barcode": barcode}, fieldname=["parent"])
if not item_code:
frappe.throw(_("No Item with Barcode {0}").format(barcode))
elif serial_no:
@@ -273,7 +273,7 @@
if not out[d[1]] or (company and args.company != company):
out[d[1]] = frappe.db.get_value("Company", args.company, d[2]) if d[2] else None
- for fieldname in ("item_name", "item_group", "barcode", "brand", "stock_uom"):
+ for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
out[fieldname] = item.get(fieldname)
return out
diff --git a/erpnext/templates/includes/cart/cart_dropdown.html b/erpnext/templates/includes/cart/cart_dropdown.html
index de29b56..38ad183 100644
--- a/erpnext/templates/includes/cart/cart_dropdown.html
+++ b/erpnext/templates/includes/cart/cart_dropdown.html
@@ -1,6 +1,11 @@
<div class="cart-dropdown-container">
<div id="cart-error" class="alert alert-danger"
style="display: none;"></div>
+ <div class="row checkout-btn">
+ <div class="col-sm-12 col-xs-12">
+ <a href="/cart" class="btn btn-block btn-primary">{{ _("Checkout") }}</a>
+ </div>
+ </div>
<div class="row cart-items-dropdown cart-item-header text-muted">
<div class="col-sm-6 col-xs-6 h6 text-uppercase">
{{ _("Item") }}
@@ -11,11 +16,10 @@
</div>
{% if doc.items %}
- <div class="cart-items-dropdown">
- {% include "templates/includes/cart/cart_items_dropdown.html" %}
- </div>
- <div class="checkout-btn">
- <a href="/cart" class="btn btn-block btn-primary">{{ _("Checkout") }}</a>
+ <div class="row cart-items-dropdown">
+ <div class="col-sm-12 col-xs-12">
+ {% include "templates/includes/cart/cart_items_dropdown.html" %}
+ </div>
</div>
{% else %}
<p>{{ _("Cart is Empty") }}</p>
diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html
index e77b8b4..c2dff8c 100644
--- a/erpnext/templates/includes/order/order_macros.html
+++ b/erpnext/templates/includes/order/order_macros.html
@@ -22,7 +22,7 @@
</div>
</div>
<div class="col-xs-8 col-sm-8">
- {{ d.item_code|truncate(25) }}
+ {{ d.item_name|truncate(25) }}
<div class="input-group number-spinner">
<span class="input-group-btn">
<button class="btn btn-default cart-btn" data-dir="dwn">
diff --git a/requirements.txt b/requirements.txt
index a6cfaf2..7b5d8da 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,3 +2,4 @@
unidecode
pygithub
googlemaps
+python-stdnum