Merge branch 'develop'

This commit is contained in:
mbauskar 2017-06-22 13:10:59 +05:30
commit 78aac9edb6
540 changed files with 35292 additions and 31643 deletions

136
.eslintrc Normal file
View File

@ -0,0 +1,136 @@
{
"env": {
"browser": true,
"node": true,
"es6": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
"tab",
{ "SwitchCase": 1 }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"off"
],
"semi": [
"warn",
"always"
],
"camelcase": [
"off"
],
"no-unused-vars": [
"warn"
],
"no-redeclare": [
"warn"
],
"no-console": [
"warn"
],
"no-extra-boolean-cast": [
"off"
],
"no-control-regex": [
"off"
],
"spaced-comment": [
"warn"
],
"no-trailing-spaces": [
"warn"
]
},
"root": true,
"globals": {
"frappe": true,
"erpnext": true,
"schools": true,
"$": true,
"jQuery": true,
"moment": true,
"hljs": true,
"Awesomplete": true,
"CalHeatMap": true,
"Sortable": true,
"Showdown": true,
"Taggle": true,
"Gantt": true,
"Slick": true,
"PhotoSwipe": true,
"PhotoSwipeUI_Default": true,
"fluxify": true,
"io": true,
"c3": true,
"__": true,
"_p": true,
"_f": true,
"repl": true,
"Class": true,
"locals": true,
"cint": true,
"cstr": true,
"cur_frm": true,
"cur_dialog": true,
"cur_page": true,
"cur_list": true,
"cur_tree": true,
"msg_dialog": true,
"is_null": true,
"in_list": true,
"has_common": true,
"has_words": true,
"validate_email": true,
"get_number_format": true,
"format_number": true,
"format_currency": true,
"round_based_on_smallest_currency_fraction": true,
"roundNumber": true,
"comment_when": true,
"replace_newlines": true,
"open_url_post": true,
"toTitle": true,
"lstrip": true,
"strip": true,
"strip_html": true,
"replace_all": true,
"flt": true,
"precision": true,
"md5": true,
"CREATE": true,
"AMEND": true,
"CANCEL": true,
"copy_dict": true,
"get_number_format_info": true,
"print_table": true,
"Layout": true,
"web_form_settings": true,
"$c": true,
"$a": true,
"$i": true,
"$bg": true,
"$y": true,
"$c_obj": true,
"$c_obj_csv": true,
"refresh_many": true,
"refresh_field": true,
"toggle_field": true,
"get_field_obj": true,
"get_query_params": true,
"unhide_field": true,
"hide_field": true,
"set_field_options": true,
"getCookie": true,
"getCookies": true,
"get_url_arg": true,
"get_server_fields": true,
"set_multiple": true
}
}

View File

@ -2,7 +2,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
__version__ = '8.0.51' __version__ = '8.1.0'
def get_default_company(user=None): def get_default_company(user=None):
@ -36,12 +36,13 @@ def get_company_currency(company):
frappe.flags.company_currency[company] = frappe.db.get_value('Company', company, 'default_currency') frappe.flags.company_currency[company] = frappe.db.get_value('Company', company, 'default_currency')
return frappe.flags.company_currency[company] return frappe.flags.company_currency[company]
def set_perpetual_inventory(enable=1, company=None):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
def set_perpetual_inventory(enable=1): company = frappe.get_doc("Company", company)
accounts_settings = frappe.get_doc("Accounts Settings") company.enable_perpetual_inventory = enable
accounts_settings.auto_accounting_for_stock = enable company.save()
accounts_settings.save()
def encode_company_abbr(name, company): def encode_company_abbr(name, company):
'''Returns name encoded with company abbreviation''' '''Returns name encoded with company abbreviation'''
@ -53,4 +54,15 @@ def encode_company_abbr(name, company):
return " - ".join(parts) return " - ".join(parts)
def is_perpetual_inventory_enabled(company):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
if not hasattr(frappe.local, 'enable_perpetual_inventory'):
frappe.local.enable_perpetual_inventory = {}
if not company in frappe.local.enable_perpetual_inventory:
frappe.local.enable_perpetual_inventory[company] = frappe.db.get_value("Company",
company, "enable_perpetual_inventory") or 0
return frappe.local.enable_perpetual_inventory[company]

View File

@ -2,26 +2,26 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.refresh = function (doc, cdt, cdn) {
if(doc.__islocal) { if (doc.__islocal) {
msgprint(__("Please create new account from Chart of Accounts.")); frappe.msgprint(__("Please create new account from Chart of Accounts."));
throw "cannot create"; throw "cannot create";
} }
cur_frm.toggle_display('account_name', doc.__islocal); cur_frm.toggle_display('account_name', doc.__islocal);
// hide fields if group // hide fields if group
cur_frm.toggle_display(['account_type', 'tax_rate'], cint(doc.is_group)==0) cur_frm.toggle_display(['account_type', 'tax_rate'], cint(doc.is_group) == 0)
// disable fields // disable fields
cur_frm.toggle_enable(['account_name', 'is_group', 'company'], false); cur_frm.toggle_enable(['account_name', 'is_group', 'company'], false);
if(cint(doc.is_group)==0) { if (cint(doc.is_group) == 0) {
cur_frm.toggle_display('freeze_account', doc.__onload && doc.__onload.can_freeze_account); cur_frm.toggle_display('freeze_account', doc.__onload && doc.__onload.can_freeze_account);
} }
// read-only for root accounts // read-only for root accounts
if(!doc.parent_account) { if (!doc.parent_account) {
cur_frm.set_read_only(); cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root account and cannot be edited.")); cur_frm.set_intro(__("This is a root account and cannot be edited."));
} else { } else {
@ -38,53 +38,53 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.add_fetch('parent_account', 'report_type', 'report_type'); cur_frm.add_fetch('parent_account', 'report_type', 'report_type');
cur_frm.add_fetch('parent_account', 'root_type', 'root_type'); cur_frm.add_fetch('parent_account', 'root_type', 'root_type');
cur_frm.cscript.account_type = function(doc, cdt, cdn) { cur_frm.cscript.account_type = function (doc, cdt, cdn) {
if(doc.is_group==0) { if (doc.is_group == 0) {
cur_frm.toggle_display(['tax_rate'], doc.account_type == 'Tax'); cur_frm.toggle_display(['tax_rate'], doc.account_type == 'Tax');
cur_frm.toggle_display('warehouse', doc.account_type=='Stock'); cur_frm.toggle_display('warehouse', doc.account_type == 'Stock');
} }
} }
cur_frm.cscript.add_toolbar_buttons = function(doc) { cur_frm.cscript.add_toolbar_buttons = function (doc) {
cur_frm.add_custom_button(__('Chart of Accounts'), cur_frm.add_custom_button(__('Chart of Accounts'),
function() { frappe.set_route("Tree", "Account"); }); function () { frappe.set_route("Tree", "Account"); });
if (doc.is_group == 1) { if (doc.is_group == 1) {
cur_frm.add_custom_button(__('Group to Non-Group'), cur_frm.add_custom_button(__('Group to Non-Group'),
function() { cur_frm.cscript.convert_to_ledger(); }, 'fa fa-retweet', 'btn-default'); function () { cur_frm.cscript.convert_to_ledger(); }, 'fa fa-retweet', 'btn-default');
} else if (cint(doc.is_group) == 0) { } else if (cint(doc.is_group) == 0) {
cur_frm.add_custom_button(__('Ledger'), function() { cur_frm.add_custom_button(__('Ledger'), function () {
frappe.route_options = { frappe.route_options = {
"account": doc.name, "account": doc.name,
"from_date": sys_defaults.year_start_date, "from_date": frappe.sys_defaults.year_start_date,
"to_date": sys_defaults.year_end_date, "to_date": frappe.sys_defaults.year_end_date,
"company": doc.company "company": doc.company
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
}); });
cur_frm.add_custom_button(__('Non-Group to Group'), cur_frm.add_custom_button(__('Non-Group to Group'),
function() { cur_frm.cscript.convert_to_group(); }, 'fa fa-retweet', 'btn-default') function () { cur_frm.cscript.convert_to_group(); }, 'fa fa-retweet', 'btn-default')
} }
} }
cur_frm.cscript.convert_to_ledger = function(doc, cdt, cdn) { cur_frm.cscript.convert_to_ledger = function (doc, cdt, cdn) {
return $c_obj(cur_frm.doc,'convert_group_to_ledger','',function(r,rt) { return $c_obj(cur_frm.doc, 'convert_group_to_ledger', '', function (r, rt) {
if(r.message == 1) { if (r.message == 1) {
cur_frm.refresh(); cur_frm.refresh();
} }
}); });
} }
cur_frm.cscript.convert_to_group = function(doc, cdt, cdn) { cur_frm.cscript.convert_to_group = function (doc, cdt, cdn) {
return $c_obj(cur_frm.doc,'convert_ledger_to_group','',function(r,rt) { return $c_obj(cur_frm.doc, 'convert_ledger_to_group', '', function (r, rt) {
if(r.message == 1) { if (r.message == 1) {
cur_frm.refresh(); cur_frm.refresh();
} }
}); });
} }
cur_frm.fields_dict['parent_account'].get_query = function(doc) { cur_frm.fields_dict['parent_account'].get_query = function (doc) {
return { return {
filters: { filters: {
"is_group": 1, "is_group": 1,

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 1, "allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"beta": 0, "beta": 0,
@ -402,35 +403,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "warehouse",
"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": "Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -545,18 +517,18 @@
"unique": 0 "unique": 0
} }
], ],
"has_web_view": 0,
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"icon": "fa fa-money", "icon": "fa fa-money",
"idx": 1, "idx": 1,
"image_view": 0, "image_view": 0,
"in_create": 0, "in_create": 0,
"in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-02-17 16:22:49.249075", "modified": "2017-04-21 17:22:41.150984",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Account", "name": "Account",

View File

@ -35,7 +35,6 @@ class Account(Document):
self.validate_group_or_ledger() self.validate_group_or_ledger()
self.set_root_and_report_type() self.set_root_and_report_type()
self.validate_mandatory() self.validate_mandatory()
self.validate_warehouse_account()
self.validate_frozen_accounts_modifier() self.validate_frozen_accounts_modifier()
self.validate_balance_must_be_debit_or_credit() self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency() self.validate_account_currency()
@ -162,46 +161,6 @@ class Account(Document):
if not self.report_type: if not self.report_type:
throw(_("Report Type is mandatory")) throw(_("Report Type is mandatory"))
def validate_warehouse_account(self):
'''If perpetual inventory is set, and warehouse is linked,
the account balance and stock balance as of now must always match.
'''
from erpnext.accounts.utils import get_balance_on
from erpnext.stock.utils import get_stock_value_on
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
return
if self.account_type == "Stock":
if self.is_group == 0 and not self.warehouse:
frappe.throw(_("Warehouse is mandatory for non group Accounts of type Stock"))
if self.warehouse:
# company must be same
if frappe.get_value('Warehouse', self.warehouse, 'company') != self.company:
frappe.throw(_("Warehouse company must be same as Account company"))
# balance must be same
stock_balance = get_stock_value_on(self.warehouse)
if self.is_new():
account_balance = 0.0
else:
account_balance = get_balance_on(self.name)
if account_balance != stock_balance:
frappe.throw(_('Account balance ({0}) for {1} and stock value ({2}) for warehouse {3} must be same')
.format(fmt_money(account_balance, currency=self.account_currency), self.name,
fmt_money(stock_balance, currency=self.account_currency), self.warehouse))
elif self.warehouse:
self.warehouse = None
def validate_warehouse(self, warehouse):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
if lft and rgt:
if frappe.db.sql_list("""select sle.name from `tabStock Ledger Entry` sle where exists (select wh.name from
tabWarehouse wh where lft >= %s and rgt <= %s and sle.warehouse = wh.name)""", (lft, rgt)):
throw(_("Stock entries exist against Warehouse {0}, hence you cannot re-assign or modify it").format(warehouse))
def update_nsm_model(self): def update_nsm_model(self):
"""update lft, rgt indices for nested set model""" """update lft, rgt indices for nested set model"""

View File

@ -37,16 +37,6 @@ frappe.treeview_settings["Account"] = {
}, },
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'), {fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'),
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'}, depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'},
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse",
depends_on: 'eval:(!doc.is_group&&doc.account_type=="Stock")',
get_query: function() {
return {
filters:{
"company": frappe.treeview_settings.filters["company"]
}
}
}
},
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency", {fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
description: __("Optional. Sets company's default currency, if not specified.")} description: __("Optional. Sets company's default currency, if not specified.")}
], ],
@ -72,8 +62,8 @@ frappe.treeview_settings["Account"] = {
click: function(node, btn) { click: function(node, btn) {
frappe.route_options = { frappe.route_options = {
"account": node.label, "account": node.label,
"from_date": sys_defaults.year_start_date, "from_date": frappe.sys_defaults.year_start_date,
"to_date": sys_defaults.year_end_date, "to_date": frappe.sys_defaults.year_end_date,
"company": frappe.defaults.get_default('company') ? frappe.defaults.get_default('company'): "" "company": frappe.defaults.get_default('company') ? frappe.defaults.get_default('company'): ""
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");

View File

@ -117,7 +117,7 @@ def get_charts_for_country(country):
def get_account_tree_from_existing_company(existing_company): def get_account_tree_from_existing_company(existing_company):
all_accounts = frappe.get_all('Account', all_accounts = frappe.get_all('Account',
filters={'company': existing_company, "warehouse": ""}, filters={'company': existing_company},
fields = ["name", "account_name", "parent_account", "account_type", fields = ["name", "account_name", "parent_account", "account_type",
"is_group", "root_type", "tax_rate"], "is_group", "root_type", "tax_rate"],
order_by="lft, rgt") order_by="lft, rgt")

View File

@ -88,7 +88,6 @@
"Items Delivered to Customs on temprary Base": {} "Items Delivered to Customs on temprary Base": {}
}, },
"Stock in Hand": { "Stock in Hand": {
"is_group": 1,
"account_type": "Stock" "account_type": "Stock"
} }
}, },

View File

@ -65,8 +65,9 @@
"account_type": "Fixed Asset" "account_type": "Fixed Asset"
}, },
"Stock": { "Stock": {
"account_type": "Stock", "Stock in Hand": {
"is_group": 1 "account_type": "Stock"
}
}, },
"root_type": "Asset" "root_type": "Asset"
}, },

View File

@ -26,8 +26,9 @@
"Earnest Money": {} "Earnest Money": {}
}, },
"Stock Assets": { "Stock Assets": {
"account_type": "Stock", "Stock in Hand": {
"is_group": 1 "account_type": "Stock"
}
}, },
"Tax Assets": { "Tax Assets": {
"is_group": 1 "is_group": 1

View File

@ -40,8 +40,9 @@
"Rental Deposits": {} "Rental Deposits": {}
}, },
"Stock Assets": { "Stock Assets": {
"is_group": 1, "Stock in Hand": {
"account_type": "Stock" "account_type": "Stock"
}
}, },
"Tax Assets": { "Tax Assets": {
"GST-Input": {} "GST-Input": {}

View File

@ -40,8 +40,9 @@
"Rental Deposits": {} "Rental Deposits": {}
}, },
"Stock Assets": { "Stock Assets": {
"account_type": "Stock", "Stock in Hand": {
"is_group": 1 "account_type": "Stock"
}
}, },
"Tax Assets": { "Tax Assets": {
"GST-Input": {} "GST-Input": {}

View File

@ -30,8 +30,10 @@ def get():
_("Earnest Money"): {} _("Earnest Money"): {}
}, },
_("Stock Assets"): { _("Stock Assets"): {
_("Stock In Hand"): {
"account_type": "Stock"
},
"account_type": "Stock", "account_type": "Stock",
"is_group": 1
}, },
_("Tax Assets"): { _("Tax Assets"): {
"is_group": 1 "is_group": 1

View File

@ -3,6 +3,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
def _make_test_records(verbose): def _make_test_records(verbose):
from frappe.test_runner import make_test_objects from frappe.test_runner import make_test_objects
@ -63,3 +65,24 @@ def _make_test_records(verbose):
} for account_name, parent_account, is_group, account_type, currency in accounts]) } for account_name, parent_account, is_group, account_type, currency in accounts])
return test_objects return test_objects
def get_inventory_account(company, warehouse=None):
account = None
if warehouse:
account = get_warehouse_account(warehouse, company)
else:
account = get_company_default_inventory_account(company)
return account
def create_account(**kwargs):
account = frappe.get_doc(dict(
doctype = "Account",
account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'),
company = kwargs.get('company')
))
account.save()
return account.name

View File

@ -14,6 +14,7 @@
"engine": "InnoDB", "engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -22,7 +23,7 @@
"description": "If enabled, the system will post accounting entries for inventory automatically.", "description": "If enabled, the system will post accounting entries for inventory automatically.",
"fieldname": "auto_accounting_for_stock", "fieldname": "auto_accounting_for_stock",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
@ -44,6 +45,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -73,6 +75,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -103,6 +106,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -131,6 +135,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -161,6 +166,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -190,6 +196,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -219,6 +226,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -249,6 +257,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -290,7 +299,7 @@
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-04-18 13:35:59.166250", "modified": "2017-06-16 17:39:50.614522",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",

View File

@ -11,24 +11,4 @@ from frappe.model.document import Document
class AccountsSettings(Document): class AccountsSettings(Document):
def on_update(self): def on_update(self):
frappe.db.set_default("auto_accounting_for_stock", self.auto_accounting_for_stock) pass
if cint(self.auto_accounting_for_stock):
# set default perpetual account in company
for company in frappe.db.sql("select name from tabCompany"):
company = frappe.get_doc("Company", company[0])
company.flags.ignore_permissions = True
company.save()
# Create account head for warehouses
warehouse_list = frappe.db.sql("""select name, company from tabWarehouse
where disabled=0""", as_dict=1)
warehouse_with_no_company = [d.name for d in warehouse_list if not d.company]
if warehouse_with_no_company:
frappe.throw(_("Company is missing in warehouses {0}")
.format(comma_and(warehouse_with_no_company)))
for wh in warehouse_list:
wh_doc = frappe.get_doc("Warehouse", wh.name)
wh_doc.flags.ignore_permissions = True
wh_doc.save()

View File

@ -75,7 +75,7 @@ frappe.ui.form.on('Asset', {
$.each(frm.doc.schedules || [], function(i, v) { $.each(frm.doc.schedules || [], function(i, v) {
x_intervals.push(v.schedule_date); x_intervals.push(v.schedule_date);
asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount); var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount);
if(v.journal_entry) { if(v.journal_entry) {
last_depreciation_date = v.schedule_date; last_depreciation_date = v.schedule_date;
asset_values.push(asset_value) asset_values.push(asset_value)
@ -187,7 +187,7 @@ frappe.ui.form.on('Depreciation Schedule', {
erpnext.asset.set_accululated_depreciation = function(frm) { erpnext.asset.set_accululated_depreciation = function(frm) {
if(frm.doc.depreciation_method != "Manual") return; if(frm.doc.depreciation_method != "Manual") return;
accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation); var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
$.each(frm.doc.schedules || [], function(i, row) { $.each(frm.doc.schedules || [], function(i, row) {
accumulated_depreciation += flt(row.depreciation_amount); accumulated_depreciation += flt(row.depreciation_amount);
frappe.model.set_value(row.doctype, row.name, frappe.model.set_value(row.doctype, row.name,
@ -285,7 +285,7 @@ erpnext.asset.transfer_asset = function(frm) {
}); });
dialog.set_primary_action(__("Transfer"), function() { dialog.set_primary_action(__("Transfer"), function() {
args = dialog.get_values(); var args = dialog.get_values();
if(!args) return; if(!args) return;
dialog.hide(); dialog.hide();
return frappe.call({ return frappe.call({

View File

@ -19,6 +19,7 @@ class Asset(Document):
self.validate_asset_values() self.validate_asset_values()
self.make_depreciation_schedule() self.make_depreciation_schedule()
self.set_accumulated_depreciation() self.set_accumulated_depreciation()
if self.get("schedules"):
self.validate_expected_value_after_useful_life() self.validate_expected_value_after_useful_life()
# Validate depreciation related accounts # Validate depreciation related accounts
get_depreciation_accounts(self) get_depreciation_accounts(self)

View File

@ -20,11 +20,11 @@ frappe.ui.form.on('Bank Guarantee', {
}); });
}, },
start_date: function(frm) { start_date: function(frm) {
end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1); var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
cur_frm.set_value("end_date", end_date); cur_frm.set_value("end_date", end_date);
}, },
validity: function(frm) { validity: function(frm) {
end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1); var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
cur_frm.set_value("end_date", end_date); cur_frm.set_value("end_date", end_date);
} }
}); });

View File

@ -43,7 +43,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.parent_cost_center = function(doc, cdt, cdn) { cur_frm.cscript.parent_cost_center = function(doc, cdt, cdn) {
if(!doc.company){ if(!doc.company){
msgprint(__('Please enter company name first')); frappe.msgprint(__('Please enter company name first'));
} }
} }

View File

@ -13,7 +13,7 @@ $.extend(cur_frm.cscript, {
this.frm.toggle_enable('year_start_date', doc.__islocal) this.frm.toggle_enable('year_start_date', doc.__islocal)
this.frm.toggle_enable('year_end_date', doc.__islocal) this.frm.toggle_enable('year_end_date', doc.__islocal)
if (!doc.__islocal && (doc.name != sys_defaults.fiscal_year)) { if (!doc.__islocal && (doc.name != frappe.sys_defaults.fiscal_year)) {
this.frm.add_custom_button(__("Default"), this.frm.add_custom_button(__("Default"),
this.frm.cscript.set_as_default, "fa fa-star"); this.frm.cscript.set_as_default, "fa fa-star");
this.frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); this.frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
@ -30,7 +30,7 @@ $.extend(cur_frm.cscript, {
year_start_date: function(doc, dt, dn) { year_start_date: function(doc, dt, dn) {
var me = this; var me = this;
year_end_date = var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(this.frm.doc.year_start_date, 12), -1); frappe.datetime.add_days(frappe.datetime.add_months(this.frm.doc.year_start_date, 12), -1);
this.frm.set_value("year_end_date", year_end_date); this.frm.set_value("year_end_date", year_end_date);
}, },

View File

@ -63,10 +63,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
frappe.model.set_default_values(this.frm.doc); frappe.model.set_default_values(this.frm.doc);
$.each(this.frm.doc.accounts || [], function(i, jvd) { $.each(this.frm.doc.accounts || [], function(i, jvd) {
frappe.model.set_default_values(jvd); frappe.model.set_default_values(jvd);
} });
);
if(!this.frm.doc.amended_from) this.frm.doc.posting_date = this.frm.posting_date || get_today(); if(!this.frm.doc.amended_from) this.frm.doc.posting_date = this.frm.posting_date || frappe.datetime.get_today();
} }
}, },
@ -124,7 +123,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// account filter // account filter
frappe.model.validate_missing(jvd, "account"); frappe.model.validate_missing(jvd, "account");
party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to"; var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]); out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
} else { } else {
// party_type and party mandatory // party_type and party mandatory

View File

@ -1311,7 +1311,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:inList([\"Credit Note\", \"Debit Note\"], doc.voucher_type)", "depends_on": "eval:in_list([\"Credit Note\", \"Debit Note\"], doc.voucher_type)",
"fieldname": "stock_entry", "fieldname": "stock_entry",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,

View File

@ -871,14 +871,9 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name: if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate") exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
elif account_details and account_details.account_type == "Bank" and \
((account_details.root_type == "Asset" and flt(credit) > 0) or
(account_details.root_type == "Liability" and debit)):
exchange_rate = get_average_exchange_rate(account)
# The date used to retreive the exchange rate here is the date passed # The date used to retreive the exchange rate here is the date passed
# in as an argument to this function. # in as an argument to this function.
if not exchange_rate and account_currency and posting_date: elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date) exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
else: else:
exchange_rate = 1 exchange_rate = 1

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import unittest, frappe import unittest, frappe
from frappe.utils import flt from frappe.utils import flt
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.exceptions import InvalidAccountCurrency from erpnext.exceptions import InvalidAccountCurrency
@ -83,7 +84,8 @@ class TestJournalEntry(unittest.TestCase):
jv = frappe.copy_doc(test_records[0]) jv = frappe.copy_doc(test_records[0])
jv.get("accounts")[0].update({ jv.get("accounts")[0].update({
"account": "_Test Warehouse - _TC", "account": get_inventory_account('_Test Company'),
"company": "_Test Company",
"party_type": None, "party_type": None,
"party": None "party": None
}) })

View File

@ -25,4 +25,5 @@ class ModeofPayment(Document):
for entry in self.accounts: for entry in self.accounts:
"""Error when Company of Ledger account doesn't match with Company Selected""" """Error when Company of Ledger account doesn't match with Company Selected"""
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company: if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
frappe.throw(_("Account does not match with Company")) frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
.format(entry.default_account, entry.company, self.name))

View File

@ -7,7 +7,7 @@ cur_frm.cscript.onload = function(doc,cdt,cdn){
refresh_field('percentages'); refresh_field('percentages');
} }
return $c('runserverobj',args={'method':'get_months', 'docs':doc}, callback1); return $c('runserverobj', {'method':'get_months', 'docs':doc}, callback1);
} }
} }

View File

@ -197,7 +197,7 @@ frappe.ui.form.on('Payment Entry', {
$.each(["party", "party_balance", "paid_from", "paid_to", $.each(["party", "party_balance", "paid_from", "paid_to",
"references", "total_allocated_amount"], function(i, field) { "references", "total_allocated_amount"], function(i, field) {
frm.set_value(field, null); frm.set_value(field, null);
}) });
} else { } else {
if(!frm.doc.party) if(!frm.doc.party)
frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier"); frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier");
@ -214,7 +214,8 @@ frappe.ui.form.on('Payment Entry', {
$.each(["party", "party_balance", "paid_from", "paid_to", $.each(["party", "party_balance", "paid_from", "paid_to",
"paid_from_account_currency", "paid_from_account_balance", "paid_from_account_currency", "paid_from_account_balance",
"paid_to_account_currency", "paid_to_account_balance", "paid_to_account_currency", "paid_to_account_balance",
"references", "total_allocated_amount"], function(i, field) { "references", "total_allocated_amount"],
function(i, field) {
frm.set_value(field, null); frm.set_value(field, null);
}) })
} }
@ -331,10 +332,12 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("source_exchange_rate", 1); frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from){ } else if (frm.doc.paid_from){
if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) { if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) {
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_average_exchange_rate", method: "erpnext.setup.utils.get_exchange_rate",
args: { args: {
account: frm.doc.paid_from from_currency: frm.doc.paid_from_account_currency,
to_currency: company_currency
}, },
callback: function(r, rt) { callback: function(r, rt) {
frm.set_value("source_exchange_rate", r.message); frm.set_value("source_exchange_rate", r.message);
@ -501,13 +504,17 @@ frappe.ui.form.on('Payment Entry', {
} }
}); });
if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") || if(
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) { (frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")
) {
if(total_positive_outstanding > total_negative_outstanding) if(total_positive_outstanding > total_negative_outstanding)
frm.set_value("paid_amount", frm.set_value("paid_amount",
total_positive_outstanding - total_negative_outstanding); total_positive_outstanding - total_negative_outstanding);
} else if (total_negative_outstanding && } else if (
(total_positive_outstanding < total_negative_outstanding)) { total_negative_outstanding &&
total_positive_outstanding < total_negative_outstanding
) {
frm.set_value("received_amount", frm.set_value("received_amount",
total_negative_outstanding - total_positive_outstanding); total_negative_outstanding - total_positive_outstanding);
} }
@ -575,9 +582,11 @@ frappe.ui.form.on('Payment Entry', {
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
if(frm.doc.allocate_payment_amount){ if(frm.doc.allocate_payment_amount){
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) { if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
if(row.outstanding_amount >= allocated_positive_outstanding) if(row.outstanding_amount >= allocated_positive_outstanding) {
row.allocated_amount = allocated_positive_outstanding; row.allocated_amount = allocated_positive_outstanding;
else row.allocated_amount = row.outstanding_amount; } else {
row.allocated_amount = row.outstanding_amount;
}
allocated_positive_outstanding -= flt(row.allocated_amount); allocated_positive_outstanding -= flt(row.allocated_amount);
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) { } else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
@ -595,7 +604,8 @@ frappe.ui.form.on('Payment Entry', {
}, },
set_total_allocated_amount: function(frm) { set_total_allocated_amount: function(frm) {
var total_allocated_amount = base_total_allocated_amount = 0.0; var total_allocated_amount = 0.0;
var base_total_allocated_amount = 0.0;
$.each(frm.doc.references || [], function(i, row) { $.each(frm.doc.references || [], function(i, row) {
if (row.allocated_amount) { if (row.allocated_amount) {
total_allocated_amount += flt(row.allocated_amount); total_allocated_amount += flt(row.allocated_amount);
@ -667,15 +677,17 @@ frappe.ui.form.on('Payment Entry', {
return; return;
} }
if(frm.doc.party_type=="Customer" if(frm.doc.party_type=="Customer" &&
&& !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype)) { !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype)
) {
frappe.model.set_value(row.doctype, row.name, "reference_doctype", null); frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx])); frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx]));
return false; return false;
} }
if(frm.doc.party_type=="Supplier" && !in_list(["Purchase Order", if(frm.doc.party_type=="Supplier" &&
"Purchase Invoice", "Journal Entry"], row.reference_doctype)) { !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.reference_doctype)
) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null); frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx])); frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
return false; return false;

View File

@ -48,6 +48,8 @@ class PaymentEntry(AccountsController):
self.validate_transaction_reference() self.validate_transaction_reference()
self.set_title() self.set_title()
self.set_remarks() self.set_remarks()
self.validate_duplicate_entry()
self.validate_allocated_amount()
def on_submit(self): def on_submit(self):
self.setup_party_account_field() self.setup_party_account_field()
@ -62,6 +64,21 @@ class PaymentEntry(AccountsController):
self.update_advance_paid() self.update_advance_paid()
self.delink_advance_entry_references() self.delink_advance_entry_references()
def validate_duplicate_entry(self):
reference_names = []
for d in self.get("references"):
if (d.reference_doctype, d.reference_name) in reference_names:
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}").format(d.idx, d.reference_doctype, d.reference_name))
reference_names.append((d.reference_doctype, d.reference_name))
def validate_allocated_amount(self):
for d in self.get("references"):
if (flt(d.allocated_amount))> 0:
if flt(d.allocated_amount) > flt(d.outstanding_amount):
frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
def delink_advance_entry_references(self): def delink_advance_entry_references(self):
for reference in self.references: for reference in self.references:
if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"): if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
@ -149,8 +166,6 @@ class PaymentEntry(AccountsController):
if self.paid_from and not self.source_exchange_rate: if self.paid_from and not self.source_exchange_rate:
if self.paid_from_account_currency == self.company_currency: if self.paid_from_account_currency == self.company_currency:
self.source_exchange_rate = 1 self.source_exchange_rate = 1
elif self.payment_type in ("Pay", "Internal Transfer"):
self.source_exchange_rate = get_average_exchange_rate(self.paid_from)
else: else:
self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency, self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency,
self.company_currency, self.posting_date) self.company_currency, self.posting_date)

View File

@ -113,6 +113,34 @@ class TestPaymentEntry(unittest.TestCase):
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 80) self.assertEqual(outstanding_amount, 80)
def test_payment_entry_retrieves_last_exchange_rate(self):
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
test_records = test_records
save_new_records(test_records)
pe = frappe.new_doc("Payment Entry")
pe.payment_type = "Pay"
pe.company = "_Test Company"
pe.posting_date = "2016-01-10"
pe.paid_from = "_Test Bank USD - _TC"
pe.paid_to = "_Test Bank - _TC"
pe.paid_amount = 100
pe.reference_no = "3"
pe.reference_date = "2016-01-10"
pe.party_type = "Supplier"
pe.party = "_Test Supplier USD"
pe.setup_party_account_field()
pe.set_missing_values()
pe.set_exchange_rate()
pe.set_amounts()
self.assertEqual(
pe.source_exchange_rate, 65.1,
"{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
)
def test_internal_transfer_usd_to_inr(self): def test_internal_transfer_usd_to_inr(self):
pe = frappe.new_doc("Payment Entry") pe = frappe.new_doc("Payment Entry")
pe.payment_type = "Internal Transfer" pe.payment_type = "Internal Transfer"

View File

@ -14,7 +14,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
this.frm.set_query('receivable_payable_account', function() { this.frm.set_query('receivable_payable_account', function() {
if(!me.frm.doc.company || !me.frm.doc.party_type) { if(!me.frm.doc.company || !me.frm.doc.party_type) {
msgprint(__("Please select Company and Party Type first")); frappe.msgprint(__("Please select Company and Party Type first"));
} else { } else {
return{ return{
filters: { filters: {
@ -29,7 +29,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
this.frm.set_query('bank_cash_account', function() { this.frm.set_query('bank_cash_account', function() {
if(!me.frm.doc.company) { if(!me.frm.doc.company) {
msgprint(__("Please select Company first")); frappe.msgprint(__("Please select Company first"));
} else { } else {
return{ return{
filters:[ filters:[
@ -96,10 +96,11 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
}, },
set_invoice_options: function() { set_invoice_options: function() {
var me = this;
var invoices = []; var invoices = [];
$.each(me.frm.doc.invoices || [], function(i, row) { $.each(me.frm.doc.invoices || [], function(i, row) {
if (row.invoice_number && !inList(invoices, row.invoice_number)) if (row.invoice_number && !in_list(invoices, row.invoice_number))
invoices.push(row.invoice_type + " | " + row.invoice_number); invoices.push(row.invoice_type + " | " + row.invoice_number);
}); });
@ -108,7 +109,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
me.frm.doc.name).options = "\n" + invoices.join("\n"); me.frm.doc.name).options = "\n" + invoices.join("\n");
$.each(me.frm.doc.payments || [], function(i, p) { $.each(me.frm.doc.payments || [], function(i, p) {
if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null; if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
}); });
} }

View File

@ -87,8 +87,8 @@ class TestPaymentRequest(unittest.TestCase):
expected_gle = dict((d[0], d) for d in [ expected_gle = dict((d[0], d) for d in [
["_Test Receivable USD - _TC", 0, 5000, si_usd.name], ["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
[pr.payment_account, 6000.0, 0, None], [pr.payment_account, 6290.0, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 1000, None] ["_Test Exchange Gain/Loss - _TC", 0, 1290, None]
]) ])
gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher

View File

@ -3,7 +3,7 @@
frappe.ui.form.on('Period Closing Voucher', { frappe.ui.form.on('Period Closing Voucher', {
onload: function(frm) { onload: function(frm) {
if (!frm.doc.transaction_date) frm.doc.transaction_date = dateutil.obj_to_str(new Date()); if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date());
}, },
setup: function(frm) { setup: function(frm) {

View File

@ -26,6 +26,23 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
}); });
}); });
frappe.ui.form.on('POS Profile', {
refresh: function(frm) {
if(frm.doc.company) {
frm.trigger("toggle_display_account_head");
}
},
company: function(frm) {
frm.trigger("toggle_display_account_head");
},
toggle_display_account_head: function(frm) {
frm.toggle_display('expense_account',
erpnext.is_perpetual_inventory_enabled(frm.doc.company));
}
})
// Income Account // Income Account
// -------------------------------- // --------------------------------
cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) { cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) {
@ -35,8 +52,8 @@ cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) {
'company': doc.company, 'company': doc.company,
'account_type': "Income Account" 'account_type': "Income Account"
} }
} };
} };
// Cost Center // Cost Center
@ -47,8 +64,8 @@ cur_frm.fields_dict['cost_center'].get_query = function(doc,cdt,cdn) {
'company': doc.company, 'company': doc.company,
'is_group': 0 'is_group': 0
} }
} };
} };
// Expense Account // Expense Account
@ -60,8 +77,8 @@ cur_frm.fields_dict["expense_account"].get_query = function(doc) {
"company": doc.company, "company": doc.company,
"is_group": 0 "is_group": 0
} }
} };
} };
// ------------------ Get Print Heading ------------------------------------ // ------------------ Get Print Heading ------------------------------------
cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) {
@ -69,13 +86,13 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn)
filters:[ filters:[
['Print Heading', 'docstatus', '!=', 2] ['Print Heading', 'docstatus', '!=', 2]
] ]
} };
} };
cur_frm.fields_dict.user.get_query = function(doc,cdt,cdn) { cur_frm.fields_dict.user.get_query = function(doc,cdt,cdn) {
return{ query:"frappe.core.doctype.user.user.user_query"} return{ query:"frappe.core.doctype.user.user.user_query"};
} };
cur_frm.fields_dict.write_off_account.get_query = function(doc) { cur_frm.fields_dict.write_off_account.get_query = function(doc) {
return{ return{
@ -84,16 +101,16 @@ cur_frm.fields_dict.write_off_account.get_query = function(doc) {
'is_group': 0, 'is_group': 0,
'company': doc.company 'company': doc.company
} }
} };
} };
// Write off cost center // Write off cost center
//----------------------- // -----------------------
cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) {
return{ return{
filters:{ filters:{
'is_group': 0, 'is_group': 0,
'company': doc.company 'company': doc.company
} }
} };
} };

View File

@ -40,7 +40,7 @@
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 1,
"unique": 0 "unique": 0
}, },
{ {
@ -1132,7 +1132,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)", "depends_on": "",
"fieldname": "expense_account", "fieldname": "expense_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1201,7 +1201,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-06-13 14:29:06.317317", "modified": "2017-06-16 17:04:33.165676",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Profile", "name": "POS Profile",

View File

@ -2,63 +2,65 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Pricing Rule", "refresh", function(frm) { frappe.ui.form.on("Pricing Rule", "refresh", function(frm) {
var help_content = ['<table class="table table-bordered" style="background-color: #f9f9f9;">', var help_content =
'<tr><td>', `<table class="table table-bordered" style="background-color: #f9f9f9;">
'<h4><i class="fa fa-hand-right"></i> ', <tr><td>
__('Notes'), <h4>
':</h4>', <i class="fa fa-hand-right"></i>
'<ul>', ${__('Notes')}
'<li>', </h4>
__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria."), <ul>
'</li>', <li>
'<li>', ${__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")}
__("If selected Pricing Rule is made for 'Price', it will overwrite Price List. Pricing Rule price is the final price, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field."), </li>
'</li>', <li>
'<li>', ${__("If selected Pricing Rule is made for 'Price', it will overwrite Price List. Pricing Rule price is the final price, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")}
__('Discount Percentage can be applied either against a Price List or for all Price List.'), </li>
'</li>', <li>
'<li>', ${__('Discount Percentage can be applied either against a Price List or for all Price List.')}
__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.'), </li>
'</li>', <li>
'</ul>', ${__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')}
'</td></tr>', </li>
'<tr><td>', </ul>
'<h4><i class="fa fa-question-sign"></i> ', </td></tr>
__('How Pricing Rule is applied?'), <tr><td>
'</h4>', <h4><i class="fa fa-question-sign"></i>
'<ol>', ${__('How Pricing Rule is applied?')}
'<li>', </h4>
__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand."), <ol>
'</li>', <li>
'<li>', ${__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")}
__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc."), </li>
'</li>', <li>
'<li>', ${__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.")}
__('Pricing Rules are further filtered based on quantity.'), </li>
'</li>', <li>
'<li>', ${__('Pricing Rules are further filtered based on quantity.')}
__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.'), </li>
'</li>', <li>
'<li>', ${__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')}
__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:'), </li>
'<ul>', <li>
'<li>', ${__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')}
__('Item Code > Item Group > Brand'), <ul>
'</li>', <li>
'<li>', ${__('Item Code > Item Group > Brand')}
__('Customer > Customer Group > Territory'), </li>
'</li>', <li>
'<li>', ${__('Customer > Customer Group > Territory')}
__('Supplier > Supplier Type'), </li>
'</li>', <li>
'</ul>', ${__('Supplier > Supplier Type')}
'</li>', </li>
'<li>', </ul>
__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.'), </li>
'</li>', <li>
'</ol>', ${__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')}
'</td></tr>', </li>
'</table>'].join("\n"); </ol>
</td></tr>
</table>`;
set_field_options("pricing_rule_help", help_content); set_field_options("pricing_rule_help", help_content);

View File

@ -130,7 +130,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
if(cint(this.frm.doc.is_paid)) { if(cint(this.frm.doc.is_paid)) {
if(!this.frm.doc.company) { if(!this.frm.doc.company) {
this.frm.set_value("is_paid", 0) this.frm.set_value("is_paid", 0)
msgprint(__("Please specify Company to proceed")); frappe.msgprint(__("Please specify Company to proceed"));
} }
} }
this.calculate_outstanding_amount(); this.calculate_outstanding_amount();
@ -195,19 +195,19 @@ cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
// Hide Fields // Hide Fields
// ------------ // ------------
function hide_fields(doc) { function hide_fields(doc) {
parent_fields = ['due_date', 'is_opening', 'advances_section', 'from_date', 'to_date']; var parent_fields = ['due_date', 'is_opening', 'advances_section', 'from_date', 'to_date'];
if(cint(doc.is_paid) == 1) { if(cint(doc.is_paid) == 1) {
hide_field(parent_fields); hide_field(parent_fields);
} else { } else {
for (i in parent_fields) { for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]]; var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]); if(!docfield.hidden) unhide_field(parent_fields[i]);
} }
} }
item_fields_stock = ['warehouse_section', 'received_qty', 'rejected_qty']; var item_fields_stock = ['warehouse_section', 'received_qty', 'rejected_qty'];
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock, cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
(cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false)); (cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false));

View File

@ -11,7 +11,7 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency, get_fiscal_year from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.controllers.stock_controller import get_warehouse_account from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_for_closed_status from erpnext.buying.utils import check_for_closed_status
@ -59,6 +59,7 @@ class PurchaseInvoice(BuyingController):
self.check_for_closed_status() self.check_for_closed_status()
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.set_expense_account(for_validate=True) self.set_expense_account(for_validate=True)
self.set_against_expense_account() self.set_against_expense_account()
self.validate_write_off_account() self.validate_write_off_account()
@ -163,7 +164,7 @@ class PurchaseInvoice(BuyingController):
frappe.msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) frappe.msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
def set_expense_account(self, for_validate=False): def set_expense_account(self, for_validate=False):
auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if auto_accounting_for_stock: if auto_accounting_for_stock:
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed") stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
@ -172,7 +173,7 @@ class PurchaseInvoice(BuyingController):
if self.update_stock: if self.update_stock:
self.validate_item_code() self.validate_item_code()
self.validate_warehouse() self.validate_warehouse()
warehouse_account = get_warehouse_account() warehouse_account = get_warehouse_account_map()
for item in self.get("items"): for item in self.get("items"):
# in case of auto inventory accounting, # in case of auto inventory accounting,
@ -185,7 +186,7 @@ class PurchaseInvoice(BuyingController):
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock: if self.update_stock:
item.expense_account = warehouse_account[item.warehouse]["name"] item.expense_account = warehouse_account[item.warehouse]["account"]
else: else:
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
@ -204,14 +205,14 @@ class PurchaseInvoice(BuyingController):
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes': if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
for d in self.get('items'): for d in self.get('items'):
if not d.purchase_order: if not d.purchase_order:
throw(_("Purchase Order number required for Item {0}").format(d.item_code)) throw(_("As per the Buying Settings if Purchase Order Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Order first for item {0}").format(d.item_code))
def pr_required(self): def pr_required(self):
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes': if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
for d in self.get('items'): for d in self.get('items'):
if not d.purchase_receipt and d.item_code in stock_items: if not d.purchase_receipt and d.item_code in stock_items:
throw(_("Purchase Receipt number required for Item {0}").format(d.item_code)) throw(_("As per the Buying Settings if Purchase Reciept Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Receipt first for item {0}").format(d.item_code))
def validate_write_off_account(self): def validate_write_off_account(self):
if self.write_off_amount and not self.write_off_account: if self.write_off_amount and not self.write_off_account:
@ -334,9 +335,7 @@ class PurchaseInvoice(BuyingController):
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None): def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = \ self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed") self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
self.negative_expense_to_be_booked = 0.0 self.negative_expense_to_be_booked = 0.0
@ -377,7 +376,7 @@ class PurchaseInvoice(BuyingController):
# item gl entries # item gl entries
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
warehouse_account = get_warehouse_account() warehouse_account = get_warehouse_account_map()
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import unittest import unittest
import frappe import frappe, erpnext
import frappe.model import frappe.model
from frappe.utils import cint, flt, today, nowdate from frappe.utils import cint, flt, today, nowdate
import frappe.defaults import frappe.defaults
@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
test_records as pr_test_records test_records as pr_test_records
from erpnext.exceptions import InvalidCurrency from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
from erpnext.accounts.doctype.account.test_account import get_inventory_account
test_dependencies = ["Item", "Cost Center"] test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"] test_ignore = ["Serial No"]
@ -24,11 +25,10 @@ class TestPurchaseInvoice(unittest.TestCase):
def tearDown(self): def tearDown(self):
unlink_payment_on_cancel_of_invoice(0) unlink_payment_on_cancel_of_invoice(0)
def test_gl_entries_without_auto_accounting_for_stock(self): def test_gl_entries_without_perpetual_inventory(self):
set_perpetual_inventory(0)
self.assertTrue(not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")))
wrapper = frappe.copy_doc(test_records[0]) wrapper = frappe.copy_doc(test_records[0])
set_perpetual_inventory(0, wrapper.company)
self.assertTrue(not cint(erpnext.is_perpetual_inventory_enabled(wrapper.company)))
wrapper.insert() wrapper.insert()
wrapper.submit() wrapper.submit()
wrapper.load_from_db() wrapper.load_from_db()
@ -50,17 +50,16 @@ class TestPurchaseInvoice(unittest.TestCase):
for d in gl_entries: for d in gl_entries:
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account)) self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
def test_gl_entries_with_auto_accounting_for_stock(self): def test_gl_entries_with_perpetual_inventory(self):
set_perpetual_inventory(1)
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
pi = frappe.copy_doc(test_records[1]) pi = frappe.copy_doc(test_records[1])
set_perpetual_inventory(1, pi.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
pi.insert() pi.insert()
pi.submit() pi.submit()
self.check_gle_for_pi(pi.name) self.check_gle_for_pi(pi.name)
set_perpetual_inventory(0) set_perpetual_inventory(0, pi.company)
def test_payment_entry_unlink_against_purchase_invoice(self): def test_payment_entry_unlink_against_purchase_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
@ -83,11 +82,10 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel) self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
def test_gl_entries_with_auto_accounting_for_stock_against_pr(self): def test_gl_entries_with_perpetual_inventory_against_pr(self):
set_perpetual_inventory(1)
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
pr = frappe.copy_doc(pr_test_records[0]) pr = frappe.copy_doc(pr_test_records[0])
set_perpetual_inventory(1, pr.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
pr.submit() pr.submit()
pi = frappe.copy_doc(test_records[1]) pi = frappe.copy_doc(test_records[1])
@ -98,7 +96,7 @@ class TestPurchaseInvoice(unittest.TestCase):
self.check_gle_for_pi(pi.name) self.check_gle_for_pi(pi.name)
set_perpetual_inventory(0) set_perpetual_inventory(0, pr.company)
def check_gle_for_pi(self, pi): def check_gle_for_pi(self, pi):
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
@ -132,10 +130,9 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertRaises(frappe.CannotChangeConstantError, pi.save) self.assertRaises(frappe.CannotChangeConstantError, pi.save)
def test_gl_entries_with_aia_for_non_stock_items(self): def test_gl_entries_with_aia_for_non_stock_items(self):
set_perpetual_inventory()
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
pi = frappe.copy_doc(test_records[1]) pi = frappe.copy_doc(test_records[1])
set_perpetual_inventory(1, pi.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
pi.get("items")[0].item_code = "_Test Non Stock Item" pi.get("items")[0].item_code = "_Test Non Stock Item"
pi.get("items")[0].expense_account = "_Test Account Cost for Goods Sold - _TC" pi.get("items")[0].expense_account = "_Test Account Cost for Goods Sold - _TC"
pi.get("taxes").pop(0) pi.get("taxes").pop(0)
@ -158,7 +155,7 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEquals(expected_values[i][0], gle.account) self.assertEquals(expected_values[i][0], gle.account)
self.assertEquals(expected_values[i][1], gle.debit) self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit) self.assertEquals(expected_values[i][2], gle.credit)
set_perpetual_inventory(0) set_perpetual_inventory(0, pi.company)
def test_purchase_invoice_calculation(self): def test_purchase_invoice_calculation(self):
pi = frappe.copy_doc(test_records[0]) pi = frappe.copy_doc(test_records[0])
@ -370,10 +367,11 @@ class TestPurchaseInvoice(unittest.TestCase):
order by account asc""", pi.name, as_dict=1) order by account asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
expected_gl_entries = dict((d[0], d) for d in [ expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 0.0, 250.0], [pi.credit_to, 0.0, 250.0],
[pi.items[0].warehouse, 250.0, 0.0] [stock_in_hand_account, 250.0, 0.0]
]) ])
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
@ -391,11 +389,12 @@ class TestPurchaseInvoice(unittest.TestCase):
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
group by account, voucher_no order by account asc;""", pi.name, as_dict=1) group by account, voucher_no order by account asc;""", pi.name, as_dict=1)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_gl_entries = dict((d[0], d) for d in [ expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 250.0, 250.0], [pi.credit_to, 250.0, 250.0],
[pi.items[0].warehouse, 250.0, 0.0], [stock_in_hand_account, 250.0, 0.0],
["Cash - _TC", 0.0, 250.0] ["Cash - _TC", 0.0, 250.0]
]) ])

View File

@ -9,11 +9,11 @@ frappe.ui.form.on("Purchase Taxes and Charges", "add_deduct_tax", function(doc,
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
if(!d.category && d.add_deduct_tax) { if(!d.category && d.add_deduct_tax) {
msgprint(__("Please select Category first")); frappe.msgprint(__("Please select Category first"));
d.add_deduct_tax = ''; d.add_deduct_tax = '';
} }
else if(d.category != 'Total' && d.add_deduct_tax == 'Deduct') { else if(d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'")); frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'"));
d.add_deduct_tax = ''; d.add_deduct_tax = '';
} }
refresh_field('add_deduct_tax', d.name, 'taxes'); refresh_field('add_deduct_tax', d.name, 'taxes');
@ -23,7 +23,7 @@ frappe.ui.form.on("Purchase Taxes and Charges", "category", function(doc, cdt, c
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
if (d.category != 'Total' && d.add_deduct_tax == 'Deduct') { if (d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
msgprint(__("Cannot deduct when category is for 'Valuation' or 'Vaulation and Total'")); frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Vaulation and Total'"));
d.add_deduct_tax = ''; d.add_deduct_tax = '';
} }
refresh_field('add_deduct_tax', d.name, 'taxes'); refresh_field('add_deduct_tax', d.name, 'taxes');

View File

@ -267,7 +267,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
if(this.frm.doc.is_pos) { if(this.frm.doc.is_pos) {
if(!this.frm.doc.company) { if(!this.frm.doc.company) {
this.frm.set_value("is_pos", 0); this.frm.set_value("is_pos", 0);
msgprint(__("Please specify Company to proceed")); frappe.msgprint(__("Please specify Company to proceed"));
} else { } else {
var me = this; var me = this;
return this.frm.call({ return this.frm.call({
@ -312,19 +312,20 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_
// Hide Fields // Hide Fields
// ------------ // ------------
cur_frm.cscript.hide_fields = function(doc) { cur_frm.cscript.hide_fields = function(doc) {
parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances', var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date']; 'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) { if(cint(doc.is_pos) == 1) {
hide_field(parent_fields); hide_field(parent_fields);
} else { } else {
for (i in parent_fields) { for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]]; var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]); if(!docfield.hidden) unhide_field(parent_fields[i]);
} }
} }
item_fields_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse', 'expense_account', 'warehouse'] var item_fields_stock = ['batch_no', 'actual_batch_qty', 'actual_qty', 'expense_account',
'warehouse', 'expense_account', 'quality_inspection']
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock, cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
(cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false)); (cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false));
@ -399,19 +400,6 @@ cur_frm.set_query("income_account", "items", function(doc) {
} }
}); });
// expense account
if (sys_defaults.auto_accounting_for_stock) {
cur_frm.fields_dict['items'].grid.get_field('expense_account').get_query = function(doc) {
return {
filters: {
'report_type': 'Profit and Loss',
'company': doc.company,
"is_group": 0
}
}
}
}
// Cost Center in Details Table // Cost Center in Details Table
// ----------------------------- // -----------------------------
@ -442,11 +430,12 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
}) })
if(cur_frm.doc.is_pos) { if(cur_frm.doc.is_pos) {
cur_frm.msgbox = frappe.msgprint(format('<a class="btn btn-primary" \ cur_frm.msgbox = frappe.msgprint(
onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">{0}</a>\ `<a class="btn btn-primary" onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">
<a class="btn btn-default" href="javascript:frappe.new_doc(cur_frm.doctype);">{1}</a>', [ ${__('Print')}</a>
__('Print'), __('New') <a class="btn btn-default" href="javascript:frappe.new_doc(cur_frm.doctype);">
])); ${__('New')}</a>`
);
} else if(cint(frappe.boot.notification_settings.sales_invoice)) { } else if(cint(frappe.boot.notification_settings.sales_invoice)) {
cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message); cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message);
@ -500,6 +489,21 @@ frappe.ui.form.on('Sales Invoice', {
filters: {'project': doc.project} filters: {'project': doc.project}
} }
} }
// expense account
frm.fields_dict['items'].grid.get_field('expense_account').get_query = function(doc) {
if (erpnext.is_perpetual_inventory_enabled(doc.company)) {
return {
filters: {
'report_type': 'Profit and Loss',
'company': doc.company,
"is_group": 0
}
}
}
}
}, },
project: function(frm){ project: function(frm){

View File

@ -324,6 +324,37 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "pos_profile",
"fieldtype": "Link",
"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": "POS Profile",
"length": 0,
"no_copy": 0,
"options": "POS Profile",
"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_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -4565,7 +4596,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-06-13 14:29:14.696232", "modified": "2017-06-16 17:07:55.483734",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
import frappe.defaults import frappe.defaults
from frappe.utils import cint, flt from frappe.utils import cint, flt
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
@ -57,7 +57,8 @@ class SalesInvoice(SellingController):
self.so_dn_required() self.so_dn_required()
self.validate_proj_cust() self.validate_proj_cust()
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_uom_is_integer("stock_uom", "qty") self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
self.check_close_sales_order("sales_order") self.check_close_sales_order("sales_order")
self.validate_debit_to_acc() self.validate_debit_to_acc()
self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.clear_unallocated_advances("Sales Invoice Advance", "advances")
@ -88,6 +89,8 @@ class SalesInvoice(SellingController):
self.validate_c_form() self.validate_c_form()
self.validate_time_sheets_are_submitted() self.validate_time_sheets_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items") self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
if not self.is_return:
self.validate_serial_numbers()
self.update_packing_list() self.update_packing_list()
self.set_billing_hours_and_amount() self.set_billing_hours_and_amount()
self.update_timesheet_billing_for_project() self.update_timesheet_billing_for_project()
@ -125,6 +128,7 @@ class SalesInvoice(SellingController):
if not self.is_return: if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Sales Order") self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit() self.check_credit_limit()
self.update_serial_no()
if not cint(self.is_pos) == 1 and not self.is_return: if not cint(self.is_pos) == 1 and not self.is_return:
self.update_against_document_in_jv() self.update_against_document_in_jv()
@ -155,6 +159,7 @@ class SalesInvoice(SellingController):
if not self.is_return: if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Sales Order") self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.update_serial_no(in_cancel=True)
self.validate_c_form_on_cancel() self.validate_c_form_on_cancel()
@ -554,6 +559,8 @@ class SalesInvoice(SellingController):
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note)) throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not self.grand_total: if not self.grand_total:
return return
@ -575,11 +582,11 @@ class SalesInvoice(SellingController):
self.doctype, self.return_against if cint(self.is_return) else self.name) self.doctype, self.return_against if cint(self.is_return) else self.name)
if repost_future_gle and cint(self.update_stock) \ if repost_future_gle and cint(self.update_stock) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): and cint(auto_accounting_for_stock):
items, warehouses = self.get_items_and_warehouses() items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items) update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
elif self.docstatus == 2 and cint(self.update_stock) \ elif self.docstatus == 2 and cint(self.update_stock) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): and cint(auto_accounting_for_stock):
from erpnext.accounts.general_ledger import delete_gl_entries from erpnext.accounts.general_ledger import delete_gl_entries
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
@ -667,8 +674,8 @@ class SalesInvoice(SellingController):
) )
# expense account gl entries # expense account gl entries
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \ if cint(self.update_stock) and \
and cint(self.update_stock): erpnext.is_perpetual_inventory_enabled(self.company):
gl_entries += super(SalesInvoice, self).get_gl_entries() gl_entries += super(SalesInvoice, self).get_gl_entries()
def make_pos_gl_entries(self, gl_entries): def make_pos_gl_entries(self, gl_entries):
@ -781,6 +788,61 @@ class SalesInvoice(SellingController):
self.due_date = None self.due_date = None
def update_serial_no(self, in_cancel=False):
""" update Sales Invoice refrence in Serial No """
for item in self.items:
if not item.serial_no:
continue
serial_nos = ["'%s'"%serial_no for serial_no in item.serial_no.split("\n")]
frappe.db.sql(""" update `tabSerial No` set sales_invoice='{invoice}'
where name in ({serial_nos})""".format(
invoice='' if in_cancel else self.name,
serial_nos=",".join(serial_nos)
)
)
def validate_serial_numbers(self):
"""
validate serial number agains Delivery Note and Sales Invoice
"""
self.validate_serial_against_delivery_note()
self.validate_serial_against_sales_invoice()
def validate_serial_against_delivery_note(self):
"""
validate if the serial numbers in Sales Invoice Items are same as in
Delivery Note Item
"""
for item in self.items:
if not item.delivery_note or not item.dn_detail:
continue
serial_nos = frappe.db.get_value("Delivery Note Item", item.dn_detail, "serial_no") or ""
dn_serial_nos = set(serial_nos.split("\n"))
serial_nos = item.serial_no or ""
si_serial_nos = set(serial_nos.split("\n"))
if si_serial_nos - dn_serial_nos:
frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note".format(item.idx)))
def validate_serial_against_sales_invoice(self):
""" check if serial number is already used in other sales invoice """
for item in self.items:
if not item.serial_no:
continue
for serial_no in item.serial_no.split("\n"):
sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
if sales_invoice and self.name != sales_invoice:
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}".format(
serial_no, sales_invoice
)))
def get_list_context(context=None): def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context) list_context = get_list_context(context)

View File

@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from erpnext.accounts.doctype.account.test_account import get_inventory_account
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
@ -487,8 +488,8 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8) self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8)
def test_sales_invoice_gl_entry_without_perpetual_inventory(self): def test_sales_invoice_gl_entry_without_perpetual_inventory(self):
set_perpetual_inventory(0)
si = frappe.copy_doc(test_records[1]) si = frappe.copy_doc(test_records[1])
set_perpetual_inventory(0, si.company)
si.insert() si.insert()
si.submit() si.submit()
@ -595,7 +596,7 @@ class TestSalesInvoice(unittest.TestCase):
order by account asc, debit asc""", si.name, as_dict=1) order by account asc, debit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"}) stock_in_hand = get_inventory_account('_Test Company')
expected_gl_entries = sorted([ expected_gl_entries = sorted([
[si.debit_to, 630.0, 0.0], [si.debit_to, 630.0, 0.0],
@ -616,6 +617,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(expected_gl_entries[i][2], gle.credit) self.assertEquals(expected_gl_entries[i][2], gle.credit)
si.cancel() si.cancel()
frappe.delete_doc('Sales Invoice', si.name)
gle = frappe.db.sql("""select * from `tabGL Entry` gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
@ -750,6 +752,12 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse")) self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no"), si.name) "delivery_document_no"), si.name)
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "sales_invoice"),
si.name)
# check if the serial number is already linked with any other Sales Invoice
_si = frappe.copy_doc(si.as_dict())
self.assertRaises(frappe.ValidationError, _si.insert)
return si return si
@ -763,6 +771,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC") self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no")) "delivery_document_no"))
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "sales_invoice"))
def test_serialize_status(self): def test_serialize_status(self):
serial_no = frappe.get_doc({ serial_no = frappe.get_doc({
@ -781,6 +790,27 @@ class TestSalesInvoice(unittest.TestCase):
self.assertRaises(SerialNoWarehouseError, si.submit) self.assertRaises(SerialNoWarehouseError, si.submit)
def test_serial_numbers_against_delivery_note(self):
"""
check if the sales invoice item serial numbers and the delivery note items
serial numbers are same
"""
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item()
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
dn = create_delivery_note(item=se.get("items")[0].item_code, serial_no=serial_nos[0])
dn.submit()
si = make_sales_invoice(dn.name)
si.save()
self.assertEquals(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
def test_invoice_due_date_against_customers_credit_days(self): def test_invoice_due_date_against_customers_credit_days(self):
# set customer's credit days # set customer's credit days
frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Fixed Days") frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Fixed Days")
@ -822,11 +852,11 @@ class TestSalesInvoice(unittest.TestCase):
["incoming_rate", "stock_value_difference"]) ["incoming_rate", "stock_value_difference"])
self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3))) self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', si1.items[0].warehouse)
# Check gl entry # Check gl entry
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si1.name, "account": "_Test Warehouse - _TC"}, "debit") "voucher_no": si1.name, "account": stock_in_hand_account}, "debit")
self.assertEquals(gle_warehouse_amount, stock_value_difference) self.assertEquals(gle_warehouse_amount, stock_value_difference)

View File

@ -7,7 +7,7 @@ import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cstr, cint from frappe.utils import cstr, cint
from frappe.geo.doctype.address.address import get_default_address from frappe.contacts.doctype.address.address import get_default_address
class IncorrectCustomerGroup(frappe.ValidationError): pass class IncorrectCustomerGroup(frappe.ValidationError): pass
class IncorrectSupplierType(frappe.ValidationError): pass class IncorrectSupplierType(frappe.ValidationError): pass

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe.utils import flt, cstr, cint from frappe.utils import flt, cstr, cint
from frappe import _ from frappe import _
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
@ -80,7 +80,7 @@ def check_if_in_list(gle, gl_map):
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost: if not from_repost:
validate_account_for_auto_accounting_for_stock(gl_map) validate_account_for_perpetual_inventory(gl_map)
round_off_debit_credit(gl_map) round_off_debit_credit(gl_map)
@ -100,11 +100,11 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost) gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
gle.submit() gle.submit()
def validate_account_for_auto_accounting_for_stock(gl_map): def validate_account_for_perpetual_inventory(gl_map):
if cint(frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock")) \ if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)) \
and gl_map[0].voucher_type=="Journal Entry": and gl_map[0].voucher_type=="Journal Entry":
aii_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount aii_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
where account_type = 'Stock' and (warehouse != '' and warehouse is not null) and is_group=0""")] where account_type = 'Stock' and is_group=0""")]
for entry in gl_map: for entry in gl_map:
if entry.account in aii_accounts: if entry.account in aii_accounts:

View File

@ -114,7 +114,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
email_prompt: function() { email_prompt: function() {
var me = this; var me = this;
fields = [{label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288}, var fields = [{label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288},
{fieldtype: "Section Break", collapsible: 1, label: "CC & Standard Reply"}, {fieldtype: "Section Break", collapsible: 1, label: "CC & Standard Reply"},
{fieldtype: "Section Break"}, {fieldtype: "Section Break"},
{label:__("Subject"), fieldtype:"Data", reqd: 1, {label:__("Subject"), fieldtype:"Data", reqd: 1,
@ -604,7 +604,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var html = ""; var html = "";
if(this.si_docs.length) { if(this.si_docs.length) {
this.si_docs.forEach(function (data, i) { this.si_docs.forEach(function (data, i) {
for (key in data) { for (var key in data) {
html += frappe.render_template("pos_invoice_list", { html += frappe.render_template("pos_invoice_list", {
sr: i + 1, sr: i + 1,
name: key, name: key,
@ -984,7 +984,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
make_item_list: function () { make_item_list: function () {
var me = this; var me = this;
if (!this.price_list) { if (!this.price_list) {
msgprint(__("Price List not found or disabled")); frappe.msgprint(__("Price List not found or disabled"));
return; return;
} }
@ -1252,7 +1252,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
remove_zero_qty_item: function () { remove_zero_qty_item: function () {
var me = this; var me = this;
idx = 0 var idx = 0;
this.items = [] this.items = []
$.each(this.frm.doc["items"] || [], function (i, d) { $.each(this.frm.doc["items"] || [], function (i, d) {
if (!in_list(me.remove_item, d.idx)) { if (!in_list(me.remove_item, d.idx)) {
@ -1270,7 +1270,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.wrapper.find('input.discount-percentage').on("change", function () { this.wrapper.find('input.discount-percentage').on("change", function () {
me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage")); me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage"));
total = me.frm.doc.grand_total var total = me.frm.doc.grand_total
if (me.frm.doc.apply_discount_on == 'Net Total') { if (me.frm.doc.apply_discount_on == 'Net Total') {
total = me.frm.doc.net_total total = me.frm.doc.net_total
@ -1461,7 +1461,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if (this.frm.doc.docstatus == 1) { if (this.frm.doc.docstatus == 1) {
this.page.set_secondary_action(__("Print"), function () { this.page.set_secondary_action(__("Print"), function () {
html = frappe.render(me.print_template_data, me.frm.doc) var html = frappe.render(me.print_template_data, me.frm.doc)
me.print_document(html) me.print_document(html)
}) })
this.page.add_menu_item(__("Email"), function () { this.page.add_menu_item(__("Email"), function () {
@ -1484,19 +1484,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
print_dialog: function () { print_dialog: function () {
var me = this; var me = this;
this.msgprint = frappe.msgprint(format('<a class="btn btn-primary print_doc" \ this.frappe.msgprint = frappe.msgprint(
style="margin-right: 5px;">{0}</a>\ `<a class="btn btn-primary print_doc"
<a class="btn btn-default new_doc">{1}</a>', [ style="margin-right: 5px;">${__('Print')}</a>
__('Print'), __('New') <a class="btn btn-default new_doc">${__('New')}</a>`);
]));
$('.print_doc').click(function () { $('.print_doc').click(function () {
html = frappe.render(me.print_template_data, me.frm.doc) var html = frappe.render(me.print_template_data, me.frm.doc)
me.print_document(html) me.print_document(html)
}) })
$('.new_doc').click(function () { $('.new_doc').click(function () {
me.msgprint.hide() me.frappe.msgprint.hide()
me.make_new_cart() me.make_new_cart()
}) })
}, },
@ -1525,9 +1524,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
//Remove the sold serial no from the cache //Remove the sold serial no from the cache
$.each(this.frm.doc.items, function(index, data) { $.each(this.frm.doc.items, function(index, data) {
sn = data.serial_no.split('\n') var sn = data.serial_no.split('\n')
if(sn.length) { if(sn.length) {
serial_no_list = me.serial_no_data[data.item_code] var serial_no_list = me.serial_no_data[data.item_code]
if(serial_no_list) { if(serial_no_list) {
$.each(sn, function(i, serial_no) { $.each(sn, function(i, serial_no) {
if(in_list(Object.keys(serial_no_list), serial_no)) { if(in_list(Object.keys(serial_no_list), serial_no)) {
@ -1550,7 +1549,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
toggle_input_field: function () { toggle_input_field: function () {
var pointer_events = 'inherit' var pointer_events = 'inherit'
disabled = this.frm.doc.docstatus == 1 ? true: false; var disabled = this.frm.doc.docstatus == 1 ? true: false;
$(this.wrapper).find('input').attr("disabled", disabled); $(this.wrapper).find('input').attr("disabled", disabled);
$(this.wrapper).find('select').attr("disabled", disabled); $(this.wrapper).find('select').attr("disabled", disabled);
$(this.wrapper).find('input').attr("disabled", disabled); $(this.wrapper).find('input').attr("disabled", disabled);
@ -1578,6 +1577,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm.doc.offline_pos_name = this.name; this.frm.doc.offline_pos_name = this.name;
this.frm.doc.posting_date = frappe.datetime.get_today(); this.frm.doc.posting_date = frappe.datetime.get_today();
this.frm.doc.posting_time = frappe.datetime.now_time(); this.frm.doc.posting_time = frappe.datetime.now_time();
this.frm.doc.pos_profile = this.pos_profile_data['name'];
invoice_data[this.name] = this.frm.doc invoice_data[this.name] = this.frm.doc
this.si_docs.push(invoice_data) this.si_docs.push(invoice_data)
this.update_localstorage(); this.update_localstorage();
@ -1590,7 +1590,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var me = this; var me = this;
this.si_docs = this.get_doc_from_localstorage(); this.si_docs = this.get_doc_from_localstorage();
$.each(this.si_docs, function (index, data) { $.each(this.si_docs, function (index, data) {
for (key in data) { for (var key in data) {
if (key == me.name) { if (key == me.name) {
me.si_docs[index][key] = me.frm.doc; me.si_docs[index][key] = me.frm.doc;
me.update_localstorage(); me.update_localstorage();
@ -1661,10 +1661,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
get_submitted_invoice: function () { get_submitted_invoice: function () {
var invoices = []; var invoices = [];
var index = 1; var index = 1;
docs = this.get_doc_from_localstorage(); var docs = this.get_doc_from_localstorage();
if (docs) { if (docs) {
invoices = $.map(docs, function (data) { invoices = $.map(docs, function (data) {
for (key in data) { for (var key in data) {
if (data[key].docstatus == 1 && index < 50) { if (data[key].docstatus == 1 && index < 50) {
index++ index++
data[key].docstatus = 0; data[key].docstatus = 0;
@ -1683,7 +1683,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.new_si_docs = []; this.new_si_docs = [];
if (this.removed_items) { if (this.removed_items) {
$.each(this.si_docs, function (index, data) { $.each(this.si_docs, function (index, data) {
for (key in data) { for (var key in data) {
if (!in_list(me.removed_items, key)) { if (!in_list(me.removed_items, key)) {
me.new_si_docs.push(data); me.new_si_docs.push(data);
} }
@ -1742,8 +1742,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
validate_serial_no: function () { validate_serial_no: function () {
var me = this; var me = this;
var item_code = serial_no = ''; var item_code = ''
for (key in this.item_serial_no) { var serial_no = '';
for (var key in this.item_serial_no) {
item_code = key; item_code = key;
serial_no = me.item_serial_no[key][0]; serial_no = me.item_serial_no[key][0];
} }
@ -1790,16 +1791,24 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
mandatory_batch_no: function () { mandatory_batch_no: function () {
var me = this; var me = this;
if (this.items[0].has_batch_no && !this.item_batch_no[this.items[0].item_code]) { if (this.items[0].has_batch_no && !this.item_batch_no[this.items[0].item_code]) {
frappe.throw(__(repl("Error: Batch no is mandatory for item %(item)s", { frappe.prompt([{
'item': this.items[0].item_code 'fieldname': 'batch',
}))) 'fieldtype': 'Select',
'label': __('Batch No'),
'reqd': 1,
'options': this.batch_no_data[this.items[0].item_code]
}],
function(values){
me.item_batch_no[me.items[0].item_code] = values.batch;
},
__('Select Batch No'))
} }
}, },
apply_pricing_rule: function () { apply_pricing_rule: function () {
var me = this; var me = this;
$.each(this.frm.doc["items"], function (n, item) { $.each(this.frm.doc["items"], function (n, item) {
pricing_rule = me.get_pricing_rule(item) var pricing_rule = me.get_pricing_rule(item)
me.validate_pricing_rule(pricing_rule) me.validate_pricing_rule(pricing_rule)
if (pricing_rule.length) { if (pricing_rule.length) {
item.pricing_rule = pricing_rule[0].name; item.pricing_rule = pricing_rule[0].name;
@ -1859,7 +1868,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
validate_condition: function (data) { validate_condition: function (data) {
//This method check condition based on applicable for //This method check condition based on applicable for
condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for] var condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for]
if (in_list(condition[1], condition[0])) { if (in_list(condition[1], condition[0])) {
return true return true
} }
@ -1892,7 +1901,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
} }
}) })
count = 0 var count = 0
$.each(priority_list, function (index, value) { $.each(priority_list, function (index, value) {
if (value == priority) { if (value == priority) {
count++ count++

View File

@ -9,8 +9,8 @@ from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, \ from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, \
add_years, get_timestamp, nowdate, flt add_years, get_timestamp, nowdate, flt
from frappe.geo.doctype.address.address import get_address_display, get_default_address from frappe.contacts.doctype.address.address import get_address_display, get_default_address
from frappe.email.doctype.contact.contact import get_contact_details, get_default_contact from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext import get_default_currency from erpnext import get_default_currency

View File

@ -20,13 +20,13 @@ frappe.query_reports["Accounts Payable"] = {
"fieldname":"report_date", "fieldname":"report_date",
"label": __("As on Date"), "label": __("As on Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"ageing_based_on", "fieldname":"ageing_based_on",
"label": __("Ageing Based On"), "label": __("Ageing Based On"),
"fieldtype": "Select", "fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date', "options": 'Posting Date\nDue Date',
"default": "Posting Date" "default": "Posting Date"
}, },
{ {

View File

@ -20,13 +20,13 @@ frappe.query_reports["Accounts Payable Summary"] = {
"fieldname":"report_date", "fieldname":"report_date",
"label": __("Date"), "label": __("Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"ageing_based_on", "fieldname":"ageing_based_on",
"label": __("Ageing Based On"), "label": __("Ageing Based On"),
"fieldtype": "Select", "fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date', "options": 'Posting Date\nDue Date',
"default": "Posting Date" "default": "Posting Date"
}, },
{ {

View File

@ -1,4 +1,4 @@
{% var letterhead= filters.letter_head || frappe.get_doc(":Company", filters.company).default_letter_head || frappe.defaults.get_default("letter_head"); %} {% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %} {% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center"> <div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %} {%= frappe.boot.letter_heads[letterhead].header %}

View File

@ -26,7 +26,7 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldname":"credit_days_based_on", "fieldname":"credit_days_based_on",
"label": __("Credit Days Based On"), "label": __("Credit Days Based On"),
"fieldtype": "Select", "fieldtype": "Select",
"options": "" + NEWLINE + "Fixed Days" + NEWLINE + "Last Day of the Next Month" "options": "\nFixed Days\nLast Day of the Next Month"
}, },
{ {
"fieldtype": "Break", "fieldtype": "Break",
@ -35,13 +35,13 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldname":"report_date", "fieldname":"report_date",
"label": __("As on Date"), "label": __("As on Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"ageing_based_on", "fieldname":"ageing_based_on",
"label": __("Ageing Based On"), "label": __("Ageing Based On"),
"fieldtype": "Select", "fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date', "options": 'Posting Date\nDue Date',
"default": "Posting Date" "default": "Posting Date"
}, },
{ {

View File

@ -26,7 +26,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"fieldname":"credit_days_based_on", "fieldname":"credit_days_based_on",
"label": __("Credit Days Based On"), "label": __("Credit Days Based On"),
"fieldtype": "Select", "fieldtype": "Select",
"options": "" + NEWLINE + "Fixed Days" + NEWLINE + "Last Day of the Next Month" "options": "\nFixed Days\nLast Day of the Next Month"
}, },
{ {
"fieldtype": "Break", "fieldtype": "Break",
@ -35,13 +35,13 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"fieldname":"report_date", "fieldname":"report_date",
"label": __("Date"), "label": __("Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"ageing_based_on", "fieldname":"ageing_based_on",
"label": __("Ageing Based On"), "label": __("Ageing Based On"),
"fieldtype": "Select", "fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date', "options": 'Posting Date\nDue Date',
"default": "Posting Date" "default": "Posting Date"
}, },
{ {
@ -70,7 +70,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
onload: function(report) { onload: function(report) {
report.page.add_inner_button(__("Accounts Receivable"), function() { report.page.add_inner_button(__("Accounts Receivable"), function() {
var filters = report.get_values(); var filters = report.get_values();
frappe.set_route('query-report', 'Accounts Receivable', {company: filters.company}); frappe.set_route('query-report', 'Accounts Receivable', { company: filters.company });
}); });
} }
} }

View File

@ -14,7 +14,7 @@ frappe.query_reports["Bank Clearance Summary"] = {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"account", "fieldname":"account",

View File

@ -25,7 +25,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
"fieldname":"report_date", "fieldname":"report_date",
"label": __("Date"), "label": __("Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today(), "default": frappe.datetime.get_today(),
"reqd": 1 "reqd": 1
}, },
] ]

View File

@ -8,7 +8,7 @@ frappe.query_reports["Budget Variance Report"] = {
label: __("Fiscal Year"), label: __("Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: sys_defaults.fiscal_year, default: frappe.sys_defaults.fiscal_year,
reqd: 1 reqd: 1
}, },
{ {

View File

@ -13,7 +13,7 @@
height: 37px; height: 37px;
} }
</style> </style>
{% var letterhead= filters.letter_head || frappe.get_doc(":Company", filters.company).default_letter_head || frappe.defaults.get_default("letter_head"); %} {% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %} {% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center"> <div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %} {%= frappe.boot.letter_heads[letterhead].header %}

View File

@ -1,4 +1,4 @@
{% var letterhead= filters.letter_head || frappe.get_doc(":Company", filters.company).default_letter_head || frappe.defaults.get_default("letter_head"); %} {% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %} {% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center"> <div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %} {%= frappe.boot.letter_heads[letterhead].header %}

View File

@ -227,7 +227,7 @@ class GrossProfitGenerator(object):
if not average_buying_rate: if not average_buying_rate:
average_buying_rate = get_valuation_rate(item_code, row.warehouse, average_buying_rate = get_valuation_rate(item_code, row.warehouse,
row.parenttype, row.parent, allow_zero_rate=True, row.parenttype, row.parent, allow_zero_rate=True,
currency=self.filters.currency) currency=self.filters.currency, company=self.filters.company)
self.average_buying_rate[item_code] = flt(average_buying_rate) self.average_buying_rate[item_code] = flt(average_buying_rate)

View File

@ -14,7 +14,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname": "item_code", "fieldname": "item_code",

View File

@ -14,7 +14,7 @@ frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales R
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"customer", "fieldname":"customer",

View File

@ -21,7 +21,7 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
fieldname:"to_date", fieldname:"to_date",
label: __("To Date"), label: __("To Date"),
fieldtype: "Date", fieldtype: "Date",
default: get_today() default: frappe.datetime.get_today()
}, },
{ {
fieldname:"payment_type", fieldname:"payment_type",

View File

@ -3,6 +3,6 @@
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() {
frappe.query_reports["Purchase Invoice Trends"] = { frappe.query_reports["Purchase Invoice Trends"] = {
filters: get_filters() filters: erpnext.get_purchase_trends_filters()
} }
}); });

View File

@ -14,7 +14,7 @@ frappe.query_reports["Purchase Register"] = {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"supplier", "fieldname":"supplier",

View File

@ -3,6 +3,6 @@
frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { frappe.require("assets/erpnext/js/sales_trends_filters.js", function() {
frappe.query_reports["Sales Invoice Trends"] = { frappe.query_reports["Sales Invoice Trends"] = {
filters: get_filters() filters: erpnext.get_sales_trends_filters()
} }
}); });

View File

@ -14,7 +14,7 @@ frappe.query_reports["Sales Register"] = {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"customer", "fieldname":"customer",

View File

@ -216,7 +216,8 @@ def get_count_on(account, fieldname, date):
else: else:
dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit" dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit"
cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit" cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit"
select_fields = "ifnull(sum(credit-debit),0)" if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)" select_fields = "ifnull(sum(credit-debit),0)" \
if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or
(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0)): (gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0)):
@ -224,8 +225,10 @@ def get_count_on(account, fieldname, date):
SELECT {0} SELECT {0}
FROM `tabGL Entry` gle FROM `tabGL Entry` gle
WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
and party = %(party)s and name != %(name)s""".format(select_fields), and party = %(party)s and name != %(name)s"""
{"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name})[0][0] .format(select_fields),
{"date": date, "voucher_no": gle.voucher_no,
"party": gle.party, "name": gle.name})[0][0]
outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
currency_precision = get_currency_precision() or 2 currency_precision = get_currency_precision() or 2
@ -519,17 +522,16 @@ def fix_total_debit_credit():
def get_stock_and_account_difference(account_list=None, posting_date=None): def get_stock_and_account_difference(account_list=None, posting_date=None):
from erpnext.stock.utils import get_stock_value_on from erpnext.stock.utils import get_stock_value_on
from erpnext.stock import get_warehouse_account_map
if not posting_date: posting_date = nowdate() if not posting_date: posting_date = nowdate()
difference = {} difference = {}
warehouse_account = get_warehouse_account_map()
account_warehouse = dict(frappe.db.sql("""select name, warehouse from tabAccount for warehouse, account_data in warehouse_account.items():
where account_type = 'Stock' and (warehouse is not null and warehouse != '') and is_group=0 if account_data.get('account') in account_list:
and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list)) account_balance = get_balance_on(account_data.get('account'), posting_date, in_account_currency=False)
for account, warehouse in account_warehouse.items():
account_balance = get_balance_on(account, posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date) stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005: if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance)) difference.setdefault(account, flt(stock_value) - flt(account_balance))

View File

@ -43,7 +43,7 @@ class PurchaseOrder(BuyingController):
self.check_for_closed_status() self.check_for_closed_status()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"]) self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_for_subcontracting() self.validate_for_subcontracting()

View File

@ -10,7 +10,7 @@ def get_data():
'internal_links': { 'internal_links': {
'Material Request': ['items', 'material_request'], 'Material Request': ['items', 'material_request'],
'Supplier Quotation': ['items', 'supplier_quotation'], 'Supplier Quotation': ['items', 'supplier_quotation'],
'Project': ['project'], 'Project': ['items', 'project'],
}, },
'transactions': [ 'transactions': [
{ {

View File

@ -1,34 +1,34 @@
frappe.listview_settings['Purchase Order'] = { frappe.listview_settings['Purchase Order'] = {
add_fields: ["base_grand_total", "company", "currency", "supplier", add_fields: ["base_grand_total", "company", "currency", "supplier",
"supplier_name", "per_received", "per_billed", "status"], "supplier_name", "per_received", "per_billed", "status"],
get_indicator: function(doc) { get_indicator: function (doc) {
if(doc.status==="Closed"){ if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"]; return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.status==="Delivered") { } else if (doc.status === "Delivered") {
return [__("Delivered"), "green", "status,=,Closed"]; return [__("Delivered"), "green", "status,=,Closed"];
}else if(flt(doc.per_received, 2) < 100 && doc.status!=="Closed") { } else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") {
if(flt(doc.per_billed, 2) < 100) { if (flt(doc.per_billed, 2) < 100) {
return [__("To Receive and Bill"), "orange", return [__("To Receive and Bill"), "orange",
"per_received,<,100|per_billed,<,100|status,!=,Closed"]; "per_received,<,100|per_billed,<,100|status,!=,Closed"];
} else { } else {
return [__("To Receive"), "orange", return [__("To Receive"), "orange",
"per_received,<,100|per_billed,=,100|status,!=,Closed"]; "per_received,<,100|per_billed,=,100|status,!=,Closed"];
} }
} else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status!=="Closed") { } else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status !== "Closed") {
return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"]; return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"];
} else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status!=="Closed") { } else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status !== "Closed") {
return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"]; return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"];
} }
}, },
onload: function(listview) { onload: function (listview) {
var method = "erpnext.buying.doctype.purchase_order.purchase_order.close_or_unclose_purchase_orders"; var method = "erpnext.buying.doctype.purchase_order.purchase_order.close_or_unclose_purchase_orders";
listview.page.add_menu_item(__("Close"), function() { listview.page.add_menu_item(__("Close"), function () {
listview.call_for_selected_items(method, {"status": "Closed"}); listview.call_for_selected_items(method, { "status": "Closed" });
}); });
listview.page.add_menu_item(__("Re-open"), function() { listview.page.add_menu_item(__("Re-open"), function () {
listview.call_for_selected_items(method, {"status": "Submitted"}); listview.call_for_selected_items(method, { "status": "Submitted" });
}); });
} }
}; };

View File

@ -56,17 +56,17 @@ frappe.ui.form.on("Request for Quotation",{
var dialog = new frappe.ui.Dialog({ var dialog = new frappe.ui.Dialog({
title: __("For Supplier"), title: __("For Supplier"),
fields: [ fields: [
{"fieldtype": "Select", "label": __("Supplier"), { "fieldtype": "Select", "label": __("Supplier"),
"fieldname": "supplier", "options":"Supplier", "fieldname": "supplier",
"options": $.map(doc.suppliers, "options": doc.suppliers.map(d => d.supplier),
function(d) { return d.supplier }), "reqd": 1 }, "reqd": 1 },
{"fieldtype": "Button", "label": __("Make Supplier Quotation"), { "fieldtype": "Button", "label": __("Make Supplier Quotation"),
"fieldname": "make_supplier_quotation", "cssClass": "btn-primary"}, "fieldname": "make_supplier_quotation", "cssClass": "btn-primary" },
] ]
}); });
dialog.fields_dict.make_supplier_quotation.$input.click(function() { dialog.fields_dict.make_supplier_quotation.$input.click(function() {
args = dialog.get_values(); var args = dialog.get_values();
if(!args) return; if(!args) return;
dialog.hide(); dialog.hide();
return frappe.call({ return frappe.call({
@ -117,7 +117,7 @@ frappe.ui.form.on("Request for Quotation Supplier",{
+"&supplier_idx="+encodeURIComponent(child.idx) +"&supplier_idx="+encodeURIComponent(child.idx)
+"&no_letterhead=0")); +"&no_letterhead=0"));
if(!w) { if(!w) {
msgprint(__("Please enable pop-ups")); return; frappe.msgprint(__("Please enable pop-ups")); return;
} }
} }
}) })

View File

@ -2,9 +2,9 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Supplier", { frappe.ui.form.on("Supplier", {
setup: function(frm) { setup: function (frm) {
frm.set_query('default_price_list', { 'buying': 1}); frm.set_query('default_price_list', { 'buying': 1 });
frm.set_query('account', 'accounts', function(doc, cdt, cdn) { frm.set_query('account', 'accounts', function (doc, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
return { return {
filters: { filters: {
@ -15,30 +15,30 @@ frappe.ui.form.on("Supplier", {
} }
}); });
}, },
refresh: function(frm) { refresh: function (frm) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Supplier'} frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' }
if(frappe.defaults.get_default("supp_master_name")!="Naming Series") { if (frappe.defaults.get_default("supp_master_name") != "Naming Series") {
frm.toggle_display("naming_series", false); frm.toggle_display("naming_series", false);
} else { } else {
erpnext.toggle_naming_series(); erpnext.toggle_naming_series();
} }
if(frm.doc.__islocal){ if (frm.doc.__islocal) {
hide_field(['address_html','contact_html']); hide_field(['address_html','contact_html']);
frappe.geo.clear_address_and_contact(frm); frappe.contacts.clear_address_and_contact(frm);
} }
else { else {
unhide_field(['address_html','contact_html']); unhide_field(['address_html','contact_html']);
frappe.geo.render_address_and_contact(frm); frappe.contacts.render_address_and_contact(frm);
// custom buttons // custom buttons
frm.add_custom_button(__('Accounting Ledger'), function() { frm.add_custom_button(__('Accounting Ledger'), function () {
frappe.set_route('query-report', 'General Ledger', frappe.set_route('query-report', 'General Ledger',
{party_type:'Supplier', party:frm.doc.name}); { party_type: 'Supplier', party: frm.doc.name });
}); });
frm.add_custom_button(__('Accounts Payable'), function() { frm.add_custom_button(__('Accounts Payable'), function () {
frappe.set_route('query-report', 'Accounts Payable', {supplier:frm.doc.name}); frappe.set_route('query-report', 'Accounts Payable', { supplier: frm.doc.name });
}); });
// indicators // indicators

View File

@ -6,7 +6,7 @@ import frappe
import frappe.defaults import frappe.defaults
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe.geo.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this

View File

@ -5,7 +5,7 @@
{% include 'erpnext/public/js/controllers/buying.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
frappe.ui.form.on('Suppier Quotation', { frappe.ui.form.on('Suppier Quotation', {
setup: function() { setup: function(frm) {
frm.custom_make_buttons = { frm.custom_make_buttons = {
'Purchase Order': 'Purchase Order' 'Purchase Order': 'Purchase Order'
} }

View File

@ -10,7 +10,6 @@ frappe.pages['purchase-analytics'].on_page_load = function(wrapper) {
new erpnext.PurchaseAnalytics(wrapper); new erpnext.PurchaseAnalytics(wrapper);
frappe.breadcrumbs.add("Buying"); frappe.breadcrumbs.add("Buying");
} }
@ -18,7 +17,6 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
init: function(wrapper) { init: function(wrapper) {
this._super({ this._super({
title: __("Purchase Analytics"), title: __("Purchase Analytics"),
page: wrapper,
parent: $(wrapper).find('.layout-main'), parent: $(wrapper).find('.layout-main'),
page: wrapper.page, page: wrapper.page,
doctypes: ["Item", "Item Group", "Supplier", "Supplier Type", "Company", "Fiscal Year", doctypes: ["Item", "Item Group", "Supplier", "Supplier Type", "Company", "Fiscal Year",
@ -193,13 +191,13 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
}, },
prepare_balances: function() { prepare_balances: function() {
var me = this; var me = this;
var from_date = dateutil.str_to_obj(this.from_date); var from_date = frappe.datetime.str_to_obj(this.from_date);
var to_date = dateutil.str_to_obj(this.to_date); var to_date = frappe.datetime.str_to_obj(this.to_date);
var is_val = this.value_or_qty == 'Value'; var is_val = this.value_or_qty == 'Value';
$.each(this.tl[this.based_on], function(i, tl) { $.each(this.tl[this.based_on], function(i, tl) {
if (me.is_default('company') ? true : tl.company === me.company) { if (me.is_default('company') ? true : tl.company === me.company) {
var posting_date = dateutil.str_to_obj(tl.posting_date); var posting_date = frappe.datetime.str_to_obj(tl.posting_date);
if (posting_date >= from_date && posting_date <= to_date) { if (posting_date >= from_date && posting_date <= to_date) {
var item = me.item_by_name[tl[me.tree_grid.item_key]] || var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
me.item_by_name['Not Set']; me.item_by_name['Not Set'];

View File

@ -3,6 +3,6 @@
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() {
frappe.query_reports["Purchase Order Trends"] = { frappe.query_reports["Purchase Order Trends"] = {
filters: get_filters() filters: erpnext.get_purchase_trends_filters()
} }
}); });

View File

@ -4,29 +4,25 @@
frappe.query_reports["Quoted Item Comparison"] = { frappe.query_reports["Quoted Item Comparison"] = {
"filters": [ "filters": [
{ {
"fieldname":"supplier_quotation", "fieldname": "supplier_quotation",
"label": __("Supplier Quotation"), "label": __("Supplier Quotation"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Supplier Quotation", "options": "Supplier Quotation",
"default": "", "default": "",
"get_query": function() { "get_query": function () {
return { return { filters: { "docstatus": ["<", 2] } }
filters: {"docstatus": ["<",2]}
} }
} },
{
"fieldname": "item",
},{
"fieldname":"item",
"label": __("Item"), "label": __("Item"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Item", "options": "Item",
"default": "", "default": "",
"reqd": 1, "reqd": 1,
"get_query": function() { "get_query": function () {
var quote = frappe.query_report_filters_by_name.supplier_quotation.get_value(); var quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
if (quote != "") if (quote != "") {
{
return { return {
query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query", query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query",
filters: { filters: {
@ -35,57 +31,56 @@ frappe.query_reports["Quoted Item Comparison"] = {
} }
} }
} }
else{ else {
return{ return {
filters: {"disabled":0} filters: { "disabled": 0 }
} }
} }
} }
} }
], ],
onload: function(report) { onload: function (report) {
//Create a button for setting the default supplier // Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), function() { report.page.add_inner_button(__("Select Default Supplier"), function () {
var reporter = frappe.query_reports["Quoted Item Comparison"]; var reporter = frappe.query_reports["Quoted Item Comparison"];
//Always make a new one so that the latest values get updated //Always make a new one so that the latest values get updated
reporter.make_default_supplier_dialog(report); reporter.make_default_supplier_dialog(report);
report.dialog.show(); report.dialog.show();
setTimeout(function() { report.dialog.input.focus(); }, 1000); setTimeout(function () { report.dialog.input.focus(); }, 1000);
}, 'Tools'); }, 'Tools');
}, },
"make_default_supplier_dialog": function (report) { "make_default_supplier_dialog": function (report) {
//Get the name of the item to change // Get the name of the item to change
var filters = report.get_values(); var filters = report.get_values();
var item_code = filters.item; var item_code = filters.item;
//Get a list of the suppliers (with a blank as well) for the user to select // Get a list of the suppliers (with a blank as well) for the user to select
var select_options = ""; var select_options = "";
for (let supplier of report.data) for (let supplier of report.data) {
{ select_options += supplier.supplier_name + '\n'
select_options += supplier.supplier_name+ '\n'
} }
//Create a dialog window for the user to pick their supplier // Create a dialog window for the user to pick their supplier
var d = new frappe.ui.Dialog({ var d = new frappe.ui.Dialog({
title: __('Select Default Supplier'), title: __('Select Default Supplier'),
fields: [ fields: [
{fieldname: 'supplier', fieldtype:'Select', label:'Supplier', reqd:1,options:select_options}, { fieldname: 'supplier', fieldtype: 'Select', label: 'Supplier', reqd: 1, options: select_options },
{fieldname: 'ok_button', fieldtype:'Button', label:'Set Default Supplier'}, { fieldname: 'ok_button', fieldtype: 'Button', label: 'Set Default Supplier' },
] ]
}); });
//On the user clicking the ok button // On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function() { d.fields_dict.ok_button.input.onclick = function () {
var btn = d.fields_dict.ok_button.input; var btn = d.fields_dict.ok_button.input;
var v = report.dialog.get_values(); var v = report.dialog.get_values();
if(v) { if (v) {
$(btn).set_working(); $(btn).set_working();
//Set the default_supplier field of the appropriate Item to the selected supplier // Set the default_supplier field of the appropriate Item to the selected supplier
frappe.call({ frappe.call({
method: "frappe.client.set_value", method: "frappe.client.set_value",
args: { args: {
@ -94,20 +89,16 @@ frappe.query_reports["Quoted Item Comparison"] = {
fieldname: "default_supplier", fieldname: "default_supplier",
value: v.supplier, value: v.supplier,
}, },
callback: function (r){ callback: function (r) {
$(btn).done_working(); $(btn).done_working();
msgprint("Successfully Set Supplier"); frappe.msgprint("Successfully Set Supplier");
report.dialog.hide(); report.dialog.hide();
} }
}); });
} }
} }
report.dialog = d; report.dialog = d;
} }
} }

View File

@ -0,0 +1,49 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals, absolute_import, print_function
import click
import frappe
from frappe.commands import pass_context, get_site
def call_command(cmd, context):
return click.Context(cmd, obj=context).forward(cmd)
@click.command('make-demo')
@click.option('--site', help='site name')
@click.option('--domain', default='Manufacturing')
@click.option('--days', default=100,
help='Run the demo for so many days. Default 100')
@click.option('--resume', default=False, is_flag=True,
help='Continue running the demo for given days')
@click.option('--reinstall', default=False, is_flag=True,
help='Reinstall site before demo')
@pass_context
def make_demo(context, site, domain='Manufacturing', days=100,
resume=False, reinstall=False):
"Reinstall site and setup demo"
from frappe.commands.site import _reinstall
from frappe.installer import install_app
site = get_site(context)
if resume:
with frappe.init_site(site):
frappe.connect()
from erpnext.demo import demo
demo.simulate(days=days)
else:
if reinstall:
_reinstall(site, yes=True)
with frappe.init_site(site=site):
frappe.connect()
if not 'erpnext' in frappe.get_installed_apps():
install_app('erpnext')
# import needs site
from erpnext.demo import demo
demo.make(domain, days)
commands = [
make_demo
]

View File

@ -121,7 +121,7 @@ def get_data():
"type": "report", "type": "report",
"is_query_report": True, "is_query_report": True,
"name": "Addresses And Contacts", "name": "Addresses And Contacts",
"label": "Sales Partner Addresses And Contacts", "label": _("Sales Partner Addresses And Contacts"),
"doctype": "Address", "doctype": "Address",
"route_options": { "route_options": {
"party_type": "Sales Partner" "party_type": "Sales Partner"
@ -226,7 +226,7 @@ def get_data():
"type": "report", "type": "report",
"is_query_report": True, "is_query_report": True,
"name": "Addresses And Contacts", "name": "Addresses And Contacts",
"label": "Customer Addresses And Contacts", "label": _("Customer Addresses And Contacts"),
"doctype": "Address", "doctype": "Address",
"route_options": { "route_options": {
"party_type": "Customer" "party_type": "Customer"

View File

@ -274,7 +274,9 @@ class AccountsController(TransactionBase):
if not account_currency: if not account_currency:
account_currency = get_account_currency(gl_dict.account) account_currency = get_account_currency(gl_dict.account)
if self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]: if gl_dict.account and self.doctype not in ["Journal Entry",
"Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency) self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency) set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)

View File

@ -73,10 +73,13 @@ class BuyingController(StockController):
def validate_stock_or_nonstock_items(self): def validate_stock_or_nonstock_items(self):
if self.meta.get_field("taxes") and not self.get_stock_items(): if self.meta.get_field("taxes") and not self.get_stock_items():
tax_for_valuation = [d.account_head for d in self.get("taxes") tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]] if d.category in ["Valuation", "Valuation and Total"]]
if tax_for_valuation: if tax_for_valuation:
frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items")) for d in tax_for_valuation:
d.category = 'Total'
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
def set_landed_cost_voucher_amount(self): def set_landed_cost_voucher_amount(self):
for d in self.get("items"): for d in self.get("items"):
@ -224,7 +227,7 @@ class BuyingController(StockController):
}) })
if not rm.rate: if not rm.rate:
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse, rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse,
self.doctype, self.name, currency=self.company_currency) self.doctype, self.name, currency=self.company_currency, company = self.company)
else: else:
rm.rate = bom_item.rate rm.rate = bom_item.rate

View File

@ -71,7 +71,7 @@ def validate_returned_items(doc):
already_returned_items = get_already_returned_items(doc) already_returned_items = get_already_returned_items(doc)
# ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock ) # ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock )
warehouse_mandatory = not (doc.doctype=="Purchase Invoice" or (doc.doctype=="Sales Invoice" and not doc.update_stock)) warehouse_mandatory = not ((doc.doctype=="Purchase Invoice" or doc.doctype=="Sales Invoice") and not doc.update_stock)
items_returned = False items_returned = False
for d in doc.get("items"): for d in doc.get("items"):
@ -101,7 +101,8 @@ def validate_returned_items(doc):
frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}") frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
.format(d.idx, s, doc.doctype, doc.return_against)) .format(d.idx, s, doc.doctype, doc.return_against))
if warehouse_mandatory and not d.get("warehouse"): if warehouse_mandatory and frappe.db.get_value("Item", d.item_code, "is_stock_item") \
and not d.get("warehouse"):
frappe.throw(_("Warehouse is mandatory")) frappe.throw(_("Warehouse is mandatory"))
items_returned = True items_returned = True

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe.utils import cint, flt, cstr from frappe.utils import cint, flt, cstr
from frappe import msgprint, _ from frappe import msgprint, _
import frappe.defaults import frappe.defaults
@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
class StockController(AccountsController): class StockController(AccountsController):
def validate(self): def validate(self):
@ -20,8 +21,8 @@ class StockController(AccountsController):
if self.docstatus == 2: if self.docstatus == 2:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): if cint(erpnext.is_perpetual_inventory_enabled(self.company)):
warehouse_account = get_warehouse_account() warehouse_account = get_warehouse_account_map()
if self.docstatus==1: if self.docstatus==1:
if not gl_entries: if not gl_entries:
@ -37,7 +38,7 @@ class StockController(AccountsController):
default_cost_center=None): default_cost_center=None):
if not warehouse_account: if not warehouse_account:
warehouse_account = get_warehouse_account() warehouse_account = get_warehouse_account_map()
sle_map = self.get_stock_ledger_details() sle_map = self.get_stock_ledger_details()
voucher_details = self.get_voucher_details(default_expense_account, default_cost_center, sle_map) voucher_details = self.get_voucher_details(default_expense_account, default_cost_center, sle_map)
@ -66,7 +67,7 @@ class StockController(AccountsController):
sle = self.update_stock_ledger_entries(sle) sle = self.update_stock_ledger_entries(sle)
gl_list.append(self.get_gl_dict({ gl_list.append(self.get_gl_dict({
"account": warehouse_account[sle.warehouse]["name"], "account": warehouse_account[sle.warehouse]["account"],
"against": item_row.expense_account, "against": item_row.expense_account,
"cost_center": item_row.cost_center, "cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock", "remarks": self.get("remarks") or "Accounting Entry for Stock",
@ -76,7 +77,7 @@ class StockController(AccountsController):
# to target warehouse / expense account # to target warehouse / expense account
gl_list.append(self.get_gl_dict({ gl_list.append(self.get_gl_dict({
"account": item_row.expense_account, "account": item_row.expense_account,
"against": warehouse_account[sle.warehouse]["name"], "against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center, "cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock", "remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2), "credit": flt(sle.stock_value_difference, 2),
@ -88,16 +89,13 @@ class StockController(AccountsController):
if warehouse_with_no_account: if warehouse_with_no_account:
for wh in warehouse_with_no_account: for wh in warehouse_with_no_account:
if frappe.db.get_value("Warehouse", wh, "company"): if frappe.db.get_value("Warehouse", wh, "company"):
frappe.throw(_("Warehouse {0} is not linked to any account, please create/link the corresponding (Asset) account for the warehouse.").format(wh)) frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account))
return process_gl_map(gl_list) return process_gl_map(gl_list)
def update_stock_ledger_entries(self, sle): def update_stock_ledger_entries(self, sle):
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
self.doctype, self.name, currency=self.company_currency) self.doctype, self.name, currency=self.company_currency, company=self.company)
sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate) sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate) sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)
@ -341,7 +339,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no)) where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account: if not warehouse_account:
warehouse_account = get_warehouse_account() warehouse_account = get_warehouse_account_map()
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items) future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date) gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
@ -406,21 +404,3 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d) gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries return gl_entries
def get_warehouse_account():
if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict()
for d in frappe.db.sql("""select
warehouse, name, account_currency
from
tabAccount
where
account_type = 'Stock'
and (warehouse is not null and warehouse != '')
and is_group=0 """, as_dict=1):
warehouse_account.setdefault(d.warehouse, d)
frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map

View File

@ -13,21 +13,22 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
onload: function() { onload: function() {
if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) { if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) { cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) {
return { query:"frappe.core.doctype.user.user.user_query" } } return { query: "frappe.core.doctype.user.user.user_query" }
}
} }
if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) { if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) { cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) {
return { query:"frappe.core.doctype.user.user.user_query" } } return { query: "frappe.core.doctype.user.user.user_query" } }
} }
}, },
refresh: function() { refresh: function() {
var doc = this.frm.doc; var doc = this.frm.doc;
erpnext.toggle_naming_series(); erpnext.toggle_naming_series();
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'name', doctype: 'Lead'} frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
if(!this.frm.doc.__islocal && this.frm.doc.__onload && !this.frm.doc.__onload.is_customer) { if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make")); this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make")); this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make"));
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make")); this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make"));
@ -35,9 +36,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
} }
if(!this.frm.doc.__islocal) { if(!this.frm.doc.__islocal) {
frappe.geo.render_address_and_contact(cur_frm); frappe.contacts.render_address_and_contact(cur_frm);
} else { } else {
frappe.geo.clear_address_and_contact(cur_frm); frappe.contacts.clear_address_and_contact(cur_frm);
} }
}, },

View File

@ -76,6 +76,36 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salutation",
"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": "Salutation",
"length": 0,
"no_copy": 0,
"options": "Salutation",
"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_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -111,21 +141,21 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "company_name", "fieldname": "gender",
"fieldtype": "Data", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 1, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Organization Name", "label": "Gender",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"oldfieldname": "company_name", "options": "Gender",
"oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
@ -265,6 +295,36 @@
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_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": "Organization Name",
"length": 0,
"no_copy": 0,
"oldfieldname": "company_name",
"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": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -1088,6 +1148,7 @@
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Lead", "name": "Lead",
"name_case": "Title Case",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {

View File

@ -4,11 +4,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import (cstr, validate_email_add, cint, comma_and, has_gravatar, now) from frappe.utils import (cstr, validate_email_add, cint, comma_and, has_gravatar, now, getdate, nowdate)
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
from frappe.geo.address_and_contact import load_address_and_contact from frappe.contacts.address_and_contact import load_address_and_contact
from erpnext.accounts.party import set_taxes from erpnext.accounts.party import set_taxes
sender_field = "email_id" sender_field = "email_id"
@ -46,7 +46,7 @@ class Lead(SellingController):
if self.is_new() or not self.image: if self.is_new() or not self.image:
self.image = has_gravatar(self.email_id) self.image = has_gravatar(self.email_id)
if self.contact_date and self.contact_date < now(): if self.contact_date and getdate(self.contact_date) < getdate(nowdate()):
frappe.throw(_("Next Contact Date cannot be in the past")) frappe.throw(_("Next Contact Date cannot be in the past"))
def on_update(self): def on_update(self):

View File

@ -122,7 +122,8 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
$.each([["lead", "lead"], $.each([["lead", "lead"],
["customer", "customer"], ["customer", "customer"],
["contact_person", "contact_query"]], function(i, opts) { ["contact_person", "contact_query"]],
function(i, opts) {
me.frm.set_query(opts[0], erpnext.queries[opts[1]]); me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
}); });
}, },
@ -180,7 +181,7 @@ cur_frm.cscript['Declare Opportunity Lost'] = function() {
}); });
dialog.fields_dict.update.$input.click(function() { dialog.fields_dict.update.$input.click(function() {
args = dialog.get_values(); var args = dialog.get_values();
if(!args) return; if(!args) return;
return cur_frm.call({ return cur_frm.call({
doc: cur_frm.doc, doc: cur_frm.doc,
@ -188,7 +189,7 @@ cur_frm.cscript['Declare Opportunity Lost'] = function() {
args: args.reason, args: args.reason,
callback: function(r) { callback: function(r) {
if(r.exc) { if(r.exc) {
msgprint(__("There were errors.")); frappe.msgprint(__("There were errors."));
} else { } else {
dialog.hide(); dialog.hide();
cur_frm.refresh(); cur_frm.refresh();

View File

@ -15,5 +15,5 @@ frappe.query_reports["Campaign Efficiency"] = {
"default": frappe.defaults.get_user_default("year_end_date"), "default": frappe.defaults.get_user_default("year_end_date"),
} }
] ]
}; };

View File

@ -4,27 +4,27 @@
frappe.query_reports["Minutes to First Response for Opportunity"] = { frappe.query_reports["Minutes to First Response for Opportunity"] = {
"filters": [ "filters": [
{ {
"fieldname":"from_date", "fieldname": "from_date",
"label": __("From Date"), "label": __("From Date"),
"fieldtype": "Date", "fieldtype": "Date",
'reqd': 1, 'reqd': 1,
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30) "default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
}, },
{ {
"fieldname":"to_date", "fieldname": "to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
'reqd': 1, 'reqd': 1,
"default":frappe.datetime.nowdate() "default": frappe.datetime.nowdate()
}, },
], ],
get_chart_data: function(columns, result) { get_chart_data: function (columns, result) {
return { return {
data: { data: {
x: 'Date', x: 'Date',
columns: [ columns: [
['Date'].concat($.map(result, function(d) { return d[0]; })), ['Date'].concat($.map(result, function (d) { return d[0]; })),
['Mins to first response'].concat($.map(result, function(d) { return d[1]; })) ['Mins to first response'].concat($.map(result, function (d) { return d[1]; }))
] ]
// rows: [['Date', 'Mins to first response']].concat(result) // rows: [['Date', 'Mins to first response']].concat(result)
}, },

View File

@ -0,0 +1,18 @@
[
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Aptitude"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Application"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Understanding"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Knowledge"
}
]

View File

@ -0,0 +1,17 @@
[
{
"doctype": "Grading Scale",
"grading_scale_name": "Standard Grading",
"description": "Standard Grading Scale",
"intervals": [
{"threshold": 100.0, "grade_code": "A", "grade_description": "Excellent"},
{"threshold": 89.9, "grade_code": "B+", "grade_description": "Close to Excellence"},
{"threshold": 80.0, "grade_code": "B", "grade_description": "Good"},
{"threshold": 69.9, "grade_code": "C+", "grade_description": "Almost Good"},
{"threshold": 60.0, "grade_code": "C", "grade_description": "Average"},
{"threshold": 50.0, "grade_code": "D+", "grade_description": "Have to Work"},
{"threshold": 40.0, "grade_code": "D", "grade_description": "Not met Baseline Expectations"},
{"threshold": 0.0, "grade_code": "F", "grade_description": "Have to work a lot"}
]
}
]

View File

@ -57,6 +57,7 @@
}, },
{ {
"is_stock_item": 0, "is_stock_item": 0,
"default_warehouse": "Stores",
"description": "Computer", "description": "Computer",
"item_code": "Computer", "item_code": "Computer",
"item_name": "Computer", "item_name": "Computer",
@ -64,6 +65,7 @@
}, },
{ {
"is_stock_item": 0, "is_stock_item": 0,
"default_warehouse": "Stores",
"description": "Mobile", "description": "Mobile",
"item_code": "Mobile", "item_code": "Mobile",
"item_name": "Mobile", "item_name": "Mobile",
@ -71,6 +73,7 @@
}, },
{ {
"is_stock_item": 0, "is_stock_item": 0,
"default_warehouse": "Stores",
"description": "ERP", "description": "ERP",
"item_code": "ERP", "item_code": "ERP",
"item_name": "ERP", "item_name": "ERP",
@ -78,12 +81,14 @@
}, },
{ {
"is_stock_item": 0, "is_stock_item": 0,
"default_warehouse": "Stores",
"description": "Autocad", "description": "Autocad",
"item_code": "Autocad", "item_code": "Autocad",
"item_name": "Autocad", "item_name": "Autocad",
"item_group": "All Item Groups" "item_group": "All Item Groups"
}, },
{ {
"default_warehouse": "Stores",
"default_warehouse": "Stores", "default_warehouse": "Stores",
"item_code": "Service", "item_code": "Service",
"item_group": "Services", "item_group": "Services",

View File

@ -1,102 +1,46 @@
[ [
{ {
"doctype": "Program", "doctype": "Program",
"name": "MCA",
"program_name": "Masters of Computer Applications", "program_name": "Masters of Computer Applications",
"program_code": "MCA", "program_code": "MCA",
"department": "Information Technology", "department": "Information Technology",
"courses": [ "courses": [
{ { "course": "MCA4010" },
"course": "Microprocessor", { "course": "MCA4020" },
"academic_term": "Semester 1" { "course": "MCA4030" }
},
{
"course": "Probability and Statistics",
"academic_term": "Semester 1"
},
{
"course": "Programing in Java",
"academic_term": "Semester 2"
}
] ]
}, },
{ {
"doctype": "Program", "doctype": "Program",
"name": "BCA",
"program_name": "Bachelor of Computer Applications", "program_name": "Bachelor of Computer Applications",
"program_code": "BCA", "program_code": "BCA",
"department": "Information Technology", "department": "Information Technology",
"courses": [ "courses": [
{ { "course": "BCA2030" },
"course": "Communication Skiils", { "course": "BCA1030" },
"academic_term": "Semester 3" { "course": "BCA2020" },
}, { "course": "BCA1040" },
{ { "course": "BCA1010" },
"course": "Object Oriented Programing - C++", { "course": "BCA2010" },
"academic_term": "Semester 3" { "course": "BCA1020" }
},
{
"course": "Basic Mathematics",
"academic_term": "Semester 2"
},
{
"course": "Data Structures and Algorithm",
"academic_term": "Semester 2"
},
{
"course": "Digital Logic",
"academic_term": "Semester 2"
},
{
"course": "Fundamentals of IT & Programing",
"academic_term": "Semester 1"
},
{
"course": "Operating System",
"academic_term": "Semester 1"
},
{
"course": "Programing in C",
"academic_term": "Semester 1"
}
] ]
}, },
{ {
"doctype": "Program", "doctype": "Program",
"name": "BBA",
"program_name": "Bachelor of Business Administration", "program_name": "Bachelor of Business Administration",
"program_code": "BBA", "program_code": "BBA",
"department": "Management Studies", "department": "Management Studies",
"courses": [ "courses": [
{ { "course": "BBA 101" },
"course": "Organizational Behavior", { "course": "BBA 102" },
"academic_term": "Semester 1" { "course": "BBA 103" },
}, { "course": "BBA 301" },
{ { "course": "BBA 302" },
"course": "Management Development and Skills", { "course": "BBA 304" },
"academic_term": "Semester 1" { "course": "BBA 505" }
},
{
"course": "Legal and Regulatory Framework",
"academic_term": "Semester 1"
},
{
"course": "Human Resource Management",
"academic_term": "Semester 2"
},
{
"course": "Entrepreneurship Management",
"academic_term": "Semester 2"
},
{
"course": "Communication Skills",
"academic_term": "Semester 2"
},
{
"course": "Business Environment",
"academic_term": "Semester 3"
},
{
"course": "Advertising and Sales",
"academic_term": "Semester 3"
}
] ]
} }
] ]

View File

@ -0,0 +1,10 @@
[
{
"doctype": "Student Batch Name",
"batch_name": "Section-A"
},
{
"doctype": "Student Batch Name",
"batch_name": "Section-B"
}
]

View File

@ -22,7 +22,7 @@ bench --site demo.erpnext.dev execute erpnext.demo.demo.simulate
""" """
def make(domain='Manufacturing'): def make(domain='Manufacturing', days=100):
frappe.flags.domain = domain frappe.flags.domain = domain
frappe.flags.mute_emails = True frappe.flags.mute_emails = True
setup_data.setup(domain) setup_data.setup(domain)
@ -35,16 +35,18 @@ def make(domain='Manufacturing'):
frappe.destroy() frappe.destroy()
frappe.init(site) frappe.init(site)
frappe.connect() frappe.connect()
simulate(domain)
def simulate(domain='Manufacturing'): simulate(domain, days)
runs_for = frappe.flags.runs_for or 150
def simulate(domain='Manufacturing', days=100):
runs_for = frappe.flags.runs_for or days
frappe.flags.company = erpnext.get_default_company() frappe.flags.company = erpnext.get_default_company()
frappe.flags.mute_emails = True frappe.flags.mute_emails = True
if not frappe.flags.start_date: if not frappe.flags.start_date:
# start date = 100 days back # start date = 100 days back
frappe.flags.start_date = frappe.utils.add_days(frappe.utils.nowdate(), -1 * runs_for) frappe.flags.start_date = frappe.utils.add_days(frappe.utils.nowdate(),
-1 * runs_for)
current_date = frappe.utils.getdate(frappe.flags.start_date) current_date = frappe.utils.getdate(frappe.flags.start_date)
@ -73,7 +75,7 @@ def simulate(domain='Manufacturing'):
stock.work() stock.work()
accounts.work() accounts.work()
projects.run_projects(current_date) projects.run_projects(current_date)
#run_messages() # run_messages()
if domain=='Manufacturing': if domain=='Manufacturing':
sales.work() sales.work()

View File

@ -15,6 +15,7 @@ def setup_data():
make_student_group() make_student_group()
make_fees_category() make_fees_category()
make_fees_structure() make_fees_structure()
make_assessment_groups()
frappe.db.commit() frappe.db.commit()
frappe.clear_cache() frappe.clear_cache()
@ -24,6 +25,9 @@ def make_masters():
import_json("Instructor") import_json("Instructor")
import_json("Course") import_json("Course")
import_json("Program") import_json("Program")
import_json("Student Batch Name")
import_json("Assessment Criteria")
import_json("Grading Scale")
frappe.db.commit() frappe.db.commit()
def setup_item(): def setup_item():
@ -81,12 +85,23 @@ def make_student_applicants():
count+=1 count+=1
def make_student_group(): def make_student_group():
for d in frappe.db.get_list("Academic Term"): for term in frappe.db.get_list("Academic Term"):
for program in frappe.db.get_list("Program"):
sg_tool = frappe.new_doc("Student Group Creation Tool") sg_tool = frappe.new_doc("Student Group Creation Tool")
sg_tool.academic_year = "2016-17" sg_tool.academic_year = "2017-18"
sg_tool.academic_term = d.name sg_tool.academic_term = term.name
sg_tool.courses = sg_tool.get_courses() sg_tool.program = program.name
sg_tool.create_student_groups() for d in sg_tool.get_courses():
d = frappe._dict(d)
student_group = frappe.new_doc("Student Group")
student_group.student_group_name = d.student_group_name
student_group.group_based_on = d.group_based_on
student_group.program = program.name
student_group.course = d.course
student_group.batch = d.batch
student_group.academic_term = term.name
student_group.academic_year = "2017-18"
student_group.save()
frappe.db.commit() frappe.db.commit()
def make_fees_category(): def make_fees_category():
@ -111,7 +126,7 @@ def make_fees_category():
def make_fees_structure(): def make_fees_structure():
for d in frappe.db.get_list("Program"): for d in frappe.db.get_list("Program"):
program = frappe.get_doc("Program", d.name) program = frappe.get_doc("Program", d.name)
for academic_term in ["Semester 1", "Semester 2", "Semester 3"]: for academic_term in ["2017-18 (Semester 1)", "2017-18 (Semester 2)", "2017-18 (Semester 3)"]:
fee_structure = frappe.new_doc("Fee Structure") fee_structure = frappe.new_doc("Fee Structure")
fee_structure.program = d.name fee_structure.program = d.name
fee_structure.academic_term = random.choice(frappe.db.get_list("Academic Term")).name fee_structure.academic_term = random.choice(frappe.db.get_list("Academic Term")).name
@ -123,6 +138,27 @@ def make_fees_structure():
program.save() program.save()
frappe.db.commit() frappe.db.commit()
def make_assessment_groups():
for year in frappe.db.get_list("Academic Year"):
ag = frappe.new_doc('Assessment Group')
ag.assessment_group_name = year.name
ag.parent_assessment_group = "All Assessment Groups"
ag.is_group = 1
ag.insert()
for term in frappe.db.get_list("Academic Term", filters = {"academic_year": year.name}):
ag1 = frappe.new_doc('Assessment Group')
ag1.assessment_group_name = term.name
ag1.parent_assessment_group = ag.name
ag1.is_group = 1
ag1.insert()
for assessment_group in ['Term I', 'Term II']:
ag2 = frappe.new_doc('Assessment Group')
ag2.assessment_group_name = ag1.name + " " + assessment_group
ag2.parent_assessment_group = ag1.name
ag2.insert()
frappe.db.commit()
def get_json_path(doctype): def get_json_path(doctype):
return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json') return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json')

View File

@ -116,7 +116,7 @@ def setup_user():
for u in json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'user.json')).read()): for u in json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'user.json')).read()):
user = frappe.new_doc("User") user = frappe.new_doc("User")
user.update(u) user.update(u)
user.flags.no_welcome_mail user.flags.no_welcome_mail = True
user.new_password = 'demo' user.new_password = 'demo'
user.insert() user.insert()

View File

@ -8,30 +8,22 @@ import random
from frappe.utils import cstr from frappe.utils import cstr
from frappe.utils.make_random import get_random from frappe.utils.make_random import get_random
from datetime import timedelta from datetime import timedelta
from erpnext.schools.api import get_student_group_students, make_attendance_records, enroll_student, get_fee_schedule, collect_fees from erpnext.schools.api import get_student_group_students, make_attendance_records, enroll_student, \
get_fee_schedule, collect_fees, get_course
from erpnext.schools.doctype.program_enrollment.program_enrollment import get_program_courses
from erpnext.schools.doctype.student_group.student_group import get_students
def work(): def work():
frappe.set_user(frappe.db.get_global('demo_schools_user')) frappe.set_user(frappe.db.get_global('demo_schools_user'))
for d in xrange(20): for d in xrange(20):
approve_random_student_applicant() approve_random_student_applicant()
enroll_random_student(frappe.flags.current_date) enroll_random_student(frappe.flags.current_date)
if frappe.flags.current_date.weekday()== 0: # if frappe.flags.current_date.weekday()== 0:
make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5)) # make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5))
mark_student_attendance(frappe.flags.current_date) mark_student_attendance(frappe.flags.current_date)
# make_assessment_plan()
make_fees() make_fees()
def mark_student_attendance(current_date):
status = ["Present", "Absent"]
for d in frappe.db.get_list("Course Schedule", filters={"schedule_date": current_date}, fields=("name", "student_group")):
students = get_student_group_students(d.student_group)
for stud in students:
make_attendance_records(stud.student, stud.student_name, d.name, status[weighted_choice([9,4])])
def make_fees():
for d in range(1,10):
random_fee = get_random("Fees", {"paid_amount": 0})
collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount"))
def approve_random_student_applicant(): def approve_random_student_applicant():
random_student = get_random("Student Applicant", {"application_status": "Applied"}) random_student = get_random("Student Applicant", {"application_status": "Applied"})
if random_student: if random_student:
@ -39,31 +31,59 @@ def approve_random_student_applicant():
frappe.db.set_value("Student Applicant", random_student, "application_status", status[weighted_choice([9,3])]) frappe.db.set_value("Student Applicant", random_student, "application_status", status[weighted_choice([9,3])])
def enroll_random_student(current_date): def enroll_random_student(current_date):
batch = ["Section-A", "Section-B"]
random_student = get_random("Student Applicant", {"application_status": "Approved"}) random_student = get_random("Student Applicant", {"application_status": "Approved"})
if random_student: if random_student:
enrollment = enroll_student(random_student) enrollment = enroll_student(random_student)
enrollment.academic_year = get_random("Academic Year") enrollment.academic_year = get_random("Academic Year")
enrollment.enrollment_date = current_date enrollment.enrollment_date = current_date
enrollment.student_batch_name = batch[weighted_choice([9,3])]
fee_schedule = get_fee_schedule(enrollment.program) fee_schedule = get_fee_schedule(enrollment.program)
for fee in fee_schedule: for fee in fee_schedule:
enrollment.append("fees", fee) enrollment.append("fees", fee)
enrolled_courses = get_course(enrollment.program)
for course in enrolled_courses:
enrollment.append("courses", course)
enrollment.submit() enrollment.submit()
frappe.db.commit() frappe.db.commit()
assign_student_group(enrollment.student, enrollment.program) assign_student_group(enrollment.student, enrollment.student_name, enrollment.program,
enrolled_courses, enrollment.student_batch_name)
def assign_student_group(student, program): def assign_student_group(student, student_name, program, courses, batch):
courses = [] course_list = [d["course"] for d in courses]
for d in frappe.get_list("Program Course", fields=("course"), filters={"parent": program }): for d in frappe.get_list("Student Group", fields=("name"), filters={"program": program, "course":("in", course_list)}):
courses.append(d.course) student_group = frappe.get_doc("Student Group", d.name)
student_group.append("students", {"student": student, "student_name": student_name,
for d in xrange(3): "group_roll_number":len(student_group.students)+1, "active":1})
course = random.choice(courses)
random_sg = get_random("Student Group", {"course": course})
if random_sg:
student_group = frappe.get_doc("Student Group", random_sg)
student_group.append("students", {"student": student})
student_group.save() student_group.save()
courses.remove(course) student_batch = frappe.get_list("Student Group", fields=("name"), filters={"program": program, "group_based_on":"Batch", "batch":batch})[0]
student_batch_doc = frappe.get_doc("Student Group", student_batch.name)
student_batch_doc.append("students", {"student": student, "student_name": student_name,
"group_roll_number":len(student_batch_doc.students)+1, "active":1})
student_batch_doc.save()
frappe.db.commit()
def mark_student_attendance(current_date):
status = ["Present", "Absent"]
for d in frappe.db.get_list("Student Group", filters={"group_based_on": "Batch"}):
students = get_student_group_students(d.name)
for stud in students:
make_attendance_records(stud.student, stud.student_name, status[weighted_choice([9,4])], None, d.name, current_date)
def make_fees():
for d in range(1,10):
random_fee = get_random("Fees", {"paid_amount": 0})
collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount"))
def make_assessment_plan(date):
for d in range(1,4):
random_group = get_random("Student Group", {"group_based_on": "Course"}, True)
doc = frappe.new_doc("Assessment Plan")
doc.student_group = random_group.name
doc.course = random_group.course
doc.assessment_group = get_random("Assessment Group", {"is_group": 0, "parent": "2017-18 (Semester 2)"})
doc.grading_scale = get_random("Grading Scale")
doc.maximum_assessment_score = 100
def make_course_schedule(start_date, end_date): def make_course_schedule(start_date, end_date):
for d in frappe.db.get_list("Student Group"): for d in frappe.db.get_list("Student Group"):

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Some files were not shown because too many files have changed in this diff Show More